4 min read

파이썬과 DDD로 라이프 캘린더 만들기 #3 데이터베이스

지난 글에서는 캘린더 모델을 도메인 계층에 정의하고 캘린더 생성 동작을 표현했습니다. 이번 글에서는 생성한 객체의 영구적인 저장을 위한 데이터베이스와 관련 도구를 설정해 보겠습니다. 여기서는 데이터베이스로 PostgreSQL을, 파이썬 데이터 엑세스 도구로 SQLAlchemyAlembic을 사용하겠습니다.

데이터베이스와 관련 도구 설치

먼저 사용하는 OS에 맞게 PostgreSQL을 설치하고(e.g. brew install postgresql) lifecalendar라는 이름으로 데이터베이스를 하나 생성해 주세요. 그리고 아래 명령어로 파이썬 의존성을 설치하고 커밋해줍니다.

poetry add psycopg2 sqlalchemy alembic

스키마 선언

src/infra/db/schema.py 경로로 파일을 하나 만들고 다음과 같이 데이터베이스 테이블을 선언해 줍니다. SQLAlchemy나 PostgreSQL 같은 데이터베이스 코드들은 비즈니스 코드 그 자체라기보다는 구체적 기술에 해당합니다. 이런 코드는 대체로 '인프라 계층'이라는 곳에 위치시킵니다. '인프라 계층'에 대한 자세한 내용은 이 문서의 해당 섹션을 참고하시면 좋을 것입니다.

import datetime
from sqlalchemy import Column, Date, DateTime, Integer, MetaData, String, Table, Uuid

metadata = MetaData()

calendar = Table(
    "calendar",
    metadata,
    Column("id", Uuid, primary_key=True),
    Column("name", String(100)),
    Column("birthday", Date),
    Column("lifespan", Integer),
    Column("inserted_at", DateTime, default=datetime.datetime.utcnow),
)

대부분의 컬럼은 캘린더 모델의 속성을 그대로 옮겼습니다만, inserted_at 컬럼은 데이터베이스 전용으로 하나 추가했습니다. 여기까지 커밋합니다.

스키마 마이그레이션

실제로 테이블을 생성하기 위해 아래 명령어로 Alembic 프로젝트를 생성해줍니다. 여기까지도 커밋합니다.

alembic init alembic

생성된 폴더와 파일 중, alembic/env.py 파일을 열고 방금 만든 테이블의 메타데이터를 연결해줍니다.

...

from src.infra.db.schema import metadata

...

target_metadata = metadata

Alembic 프로젝트가 PostgreSQL에 접속할 수 있게 alembic.ini 파일에 접속 정보를 입력해 줍시다. 여기까지 커밋합니다.

sqlalchemy.url = postgresql://username@localhost/lifecalendar

다음 명령어로 Alembic 리비전을 하나 생성해 줍니다.

alembic revision --autogenerate -m 'create calendar table'

아마 alembic/versions 폴더에 어떤 해시 prefix와 함께 파일이 하나 생성되었을 것이고, 그 파일의 내용은 다음과 같을 것입니다.

"""create calendar table

Revision ID: e86a318afe2c
Revises: 
Create Date: 2023-09-16 15:54:44.985867

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'e86a318afe2c'
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('calendar',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('name', sa.String(length=100), nullable=True),
    sa.Column('birthday', sa.Date(), nullable=True),
    sa.Column('lifespan', sa.Integer(), nullable=True),
    sa.Column('inserted_at', sa.DateTime(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('calendar')
    # ### end Alembic commands ###

자동으로 생성된 리비전 파일에 문제가 없는지 확인하고, 생성된 리비전을 적용합시다.

alembic upgrade head

데이터베이스에 접속해서 calendar 테이블과 alembic_version 테이블이 잘 생성 되었는지 확인해줍니다. 여기까지 커밋합시다.

이제 데이터베이스와 그 도구들이 준비 되었으니 아까 못다한 '캘린더 생성 기능'을 다음 글에서 완성하러 가봅시다.