[FastAPI] 4. SQLAlchemy + Alembic ์กฐํฉ์ ์ด์ฉํ Database Migration ๊ฐ์ด๋
์น ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๊ณ ๋ฐฐํฌํ ๋ค์ ๋ฐ๋์ ํ ๋ฒ ์ฏค ๋ฐ๋ฅด๋ ์ ๋ฐ์ดํธ ์ฌํญ์ด ๋ฐ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ์ ๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ง์ด๊ทธ๋ ์ด์ ํ๋ ๊ฒฝ์ฐ๋ ๋ณดํต ์๋์ 3๊ฐ์ง๊ฐ ๋ํ์ ์ ๋๋ค.
- ์ธ์คํด์ค์ ์ด์
ํด๋ผ์ฐ๋๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๋ค๋ฅธ ์ธ์คํด์ค, ํน์ ๋ค๋ฅธ ๋ฆฌ์ ์ผ๋ก์ ์ด์ - ํน์ Entity์ ๋ํ ๋ณ๊ฒฝ ์์ฒญ
์ฐ๋ฆฌ ๋ถ์ ํน์ ์ฐ๋ฆฌ ์๋น์ค์๋ ์ด ๊ธฐ๋ฅ์ ๋ฃ๊ธฐ ์ํด ์ด ์นผ๋ผ์ด ๋ ์ถ๊ฐ๋ก ํ์ํ ๊ฒ ๊ฐ์์. - ์ผ๋ถ ์์๋ ๋ฐ์ดํฐ๋ฅผ ํน์ ๋ฐ์ดํฐ๋ก ๋ณ๊ฒฝ
์ด์ ๋ ์ด ๋ช ์นญ์ ์ด ๋ช ์นญ์ผ๋ก ๋ฐ๊ฟ๊ฑฐ์์. ๋ชจ๋ ์ฌํญ์ ์ ์ฉ๋์์ผ๋ฉด ์ข๊ฒ ์ด์.
๊ฐ๋จํ๊ฒ ์๊ฐํ๋ค๋ฉด ์ด๋ฌํ ์์ ์ ์๋์ผ๋ก ์ฒ๋ฆฌํ ์๋ ์์ต๋๋ค. ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ์คํด์ค๋ฅผ ํ๋ ํ ์คํธ์ฉ์ผ๋ก ๋ง๋ค๊ณ , SQL ์ฟผ๋ฆฌ๋ฅผ ์ง๋ณธ ๋ค์ ์ฟผ๋ฆฌ ํ ์คํธํ๊ณ , ํ์คํ ์ฟผ๋ฆฌ๊ฐ ์์ฑ๋์๋ค๋ฉด ์ด๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ฐ๋ ๊ฒ์ด์ฃ .
๊ทธ๋ฐ๋ฐ, ์ด๋ฌํ ๋ง์ด๊ทธ๋ ์ด์ ์ด ํ๋ ๋ ์์ด๋ค ๋ณด๋ฉด ์ฒ์์๋ ์ด๋ป๊ฒ ์ฌ์ฉํ๊ณ , ์ด๋ ํ ์ ์ ๋ณํํ๋์ง์ ๋ํ ์ถ์ ์ด ์ด๋ ค์์ง๊ฒ ๋ฉ๋๋ค. ์ฐ๋ฆฌ ์๋น์ค๋ ์ฒ์์ ์ด๋ ๊ฒ ์ฌ์ฉํ๋ค๊ฐ ์ด๋ฌํ ์ ์ด ํ์ํ์ด์, ํน์ ์์ฒญ์ด ๋์ ๋ผ๋ ๊ฒ๋ค์ ๊ธฐ๋กํด์ผ ํ ์ ์์ต๋๋ค.
๋ ํ ๊ฐ์ง, ๊ฐ๋ฐ์๊ฐ Dev, Stage, Prod ์ด๋ ๊ฒ ๊ฐ์ ์๋น์ค๋ฅผ ์ฌ๋ฌ ๊ฐ์ ์ธ์คํด์ค ํ๊ฒฝ์ผ๋ก ๋๋์ด์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ด๋ค ํ๊ฒฝ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ์ด ์ด๋ฃจ์ด์ก๊ณ , ์ด๋ฃจ์ด์ง์ง ์์๋์ง ํ์ธ์ด ์ด๋ ต์ต๋๋ค. ํน์ฌ๋ผ๋ ์ด๋ฏธ ๋ง์ด๊ทธ๋ ์ด์ ์ด ์งํ๋ ์ธ์คํด์ค์ ๋ค์ ํ ๋ฒ ๋ง์ด๊ทธ๋ ์ด์ ์ด ์งํ๋๋ค๋ฉด ์ค๋ณต์ด ๋ฐ์ํ๊ณ , ์ด๋ก ์ธํ ๋ฐ์ดํฐ ๋ณํ์ ๋ํด ์ผ๋ถ ๋ฐ์ดํฐ ์์ค์ ๋ํ ์์ธ์ด ๋๊ธฐ๋ ํ๋ฉฐ ์คํ๋ ค ์์ฐ์ฑ์ ๋จ์ด๋จ๋ฆฌ๊ฒ ๋๊ฑฐ๋ ์ฌ๊ฐํ๊ฒ๋ ์๋น์ค์ ์ฅ์๊ฐ ์ฅ์ ๋ก๋ ์ด์ด์ง๊ฒ ๋ฉ๋๋ค.
Alembic
Python ๊ฐ๋ฐ ์คํ์์ alembic์ ์ด๋ฌํ DB ๋ง์ด๊ทธ๋ ์ด์ ๋ฒ์ ๊ด๋ฆฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. Spring ๊ฐ๋ฐ ์คํ์์ Flyway์ ๋์ผํ ์ญํ ์ ํ๋ ๋ ์์ ๋๋ค.
๋ชจ๋ ๋ง์ด๊ทธ๋ ์ด์ alembic ์คํฌ๋ฆฝํธ์ ์ํด ์ด๋ฃจ์ด์ง๋๋ค. ๋ฒ์ ๊ด๋ฆฌ๋ฅผ ์ํด ๊ฐ ์ธ์คํด์ค์์๋ migration ํ ์ด๋ธ์ด ์กด์ฌํ๊ณ , ์ด ํ ์ด๋ธ์ ํตํด ์ด๋๊น์ง ๋ง์ด๊ทธ๋ ์ด์ ์ด ๋์๋์ง๋ฅผ ํ์ธํ ์ ์์ผ๋ฉฐ ์ด๋ฅผ ํตํด ์ค๋ณต ๋ง์ด๊ทธ๋ ์ด์ ๋ฐ ๋ฒ์ ๊ด๋ฆฌ๋ฅผ ํ ์ ์์ต๋๋ค.
Flyway์ ๋ค๋ฅธ ์ ์ด ์๋ค๋ฉด Flyway๋ ์ด๋ฌํ ๋ณ๊ฒฝ ์ ๋ณด๋ฅผ ๊ฐ ํ ์ด๋ธ์ ๋ชจ๋ ๊ธฐ๋กํ๋ ๋ฐ๋ฉด, alembic์ ๋ง์ง๋ง์ ๋ง์ด๊ทธ๋ ์ด์ ๋ ๋ฒ์ ์ ํด์๊ฐ๋ง์ ๊ธฐ๋กํฉ๋๋ค.
Initialization alembic
alembic์ ์ฌ์ฉํ๊ธฐ ์ํด ๋ํ๋์๋ฅผ ์ค์นํ ๋ค์ ์๋์ ๋ช ๋ น์ด๋ฅผ ์ด์ฉํ์ฌ ํ๋ก์ ํธ ์ต์์์ ์ํ๋ ๋๋ ํฐ๋ฆฌ ์ด๋ฆ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํด๋๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
$ alembic init migrations
alembic init ๋ช ๋ น์ด๋ฅผ ์ด์ฉํด์ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๋ฅผ ์ด๊ธฐํํ๊ณ , ์ด๊ธฐํํ ์คํฌ๋ฆฝํธ์ ํ์ผ์ migrations ๋ผ๋ ์๋ก์ด ํด๋๋ฅผ ์์ฑํ์ฌ ๋ง๋ค์ด์ค๋๋ค.
๊ฐ ํ์ผ์ ๋ํด ๊ฐ๋ตํ ์ค๋ช ์ ๋๋ฆฌ์๋ฉด..
- versions
์ด ํด๋์ ๋ง์ด๊ทธ๋ ์ด์ ํ ์คํฌ๋ฆฝํธ ์ฝ๋๊ฐ ๋ค์ด๊ฐ๋๋ค. - env.py
๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ์ ์คํ๋๋ ์๋ฒ ์ฐ๊ฒฐ ๋ฐ ๋ง์ด๊ทธ๋ ์ด์ ์คํ ์ฝ๋์ ๋๋ค. - script.py.mako
๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ ํ ํ๋ฆฟ ํ์ผ์ ๋๋ค. - alembic.ini
env.py ํ์ผ์์ Configuration ํ์ผ๋ก ์ฌ์ฉ๋๋ alembic ์ค์ ํ์ผ์ ๋๋ค.
์ฌ๊ธฐ์ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํด์ผ ํ๋ ํ์ผ์ env.py์ versions ๋ ๊ฐ์ง๊ฐ ๋๋ฉฐ env.py ํ์ผ๋ก ํ ๋ฒ ํ๊ฒฝ์ ์ ํ ํ๊ณ ๋ ๋ค์๋ ๊ทธ ๋ค์๋ถํฐ๋ versions๋ฅผ ์ด์ฉํ์ฌ ๊ด๋ฆฌํ๊ฒ ๋ฉ๋๋ค.
Set database configuration
alembic.ini ํ์ผ์ ์ด๊ณ , sqlalchemy.url ๋ณ์์ ์ฐ๊ฒฐํ๊ณ ์ ํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ์คํด์ค ์ฃผ์๋ฅผ ์ ๋ ฅํด์ค๋๋ค.
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = postgressql://postgres:postgres@127.0.0.1/nkmemo
ini ํ์ผ์ด๊ธฐ ๋๋ฌธ์ Python์์ ์ ๊ณตํ๋ string ๊ด๋ จ ํจ์๋ฅผ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ์ค์ง ์ ์ ๋ฌธ์์ด๋ง์ ์ด์ฉํด์ผ ํฉ๋๋ค. ๊ทธ๋ฐ๋ฐ, ์ด๋ ๊ฒ ์ค์ ํ๊ฒ ๋๋ฉด ํ๊ฒฝ๋ณ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ง์ด๊ทธ๋ ์ด์ ํ ๋๋ง๋ค ์ธ์คํด์ค ์ฃผ์๋ฅผ ๋ฐ๊ฟ์ค์ผ ํ๋ ๋ถํธํจ์ด ์๊น๋๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์๋ alembic.ini ํ์ผ์ ์๋ sqlalchemy.url ๋ณ์๋ฅผ ์ ๊ฑฐํ ํ env.py ํ์ผ์์ sqlalchemy.url์ ์๋์ผ๋ก ์ค์ ํ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
config = context.config
if not config.get_main_option('sqlalchemy.url'):
config.set_main_option('sqlalchemy.url', 'postgresql://{username}:{password}@{host}:{port}/{db_name}'.format(
username='postgres', password='postgres', host='127.0.0.1', port='5432', db_name='nkmemo'
))
env.py ํ์ผ์๋ alembic์์ ์ ๊ณตํ๋ context๊ฐ ์๊ณ , ์ด context๋ฅผ ํตํด์ DB ์ธ์คํด์ค ์ฃผ์๋ฅผ ๊ฒฐ์ ํ ์ ์์ต๋๋ค. env.py ํ์ผ์ Python ์ฝ๋์ด๊ธฐ ๋๋ฌธ์ Python์์ ์ ๊ณตํ๋ ํจ์ ๋ฑ์ ์ด์ฉํ์ฌ ํ๊ฒฝ๋ณ๋ก DB ๋ง์ด๊ทธ๋ ์ด์ ์ ์ฝ๊ฒ ํ ์ ์์ต๋๋ค.
Generate Migration script
๊ทธ๋ผ ์ด์ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๋ฅผ ํ๋ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค. alembic์์ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ ํ ํ๋ฆฟ์ ์๋์ผ๋ก ๋ง๋ค์ด์ฃผ๋ ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ ์ด๊ธฐ ํ์ผ์ ๋ง๋ค์ด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
$ alembic revision -m "Initialize nkmemo entity.."
alembic revision ๋ช ๋ น์ด์ m ์ต์ ์ ์ฌ์ฉํ์ฌ ํด๋น ๋ง์ด๊ทธ๋ ์ด์ ์ ๋ํ ์ฝ๋ฉํธ๋ฅผ ๋จ๊ธฐ์ด ์ด ๋ง์ด๊ทธ๋ ์ด์ ์ด ์ด๋ค ๋ณ๊ฒฝ ์ฌํญ์ธ์ง๋ฅผ ์ฝ๊ฒ ํ์ ํ ์ ์์ต๋๋ค.
"""Initialize nkmemo entity..
Revision ID: 906254a28c71
Revises:
Create Date: 2021-01-16 13:10:10.770502
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '906254a28c71'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
pass
def downgrade():
pass
์ฝ๋๋ฅผ ํ์ธํ๋ฉด revision, down_revision์ด๋ผ๋ ๋ณ์๊ฐ ๋ณด์ ๋๋ค. ์ด๋ค ๋ณ์๋ฅผ ์ด์ฉํ์ฌ alembic์ ์ด ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๊ฐ ๋์๋์๋์ง๋ฅผ ํ์ธํ๊ฒ ๋ฉ๋๋ค.
upgrade ํจ์๋ ์ด ๋ง์ด๊ทธ๋ ์ด์ ์ ์ ์ฉํ์ ๋ ์ฌ์ฉํ ์คํฌ๋ฆฝํธ๊ฐ ๋ค์ด๊ฐ๋ฉฐ, downgrade๋ ๋ง์ด๊ทธ๋ ์ด์ ์ดํ ์ด๋ฅผ ๋ค์ ๋กค๋ฐฑํ ๊ฒฝ์ฐ ๋ฐ์ํ๋๋ฐ, ๊ตฌ์ฒด์ ์ผ๋ก๋ ์๋์ ๊ฐ์ด ๋์ํฉ๋๋ค.
$ alembic upgrade [revision_hash]
$ alembic downgrade [revision_hash or current_revision +- 1]
alembic upgrade ๋ช ๋ น์ด์ ํจ๊ป revision ํด์ฌ๋ฅผ ์ ๋ ฅํ๋ฉด ํด๋น hash์ ๋ง๋ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ์ upgrade ํจ์๋ฅผ ์์ฐจ์ ์ผ๋ก ํธ์ถํ์ฌ ๋ง์ด๊ทธ๋ ์ด์ ์ด ์งํ๋ฉ๋๋ค.
๋ฐ๋๋ก downgrade๋ ํ์ฌ์ revision์์ +- ์ซ์๋ฅผ ์กฐ์ ์ ๋ ฅํ์ฌ ๋กค๋ฐฑ๊ณผ ์ ๊ทธ๋ ์ด๋๋ฅผ ๋ฐ๋ณตํ ์ ์์ต๋๋ค.
๊ทธ๋ผ ์ง์ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๋ฅผ ์ง๋ณด๋๋ก ํ์ฃ .
"""Initialize nkmemo entity..
Revision ID: 906254a28c71
Revises:
Create Date: 2021-01-16 13:10:10.770502
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '906254a28c71'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
op.create_table('memos',
sa.Column('id', sa.String(length=120), nullable=False),
sa.Column('title', sa.String(length=80), nullable=False, index=True),
sa.Column('content', sa.Text, nullable=True),
sa.Column('is_favorite', sa.Boolean, nullable=False),
sa.PrimaryKeyConstraint('id'))
def downgrade():
op.drop_table('memos')
alembic์ op์์ ์ ๊ณตํ๋ create_table, drop_table ๋ฑ์ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์์ ์ฟผ๋ฆฌ๋ฅผ ์ ์ํ๊ณ , sqlalchemy์ Column, String ๋ฑ์ ์ฌ์ฉํ์ฌ ์ปฌ๋ผ์ ์ ๋ณด๋ฅผ ์ ์ํ ์ ์์ต๋๋ค.
$ alembic upgrade head
alembic upgrade์ head๋ฅผ ์ด์ฉํ๋ฉด ์ต์ ์ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๊น์ง๋ฅผ ์ ๋ถ ์ ์ฉํ๋ฉฐ ์ ์ฉ์ด ๋๋ฌ์ผ๋ฉด memos๋ผ๋ ํ ์ด๋ธ์ด ๋ง๋ค์ด์ ธ ์์์ ํ์ธํ ์ ์์ต๋๋ค.
Using auto generate
Alembic์์ ์ ๊ณตํ๋ autogenerate ์ต์ ์ SQLAlchemy์ ORM์ ์ด์ฉํ์ฌ ์ ์ํ Model์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ์๋์ผ๋ก ํ์ธํ์ฌ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํด์ฃผ๋ ์ข์ ์ต์ ์ ๋๋ค.
๊ทธ๋ฌ๋ ๋จ์ ์ด ์๋ค๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฑ์ด ์์๋ ์์ ๋ถํฐ autogenerate๋ฅผ ๊พธ์คํ ์ฌ์ฉํด์ผ ํ๋ฉฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ ์ค๊ฐ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ๋ณ๊ฒฝ์ ์ alembic์ ํ์ ํ์ง ๋ชปํ์ฌ ์คํ๋ ค ๋ฐฐ๋ณด๋ค ๋ฐฐ๊ผฝ์ด ๋ ํฐ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํ๊ธฐ๋ ํฉ๋๋ค.
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = Base.metadata
autogenerate ์ต์ ์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ๋จผ์ target_metadata ๋ณ์๋ฅผ ์ค์ ํด์ค๋๋ค. ์ด๊ธฐ๊ฐ์ด None์ผ๋ก ์ค์ ์ด ๋์ด ์๋๋ฐ, SQLAlchemy๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด SQLAlchemy์์ ์ ๊ณตํ๋ declarative_base ํจ์์์ ๋ฐํํ๋ metadata๋ฅผ ์ฌ์ฉํ์ค ์ ์์ต๋๋ค.
class Memo(Base):
__tablename__ = 'memos'
id = Column(String(120), primary_key=True, default=lambda: str(uuid.uuid4()))
title = Column(String(80), default='No title', nullable=False, index=True)
content = Column(Text, nullable=True)
is_favorite = Column(Boolean, nullable=False, default=False)
์ง๋๋ฒ์ ์์ฑํ Memo ๋ชจ๋ธ์ ๋ค์ ํ ๋ฒ ์ ์ฉํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
$ alembic revision --autogenerate -m "Initialize nkmemo entity.."
"""Initialize nkmemo entity..
Revision ID: 62040f1ba614
Revises:
Create Date: 2021-01-16 13:37:22.969248
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '62040f1ba614'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('memos',
sa.Column('id', sa.String(length=120), nullable=False),
sa.Column('title', sa.String(length=80), nullable=False),
sa.Column('content', sa.Text(), nullable=True),
sa.Column('is_favorite', sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_memos_title'), 'memos', ['title'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_memos_title'), table_name='memos')
op.drop_table('memos')
# ### end Alembic commands ###
๊ทธ๋ฌ๋ฉด ์ด๋ ๊ฒ ์๋์ผ๋ก ์์ฑ๋ memos ํ ์ด๋ธ์ ๋ง์ด๊ทธ๋ ์ด์ ์ฝ๋๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
Alembic version management
alembic์ด ์ด๋ค์์ผ๋ก ๋ฒ์ ์ ๊ด๋ฆฌํ๋์ง ์ ๊น ๋๋ฌ๋ณด๋๋ก ํ ๊น์?
๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๋ฅผ ์ต์ด๋ก ๋๋ฆฌ๋ฉด ์ด๋ ๊ฒ ํ ์ด๋ธ์ด ๋ง๋ค์ด์ง๊ณ , ๊ทธ์ ํจ๊ป alembic_version์ด๋ผ๋ ์ด๋ฆ์ ์๋ก์ด ํ ์ด๋ธ์ด ํ๋ ๋ ์์ฑ์ด ๋๋๋ฐ์.
์ด ๋ฒํธ๋ ์๊น ์ฐ๋ฆฌ๊ฐ ์์์ ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ revision ํด์ ๋ฒํธ์ ๋๋ค. ์ค์ ๋ก alembic์ ์ด ๋ฒํธ๋ฅผ ๋ณด๊ณ ์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ด๋๊น์ง ๋ง์ด๊ทธ๋ ์ด์ ๋์๋์ง๋ฅผ ํ์ธํฉ๋๋ค.
๊ทธ๋ฐ๋ฐ, ์๋น์ค๋ฅผ ์ด์ํ๋ ์คํค๋ง๊ณผ ๊ฐ์ ๊ณณ์ ๋ฒ์ ๊ด๋ฆฌ ํ ์ด๋ธ์ ๋๊ฒ ๋๋ ์ด๋ฅผ ๊ตฌ๋ถํ๊ธฐ๊ฐ ์ด๋ ต์ต๋๋ค. alembic์ ๋ฒ์ ๊ด๋ฆฌ ํ ์ด๋ธ์ ๋ค๋ฅธ ์คํค๋ง์์ ์ด์ํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น์?
ํด๋น ์ํ์์ ์คํค๋ง๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ ํ๋ค๋ฉด, alembic downgrade๋ฅผ ์งํํ ๋ค, env.py ํ์ผ์์ ์คํค๋ง๋ฅผ ์ง์ ํ์ฌ alembic์ ํ ์ด๋ธ์ ๋ณ๋๋ก ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata,
include_schema=True,
version_table_schema='alembic'
)
connection.execute('CREATE SCHEMA IF NOT EXISTS {schema}'.format(schema='alembic'))
with context.begin_transaction():
context.run_migrations()
์ ๋ ์คํค๋ง๋ฅผ alembic์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ์ฌ์ฉํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ๋ง์ฝ์ ๋๋นํ์ฌ alembic ์คํค๋ง๊ฐ ์์ ๊ฒฝ์ฐ, alembic ์คํค๋ง๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด ์ค ์ ์๋๋ก CREATE SCHEMA IF ์ฟผ๋ฆฌ๋ฅผ ์ฝ์ ํด์ฃผ๋๋ก ํฉ์๋ค.
๊ทธ๋ฌ๋ฉด ์ด๋ ๊ฒ ๊น๋ํ๊ฒ ์๋น์ค ์คํค๋ง์ ๋ง์ด๊ทธ๋ ์ด์ ๊ด๋ฆฌ ์คํค๋ง๊ฐ ๋ถ๋ฆฌ๋์ด, ์ข ๋ ๋ชจ๋ธ์ด ๋ณด๊ธฐ ์ฌ์์ง ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
๋ง์น๋ฉฐ...
์ฌ๊ธฐ๊น์ง alembic๊ณผ SQLAlchemy๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ์ ์ข ๋ ํจ์จ์ ์ผ๋ก ํธ๋ฆฌํ๊ฒ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ดค์ต๋๋ค.
์ด ๊ธ์ ์นดํ ๊ณ ๋ฆฌ ๋ฒ์ฃผ๊ฐ FastAPI๋ก ์ํด์ ธ ์๊ธด ํ์ง๋ง ๋ค๋ฅธ ํ๋ ์์ํฌ์์๋ ์ถฉ๋ถํ ์ด ๋ฐฉ๋ฒ์ ์์ฉํ ์ ์์ต๋๋ค. ๋ค๋ง Django๋ Flask์ ๊ฐ์ด ์ ์ฉ ORM์ด ์กด์ฌํ๋ ๊ฒฝ์ฐ์ ๋ํด์๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด์ผ ํ๊ณ , ์์ SQLAlchemy ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ง ์ด ๊ธ์ ๋ฐ๋ฅด์๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค.
๊ฐ์ธ์ ์ผ๋ก Flyway๋ฅผ ์ฌ์ฉํ์ ๋๋ณด๋ค ์ง๊ด์ ์ธ ๋ฉด์ด ์กฐ๊ธ ๋จ์ด์ง๋ ๋๋์ ๋ฐ์์ต๋๋ค. Flyway๋ ๋ง์ด๊ทธ๋ ์ด์ ํ ์คํฌ๋ฆฝํธ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ์ ๋ถ ๋ณด์ฌ์ค์ผ๋ก์จ ์ด๋ค ๋ง์ด๊ทธ๋ ์ด์ ์ด ์คํ๋์๊ณ , ์คํ๋์ง ์์๋์ง๊ฐ ํ ๋์ ๋ณด์ด๋ ํธ๋ฆฌํจ์ด ์์์ง๋ง alembic์ ๋จ์ํ ๋ง์ง๋ง์ ๋ง์ด๊ทธ๋ ์ด์ ๋ ๋ฒ์ ๋ง์ ๋ณด์ฌ์ฃผ๊ธฐ ๋๋ฌธ์ ์ผ์ผ์ด ๊ณผ๊ฑฐ์ ์ด๋ค ๋ง์ด๊ทธ๋ ์ด์ ์ด ์์๋์ง๋ฅผ ์ง์ ์ถ์ ํด์ผํ๋ ๋ถํธํจ์ ์ด๋ํ์ต๋๋ค.
๋ฐ๋ฉด์ Flyway๋ ๋ง์ด๊ทธ๋ ์ด์ ์ Java ์ฝ๋์ ๋ณํ์์ด ์ค์ง SQL ์ฝ๋๋ง์ ์์ฑํด์ผ ํ๋ ๋ถํธํจ์ด ์๊ณ , ์ฌ๋ฌ ๊ฐ์ Dialect๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ํด๋น Dialect๋ง๋ค ๋ง์ด๊ทธ๋ ์ด์ ์ฝ๋๋ฅผ ๋ณ๋๋ก ์์ฑํด์ผ ํ๋ ๋ฒ๊ฑฐ๋ก์์ ๊ฐ์ํด์ผ ํฉ๋๋ค. ๊ทธ๋ฌ๋ alembic์ ์ด๋ ์ ๋ ๋์ด๋ ์๋ ์ฟผ๋ฆฌ์ ๋ํด์๋ ํตํฉ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์๋๋ก Python script๋ฅผ ์ฌ์ฉํ๊ฒ ํด์ค๋ค๋ ์ ์์ ํธ๋ฆฌํจ์ ์ป์๋ค๋ ๊ฒ์ด ์ ์ ๊ฐ์ธ์ ์ธ ์๊ฒฌ์ด์์ต๋๋ค.
๋ง๋ถ์ฌ์, autogenerate ๊ธฐ๋ฅ์ด ์๋ฒฝํ์ง๋ ์์ง๋ง ๊ธฐ๋ณธ์ ์ธ ์ปฌ๋ผ ๋ณํ๋ index์ ์ถ๊ฐ ์ ๋ฌด์ ์ ํํ๊ฒ ํธ๋ํนํจ์ผ๋ก์จ ๋ง์ด๊ทธ๋ ์ด์ ์ ๋ค์ด๊ฐ๋ ๋น์ฉ์ ๋ง์ด ๋ฎ์ถฐ์ฃผ๋ ์ฅ์ ์ ๋ณผ ์ ์์๊ณ , ๊ฐ๋จํ ์ปค๋งจ๋ ์ฌ์ฉ์ผ๋ก ํ ํ๋ฆฟ ์ฝ๋๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด์ค๋ค๋ ์ ์ ๊ฐ๋ช ๊น์๋ค์.