codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›data

3 단 캐시

2026-04-28 게시· 2026-05-18 갱신·0회 조회

3 단 캐시

캐시는 한 곳에만 있는 일이 드뭅니다. 보통 여러 층으로 쌓입니다. 클라이언트 가까이에 두면 빠르고 DB 가까이에 두면 정확합니다.

1. L1 — 인메모리 캐시

가장 짧은 응답 시간을 노리는 층입니다. 같은 프로세스 안의 LRU 맵일 수도 있고, 별도 프로세스인 Redis · Memcached 일 수도 있습니다.

도구 출자
Memcached 2003, Brad Fitzpatrick (LiveJournal)
Redis 2009, Salvatore Sanfilippo
Hazelcast / Infinispan JVM 분산 인메모리
Caffeine JVM 라이브러리 (Ben Manes)

특징은 휘발성입니다. 재시작·노드 교체 시 데이터가 사라진다는 가정이 기본입니다 (Redis 의 RDB·AOF 로 일부 보존 가능).

2. L2 — 영속 캐시

캐시지만 사라지면 곤란한 자리입니다. PostgreSQL 의 캐시 테이블, 마테리얼라이즈드 뷰, 또는 S3 같은 객체 저장소가 이 자리에 섭니다.

대표 패턴.

  • 외부 API 응답을 PostgreSQL 테이블에 적재하고 TTL 컬럼으로 만료 관리.
  • 비싼 집계 쿼리 결과를 마테리얼라이즈드 뷰로 굳히고 주기적으로 REFRESH.
  • 정적 자산 (이미지·PDF) 을 객체 저장소에 두고 CDN 으로 노출.

L1 이 죽었을 때 L2 가 폴백 역할을 합니다. L1 비어있음 → L2 조회 → 결과를 L1 에 채움(cache-aside) 흐름이 흔합니다.

3. L3 — 프레임워크 캐시

웹 프레임워크가 라우트·fetch 단위로 제공하는 캐시입니다.

  • Next.js unstable_cache · fetch 의 next: { revalidate } — 데이터 페칭 결과를 라우트 라이프사이클에 맞춰 캐싱.
  • Next.js Full Route Cache — 정적/동적 라우트의 렌더 결과 보관 (버전·구성에 따라 동작이 변합니다).
  • HTTP 캐시 헤더 — Cache-Control · ETag · Last-Modified 가 클라이언트·중간 프록시·CDN 에 의해 해석됩니다.
  • CDN 캐시 — Cloudflare · Fastly · Akamai 가 엣지에서 응답을 보관.

이 층의 특징은 "코드를 거의 바꾸지 않고도 작동" 한다는 점입니다. 동시에 디버깅이 어렵다는 점이 한계입니다 — 어떤 응답이 어디 캐시에 있고 언제 비워지는지 파악이 흩어집니다.

4. Cache-aside (Lazy loading)

가장 흔한 패턴입니다.

read:
  v = cache.get(k)
  if v is null:
    v = db.query(k)
    cache.set(k, v, ttl)
  return v

write:
  db.update(k, v)
  cache.delete(k)   # 또는 set(k, v)

장점은 단순함, 한계는 캐시 미스 시점의 사이즈드 부하·일관성 빈틈입니다.

5. Write-through · Write-behind · Refresh-ahead

Write-through 는 쓰기 시 캐시와 DB 를 모두 갱신합니다.

write:
  cache.set(k, v)
  db.update(k, v)

쓰기 일관성이 좋아지지만 자주 갱신되는 키가 있다면 캐시도 같이 부하를 받습니다.

Write-behind 는 쓰기를 일단 캐시에만 기록하고 비동기 워커가 DB 에 반영합니다. 처리량이 큰 자리에서 쓰이지만 캐시 손실 시 데이터 손실 위험이 있습니다.

Refresh-ahead 는 TTL 만료가 임박한 항목을 백그라운드로 미리 갱신합니다. 사용자 응답에서 캐시 미스를 줄이는 효과입니다. JVM 진영의 Caffeine 등이 직접 지원합니다.

6. TTL 과 stale-while-revalidate

TTL 은 "이 값이 얼마나 늦어도 괜찮은가" 의 약속입니다. 너무 짧으면 캐시 의미가 약해지고, 너무 길면 사용자에게 낡은 결과가 갑니다. 결정의 기준은 데이터의 변경 주기와 사용자 허용도입니다.

stale-while-revalidate 패턴은 만료된 값이라도 일단 돌려주고 백그라운드에서 갱신을 끌고 옵니다. HTTP 의 Cache-Control: stale-while-revalidate=... 가 표준화돼 있습니다.

7. Cache stampede

자주 호출되는 키의 TTL 이 동시에 만료되면 여러 요청이 한꺼번에 원본을 조회하며 부하가 튑니다. 완화책은 다음 중 한두 가지를 묶는 형태입니다.

  • TTL 에 약간의 무작위 jitter 를 더합니다.
  • 단일 요청만 원본을 갱신하도록 분산 락(Redis SET NX) 을 둡니다.
  • early refresh — TTL 만료 전에 갱신을 시작합니다.
  • request coalescing — 같은 키에 대한 동시 요청을 하나로 합칩니다.

8. 키 네이밍 관례

  • <service>:<entity>:<id> — 예: users:profile:42.
  • 버전을 prefix 에 넣어 통째로 무효화: v3:users:profile:42.
  • 환경 prefix: prod: · staging:.

키가 길어질수록 메모리 오버헤드가 커진다는 점은 Redis 같은 인메모리 저장소에서 고려할 만합니다.

9. 직렬화와 모니터링

  • JSON — 사람에게 읽기 좋습니다. 직렬화 비용이 큽니다.
  • MessagePack · CBOR — 바이너리, 더 작습니다.
  • Protobuf · Avro — 스키마 기반, 다언어.

캐시 응답을 빈번히 직렬화/역직렬화한다면 형식 선택이 응답 시간에 보입니다.

모니터링 항목:

  • 적중률 (hit rate) — 너무 낮으면 키 설계나 TTL 의 문제.
  • 메모리 사용량 · 키 카운트 · eviction 횟수.
  • 원본 (DB·외부 API) 호출 횟수 — 캐시 도입 효과 측정.

10. 자주 걸리는 자리

부분 무효화 — 한 사용자 정보를 10 곳에서 캐싱하면 비우기가 어렵습니다. 키 컨벤션·태그가 처음부터 필요합니다.

null 캐싱 — "결과 없음" 도 캐시할지 결정해야 합니다. 안 하면 매 요청이 DB 까지 가고, 하면 새로 생긴 항목이 보이지 않을 수 있습니다.

TTL 무한 — 영속 데이터가 캐시에 남아 새 코드와 충돌합니다. 명시적 만료 또는 버전 prefix 가 안전합니다.

쓰기 후 즉시 읽기 — 클라이언트가 자기 변경을 보지 못합니다. write-through 또는 클라이언트 측 read-your-writes 처리가 필요합니다.

L3 의 빌드 시점 캐시 — Next.js 의 정적 캐시는 빌드 시점 또는 revalidate 주기에 묶입니다. CMS 의 즉시 반영 요구와 어긋날 수 있습니다.

하고픈 말

캐시는 들이는 만큼 무효화가 어려워집니다. "이 캐시 누가 만들고 누가 비우는지" 가 명확한 자리부터 시작하는 게 안전합니다. Phil Karlton 의 농담 ("There are only two hard things in Computer Science: cache invalidation and naming things") 이 괜히 회자되는 게 아닙니다.

Next

  • redis-roles
  • data-pipeline

HTTP Caching (MDN) · RFC 5861 — stale-while-revalidate · Redis 캐시 패턴 · Next.js Caching · Caffeine GitHub 를 참고합니다.

data 카테고리의 다른 글

카테고리 전체 보기 →
  • DB 시드 소스를 코드 트리 안에 두지 않는다
  • Supabase Storage — 파일 업로드와 권한
  • Kafka 실무 — 토픽 설계와 메시지 흐름
  • 여러 PostgreSQL 풀 한 앱에서 관리하기
  • 백업과 복구
  • 이미지 파이프라인