한양과학기술고등학교

에듀테크 프로젝트 대시보드

캐싱 전략

  캐싱 전략 — 느린 걸 두 번 하지 않기

데이터베이스 조회, 외부 API 호출, 복잡한 계산은 느리다. 같은 결과를 매번 다시 만드는 대신 한 번 만든 결과를 가까운 곳에 잠깐 저장해 두고 재사용하는 것이 캐시(cache)다. 요청이 캐시에서 해결되면 HIT(빠름), 없어서 원본까지 다녀오면 MISS(느림). 아래에서 캐시 크기와 축출 정책을 바꿔가며 적중률(hit ratio)이 어떻게 변하는지 직접 확인해 보자.

1. 캐시 시뮬레이터 — HIT vs MISS

캐시 크기 3
데이터 종류 8
축출 정책:

요청

–
➜

캐시 (3칸)

0
전체 요청
0
HIT
0
MISS
0%
적중률
적중률 (HIT / 전체)0%
평균 응답 시간 (캐시 HIT 1ms · MISS 60ms 가정)–
‘요청 1개’ 또는 ‘자동 재생’을 눌러 시작하세요.

2. 캐시가 꽉 차면 누구를 버릴까? (축출 정책)

LRU — Least Recently Used

가장 오랫동안 안 쓰인 항목을 버린다. "최근에 썼으면 곧 또 쓸 것"이라는 지역성 가정. 가장 널리 쓰이며 Redis, 브라우저, OS 페이지 교체의 기본.

LFU — Least Frequently Used

가장 적게 사용된(호출 횟수가 낮은) 항목을 버린다. 꾸준히 인기 있는 데이터를 잘 지키지만, 한때 인기였다가 식은 항목이 계속 남는 단점이 있다.

FIFO — First In First Out

들어온 순서대로 버린다. 구현이 가장 단순하지만, 방금 자주 쓴 항목도 오래됐다는 이유로 쫓겨날 수 있어 적중률은 보통 LRU보다 낮다.

TTL — Time To Live

정책과 별개로, 각 항목에 유효 기간을 둔다. TTL=60s면 60초 뒤 자동 만료되어 다음 요청은 MISS가 된다. 데이터가 낡는 것을 막는 안전장치.

3. 캐시는 한 층이 아니다 — 요청이 지나는 길

브라우저 캐시 한 번 받은 이미지·CSS·JS를 디스크에 저장. Cache-Control, ETag 헤더로 제어.
CDN (엣지) 전 세계에 흩어진 서버가 사용자와 가까운 곳에서 정적 파일을 대신 응답. 원본 서버 부하를 크게 줄인다.
리버스 프록시 Nginx·Varnish가 동일한 페이지 응답을 통째로 캐싱(proxy_cache). 앱 서버까지 안 가도 됨.
애플리케이션 캐시 Redis·Memcached 같은 인메모리 저장소에 DB 조회 결과·세션·계산값을 보관. 마이크로초 단위 응답.
DB 버퍼 풀 / 원본 데이터베이스도 자주 읽는 페이지를 메모리에 캐싱. 그래도 여기까지 오면 가장 느린 경로.

위로 갈수록 빠르고 사용자와 가깝다. 캐싱의 목표는 요청이 최대한 위층에서 끝나게 하는 것.

4. 데이터가 바뀌면? — 읽기 · 쓰기 패턴

패턴동작특징
Cache-Aside
(Lazy Loading)
앱이 먼저 캐시를 본다 → 없으면 DB에서 읽어 캐시에 채운 뒤 반환. 쓰기는 DB에 하고 캐시 항목은 삭제(무효화). 가장 흔함. 필요한 것만 캐싱돼 메모리 효율적. 첫 요청은 항상 MISS.
Write-Through 쓰기를 할 때 캐시와 DB에 동시에 기록. 캐시가 항상 최신. 쓰기가 조금 느려지고, 안 읽힐 데이터도 캐싱될 수 있음.
Write-Back
(Write-Behind)
쓰기를 캐시에만 하고, DB 반영은 나중에 모아서. 쓰기가 매우 빠름. 캐시가 죽으면 데이터 유실 위험.
# Cache-Aside 읽기 (가장 일반적인 패턴) def get_user(uid): user = cache.get(f"user:{uid}") # 1) 캐시 먼저 if user is not None: return user # HIT — 끝 user = db.query("SELECT ... WHERE id=?", uid) # MISS — DB로 cache.set(f"user:{uid}", user, ttl=300) # 캐시에 채우기(5분) return user def update_user(uid, data): db.update(uid, data) cache.delete(f"user:{uid}") # 낡은 값 무효화

5. 캐시의 함정

오래된 데이터 (Stale)

원본은 바뀌었는데 캐시가 그대로면 사용자는 옛날 값을 본다. TTL을 짧게 하거나, 쓰기 때 확실히 무효화해야 한다. "캐시 무효화는 컴퓨터 과학의 어려운 문제 2가지 중 하나".

캐시 스탬피드 (Stampede)

인기 항목의 TTL이 만료되는 바로 그 순간 수천 요청이 동시에 MISS → 전부 DB로 몰려 서버가 다운. 잠금(lock)이나 만료 시각 살짝 무작위화로 완화.

콜드 스타트

서버를 막 켜면 캐시가 비어 있어 한동안 MISS 폭탄. 미리 자주 쓰는 데이터를 채워 넣는 캐시 워밍(warming)으로 대비한다.

캐시 키 설계

키가 너무 구체적이면(list?page=3&sort=name&...) 적중률이 0에 수렴한다. 꼭 필요한 파라미터만 정규화해 키에 넣어야 한다.

"There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton