Alembic
Alembic은 DB Migration을 위한 도구입니다.
파이썬에서도 Alembic 라이브러리를 제공합니다.

현재 MSP Backend 프로젝트는 이미 models 디렉터리 안에서 파이썬 코드를 통해 DB 테이블 모델을 정의하고 있습니다. ( 기능, 테이블 관계에 따라 모델 코드 2개로 나눈 상태 )
또한 현재 테이블은 전부 이 Model 코드를 기반으로 생성되었습니다.
이때 테이블 구조가 바뀌는 상황이 발생한다면 Alembic을 사용하는 것이 효율적입니다.
만약 Alembic을 사용하지 않는다면 테이블 구조를 바꿔야 하는 상황에서
- 테이블을 삭제하고 모델 코드 수정 후 다시 생성
- SQL 쿼리를 통해 테이블 수정 후 다시 모델 코드 작업
위와 같은 방법들을 적용해야 합니다.
그러나 Alembic을 사용한다면 데이터베이스 변경 사항을 코드로 수정할 수 있게 됩니다.
Model 구현 → Migration 설정 ( Alembic ) → Schemas 구현
위와 같은 순서 및 구조로 코드를 작성한다면
쿼리 없이 파이썬 코드에서 DB를 전부 관리할 수 있습니다.
Migration 설정

우선 migrations 디렉터리를 생성합니다.
alembic 세팅 시 설치되는 파일 및 코드를 저장하는 공간입니다.
현재 MSP 프로젝트에서는 database 폴더에서 migrations까지 함께 관리합니다.
alembic init database/migrations
이후 프로젝트의 루트 디렉터리에서 해당 명령어를 실행합니다.

명령어가 성공적으로 실행되면 migrations 폴더 내에 파일들이 생성됩니다.

또한 루트 디렉터리에도 alembic 파일이 하나 생성됩니다.
alembic.ini

alembic 파일은 가장 핵심적인 설정 파일입니다.
해당 파일에서 주로 데이터베이스 연결 URL을 설정하지만 MSP 프로젝트에서는 config.py에서 데이터베이스 URL을 함께 설정하기 때문에 해당 부분은 주석 처리해야 합니다.
versions
Migration 파일들이 저장되는 디렉터리입니다.
Alembic은 새로운 Migration이 생성될 때마다 이 디렉터리 안에서 파일을 생성합니다.
env.py
환경 설정 파일입니다.
데이터베이스 연결을 관리하고 Migration 처리를 하는 파일입니다.
기존 코드에서 run_migrations_offine함수 정의 부분까지는 아래 코드로 변경해야 합니다.
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
from database.base import DATABASE_URL, Base
config = context.config
if config.config_file_name is not None:
fileConfig(config.config_file_name)
target_metadata = Base.metadata
config.set_main_option("sqlalchemy.url", DATABASE_URL)
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
#url = config.get_main_option("sqlalchemy.url")
context.configure(
url=DATABASE_URL,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
이렇게 되면 base 코드에서 작성한 DB URL로 접속하게 됩니다.
Migration 연동
alembic revision --autogenerate -m "Initial migration"
프로젝트의 루트 디렉터리에서 해당 명령어를 입력합니다.

명령어가 성공적으로 실행되면 versions 폴더에 파이썬 코드가 생성됩니다.
해당 코드는 실제 모델 코드를 인식하고 Alembic이 직접 SQL Alchemy 기능이 포함된 파이썬 코드를 생성해준 결과입니다.

데이터베이스에는 위처럼 alembic_version이라는 이름의 테이블이 자동 생성됩니다.
해당 테이블은 DB가 어떤 Migration 상태에 있는지 추적하기 위한 역할입니다.
테이블에는 Version Column 안에서 마지막 Migration 적용 버전을 저장합니다.
alembic upgrade head
이 명령어를 실행하면 생성된 versions의 코드 내용대로 DB Migration이 적용됩니다.
Alembic이 모델 코드를 잘못 인식했을 가능성을 고려해,
version에 새로운 파일을 생성하면서 함께 출력하는 LOG 내용들을 점검하고 적용해야 합니다.
테스트
class TestTable(Base):
__tablename__ = 'test_table'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), nullable=False)
임시로 모델 코드에 테스트 테이블 생성
alembic revision --autogenerate -m "test_migration"
해당 명령어 실행 후, test_migration의 생성에 관한 내용이 upgrade() 함수에 포함되는지 확인
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'fd08a37a472d'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('test_table',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_foreign_key(None, 'session', 'project', ['project_id'], ['project_id'])
정상적으로 설정이 완료되었다면 위와 같은 코드가 생성됩니다.
alembic upgrade head
이후 위 명령어를 적용했을 때 기존 테이블이 유지되고, Test 테이블이 생성되는지 확인합니다.
