FastAPI를 공부하면서 가장 헷갈렸던 부분이
“언제 def를 쓰고 언제 async def를 써야 하는가?”,
그리고 Starlette / async-await의 정확한 역할이었다.
이 글은 개념 정리 + 실무에서 기억해야 할 규칙 위주로 정리한 기록이다.
FastAPI는 단독 프레임워크라기보다는 조합형 프레임워크다.
👉 실제 요청 처리 흐름의 대부분은 Starlette가 담당한다.
Starlette/FastAPI는 ASGI(Async Server Gateway Interface) 기반이다.
ASGI 서버(Uvicorn 등)는 요청을 받아
→ 이벤트 루프에서 coroutine을 스케줄링한다.
async def foo():
print("hi")
foo() # 실행 ❌ (coroutine 객체 생성)
await foo() # 실행 ⭕
👉 한 스레드로 많은 요청을 처리할 수 있는 이유
| 항목 | JavaScript | Python |
|---|---|---|
| 기본 모델 | 비동기 | 동기 |
| async 사용 | 기본 | 선택 |
| 동기 I/O | 거의 없음 | 매우 흔함 |
| 위험 포인트 | 상대적으로 적음 | async 안에 sync 코드 |
👉 Python에서는 async 함수 안에 동기 I/O를 넣으면 이벤트 루프가 막힌다
한 문장 요약
await 가능한 비동기 I/O를 쓰면 async def,
아니면 def
• async HTTP 클라이언트 (httpx.AsyncClient)
• async DB 드라이버 / ORM
• Redis async 클라이언트
• WebSocket
• StreamingResponse, SSE
• 여러 I/O를 동시에 처리 (asyncio.gather)
👉 await가 있는 설계
⸻
• 동기 HTTP (requests)
• 동기 DB (SQLAlchemy sync, psycopg2 등)
• boto3 같은 동기 SDK
• CPU 바운드 작업 (이미지 처리, 압축, ML 추론 등)
👉 FastAPI는 def 엔드포인트를 자동으로 스레드풀에서 실행해서 이벤트 루프를 보호한다.
async def endpoint():
r = requests.get("https://example.com") # ❌
return r.json()
• 이벤트 루프 전체가 멈춤
• 다른 요청 처리 불가
• 서버가 느려지거나 멎은 것처럼 보임
• def endpoint()로 변경
• 또는 진짜 async 클라이언트 사용
| 내부로직 | 권장 |
|---|---|
| async I/O | async def |
| sync I/O | def |
비동기 I/O 라이브러리를 사용한다
• HTTP: httpx.AsyncClient, aiohttp
• DB: async driver/ORM (예: async SQLAlchemy + asyncpg)
• Redis: redis.asyncio
• 비동기 파일/스트리밍 등
WebSocket, StreamingResponse, 서버센트이벤트(SSE) 같이
연결을 오래 붙잡고 중간중간 await로 양보해야 한다
“여러 I/O를 동시에” 처리하고 싶다
• await asyncio.gather(...) 같은 패턴
👉 요약: await로 “기다리는 구간”이 있는 설계면 async def
👉 요약: “await가 없는 코드”면 def가 기본값
| 작업 종류 | 권장 | 이유 |
|---|---|---|
| async HTTP/DB/Redis 등 await 가능한 I/O | async def | await로 양보 가능 |
| sync HTTP(requests)/sync DB/레거시 SDK | def | 스레드풀로 격리 |
| WebSocket / 스트리밍 / SSE | async def | 협력적 양보 필요 |
| 무거운 CPU 연산 | def + 워커/프로세스 | 이벤트 루프 보호 |
FastAPI에서 async는 “성능 마법”이 아니라
“이벤트 루프를 막지 않기 위한 약속”이다.
sync 코드를 async로 감싸는 건 최악의 선택이 될 수 있다.
• async는 I/O 대기 최적화
• CPU 작업은 async로 해결 안 됨
• async def 안에 sync 코드 ❌
• def는 느린 게 아니라 안전한 선택일 수 있음
• “라이브러리가 진짜 async인지” 항상 확인
유익한 글 잘 보고 갑니다.