Redis를 캐시로 사용할 때 주요 캐싱 전략과 주의사항을 설명해주세요.
답변 포인트
Cache Aside, Write Through, TTL, 캐시 무효화를 중심으로 생각해보세요.
정답 및 해설
빠른 요약
Redis 캐싱 전략에는 Cache Aside, Write Through, Write Behind 등이 있습니다. Cache Aside는 캐시를 먼저 조회하고 없으면 DB에서 읽어 캐시에 저장하는 방식으로 가장 흔합니다.
Redis는 메모리 기반 key-value 저장소로, 빠른 조회가 필요한 데이터를 캐싱할 때 자주 사용됩니다. 하지만 캐시는 원본 데이터베이스와의 일관성, 만료 정책, 장애 상황을 함께 고려해야 합니다.
Cache Aside
애플리케이션이 캐시를 먼저 조회하고, 없으면 DB에서 읽은 뒤 캐시에 저장하는 방식입니다.
async function getUser(id: string) {
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached);
const user = await userRepository.findById(id);
await redis.set(`user:${id}`, JSON.stringify(user), { EX: 300 });
return user;
}장점은 구현이 단순하고 읽기 중심 서비스에 적합하다는 점입니다. 단점은 캐시 미스 시 DB 부하가 발생하고, 데이터 변경 시 캐시 무효화를 직접 처리해야 한다는 점입니다.
Write Through
쓰기 요청이 들어오면 DB와 캐시에 동시에 반영합니다.
async function updateUser(id: string, input: UpdateUserInput) {
const user = await userRepository.update(id, input);
await redis.set(`user:${id}`, JSON.stringify(user), { EX: 300 });
return user;
}읽기 일관성은 좋아지지만, 쓰기 경로가 느려질 수 있습니다.
Write Behind
먼저 캐시에 쓰고, DB 반영은 비동기로 지연 처리합니다. 쓰기 성능은 좋지만 캐시 장애나 큐 유실 시 데이터 손실 위험이 있으므로 신중하게 사용해야 합니다.
캐시 무효화
데이터가 변경되면 관련 캐시를 삭제하거나 갱신해야 합니다.
async function changeNickname(id: string, nickname: string) {
await userRepository.changeNickname(id, nickname);
await redis.del(`user:${id}`);
}무효화 대상이 여러 개인 경우 키 설계가 중요합니다.
user:10
user:10:posts
post:list:popularTTL 설정
TTL이 없으면 오래된 데이터가 계속 남거나 메모리가 가득 찰 수 있습니다.
await redis.set("ranking:daily", JSON.stringify(ranking), { EX: 60 });데이터 성격에 따라 TTL을 다르게 둡니다.
- 사용자 프로필: 수분
- 인기 게시글: 수십 초
- 설정값: 수십 분 이상
- 인증/인가 데이터: 보안 요구에 맞춰 짧게
Cache Stampede
인기 키가 동시에 만료되면 많은 요청이 한꺼번에 DB로 몰릴 수 있습니다.
해결 방법:
- TTL에 랜덤 지터 추가
- 락을 사용해 한 요청만 DB를 조회
- 만료 전 백그라운드 갱신
- stale 데이터를 잠시 허용
const ttl = 300 + Math.floor(Math.random() * 60);
await redis.set(key, value, { EX: ttl });주의사항
Redis는 빠르지만 영구 저장소의 대체재가 아닙니다. 캐시 데이터는 언제든 사라질 수 있다고 가정해야 하며, 원본 데이터는 DB에 있어야 합니다. 또한 사용자별 민감 정보를 캐싱할 때는 키 충돌, TTL, 접근 권한을 엄격히 관리해야 합니다.
정리
Redis 캐싱은 읽기 성능을 크게 개선하지만, 캐시 무효화와 만료 전략이 핵심입니다. Cache Aside를 기본으로 시작하고, 쓰기 일관성 요구와 트래픽 패턴에 따라 Write Through, pre-warming, stampede 방지 전략을 추가하는 것이 일반적입니다.
추가로 고려할 운영 포인트
Redis 캐시는 코드 한 줄로 성능을 올려주지만, 운영에서는 키 설계와 장애 대응이 더 중요합니다.
좋은 키 예시
user:{userId}:profile:v1
post:list:popular:{date}
product:{productId}:stock-cache버전을 키에 포함하면 데이터 구조가 바뀌었을 때 전체 삭제 없이 자연스럽게 새 캐시로 전환할 수 있습니다.
장애 시나리오
- Redis 장애: DB로 fallback할 것인지, 일부 기능을 제한할 것인지 결정합니다.
- 캐시 대량 만료: TTL jitter와 background refresh로 DB 스파이크를 줄입니다.
- 큰 value 저장: 네트워크 비용과 직렬화 비용 때문에 오히려 느려질 수 있습니다.
- hot key: 특정 키에 트래픽이 몰리면 Redis 단일 shard가 병목이 될 수 있습니다.
실무 체크리스트
- 모든 캐시에는 소유자와 만료 정책이 있는가?
- 개인정보/권한 정보가 다른 사용자에게 섞일 가능성은 없는가?
- 캐시 삭제 실패 시 데이터 불일치를 허용할 수 있는가?
- hit ratio, latency, evicted keys를 모니터링하고 있는가?