Alert
์ด ๊ธ์ Claude Code์ ๋์์ ๋ฐ์ ์์ฑ๋์์ต๋๋ค
TL;DR
- Alembic์ SQLAlchemy ์ ์ฉ DB ๋ง์ด๊ทธ๋ ์ด์ ๋๊ตฌ
- ๋ชจ๋ธ ๋ณ๊ฒฝ ์ฌํญ์ ์๋ ๊ฐ์งํด์ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ ์์ฑ ๊ฐ๋ฅ
upgrade/downgrade๋ก ์คํค๋ง ๋ฒ์ ๊ด๋ฆฌ ๋ฐ ๋กค๋ฐฑ ์ง์- FastAPI + SQLAlchemy ์กฐํฉ์์ ์ฌ์ค์ ํ์ค
Alembic?
- SQLAlchemy ํ๋ก์ ํธ์ DB ์คํค๋ง๋ฅผ ๋ฒ์ ๊ด๋ฆฌํ๋ ๋ง์ด๊ทธ๋ ์ด์ ๋๊ตฌ
- Django์
makemigrations/migrate์ ๊ฐ์ ์ญํ- SQLAlchemy ์ ์์(Mike Bayer)๊ฐ ์ง์ ๊ฐ๋ฐ
- https://alembic.sqlalchemy.org/
1. ์ ํ์ํ๊ฐ
DB ์คํค๋ง๋ ๊ฐ๋ฐ ๊ณผ์ ์์ ๊ณ์ ๋ฐ๋๋ค. ํ ์ด๋ธ ์ถ๊ฐ, ์ปฌ๋ผ ๋ณ๊ฒฝ, ์ธ๋ฑ์ค ์์ฑ ๋ฑ์ ๋ณ๊ฒฝ์ ์๋์ผ๋ก SQL์ ์คํํด์ ๊ด๋ฆฌํ๋ฉด ๋ค์ ๋ฌธ์ ๊ฐ ์๊ธด๋ค.
- ํ์ ๊ฐ ์คํค๋ง ๋ถ์ผ์น
- ์ด๋ค ๋ณ๊ฒฝ์ด ์ธ์ ์ ์ฉ๋์๋์ง ์ถ์ ๋ถ๊ฐ
- ๋กค๋ฐฑ์ด ์ด๋ ค์
- ๋ก์ปฌ/์คํ ์ด์ง/ํ๋ก๋์ ํ๊ฒฝ ๊ฐ ์คํค๋ง ์ฐจ์ด
Alembic์ ์ด๋ฐ ๋ณ๊ฒฝ์ Python ์คํฌ๋ฆฝํธ๋ก ๊ด๋ฆฌํ๊ณ , git์ฒ๋ผ ๋ฒ์ ์ ์ถ์ ํ๋ค.
2. ์ค์น
pip install alembicSQLAlchemy๊ฐ ํจ๊ป ์ค์น๋๋ค. uv๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ:
uv add alembic3. ํ๋ก์ ํธ ์ด๊ธฐํ
ํ๋ก์ ํธ ๋ฃจํธ์์ alembic init ๋ช
๋ น์ผ๋ก ์ด๊ธฐํํ๋ค.
alembic init alembic์๋์ ๊ฐ์ ๊ตฌ์กฐ๊ฐ ์์ฑ๋๋ค.
project/
โโโ alembic.ini # ์ค์ ํ์ผ (DB ์ฐ๊ฒฐ ์ ๋ณด ๋ฑ)
โโโ alembic/
โ โโโ env.py # ๋ง์ด๊ทธ๋ ์ด์
์คํ ํ๊ฒฝ ์ค์
โ โโโ script.py.mako # ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ ํ
ํ๋ฆฟ
โ โโโ versions/ # ๋ง์ด๊ทธ๋ ์ด์
ํ์ผ ์ ์ฅ ๋๋ ํ ๋ฆฌ
โโโ models.py # SQLAlchemy ๋ชจ๋ธ (์ง์ ์์ฑ)
alembic.ini ์ค์
DB ์ฐ๊ฒฐ ๋ฌธ์์ด์ ์ค์ ํ๋ค.
# alembic.ini
sqlalchemy.url = postgresql://user:password@localhost/mydbํ๊ฒฝ๋ณ์ ์ฌ์ฉ
DB ๋น๋ฐ๋ฒํธ๋ฅผ ini ํ์ผ์ ์ง์ ๋ฃ์ง ๋ง๊ณ ,
env.py์์ ํ๊ฒฝ๋ณ์๋ก ์ฃผ์ ํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.# env.py import os config.set_main_option( "sqlalchemy.url", os.environ["DATABASE_URL"] )
4. ๋ชจ๋ธ๊ณผ ์ฐ๋ (autogenerate ์ค์ )
Alembic์ด SQLAlchemy ๋ชจ๋ธ ๋ณ๊ฒฝ์ ์๋ ๊ฐ์งํ๋ ค๋ฉด env.py์์ ๋ชจ๋ธ์ MetaData๋ฅผ ์ฐ๊ฒฐํด์ผ ํ๋ค.
# models.py
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id = mapped_column(Integer, primary_key=True)
name = mapped_column(String(100), nullable=False)
email = mapped_column(String(200), unique=True)# alembic/env.py
from models import Base
target_metadata = Base.metadata์ด ์ค์ ์ด ๋๋๋ฉด --autogenerate ์ต์
์ผ๋ก ๋ชจ๋ธ๊ณผ DB์ ์ฐจ์ด๋ฅผ ์๋ ๊ฐ์งํ ์ ์๋ค.
5. ๋ง์ด๊ทธ๋ ์ด์ ์์ฑ
์๋ ์์ฑ (autogenerate)
๋ชจ๋ธ ์ฝ๋์ ์ค์ DB๋ฅผ ๋น๊ตํด์ ์ฐจ์ด์ ์ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๋ก ๋ง๋ ๋ค.
alembic revision --autogenerate -m "add users table"์๋ ์์ฑ
๋น ๋ง์ด๊ทธ๋ ์ด์ ํ์ผ์ ๋ง๋ค๊ณ ์ง์ ๋ด์ฉ์ ์์ฑํ๋ค.
alembic revision -m "add custom index"์์ฑ๋ ํ์ผ ์์
versions/ ๋๋ ํ ๋ฆฌ์ ์๋์ ๊ฐ์ ํ์ผ์ด ์์ฑ๋๋ค.
"""add users table"""
# revision identifiers
revision = 'a1b2c3d4e5f6'
down_revision = None # ์ฒซ ๋ฒ์งธ ๋ง์ด๊ทธ๋ ์ด์
์ด๋ฉด None
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table(
'users',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String(100), nullable=False),
sa.Column('email', sa.String(200), unique=True),
)
def downgrade():
op.drop_table('users')upgrade(): ์คํค๋ง๋ฅผ ์์ผ๋ก ์ ์ฉํ๋ ๋ก์งdowngrade(): ๋กค๋ฐฑ ๋ก์งdown_revision: ์ด์ ๋ง์ด๊ทธ๋ ์ด์ ์ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ (linked list ๊ตฌ์กฐ)
6. ๋ง์ด๊ทธ๋ ์ด์ ์ ์ฉ
# ์ต์ ๋ฒ์ ์ผ๋ก ์ ์ฉ
alembic upgrade head
# ํน์ ๋ฆฌ๋น์ ์ผ๋ก ์ ์ฉ
alembic upgrade a1b2c3d4e5f6
# ํ ๋จ๊ณ๋ง ์ ์ฉ
alembic upgrade +17. ๋กค๋ฐฑ
# ํ ๋จ๊ณ ๋กค๋ฐฑ
alembic downgrade -1
# ํน์ ๋ฆฌ๋น์ ์ผ๋ก ๋กค๋ฐฑ
alembic downgrade a1b2c3d4e5f6
# ๋ชจ๋ ๋ง์ด๊ทธ๋ ์ด์
๋กค๋ฐฑ (์ด๊ธฐ ์ํ๋ก)
alembic downgrade base8. ์ํ ํ์ธ
# ํ์ฌ ์ ์ฉ๋ ๋ฆฌ๋น์ ํ์ธ
alembic current
# ์ ์ฒด ๋ง์ด๊ทธ๋ ์ด์
ํ์คํ ๋ฆฌ
alembic history
# ์์ธ ํ์คํ ๋ฆฌ
alembic history --verbose9. ์์ฃผ ์ฌ์ฉํ๋ op ํจ์
Alembic์ op ๋ชจ๋์ด ์ ๊ณตํ๋ ์ฃผ์ ์คํค๋ง ๋ณ๊ฒฝ ํจ์๋ค์ด๋ค.
from alembic import op
import sqlalchemy as sa
# ํ
์ด๋ธ ์์ฑ/์ญ์
op.create_table('posts', sa.Column('id', sa.Integer, primary_key=True))
op.drop_table('posts')
# ์ปฌ๋ผ ์ถ๊ฐ/์ญ์ /๋ณ๊ฒฝ
op.add_column('users', sa.Column('age', sa.Integer))
op.drop_column('users', 'age')
op.alter_column('users', 'name', type_=sa.String(200))
# ์ธ๋ฑ์ค ์์ฑ/์ญ์
op.create_index('ix_users_email', 'users', ['email'])
op.drop_index('ix_users_email')
# ์ธ๋ํค ์ถ๊ฐ
op.create_foreign_key('fk_post_user', 'posts', 'users', ['user_id'], ['id'])
# ์์ SQL ์คํ
op.execute("UPDATE users SET active = true")10. autogenerate๊ฐ ๊ฐ์งํ์ง ๋ชปํ๋ ๊ฒ
์ฃผ์
autogenerate๋ ๋ง๋ฅ์ด ์๋๋ค. ์๋ ํญ๋ชฉ์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์ ์ ์์ฑํด์ผ ํ๋ค.
- ํ ์ด๋ธ/์ปฌ๋ผ ์ด๋ฆ ๋ณ๊ฒฝ (์ญ์ + ์์ฑ์ผ๋ก ์ธ์)
- ๋ฐ์ดํฐ ๋ง์ด๊ทธ๋ ์ด์ (๊ธฐ์กด ๋ฐ์ดํฐ ๋ณํ)
- DB ํนํ ๊ธฐ๋ฅ (ํํฐ์ , ํธ๋ฆฌ๊ฑฐ, ์คํ ์ด๋ ํ๋ก์์ ๋ฑ)
CHECK์ ์ฝ ์กฐ๊ฑด ๋ณ๊ฒฝ
11. ์ค๋ฌด ํ
๋ง์ด๊ทธ๋ ์ด์ ํ์ผ์ ๋ฐ๋์ ์ปค๋ฐํ๋ค
versions/ ๋๋ ํ ๋ฆฌ์ ํ์ผ์ git์ ํฌํจํด์ผ ํ๋ค. ํ์๋ค์ด ๊ฐ์ ๋ง์ด๊ทธ๋ ์ด์
์ ์์๋๋ก ์ ์ฉํ ์ ์์ด์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
ํ๋ก๋์ ์ ์ฉ ์ ํ์ธ
# SQL๋ง ์ถ๋ ฅํ๊ณ ์ค์ ์ ์ฉํ์ง ์์
alembic upgrade head --sql์์ฑ๋ SQL์ ๋ฆฌ๋ทฐํ ๋ค ์ ์ฉํ๋ฉด ์์ ํ๋ค.
๋ธ๋์น ์ถฉ๋ ํด๊ฒฐ
์ฌ๋ฌ ํ์์ด ๋์์ ๋ง์ด๊ทธ๋ ์ด์
์ ๋ง๋ค๋ฉด down_revision์ด ์ถฉ๋ํ ์ ์๋ค. ์ด ๊ฒฝ์ฐ merge๋ก ํด๊ฒฐํ๋ค.
alembic merge -m "merge migrations" revision1 revision2