FastAPI로 간단한 API 개발을 시도해 본다 - (3) 데이터소스 연동하기

FastAPI를 이용한 간단한 API 개발 - 데이터소스 연동하기

이번 포스팅에서는 실제 데이터소스를 연동하여 FastAPI를 통해 CRUD 작업을 진행할 수 있도록 개발해 본다.

지난 포스팅까지 살펴본 과정은 FastAPI가 제공하는 기능에 강한 종속성이 걸리는 과정이었다면, 지금부터는 그렇지 않다. 만약 이미 익숙해진 Python의 데이터소스 관련 기술이 있다면, 그것을 활용하면 된다. 또한 이 포스팅에선 MySQL 기반의 개발을 진행하겠지만, 경우에 따라 NoSQL이나 기타 다른 데이터소스의 연동을 진행할 필요가 얼마든지 생길 것이다. 그렇다면 그에 맞춰 유연히 프로젝트를 진행하면 된다.

FastAPI가 공식 문서에서 권장하는 관계형 데이터베이스 연동 방법 은 SQLAlchemy를 이용한 연동이다. SQLAlchemy는 Python 진영에서 널리 쓰이는 ORM 프레임워크(라고 알고있)다.

이 포스팅에서도 관계형 데이터베이스를 SQLAlchemy를 통해 연동하는 방법에 대해 살펴보도록 한다.

필요 종속성 추가

아래 명령을 수행해 필요한 종속성을 추가해준다.

1
2
pip install SQLAlchemy
pip install mysql

설정 클래스 작성

여느 ORM 프레임워크가 그렇듯이 설정 클래스의 작성부터 시작해야 한다.

SQLAlchemy를 통해 DB 커넥션이 추상화되어 있고, 추상화된 커넥션을 주입받아 앞으로의 요청을 처리하게 될 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

DB_URL = {DB_HOST}

engine = create_engine(DB_URL, encoding='utf-8')

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()


def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

Base 객체는 앞으로 모델 클래스를 작성할 때 상속받을 베이스 모델이 된다. 또한, get_db() 는 요청을 처리하는 곳에서 주입받아 DB와의 커넥션을 획득하는 메소드가 될 것이다.

모델 클래스 작성

설정이 완료되었다면, 모델 클래스의 작성을 시작한다. 보통의 ORM 프레임워크처럼 DB에 기술된 칼럼 정보를 사용하고 있는 언어의 객체로 옮겨오는 작업이다.

칼럼 타입과 매핑 정보를 ORM 프레임워크의 문법에 맞게 작성해주면 된다. 이때, 좀전의 설정 클래스에서 선언했던 Base 클래스를 불러와 상속받는다는 개념을 잊으면 안 된다.

1
2
3
4
5
6
7
8
9
10
11
12
from sqlalchemy import Column, Integer, String, Float, Boolean

from . import database


class User(database.Base):
__tablename__ = "user"

id = Column(Integer, primary_key=True, index=True)
username = Column(String, nullable=False)
password = Column(String, nullable=False)
nickname = Column(String, nullable=False)

Base 를 상속받아 간단한 유저 클래스를 만들어 보았다. 앞서 언급했던 것처럼 DB에 기술된 칼럼 타입과, 클래스가 가질 속성을 매핑해주는 작업이라고 보면 된다.

놀랍게도 여기까지 진행했다면, 실제 요청의 처리에서 DB를 연동할 준비는 사실상 모두 끝난 것이다.

FastAPI와의 연동

여기까지 잘 진행했다면, FastAPI의 요청 처리부와 데이터베이스 관련 클래스들을 잘 연동시켜주면 된다.

데이터베이스 설정 클래스를 작성할때, get_db() 함수를 이용해 데이터베이스 커넥션이 수립될 것이라고 언급했다. FastAPI가 제공하는 Depends 를 이용해 이 함수의 결과값을 종속성 주입으로 주입받고, 요청이 들어올 때마다 데이터베이스를 연결하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from fastapi import FastAPI, Depends, Path, HTTPException
from sqlalchemy.orm import Session
from models import models, database


@app.get(path="/api/v1/users/{user_id}")
def get_place(
place_id: int,
db: Session = Depends(database.get_db)):
result = db.query(models.User).filter(models.User.id == user_id).first()

if result is None:
raise HTTPException(status_code=404, detail="ID에 해당하는 User가 없습니다.")

return {
"status": "OK",
"data": result
}

SQLAlchemy가 제공하는 filter 를 이용해, request parameter로 받은 ID에 해당하는 사용자를 검색했고, 이를 리스폰스로 리턴하는 간단한 코드를 작성해 보았다.

마무리

이 포스팅을 통해 간단한 SQLAlchemy 모델 클래스를 구현하여 FastAPI 앱과 연동하고, 쿼리 결과를 API로 노출하는 구현을 진행해 보았다.

이 단계까지 진행했다면 앞으로 FastAPI를 통해 간단한 웹 애플리케이션을 구현하는 것에는 아무런 문제가 없을 것이다. 간단한 코드를 통해 데이터베이스 연동까지 마칠 수 있으므로 PoC 단계의 API 애플리케이션이나 개발 속도가 중요한 API 프로젝트 개발 진행에는 주요한 옵션으로 FastAPI가 채택될 수 있을 것 같다.

물론 uvicorn이라는 생소한 ASGI 프레임워크를 사용해야 하고, 개인적으론 Python 기반 웹 애플리케이션의 디버깅이나 다양한 이슈 해결에 관한 경험이 부족하므로 현업 개발에서 이 프레임워크를 통해 API를 서브하기에는 큰 부담감을 느낄 것 같기는 하다.

이미 Python 기반 웹 애플리케이션 서빙에 익숙해져있거나, Flask나 DRF 일변도의 API 개발에 지루함을 느끼는 개발자들이 FastAPI를 많이 택해 주시고, 현업에서의 운영 후기도 많이 남겨주셨으면 하는 바램을 가져본다.