FastAPI로 마이크로서비스 전환하며 겪은 성능 개선
배경
사내 API 서버가 Flask로 구성되어 있었는데, 트래픽이 늘면서 응답 속도가 눈에 띄게 느려졌다. 동기 처리 방식의 한계가 명확했고, FastAPI로의 전환을 검토하게 되었다.
전환 과정
1. 라우터 구조 변경
Flask의 Blueprint를 FastAPI의 APIRouter로 마이그레이션했다.
# Before (Flask)
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = db.query(User).filter(User.id == user_id).first()
return jsonify(user.to_dict())
# After (FastAPI)
@router.get('/api/users/{user_id}', response_model=UserResponse)
async def get_user(user_id: int, db: Session = Depends(get_db)):
user = await db.query(User).filter(User.id == user_id).first()
return user
2. 비동기 DB 연결
SQLAlchemy를 비동기 모드로 전환했다. databases 라이브러리를 활용해 PostgreSQL 연결을 async/await 패턴으로 변경했다.
from databases import Database
database = Database(DATABASE_URL)
@app.on_event("startup")
async def startup():
await database.connect()
@app.on_event("shutdown")
async def shutdown():
await database.disconnect()
3. Pydantic 모델 도입
타입 검증과 직렬화를 Pydantic으로 통합했다. 런타임 에러가 크게 줄었고, API 문서가 자동 생성되는 점도 좋았다.
class UserCreate(BaseModel):
email: EmailStr
username: str
age: Optional[int] = None
class Config:
orm_mode = True
결과
- 평균 응답 속도: 250ms → 150ms (40% 개선)
- CPU 사용률: 65% → 45%
- 자동 생성되는 OpenAPI 문서로 프론트엔드 협업 개선
겪은 문제
동기 라이브러리를 비동기 컨텍스트에서 호출해 블로킹이 발생하는 경우가 있었다. run_in_executor로 감싸 해결했지만, 처음부터 async 지원 라이브러리를 선택하는 게 중요하다는 걸 배웠다.
다음은 Celery 태스크를 FastAPI Background Tasks로 전환할 예정이다.