파이썬/Fast API
FastAPI-Cache로 구현하는 효율적인 API 캐싱
코샵
2025. 2. 2. 11:11
반응형
FastAPI-Cache 소개와 설치
FastAPI-Cache는 FastAPI 애플리케이션에서 손쉽게 캐싱을 구현할 수 있게 해주는 라이브러리입니다. Redis, Memcached 등 다양한 백엔드를 지원합니다.
pip install fastapi-cache2[redis] # Redis 백엔드 사용시
기본 설정
FastAPI 애플리케이션에 캐시를 설정하는 방법입니다.
from fastapi import FastAPI
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from redis import asyncio as aioredis
app = FastAPI()
@app.on_event("startup")
async def startup():
redis = aioredis.from_url("redis://localhost", encoding="utf8", decode_responses=True)
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
기본적인 캐시 사용
엔드포인트에 캐시를 적용하는 가장 기본적인 방법입니다.
from fastapi_cache.decorator import cache
@app.get("/users/{user_id}")
@cache(expire=60) # 60초 동안 캐시
async def get_user(user_id: int):
# 데이터베이스 조회 시뮬레이션
await asyncio.sleep(1) # 실제 DB 조회라고 가정
return {"id": user_id, "name": f"User {user_id}"}
# 동적 만료 시간 설정
def get_cache_expire(request: Request):
if "premium" in request.headers:
return 30 # premium 사용자는 30초
return 60 # 일반 사용자는 60초
@app.get("/products/{product_id}")
@cache(expire=get_cache_expire)
async def get_product(product_id: int):
return {"id": product_id, "name": f"Product {product_id}"}
캐시 키 커스터마이징
캐시 키를 커스터마이징하여 더 세밀한 캐시 제어가 가능합니다.
from fastapi_cache.decorator import cache
from fastapi import Request
def custom_key_builder(
func,
namespace: str | None = "",
request: Request | None = None,
*args,
**kwargs,
):
prefix = FastAPICache.get_prefix()
cache_key = f"{prefix}:{namespace}:{func.__module__}:{func.__name__}:"
# URL 파라미터 포함
if kwargs:
cache_key += ":".join(f"{k}:{v}" for k, v in kwargs.items())
# 쿼리 파라미터 포함
if request:
query_params = request.query_params
if query_params:
cache_key += ":".join(f"{k}:{v}" for k, v in query_params.items())
return cache_key
@app.get("/search")
@cache(expire=300, key_builder=custom_key_builder)
async def search_items(q: str, category: str | None = None):
# 검색 로직
return {"query": q, "category": category}
조건부 캐싱
특정 조건에서만 캐시를 적용하는 방법입니다.
from typing import Callable
def cache_if(condition: Callable[..., bool]):
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
if not condition(*args, **kwargs):
return await func(*args, **kwargs)
# 캐시 로직
cache_key = f"{func.__module__}:{func.__name__}:{args}:{kwargs}"
cached_value = await FastAPICache.get(cache_key)
if cached_value is not None:
return cached_value
value = await func(*args, **kwargs)
await FastAPICache.set(cache_key, value, expire=60)
return value
return wrapper
return decorator
def should_cache(user_id: int) -> bool:
return user_id > 100 # ID가 100보다 큰 사용자만 캐시
@app.get("/users/{user_id}/stats")
@cache_if(should_cache)
async def get_user_stats(user_id: int):
return {"id": user_id, "stats": "some heavy computation"}
캐시 무효화
캐시를 수동으로 무효화하는 방법입니다.
from fastapi_cache.decorator import cache
@app.post("/users")
async def create_user(user: UserCreate):
new_user = await create_user_in_db(user)
# users 네임스페이스의 모든 캐시 삭제
await FastAPICache.clear(namespace="users")
return new_user
@app.delete("/users/{user_id}")
async def delete_user(user_id: int):
await delete_user_from_db(user_id)
# 특정 사용자 관련 캐시만 삭제
cache_key = f"user:{user_id}"
await FastAPICache.delete(cache_key)
return {"status": "success"}
고급 캐시 패턴 구현
더 복잡한 캐싱 시나리오를 위한 구현입니다.
from fastapi_cache.decorator import cache
from typing import Optional
class CacheSettings:
def __init__(
self,
expire: int = 60,
namespace: Optional[str] = None,
key_builder: Optional[callable] = None
):
self.expire = expire
self.namespace = namespace
self.key_builder = key_builder
class CachedAPIRouter:
def __init__(self, cache_settings: CacheSettings):
self.router = APIRouter()
self.cache_settings = cache_settings
def cached_route(self, path: str, **kwargs):
def decorator(func):
# 캐시 데코레이터 적용
cached_func = cache(
expire=self.cache_settings.expire,
namespace=self.cache_settings.namespace,
key_builder=self.cache_settings.key_builder
)(func)
# 라우터에 등록
self.router.add_api_route(
path,
cached_func,
**kwargs
)
return cached_func
return decorator
# 사용 예시
user_cache_settings = CacheSettings(
expire=300,
namespace="users",
key_builder=custom_key_builder
)
user_router = CachedAPIRouter(user_cache_settings)
@user_router.cached_route("/users/{user_id}", methods=["GET"])
async def get_user(user_id: int):
return {"id": user_id}
app.include_router(user_router.router)
FastAPI-Cache를 활용하면 API의 성능을 크게 향상시킬 수 있습니다. 특히 데이터베이스 조회가 빈번하거나 계산 비용이 높은 엔드포인트에서 효과적입니다. 단, 캐시 만료 시간과 무효화 전략은 비즈니스 요구사항에 맞게 신중히 설정해야 합니다.