[FastAPI] 13. SQLAlchemy와 Pydantic을 이용한 관계 데이터 매핑

반응형

SQLAlchemy를 사용하다보면 원하지 않을 때 API에서 모든 컬럼에 대한 데이터를 받아와 최적화 하기가 어려울 때가 있습니다. 어떤 API에서는 특정 컬럼에 대한 데이터 혹은 관계 데이터가 필요할 때가 있는데, 그렇지 않은 데이터까지 모두 나오게 되어 오히려 API 로딩 속도를 저하시키고 서버 부하에 원인이 되기도 합니다.

 

 

 

SQLAlchemy ORM의 relationship

SQLAlchemy ORM에서는 관계된 데이터를 가져오기 위해 relationship을 사용할 수 있습니다. 예를 들어 블로그를 하나 만드려는데, 어떤 카테고리의 글인지를 알기 위해서 아래와 같이 데이터를 설계해 볼 수 있습니다.

 

카테고리는 하위 카테고리를 가질 수 있고, 하나의 컨텐츠는 카테고리 하나를 가질 수 있다라고 했을 때 위와 같은 데이터를 설계해 볼 수 있습니다. (위 설계는 반드시 정답이 될 수는 없습니다.)

 

위 상황에서 SQLAlchemy ORM으로 코드를 짠다면 아래와 같은 코드 형태가 됩니다.

 

블로그 컨텐츠를 조회했을 때 어떤 카테고리인지를 확인하기 위해 relationship을 사용하여 연결된 카테고리를 CategoryEntity로 불러올 수 있습니다. 

 

 

 

Pydantic with relationship

FastAPI에서 response로 응답하기 위해서는 어떻게 해야할까요? 가장 간단하게는 ORM 모델을 JsonDecoder 등을 이용하여 JSON으로 변환하는 방법이 있습니다. 하지만 이 방법은 DateTime이나 지금처럼 relationship을 이용하는 경우 해당 모델에서 사용하는 모든 타입에 대해 JSON 타입으로 변환해야 하는 로직을 일일이 수행해줘야 한다는 단점이 존재합니다.

 

이를 좀 더 편하게 사용하기 위해 Pydantic에 내장된 ORM Mode와 FastAPI의 jsonable_encoder를 사용하면 우리가 직접 JSON 파서를 구현하지 않아도 Pydantic 모델로 변환해주고 이에 맞춰 JSON 포맷으로 인코딩 해주기 때문에 두 가지 편리한 이점을 얻어낼 수 있습니다.

 

Pydantic에서 BaseModel은 Python의 dict 형태의 데이터를 Pydantic 모델로 변환하는 데 사용하며 여기에 orm_mode를 추가하는 경우 SQLAlchemy의 ORM Model 형태의 데이터를 Pydantic 모델로 변환하는 로직을 사용하게 됩니다.

 

이를 API로 구현하면 아래와 같이 구현할 수 있습니다.

 

여기서 구체적인 코드는 적지 않도록 하겠습니다. 여러분들이 어떤 형태로 사용하는지에 따라 여부가 달라질 수 있기 때문입니다. 중요한 점은 response_model에 위에서 정의한 Pydantic 모델을 정의하면 되고 api에서 반환하는 데이터는 ORM 모델만 반환하면 됩니다.

 

그런데, 어떻게 API에서 ORM 모델만 반환했는데, 바로 Pydantic 모델이 되어 JSON으로 나올 수가 있는 것일까요?

 

 

 

FastAPI jsonable_encoder

FastAPI에서는 서버 데이터 반환시 JSON으로 변환하기 위한 인코더를 제공합니다. 이 인코더는 우리가 사용하는 json의 dumps와 같은 로직이 아닙니다. 우리가 원한다면 ujson을 쓸 수도 있고 orjson을 쓸 수도 있으며 이에 맞춰서 자동으로 데이터 값을 인코딩해주는 아주 좋은 모듈입니다.

 

실제로 FastAPI는 데이터 모델로 Pydantic을 채택하고 있으며 response_model에 Pydantic 모델을 명시하는 경우 jsonable_encoder 함수가 작동합니다. 이 때 jsonable_encoder는 오직 dict 형태의 데이터만을 json 값으로 반환해주는데, orm_mode가 설정되어 있는 경우 Pydantic의 from_orm이라는 함수에 있는 로직 그대로를 반환하여 json 값으로 변환해주게 됩니다.

 

위 사진은 실제로 FastAPI가 response_model을 정의하였을 때 동작하는 코드입니다. Pydantic 모델을 정의하였을 때는 바로 dict로 변환해서 json으로 반환하고, orm_mode를 사용하는 경우에는 from_orm 함수에 의존한 채 바로 json으로 변환하게 됩니다.

 

여기서 from_orm은 FastAPI에서 제공하는 함수가 아닌 Pydantic BaseModel에서 제공하는 classmethod로써 FastAPI는 dict로 변환 없이 이 데이터만을 이용해 json으로 반환합니다.

 

코드를 보면 SQLALchemy 모델에서 수동으로 dict로 변환할 때 사용하는 파이썬 객체 메서드의 __dict_-와 __fieds_set__을 보고 값을 validation 하여 가져오는 것을 볼 수 있습니다.

 

 

 

마치며...

relationship을 이용하여 관계형 데이터를 어떤식으로 json 값에 넣어 보내는지를 알아봤습니다. 간단히 쓰기에는 어려워 보이지 않지만 ManyToOne, ManyToMany 등 다양한 관계에서 사용하기에는 아직 다소 부족한 부분이 있습니다. 이 부분에 대해서는 다음 포스트에서 다뤄볼 것입니다.

 

이번 포스트에서는 FastAPI에서 어떤 식으로 relationship을 다루고 사용하는 것에 대해 간단히 다루는 것을 목적으로 하였으며 우리는 이 방식이 어떤 방식인지를 이제 알았으니 다양한 방법의 relationship 사용법을 알아보면서 쿼리를 최적화 하는 방법을 보면 되겠습니다.

 

반응형