- Published on
FastAPI 성능 최적화: 초고속 API 만들기 🚄
- Authors
- Name
- devnmin
FastAPI 성능 최적화: 로켓 🚀처럼 빠른 API 만들기!
안녕하세요! 오늘은 여러분의 FastAPI를 더욱 빠르게 만드는 방법을 알아볼 거예요. 이미 빠른 FastAPI를 더 빠르게? 네! 가능합니다! 함께 알아볼까요? 😎
1. 비동기 처리 최적화 ⚡
비동기 데이터베이스 쿼리
# database.py
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
# 비동기 엔진 생성 (부우우웅~ 🏎️)
DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"
async_engine = create_async_engine(DATABASE_URL, echo=True)
# 비동기 세션
async def get_async_session():
async with AsyncSession(async_engine) as session:
try:
yield session
finally:
await session.close()
# API 엔드포인트
@app.get("/items/")
async def get_items(session: AsyncSession = Depends(get_async_session)):
# 비동기로 데이터 조회
result = await session.execute(select(Item))
items = result.scalars().all()
return items
병렬 처리
# 여러 작업 동시 처리하기
import asyncio
@app.get("/dashboard/")
async def get_dashboard():
# 여러 API를 동시에 호출! (시간 절약! ⏰)
user_task = asyncio.create_task(get_user_data())
stats_task = asyncio.create_task(get_statistics())
posts_task = asyncio.create_task(get_recent_posts())
# 모든 결과를 한번에 기다려요
user_data, stats, posts = await asyncio.gather(
user_task, stats_task, posts_task
)
return {
"user": user_data,
"stats": stats,
"posts": posts
}
2. 캐싱 도입하기 💾
Redis 캐시 설정
# cache.py
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
import aioredis
# Redis 연결 설정
@app.on_event("startup")
async def startup():
redis = aioredis.from_url("redis://localhost", encoding="utf8")
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache:")
# 캐시 적용 (자주 바뀌지 않는 데이터는 캐시해두면 좋아요! 📦)
@app.get("/products/{id}")
@cache(expire=3600) # 1시간 동안 캐시
async def get_product(id: int):
return await get_product_from_db(id)
인메모리 캐싱
from functools import lru_cache
# 메모리에 결과를 캐시해두고 재사용해요!
@lru_cache(maxsize=1000)
def get_settings():
return Settings()
3. 데이터베이스 최적화 📊
N+1 문제 해결
# 이렇게 하면 안 돼요! ❌
@app.get("/users/")
async def get_users_bad():
users = await db.query(User).all()
# N+1 문제 발생!
for user in users:
await db.query(Post).filter(Post.user_id == user.id).all()
# 이렇게 하세요! ✅
from sqlalchemy.orm import joinedload
@app.get("/users/")
async def get_users_good():
# 한 번의 쿼리로 모든 데이터를 가져와요!
users = await db.query(User).options(
joinedload(User.posts)
).all()
인덱스 최적화
# models.py
from sqlalchemy import Index
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
email = Column(String, index=True) # 자주 검색하는 필드에 인덱스!
username = Column(String)
# 복합 인덱스도 만들 수 있어요!
__table_args__ = (
Index('idx_username_email', 'username', 'email'),
)
4. 응답 최적화 📦
응답 압축
from fastapi.middleware.gzip import GZipMiddleware
# 응답을 쏙~ 압축해서 보내요! 🎁
app.add_middleware(GZipMiddleware, minimum_size=1000)
JSON 직렬화 최적화
from fastapi.encoders import jsonable_encoder
from orjson import orjson
from fastapi.responses import ORJSONResponse
# orjson은 기본 json보다 훨씬 빨라요! 🏃♂️
app = FastAPI(default_response_class=ORJSONResponse)
5. 부하 테스트 🔨
# locustfile.py
from locust import HttpUser, task, between
class FastAPIUser(HttpUser):
wait_time = between(1, 3)
@task
def get_items(self):
self.client.get("/items/")
# 부하 테스트 실행
locust -f locustfile.py --host=http://localhost:8000
6. 프로파일링 🔍
import cProfile
import pstats
def profile_endpoint():
profiler = cProfile.Profile()
profiler.enable()
# 여기에 테스트할 코드를 넣어요
profiler.disable()
stats = pstats.Stats(profiler).sort_stats('cumtime')
stats.print_stats()
실전 최적화 팁! 💡
- 배치 처리
# 한 번에 여러 데이터 처리하기
async def bulk_create_items(items: List[Item]):
await db.execute(insert(Item), items)
- 페이지네이션
@app.get("/items/")
async def get_items(skip: int = 0, limit: int = 10):
return await db.query(Item).offset(skip).limit(limit).all()
- 백그라운드 작업
from fastapi.background import BackgroundTasks
@app.post("/send-email/")
async def send_email(background_tasks: BackgroundTasks):
# 메일 전송은 백그라운드에서 처리! ✉️
background_tasks.add_task(send_email_task)
return {"message": "이메일 전송이 시작되었어요!"}
성능 모니터링 📊
from prometheus_fastapi_instrumentator import Instrumentator
# 성능 지표 수집 (우리 앱이 얼마나 빠른지 볼까요? 📈)
Instrumentator().instrument(app).expose(app)
자주 하는 실수들 🚨
불필요한 데이터베이스 쿼리
- 필요한 필드만 선택하세요!
캐시 키 설계 미흡
- 적절한 캐시 키와 만료 시간을 설정하세요!
너무 큰 페이로드
- 응답 데이터는 꼭 필요한 것만!
다음 단계는? 🎯
Pro Tip: 성능 최적화는 실제 문제가 있을 때 하세요! 너무 이른 최적화는 독이 될 수 있어요! 📊
유용한 자료들 📚
다음 시간에는... FastAPI로 마이크로서비스 아키텍처 구현하는 방법을 알아볼 거예요! 기대해주세요! 🚀