[FastAPI] [Chapter 11] 데이터베이스 연결과 DB 모델
- -
이번 포스팅에서는 데이터베이스로 MySQL의 Docker 컨테이너를 설정해, ToDo 앱에서 데이터베이스에 접속하는 방법을 알아본다.
01 MySQL 컨테이너 실행
Docker 컨테이너로 MySQL을 실행해 앱에서 접속해 본다.
demo-app과 함께 demo라는 이름의 데이터베이스를 가진 db 서비스를 추가한다.
docker-compose.yaml
services:
demo-app:
build: .
volumes:
- .dockervenv:/src/.venv
- .:/src
ports:
- 8000:8000 # 호스트 머신의 8000번 포트를 docker의 8000번 포트에 연결
environment:
- WATCHFILES_FORCE_POLLING=true # 환경에 따라 핫 리로드를 위해 필요함
db:
image: mysql:8.0
platform: linux/x86_64
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
MYSQL_DATABASE: 'demo'
TZ: 'Asia/Seoul'
volumes:
- mysql_data:/var/lib/mysql # mysql_data라는 볼륨을 MySQL 데이터베이스 경로에 마운트
command: --default-authentication-plugin=mysql_native_password
ports:
- 33306:3306
volumes:
mysql_data: # 이 부분은 전역적으로 정의된 볼륨을 의미
ToDo 앱과 MySQL이 다음과 같이 동시에 실행된다.
> sudo docker compose up
컨테이너 내 MySQL 데이터베이스에 접근할 수 있는지 확인해보자.
docker compose up이 실행된 상태에서 다른 터미널을 열고, 프로젝트 디렉터리에서 docker compose exec db mysql demo를 실행한다.
다음처럼 MySQL 클라이언트가 실행되고 DB에 접속된 것을 확인할 수 있다.
> sudo docker compose exec db mysql demo
02 앱에서 DB에 접속하기 위한 준비
FastAPI 앱에서 MySQL에 접속하기 위한 준비를 해보자.
MySQL 클라이언트 설치
FastAPI에서는 MySQL과의 연결을 위해 sqlalchemy라는 ORM (Object-Relational Mapper) 라이브러리를 사용한다.
ORM은 객체지향 프로그래밍과 데이터베이스 간의 연결을 쉽게 해주는 기술이다.
이를 통해 데이터베이스의 데이터를 객체로 다루고, 객체를 데이터베이스에 저장하거나 조회할 수 있다.
sqlalchemy는 파이썬에서는 상당히 대중적인 라이브러리로, Flask 등 다른 웹 프레임워크에서도 사용된다.
ORM이란?
ORM은 파이썬 객체를 MySQL과 같은 관계형 데이터베이스 (RDBMS)의 데이터 구조로 변환한다.
MySQL의 경우 테이블 구조를 클래스로 정의하면 이를 읽거나 저장하는 SQL문을 발행해 준다.
FastAPI에서는 Peewee라는 ORM도 지원한다.
sqlalchemy는 백엔드에 다양한 데이터베이스를 이용할 수 있다.
이번에 MySQL 클라이언트로 pymysql도 함께 설치한다.
demo-app이 실행된 상태에서 poetry add를 실행하여 두 의존성 패키지를 설치한다.
> sudo docker compose exec demo-app poetry add sqlalchemy pymysql
설치하면 pyproject.toml과 poetry.lock의 내용의 변경된 것을 확인할 수 있다.
pyproject.toml
[tool.poetry]
name = "demo-app"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.11"
fastapi = "^0.115.2"
uvicorn = {extras = ["standard"], version = "^0.32.0"}
sqlalchemy = "^2.0.36"
pymysql = "^1.1.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
DB 연결 함수
api/db.py를 추가한다.
api/db.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
DB_URL = "mysql+pymysql://root@db:3306/demo?charset=utf8"
db_engine = create_engine(DB_URL, echo=True)
db_session = sessionmaker(autocommit=False, autoflush=False, bind=db_engine)
Base = declarative_base()
def get_db():
with db_session() as session:
yield session
DB_URL에 정의한 MySQL의 Docker 컨테이너에 접속할 세션을 생성
라우터에서는 get_db() 함수로 이 세션을 가져와 DB에 접근할 수 있도록 한다.
03 SQLAlchemy의 DB 모델 정의
FastAPI에 DB 모델을 정의한다.
ToDo 앱을 위해 아래 2개의 표를 정의한다.
tasks 테이블 정의
컬럼명 | Type | 비고 |
id | INT | primary, auto increment |
title | VARCHAR (1024) |
dones 테이블 정의
컬럼명 | Type | 비고 |
id | INT | primary, auto increment, foreign key (task.id) |
Tasks의 레코드는 작업 하나하나에 대응하며, dones는 Tasks 중 완료된 작업만 해당 Task와 동일한 id의 레코드를 가진다. 여기서 Tasks의 id와 dones의 id는 1:1 매핑으로 설정되어 있다.
보통 1:1 매핑의 경우 정구화 측면에서 하나의 테이블로 하는 경우가 많지만, 해당 책에서는 Task와 done의 리소스를 명확하게 분리하여, 이해하기 쉽도록 별도의 테이블로 정의하였다.
api/models/task.py를 작성하자
api/models/task.py
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from api.db import Base
class Task(Base):
__tablename__ = "tasks"
# Column은 테이블의 각 컬럼을 나탄냄
id = Column(Integer, primary_key=True)
title = Column(String(1024))
# 테이블간의 관계를 정의 : Task 객체에서 Done 객체를 참조하거나 그 반대도 가능
done = relationship("Done", back_populates="task", cascade="delete")
class Done(Base):
__tablename__ = "dones"
id = Column(Integer, ForeignKey("tasks.id"), primary_key=True)
task = relationship("Task", back_populates="done")
Column은 테이블의 각 컬럼을 나타낸다.
첫번째 인수는 컬럼의 타입을 전달한다. (Integer 또는 String)
그리고 두번째 인수 이후에 컬럼의 설정을 작성한다.
위의 primary_key=True나 ForeignKey("tasks.id") 외에도, Null 제약 조건 (nullable=False) 등 지원
relationship은 테이블 (모델 클래스) 간의 관계를 정의한다.
이를 통해 Task 객체에서 Done 객체를 참조하거나 그 반대도 가능
cascade="delete"를 지정하면
DELETE /tasks/{task_id} 인터페이스에서 Task를 삭제할 때, 외부 키에 지정된 동일한 id의 done이 있으면 자동으로 삭제된다.
DB 마이그레이션
작성한 ORM 모델을 바탕으로 DB에 테이블을 생성하고, DB 마이그레이션 용 스크립트를 작성한다.
api/migrate_db.py
from sqlalchemy import create_engine
from api.models.task import Base
DB_URL = "mysql+pymysql://root@db:3306/demo?charset=utf8"
engine = create_engine(DB_URL, echo=True)
def reset_database():
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
if __name__=="__main__":
reset_database()
아래 스크립트를 실행하여 Docker 컨테이너의 MySQL에 테이블을 작성한다.
이미 같은 이름의 테이블이 있는 경우 삭제한 후 재작성된다.
> sudo docker compose exec demo-app poetry run python -m api.migrate_db
이것으로 DB에 테이블이 생성되었다.
DB 테이블 확인
실제로 테이블이 생성되었는지 확인해보자.
docker compose up으로 컨테이너가 실행된 상태에서 MySQL 클라이언트를 실행한다.
> sudo docker compose exec db mysql demo
다음 포스팅에서 DB의 쓰기와 읽기 처리를 작성하고
API와 연결해본다.
'이것저것 개발노트' 카테고리의 다른 글
FastAPI 웹 배포 AWS 고정 IP, NginX (0) | 2024.10.25 |
---|---|
[FastAPI] [Chapter 12] DB 조작 (CRUDs) (0) | 2024.10.18 |
[FastAPI] [Chapter 10] 스키마 - 요청 (0) | 2024.10.17 |
[FastAPI] [Chapter 9] 스키마 - 응답 (0) | 2024.10.17 |
[FastAPI] [Chapter 8] 라우터 (2) | 2024.10.17 |
소중한 공감 감사합니다