본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 26. 17:03

프로덕션 환경에서 사용해야 하는 FastAPI 패턴

요약

FastAPI를 단순한 CRUD 수준을 넘어 프로덕션 환경에서 안정적으로 운영하기 위한 고급 패턴을 다룹니다. 의존성 주입의 계층화, 클래스 기반 의존성 활용, 최신 Lifespan 이벤트 사용법 및 미들웨어 구현 전략을 설명합니다.

핵심 포인트

  • 계층화된 의존성 주입을 통한 테스트 용이성 확보
  • 상태 관리를 위한 클래스 기반 의존성 활용
  • Deprecated된 on_event 대신 Lifespan 컨텍스트 매니저 사용
  • 추적 가능성을 위한 커스텀 미들웨어 구현

FastAPI는 이미 구축하기에 빠르지만 — 여러분은 그 잠재력을 최대한 활용하고 계신가요?

서론 (Introduction)

대부분의 FastAPI 튜토리얼은 CRUD 라우트, Pydantic 모델, 그리고 uvicorn main:app --reload 단계에서 멈춥니다. 시작 단계에서는 괜찮지만, 프로덕션 (Production) 애플리케이션은 더 많은 것을 요구합니다: 대규모 의존성 주입 (Dependency Injection), 백그라운드 태스크 (Background Tasks), 커스텀 미들웨어 (Custom Middleware), 이벤트 기반 설계 (Event-driven Design), 그리고 성능 최적화 (Performance Tuning)가 필요합니다.

이 글에서는 장난감 프로젝트와 프로덕션급 FastAPI 서비스의 차이를 만드는 패턴들을 깊이 있게 다룹니다.

전제 조건 (Prerequisites): FastAPI 기초, 비동기 Python (Async Python), 그리고 Pydantic v2에 익숙해야 합니다.

1. 의존성 주입 (Dependency Injection) — 올바른 방법

FastAPI의 Depends() 시스템은 믿을 수 없을 정도로 강력하지만, 대부분의 사람들은 데이터베이스 세션(Database Sessions) 수준에서만 겉핥기식으로 사용합니다. 더 깊이 들어가 봅시다.

계층화된 의존성 (Layered Dependencies)

from fastapi import Depends, HTTPException, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

...

이제 여러분은 token → user → admin이라는 깔끔한 체인을 갖게 됩니다. 각 계층은 독립적으로 테스트 가능하며 조합 가능합니다.

클래스 기반 의존성 (Class-Based Dependencies)

상태를 가진 의존성 (Rate limiters, Feature flags, DB repos)의 경우, 클래스가 더 우수합니다:

class PaginationParams:
    def __init__(self, page: int = 1, size: int = 20, max_size: int = 100):
        if size > max_size:
...

테스트에서의 의존성 오버라이드 (Dependency Overrides in Tests)

이 부분은 테스트를 위해 FastAPI가 진정으로 빛을 발하는 지점입니다:

# 테스트 파일 내에서
app.dependency_overrides[get_current_user] = lambda: User(id=1, role="admin")

...

모킹 (Mocking)도, 패칭 (Patching)도 필요 없습니다. 그저 깔끔한 오버라이드 (Overrides)만 있으면 됩니다.

2. 라이프스팬 이벤트 (Lifespan Events) — @app.on_event 대체하기

기존의 @app.on_event("startup")는 지원 중단(Deprecated)되었습니다. 대신 lifespan 컨텍스트 매니저 (Context Manager)를 사용하세요. 이것이 더 깔끔하며 에러를 적절하게 처리합니다:

from contextlib import asynccontextmanager
from fastapi import FastAPI

...

request.app.state.redis를 통해 어디에서나 이들에 접근할 수 있습니다.

3. 커스텀 미들웨어 (Custom Middleware) — CORSMiddleware를 넘어서

요청 ID 미들웨어 (Request ID Middleware)

프로덕션 환경의 모든 요청에는 추적 가능한 ID가 있어야 합니다:

import uuid
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
...

타이밍 미들웨어 (Timing Middleware)

import time

class TimingMiddleware(BaseHTTPMiddleware):
...

⚠️ 주의사항 (Gotcha): BaseHTTPMiddleware는 오버헤드 (Overhead)를 발생시킵니다. 초고성능 처리량 (Ultra-high throughput)이 필요한 경우에는 순수 ASGI 미들웨어 (Pure ASGI middleware)를 사용하거나 starlette.routing을 직접 사용하세요.

4. 백그라운드 작업 (Background Tasks) vs. Celery — 언제 무엇을 사용할 것인가

FastAPI의 내장 백그라운드 작업 (Built-in Background Tasks)

재시도 (Retries)나 모니터링이 필요 없는 단순 실행 후 방치 (Fire-and-forget) 작업에 완벽합니다:

from fastapi import BackgroundTasks

async def send_welcome_email(email: str, name: str):
...

Celery (또는 ARQ/Dramatiq)를 사용해야 하는 경우

다음과 같은 기능이 필요할 때는 적절한 작업 큐 (Task queue)를 사용하세요:

  • 실패 시 재시도 로직 (Retry logic)
  • 예약된 작업 (Scheduled tasks) (cron 방식)
  • 진행 상황 추적 (Progress tracking)
  • 워커 (Worker)당 속도 제한 (Rate limiting)
  • 여러 머신에 걸친 분산 처리 (Distributed processing)
# ARQ 사용 시 (비동기 네이티브이며, FastAPI와 매우 잘 어울립니다)
async def process_video(ctx, video_id: int):
    video = await Video.get(video_id)
...

5. 고급 Pydantic v2 패턴

계산된 필드 (Computed Fields)

from pydantic import BaseModel, computed_field

class Order(BaseModel):
...

모델 검증기 (Model Validators)

from pydantic import model_validator

class DateRange(BaseModel):
...

커스텀 직렬화 (Custom Serialization)

from pydantic import field_serializer
from datetime import datetime

...

6. 구조화된 에러 핸들링 (Structured Error Handling)

예외 (Exceptions)가 응답에 그대로 노출되지 않도록 하세요. 전역 에러 시스템을 구축해야 합니다:

from fastapi import Request
from fastapi.responses import JSONResponse

...

7. 스트리밍 응답 (Streaming Responses)

LLM (대규모 언어 모델), 대용량 파일 다운로드, 또는 서버 전송 이벤트 (Server-sent events)에 완벽합니다:

from fastapi.responses import StreamingResponse
import asyncio

...

파일 스트리밍의 경우:

@router.get("/exports/{report_id}")
async def download_report(report_id: int):
    async def generate():
...

8. 대규모 환경에서의 Router Organization (라우터 구성)

애플리케이션이 성장함에 따라 구조가 중요해집니다:

app/
├── main.py
├── core/
...
# api/v1/router.py
from fastapi import APIRouter
from .users import router as users_router
...

9. 성능: 올바른 Async (비동기) 활용

가장 흔한 Async 실수

비동기 라우트 (async routes)에서 차단형 I/O (blocking I/O)를 실행하면 처리량 (throughput)이 급감합니다. run_in_executor 또는 asyncio.to_thread를 사용하세요:

import asyncio
from PIL import Image  # Blocking library (차단형 라이브러리)

...

Redis를 이용한 Response Caching (응답 캐싱)

import json
from functools import wraps

...

10. Async FastAPI를 제대로 테스트하기

import pytest
from httpx import AsyncClient, ASGITransport
from app.main import app
...

마무리

지금까지 다룬 내용은 다음과 같습니다:

  • 계층형 및 클래스 기반 의존성을 활용한 대규모 Dependency injection (의존성 주입)
  • 적절한 리소스 관리를 위한 Lifespan events (생명주기 이벤트)
  • 관찰 가능성 (observability)을 위한 Custom middleware (커스텀 미들웨어)
  • Background tasks (백그라운드 작업) 및 적절한 큐 (queue)를 사용해야 하는 시점
  • Pydantic v2 고급 기능: computed fields (계산된 필드), validators (검증기), serializers (직렬화기)
  • 커스텀 예외 계층 구조를 통한 Structured error handling (구조화된 에러 핸들링)
  • 실시간 및 대용량 데이터를 위한 Streaming responses (스트리밍 응답)
  • 성장하는 코드베이스를 위한 Router organization (라우터 구성)
  • Performance patterns (성능 패턴): async threading (비동기 스레딩) 및 response caching (응답 캐싱)
  • httpx.AsyncClient를 이용한 Async testing (비동기 테스트)

FastAPI는 놀라운 기본 요소 (primitives)들을 제공합니다. 취약한 프로토타입과 탄력적인 서비스의 차이는 여러분이 이 요소들을 얼마나 의도적으로 조합하느냐에 달려 있습니다.

여러분은 어떤 고급 FastAPI 패턴이 필수적이라고 느끼셨나요? 댓글로 공유해 주세요!

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0