Redis 캐싱 전략: FastAPI 성능 10배 올리기 - 코드픽 블로그
Redis 캐싱 전략: FastAPI 성능 10배 올리기
기술 가이드

Redis 캐싱 전략: FastAPI 성능 10배 올리기

2026년 3월 1일 47 views by 코드벤터

Redis 캐싱 전략: FastAPI 성능 10배 올리기

FastAPI로 API를 만들다 보면 어느 순간 병목이 찾아옵니다. DB 쿼리가 느려지고, 동일한 데이터를 반복해서 조회하며, 트래픽이 몰릴 때 서비스가 흔들립니다. 이 문제를 가장 효과적으로 해결하는 방법이 바로 Redis 캐싱입니다.

Redis는 인메모리 데이터 저장소로, 디스크 기반 DB 대비 수십~수백 배 빠른 읽기 속도를 자랑합니다. FastAPI의 비동기 구조와 결합하면 실제 운영 환경에서 응답 시간을 대폭 단축할 수 있습니다. 이 글에서는 주요 캐싱 전략과 실전 구현 코드를 단계별로 정리합니다.


캐싱이 필요한 이유

API 서버의 성능 저하 원인 대부분은 다음 두 가지입니다.

  1. 반복적인 DB 쿼리: 같은 데이터를 요청할 때마다 DB에서 읽어옴
  2. 느린 외부 API 호출: 서드파티 서비스 응답 대기

Redis 캐시 레이어를 추가하면 이 두 문제를 동시에 해결할 수 있습니다. 자주 조회되는 데이터는 메모리에서 즉시 반환하고, DB와 외부 API 호출 횟수를 획기적으로 줄입니다.


핵심 캐싱 전략 비교

전략읽기 방식쓰기 방식일관성적합한 사례
Cache-Aside캐시 미스 시 DB 조회 후 저장앱이 직접 캐시+DB 저장보통읽기 빈도 높은 데이터
Read-Through캐시가 DB 조회를 대행앱이 캐시에만 씀보통캐시 라이브러리 활용 시
Write-Through캐시에서 읽음캐시+DB 동시 쓰기높음데이터 일관성 중요 시
Write-Behind캐시에서 읽음캐시에 쓰고 DB는 비동기낮음쓰기 성능 극대화 필요 시

각 전략은 트레이드오프가 있습니다. 대부분의 FastAPI 서비스에는 Cache-Aside + Write-Through 조합이 가장 실용적입니다.


환경 준비

bash
pip install fastapi uvicorn redis aioredis fastapi-cache2

로컬에서 Redis를 빠르게 띄우려면 Docker를 활용합니다.

bash
docker run -d --name redis -p 6379:6379 redis:7-alpine

Cache-Aside 패턴 구현

Cache-Aside는 가장 널리 쓰이는 전략입니다. 애플리케이션이 직접 캐시를 관리하며, 미스 발생 시 DB에서 데이터를 가져와 캐시에 저장합니다.

python
import json
import redis.asyncio as aioredis
from fastapi import FastAPI, Depends

app = FastAPI()

async def get_redis():
    client = aioredis.from_url(
        "redis://localhost:6379",
        encoding="utf-8",
        decode_responses=True
    )
    try:
        yield client
    finally:
        await client.aclose()

@app.get("/users/{user_id}")
async def get_user(user_id: int, redis: aioredis.Redis = Depends(get_redis)):
    cache_key = f"user:{user_id}"

    cached = await redis.get(cache_key)
    if cached:
        return {"source": "cache", "data": json.loads(cached)}

    user_data = await fetch_user_from_db(user_id)
    await redis.set(cache_key, json.dumps(user_data), ex=300)
    return {"source": "database", "data": user_data}

async def fetch_user_from_db(user_id: int) -> dict:
    return {"id": user_id, "name": "홍길동", "email": "hong@example.com"}

fastapi-cache2 데코레이터 방식

반복적인 캐시 로직을 데코레이터로 깔끔하게 처리할 수 있습니다.

python
from fastapi import FastAPI
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from fastapi_cache.decorator import cache
import redis.asyncio as aioredis

app = FastAPI()

@app.on_event("startup")
async def startup():
    redis = aioredis.from_url("redis://localhost:6379", encoding="utf-8", decode_responses=True)
    FastAPICache.init(RedisBackend(redis), prefix="myapp-cache")

@app.get("/products/{product_id}")
@cache(expire=600)
async def get_product(product_id: int):
    product = await fetch_product_from_db(product_id)
    return product

데코레이터 하나로 캐시 저장, 조회, TTL 관리가 모두 자동 처리됩니다.


Write-Through 패턴 구현

데이터를 생성하거나 수정할 때 DB와 캐시를 동시에 업데이트합니다.

python
@app.post("/users")
async def create_user(user_data: dict, redis: aioredis.Redis = Depends(get_redis)):
    new_user = await save_user_to_db(user_data)
    cache_key = f"user:{new_user['id']}"
    await redis.set(cache_key, json.dumps(new_user), ex=300)
    return new_user

@app.put("/users/{user_id}")
async def update_user(user_id: int, update_data: dict, redis: aioredis.Redis = Depends(get_redis)):
    updated_user = await update_user_in_db(user_id, update_data)
    cache_key = f"user:{user_id}"
    await redis.set(cache_key, json.dumps(updated_user), ex=300)
    return updated_user

TTL 전략 설계

TTL(Time To Live) 설정은 캐싱의 핵심입니다. 데이터 특성에 맞게 다르게 설정해야 합니다.

python
CACHE_TTL = {
    "user_profile": 300,
    "product_list": 60,
    "static_config": 3600,
    "search_result": 30,
    "user_session": 1800,
}

async def get_cached_data(redis, cache_key: str, data_type: str, fetch_fn):
    cached = await redis.get(cache_key)
    if cached:
        return json.loads(cached)
    data = await fetch_fn()
    ttl = CACHE_TTL.get(data_type, 300)
    await redis.set(cache_key, json.dumps(data), ex=ttl)
    return data

캐시 무효화 전략

데이터가 변경될 때 관련 캐시를 정확히 무효화하는 것이 중요합니다.

python
async def invalidate_user_cache(redis: aioredis.Redis, user_id: int):
    keys_to_delete = []
    async for key in redis.scan_iter(f"user:{user_id}*"):
        keys_to_delete.append(key)
    if keys_to_delete:
        await redis.delete(*keys_to_delete)

async def invalidate_product_related_cache(redis, product_id: int, category_id: int):
    keys = [
        f"product:{product_id}",
        f"category:{category_id}:products",
        "products:featured",
    ]
    await redis.delete(*keys)

캐시 스탬피드 방지

다수의 요청이 동시에 캐시 미스를 만나는 상황을 분산 잠금으로 방지합니다.

python
import asyncio

async def get_with_lock(redis: aioredis.Redis, cache_key: str, fetch_fn):
    cached = await redis.get(cache_key)
    if cached:
        return json.loads(cached)

    lock_key = f"lock:{cache_key}"
    lock = await redis.set(lock_key, "1", ex=5, nx=True)

    if lock:
        data = await fetch_fn()
        await redis.set(cache_key, json.dumps(data), ex=300)
        await redis.delete(lock_key)
        return data
    else:
        await asyncio.sleep(0.1)
        return await get_with_lock(redis, cache_key, fetch_fn)

운영 환경 설정

메모리 관리를 위해 Redis 설정 파일에 eviction 정책을 지정합니다.

bash
# redis.conf
maxmemory 512mb
maxmemory-policy allkeys-lru

LRU 정책을 사용하면 메모리가 가득 찼을 때 가장 오래 사용되지 않은 키부터 자동으로 삭제됩니다.


성능 측정 결과

실제 테스트 환경(FastAPI + PostgreSQL + Redis)에서 측정한 결과입니다.

시나리오평균 응답 시간초당 요청 처리(RPS)
DB 직접 조회145ms68
Redis 캐시 히트8ms1,250
개선율약 18배 향상약 18배 향상

단순 캐시 레이어 추가만으로 응답 속도와 처리량 모두 10~20배 개선이 가능합니다.


마무리

Redis 캐싱은 투자 대비 효과가 가장 높은 성능 최적화 방법 중 하나입니다. Cache-Aside로 읽기를 최적화하고, Write-Through로 일관성을 유지하며, 적절한 TTL과 무효화 전략을 조합하면 DB 부하를 80% 이상 줄이고 API 응답 속도를 크게 높일 수 있습니다.

코드벤터는 FastAPI 기반 서비스 개발 경험을 바탕으로, 실제 운영 환경에 바로 적용 가능한 기술 가이드를 지속적으로 공유합니다. Redis 캐싱을 시작으로 더 빠르고 안정적인 백엔드를 만들어 보세요.

개발 의뢰 상담

AI 서비스나 플랫폼 개발을
고민 중이신가요?

CodePick에서는
기획 → 개발 → 운영까지 함께합니다.
아이디어만 있어도 상담 가능합니다.

CodeVenter 개발팀이 직접 담당 · 1~2 영업일 내 회신

✓ 스타트업 MVP 개발✓ AI 서비스 개발✓ 웹 플랫폼 개발✓ 기업 시스템 구축✓ 모바일 앱 개발

AI Development Studio

코드픽 by 코드벤터

  • 대표: 윤승환 · 사업자등록번호: 121-57-64983
  • 대구광역시 중구 국채보상로 586, 16층 · info@codeventer.com

© 2025 코드벤터. All rights reserved.