FastAPI 의 설계 철학
FastAPI 의 설계 철학
FastAPI 는 비교적 짧은 시간 안에 Python 웹 프레임워크의 주류로 자리 잡았습니다. 빠른 등장의 배후에는 type hint · Pydantic · async 를 일관된 한 문장으로 엮은 설계가 있습니다.
1. FastAPI 에 대한 이야기
FastAPI 는 Sebastián Ramírez (tiangolo) 가 만든 Python 웹 프레임워크로 2018 년 12 월 첫 공개로 알려져 있습니다. 두 라이브러리 위에 서 있습니다.
| 기반 | 역할 | 출시 |
|---|---|---|
| Starlette | ASGI 라우팅·미들웨어·테스트 클라이언트 | encode 팀, 2018 |
| Pydantic | 타입 기반 데이터 검증·직렬화 | Samuel Colvin, 2017 |
FastAPI 는 라우팅·DI·보안·OpenAPI 자동 생성을 더해 "type hint 만으로 검증·문서화가 따라온다" 는 경험을 만듭니다.
2. Type hint 가 곧 스키마
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
app = FastAPI()
@app.post('/items')
def create(item: Item) -> Item:
return item
런타임에 item: Item 을 보고 요청 body 를 Pydantic 으로 검증합니다. 같은 정보가 OpenAPI 스키마로 노출되고, /docs (Swagger UI) · /redoc 페이지에서 바로 시도할 수 있습니다.
3. Pydantic v1 → v2
Pydantic v2 (2023-06) 는 핵심 검증을 Rust (pydantic-core) 로 재작성하면서 v1 대비 큰 폭의 성능 향상을 발표했습니다. v1 과 v2 의 API 가 일부 다르며 마이그레이션 가이드가 별도 제공됩니다.
class Config 가 v2 에서 model_config 로 바뀌고 일부 데코레이터·메서드가 변경됐습니다. 라이브러리 간 버전 일치를 먼저 확인합니다.
4. Dependency Injection
FastAPI 의 DI 는 함수 시그니처에 Depends(...) 를 적는 것이 전부입니다.
from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get('/users/{id}')
def read(id: int, db = Depends(get_db)):
return db.query(User).get(id)
의존도 함수도 type hint 로 자기 의존을 선언할 수 있어 합성됩니다. yield 를 통해 setup/teardown 을 한 함수로 묶는 패턴은 컨텍스트 매니저와 비슷합니다.
같은 요청 안에서 동일 의존은 한 번만 평가됩니다 (캐시). side-effect 가 필요한 의존은 의도와 다를 수 있습니다.
5. async 우선
엔드포인트를 async def 로 선언하면 ASGI 이벤트 루프 위에서 동시 처리됩니다. 동기 함수 (def) 도 그대로 받을 수 있는데, 이 경우 FastAPI 가 별도 스레드 풀에서 실행해 이벤트 루프를 막지 않습니다. DB 드라이버·HTTP 클라이언트가 async 를 지원하지 않으면 동기 함수로 두는 편이 안전합니다.
6. 다른 프레임워크와의 비교
| 프레임워크 | 첫 릴리스 | 성격 |
|---|---|---|
| Django | 2005 | "batteries included". ORM · admin · auth 통합. 동기 중심이지만 ASGI 도 지원. |
| Flask | 2010, Armin Ronacher | 마이크로 프레임워크. 확장으로 키워 쓰는 방식. |
| FastAPI | 2018 | type hint + 자동 문서. ASGI 우선. |
| Starlette | 2018 | ASGI 토대. FastAPI 가 그 위에 서 있음. |
| Litestar | 2022 (전 Starlite) | type hint 기반의 또 다른 ASGI 프레임워크. |
| Sanic | 2016 | async 우선의 초기 시도 중 하나. |
Django 는 풀스택·관리도구 강점. Flask 는 작고 자유로운 확장. FastAPI 는 타입과 OpenAPI 의 결합. 어느 쪽이 우월하기보다 작업 성격에 맞춘 선택입니다.
7. 라우터 분리
# routers/users.py
from fastapi import APIRouter
router = APIRouter(prefix='/users', tags=['users'])
@router.get('/')
def list_users(): ...
# main.py
app.include_router(users.router)
규모가 자라면 도메인 단위로 라우터를 나누는 편이 자연스럽습니다.
8. 응답 모델·상태 코드·백그라운드
@app.post('/items', response_model=ItemOut, status_code=201)
def create(item: ItemIn) -> ItemOut: ...
response_model 을 명시하면 응답 직렬화 스키마가 입력과 분리되고 비밀 필드 노출을 막는 가드 역할도 합니다.
BackgroundTasks 는 응답 후 실행할 가벼운 작업을 위한 도구입니다. 길거나 안정성이 필요한 작업은 별도 큐 (Celery · RQ · Arq) 로 분리하는 편이 적절합니다.
9. 라이프사이클 이벤트
앱 시작·종료 시 한 번씩 실행할 자원 초기화는 lifespan 컨텍스트로 표현합니다.
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
app.state.db = await create_pool()
yield
await app.state.db.close()
app = FastAPI(lifespan=lifespan)
이전의 @app.on_event("startup") · @app.on_event("shutdown") 데코레이터는 deprecated 로 전환되어 lifespan 이 권장됩니다.
10. OpenAPI 와 /docs
/openapi.json 이 사양 그 자체. /docs 가 Swagger UI, /redoc 이 Redoc 페이지입니다. 운영 환경에서 사양을 외부에 노출하지 않으려면 FastAPI(docs_url=None, redoc_url=None, openapi_url=None) 또는 게이트웨이에서 차단합니다.
11. 자주 걸리는 자리
async def 안에서의 블로킹 호출 — requests.get(...) · 동기 ORM 호출. 이벤트 루프가 막힙니다. httpx.AsyncClient · asyncpg · SQLAlchemy 2.x async 같은 비동기 대체를 쓰거나, 라우트를 동기 함수로 두고 별도 스레드 풀에 맡깁니다.
OpenAPI 자동 생성의 한계 — 응답 형태가 동적이거나 멀티 콘텐츠 타입이면 수동으로 보완해야 합니다.
하고픈 말
FastAPI 는 type hint 한 줄에 검증·문서·DI 가 자연스럽게 따라오는 경험이 가장 큰 차별점입니다. Python 의 type hint 가 단순한 주석이 아니라 런타임 도구로 살아 있는 자리입니다. 작은 API 부터 시작해도 큰 비용이 들지 않습니다.
Next
- python-folder-philosophy
FastAPI 공식 · FastAPI GitHub · Starlette 공식 · Pydantic 공식 · ASGI 사양 · Flask 공식 · Django REST framework 을 참고합니다.