
1. FastAPI에서 페이지네이션 처리 방법
FastAPI에서는 기본적으로 페이지네이션 기능을 제공하지 않는다.
페이지네이션 기능을 구현하기 위해 fastapi-pagination이라는 별도의 라이브러리를 사용한다.
2. 요구사항
데이터베이스의 Item 테이블에는 id, name, price 필드가 존재한다.

Item의 전체 목록을 반환하는 페이지네이션 API를 구현해야 한다.
id, name, price 필드와 함께 price의 90% 가격인 할인 후 가격(discounted_price)을 계산해서 반환해야 한다.

이 요구사항을 어떻게 구현할 수 있을까?
3. 기본 페이지네이션 함수로 구현

코드는 다음과 같이 동작한다:
- get_data_from_db() 함수에서 데이터베이스의 정보를 sqlalchemy model 객체로 가져온다.
- discounted_price 필드를 추가하기 위해 for-loop를 돌며 pydantic 객체로 변환한다.
- paginate() 함수를 사용하여 페이지네이션을 진행한다.
fastapi-pagination 라이브러리가 기본적으로 제공하는 paginate() 함수는 모든 정보가 메모리 내에 있어야 페이지네이션이 가능하다.
The default paginate function is used to paginate the data that is already in memory.
- fastapi-pagination 공식 문서 中
따라서 위 코드는 데이터베이스의 모든 정보를 불러온 후 페이지네이션을 진행하고 있다.
데이터를 조회한 뒤 바로 반환한다면 큰 문제는 없을 것이다.
그러나 이번 요구사항처럼 데이터를 조회한 후 후속 작업이 필요할 때 문제가 발생한다.
Item 데이터가 10 만개일 때, 위 코드의 조회 속도는 1초가 넘는다.

왜 느려질까?
위 코드에서는 for-loop를 돌며 discounted_price를 계산하고 있다.
이 상황에서 Item 테이블의 데이터가 10만 개라면, 이 데이터를 전부 가져와 for-loop를 10만 번 돌게 될 것이다.
FastAPI와 async sqlalchemy를 사용하는 비동기 서버지만 for-loop를 도는 구간에서 병목이 발생한다.
병목은 task 처리 시간 증가로 이어지고, event loop가 다음 task를 처리하는 속도를 늦춰 서버 전체의 속도 저하로 이어질 수 있다.
4. 새로운 페이지네이션 함수 도입
이 문제를 해결하기 위해서는 데이터베이스에서 조회하는 시점에 페이지네이션이 이뤄져야 한다.
fastapi-pagination 라이브러리는 ORM이 페이지네이션을 적용할 수 있도록 전용 paginate() 함수를 제공하고 있다.
https://uriyyo-fastapi-pagination.netlify.app/integrations/sqlalchemy/paginate/#subquery_count-param
Paginate Function - FastAPI Pagination
Paginate Function fastapi_pagination.ext.sqlalchemy.paginate allows you to paginate sqlalchemy queries easily. It can work for both sync and async SQLAlchemy engines. paginate accepts the following sqlachemy related arguments: conn - can be either a Connec
uriyyo-fastapi-pagination.netlify.app
전용 paginate() 함수는 데이터베이스 세션과 쿼리를 인자로 받는다.
이를 통해 라이브러리 내에서 페이지네이션을 위한 쿼리를 작성한다.
하지만 이 함수를 바로 적용하려면 다음과 같은 문제가 생긴다.

paginate() 함수가 바로 데이터를 반환하면서 후처리 코드가 들어갈 자리가 없어졌다.
기존 작업은 쿼리로 데이터 가져오기 -> 후처리 작업 -> 페이지네이션 순서로 진행하였다.
하지만 라이브러리 내에서 페이지네이션을 위한 쿼리를 작성하기 위해서는, 후처리 작업을 paginate() 함수 내에서 처리해야 한다.
5. Items Transformer를 이용한 후처리 작업 이동
fastapi-pagination 라이브러리에서 제공하는 Items Transformer 기능을 이용해 후처리 작업을 paginate() 함수 내에서 진행할 수 있다.
https://uriyyo-fastapi-pagination.netlify.app/learn/tutorial_user_guide/items_transformer/
Items Transformer - FastAPI Pagination
Items Transformer Items Transformer The items transformer allows to transform the items before they are returned in the response. This can be useful for formatting the data or adding additional information to the items. It's a class that accepts a chunk of
uriyyo-fastapi-pagination.netlify.app
기존 후처리 작업을 transformer() 함수로 분리하여 해당 함수를 paginate() 함수의 인자로 전달하도록 수정하였다.

개선 후 코드에서는 라이브러리가 쿼리에 offset과 limit을 추가하여 필요한 데이터만 가져온다.
즉 기존 10만번 돌던 for-loop를 페이지네이션에 지정된 size만큼만 돌게 된다.

개선을 통해 for-loop의 병목이 해소되며 API 속도를 0.1초대까지 줄일 수 있다.
6. 마치며
조회 속도 개선을 하던 중 fastapi-pagination 라이브러리를 분석하고 개선점을 찾는 경험을 하였다.
실제 문제는 훨씬 복잡하였기에 공식 문서와 오픈 소스 코드를 상당히 많이 본 후에야 해결 방법을 찾을 수 있었다.
ORM과 라이브러리라는 추상화 속에 가려져 있는 문제를 찾는 것은 쉽지 않은 것 같다.
뭐든 제대로 이해하고 사용하는 것이 중요하다는 것을 느꼈다.
'공부' 카테고리의 다른 글
FastAPI에서 StreamingResponse 활용 시 DB 세션 관리 (0) | 2025.03.30 |
---|---|
Triton Inference Server와 비동기 gRPC로 통신하기 (0) | 2025.02.11 |
DB 암호화 방식 (0) | 2025.01.18 |
브라우저의 요청이 서버까지 가는 과정 (DNS 요청 과정) (0) | 2024.12.13 |
AWS MediaConvert의 userMetadata 개수 제한 (0) | 2024.12.06 |
댓글