서비스를 운영하려다 보니 자연히 데이터베이스 스키마를 관리할 수 있는 마이그레이션 도구가 필요했다. 현재 백엔드에 Node.js를 사용하고 있기는 하지만 마이그레이션 도구도 Node.js일 필요는 없다고 생각했다. Node.js는 데이터베이스쪽이 좀 약하다고 생각하는 편인데 데이터베이스 라이브러리도 쓸만한 게 있고 드라이버도 꽤 안정적으로 개발되고 있지만, 역사나 인구가 적다 보니 마이그레이션 도구 같은 부분은 선택의 폭이 높지 않다. 그리고 마이그레이션 도구를 애플리케이션 내에서 자동으로 돌릴 게 아니라 별로도 실행할 것이므로 Node.js에 국한하지 않고 쓸만한 도구를 찾기 시작했다.
Alembic
주변 사람들이 어떤 마이그레이션 도구를 사용하는지 수소문해서 알게 된 도구가 Alembic이다. Alembic은 SQLAlchemy 기반의 마이그레이션 도구로 Python을 사용하지만, 별도로 사용할 수 있으므로 프로젝트가 꼭 파이썬이거나 SQLAlchemy를 사용할 필요는 없다. 문서도 잘 작성되어 있고 사용법이 간단해서 금새 익힐 수 있다.
설치는 pip install alembic
로 간단히 설치할 수 있다. 물론 Python이나 pip는 설치되어 있어야 한다. 만약 No module named psycopg2
라는 오류가 발생하면 sudo easy_install psycopg2
로 설치해야 한다.
초기화
설치하면 커맨드라인에서 alembic
명령어를 사용할 수 있고 다음 명령어로 초기화를 할 수 있다.
$ alembic init myproject
alembic으로 초기화를 하면 다음과 같은 파일들이 생성된다.
├── alembic.ini
└── myproject
├── README
├── env.py
├── env.pyc
├── script.py.mako
└── versions
alembic.ini
이 설정 파일이므로 여기서 데이터베이스 설정을 할 수 있다.
[alembic]
# path to migration scripts
script_location = myproject
sqlalchemy.url = driver://user:pass@localhost/dbname
...
위 파일에서 sqlalchemy.url
부분에 사용하는 데이터베이스를 지정하면 된다.
마이그레이션 관리
alembic은 리비전을 사용해서 데이터베이스 스키마를 관리한다.
$ alembic revision -m "create user table"
Generating /myproject/versions/2987e9c41e5e_create_user_table.py ... done
위처럼 새로운 리비전을 추가하면 versions
폴더 아래 새로운 마이그래이션 파일이 생기고 이 파일은 다음과 같이 생겼다.
"""create user table
Revision ID: 2987e9c41e5e
Revises:
Create Date: 2015-05-10 22:10:18.235580
"""
# revision identifiers, used by Alembic.
revision = '2987e9c41e5e'
down_revision = None
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
pass
def downgrade():
pass
upgrade()
는 마이그래이션 버전을 적용할 때 실행되고 downgrade()
는 적용한 마이그래이션을 취소할 때 실행된다. 처음에는 아무 동작도 하지 않게 pass
로 되어 있으므로 여기에 테이블 추가나 칼럼변경 같은 코드를 작성해야 한다.
def upgrade():
op.create_table(
'users',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String(50), nullable=False)
)
def downgrade():
op.drop_table('account')
alembic이 SQLAlchemy를 사용하므로 위처럼 sa
객체를 이용해서 마이그레이션 코드를 작성할 수 있다. 애플리케이션도 SQLAlchemy를 사용하고 있다면 이 마이그레이션 코드도 자동으로 생성하는 등의 장점이 있는 것 같은데 그렇지 않다면 직접 작성해야 한다. SQLAlchemy를 사용해 봤다면 쉽게 작성할 수 있지만, 나처럼 SQLAlchemy를 사용해 본 적이 없다면 SQLAlchemy로 코드를 작성하는 것이 좀 힘 들 수 있다.
def upgrade():
op.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name VARCHAR(50) NOT NULL
)
""")
def downgrade():
op.execute("""
DROP TABLE users
""")
위처럼 op.execute()
를 사용하면 SQL을 사용할 수 있으므로 SQLAlchemy에 익숙하지 않다면 차라리 이 방법이 더 편한다.
$ alembic upgrade head
INFO [alembic.migration] Context impl PostgresqlImpl.
INFO [alembic.migration] Will assume transactional DDL.
INFO [alembic.migration] Running upgrade -> 2987e9c41e5e, create user table
이제 alembic upgrade head
을 실행하면 위처럼 upgrade()
함수가 실행되어 users
테이블이 생성된다. 데이터베이스에는 alembic_version
라는 테이블이 생성되고 여기서 현재 적용된 리비전을 관리하게 된다.
$ alembic revision -m "add age column"
Generating /myproject/versions/4c32125f4f5a_add_age_column.py ... done
4c32125f4f5a_add_age_column.py
파일을 다음과 같이 수정해서 스키마 변경을 계속 관리할 수 있다.
"""add age column
Revision ID: 4c32125f4f5a
Revises: 40686f5279e2
Create Date: 2015-05-11 00:20:47.794986
"""
# revision identifiers, used by Alembic.
revision = '4c32125f4f5a'
down_revision = '40686f5279e2'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
def upgrade():
op.execute("""
ALTER TABLE users
ADD age INTEGER
""")
def downgrade():
op.execute("""
ALTER TABLE users
DROP COLUMN age
""")
이 파일을 보면 아까 추가한 리비전이 지정되어 있음을 알 수 있고 여기서는 users
테이블에 새로운 칼럼을 추가했다.
$ alembic upgrade head
INFO [alembic.migration] Context impl PostgresqlImpl.
INFO [alembic.migration] Will assume transactional DDL.
INFO [alembic.migration] Running upgrade 40686f5279e2 -> 4c32125f4f5a, add age column
이런 식으로 스키마에 변경사항이 생길 때마다 마이그레이션 파일을 추가하고 적용할 수 있으며 개발 데이터베이스나 프로덕션 데이터베이스에서도 쉽게 적용하고 관리할 수 있다.
$ alembic history
40686f5279e2 -> 4c32125f4f5a (head), add age column
<base> -> 40686f5279e2, create user table
alembic history
명령어를 사용하면 스키마 히스토리도 확인할 수 있다. 러닝 커브도 높지 않고 간단히 사용할 수 있어서 현재 만족하며 사용 중이다.
Comments