Alembic 초기 설정
$ alembic init alembic
# alembic/env.py 수정
from app.models.base import Base
target_metadata = Base.metadata
# DB URL을 환경변수에서 로드
config.set_main_option("sqlalchemy.url",
os.environ["DATABASE_URL"])
마이그레이션 파일 명명 규칙
# 날짜 + 설명으로 명명 (자동 생성 해시보다 가독성 높음)
$ alembic revision --autogenerate -m "add_family_group_table"
$ alembic revision --autogenerate -m "add_uuid_to_children"
$ alembic revision --autogenerate -m "add_soft_delete_columns"
# 생성된 파일: alembic/versions/
# 20260101_001_add_family_group_table.py
# 20260105_002_add_uuid_to_children.py
autogenerate가 놓치는 것들
# autogenerate가 감지하지 못하는 변경사항
# 1. 인덱스 이름 변경
# 2. 함수/트리거
# 3. 체크 제약조건 (DB에 따라 다름)
# 4. ENUM 타입 변경 (PostgreSQL)
# 수동으로 추가 필요
def upgrade():
op.create_index(
"ix_schedules_child_id_start_time",
"schedules",
["child_id", "start_time"],
unique=False
)
def downgrade():
op.drop_index("ix_schedules_child_id_start_time", "schedules")
데이터 마이그레이션 패턴
# 스키마 변경 + 데이터 변환을 한 마이그레이션에
def upgrade():
# 1. 컬럼 추가 (nullable)
op.add_column("users",
sa.Column("family_group_id", sa.String(36), nullable=True))
# 2. 기존 데이터 채우기
conn = op.get_bind()
users = conn.execute(sa.text("SELECT id FROM users WHERE family_group_id IS NULL")).fetchall()
for user in users:
group_id = str(uuid.uuid4())
conn.execute(sa.text(
"INSERT INTO family_groups (id, created_at) VALUES (:id, NOW())"
), {"id": group_id})
conn.execute(sa.text(
"UPDATE users SET family_group_id = :gid WHERE id = :uid"
), {"gid": group_id, "uid": user[0]})
# 3. NOT NULL 제약 추가
op.alter_column("users", "family_group_id", nullable=False)
def downgrade():
op.drop_column("users", "family_group_id")
# family_groups 데이터는 삭제하지 않음 — 안전한 downgrade
프로덕션 배포 시 마이그레이션
# Kubernetes Job으로 배포 전 마이그레이션 실행
# k8s/migrate-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: ivent-migrate
spec:
template:
spec:
containers:
- name: migrate
image: ivent-backend:latest
command: ["alembic", "upgrade", "head"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: ivent-secrets
key: database-url
restartPolicy: Never
브랜치 충돌 방지
# CI에서 마이그레이션 브랜치 검사
$ alembic heads
# 결과가 1개여야 함 — 2개면 merge 필요
# GitHub Actions
- name: Check alembic heads
run: |
heads=$(alembic heads | wc -l)
if [ $heads -gt 1 ]; then
echo "Multiple alembic heads detected!"
exit 1
fi
교훈
데이터 마이그레이션은 스키마 변경과 함께 하나의 트랜잭션에서 처리해야 합니다. alembic heads를 CI에서 검사하면 브랜치 충돌을 조기에 발견할 수 있습니다. downgrade는 최대한 안전하게 — 데이터 삭제를 피하고 컬럼 제거만 합니다.