캐싱 완벽 정리: 무엇을 캐싱하지 않을지부터 정한다
캐시가 왜 빠른가를 넘어, 왜 어렵고 무엇을 포기하는지까지. 지역성과 메모리 계층, Cache-Aside/Write-Through/Write-Back, eviction 정책, 관통·눈사태·스탬피드 같은 장애 패턴, 정합성 패턴, 그리고 '언제 캐싱하지 말아야 하는가'의 결정 감각까지 정리했다. 주니어와 함께 토론할 질문도 담았다.
캐싱 완벽 정리: 무엇을 캐싱하지 않을지부터 정한다
목표는 "왜 빠른가"를 넘어 **"왜 어렵고, 무엇을 포기하는가"**까지 설명할 수 있게 만드는 것이다. 사내 스터디에서 주니어들과 함께 다룬 내용을 정리했고, 중간중간 토론 포인트를 그대로 남겨뒀다.
관련 글: Kafka 깊은 이야기 (page cache·offset cache) · 큐 완벽 정리 (버퍼링 관점 비교) · MSA 시리즈 (분산 캐시) · 이벤트 드리븐 아키텍처 (CDC 기반 무효화)
함께 보면 좋은 인터랙티브 시각화 3개를 만들어 뒀다. 글 중간에 막히면 직접 단계를 눌러가며 보면 빠르게 잡힌다.
- 캐시 기본 동작 시각화 — Cache Hit / Miss, 읽기·쓰기 경로가 원본(DB)과 어떻게 오가는지 단계별로
- Eviction 정책 시각화 — LRU / LFU / FIFO가 같은 접근 패턴에서 무엇을 버리는지 비교 (§6)
- 장애 패턴 시각화 — 관통·눈사태·스탬피드가 어떻게 DB로 트래픽을 몰아넣는지, 방어가 어떻게 막는지 (§7)
§1. 캐싱이란 무엇인가
캐시(Cache)는 "느리고 비싼 원본(origin)"에 대한 접근을 줄이기 위해, "빠르고 가까운 곳"에 결과를 미리 복사해두는 것이다. 캐싱은 그 복사본을 만들고 활용하는 행위 전체다.
핵심은 캐시가 원본의 대체물이 아니라 사본이라는 점이다.
- 원본은 어딘가에 항상 존재한다.
- 캐시는 언제든 비워져도 시스템이 (느려질 뿐) 정상 동작해야 한다.
- 이 전제가 깨지면 그건 캐시가 아니라 그냥 또 하나의 저장소다.
토론 포인트 1: "Redis에만 있고 DB엔 없는 데이터"는 캐시인가? (답: 아니다. 그건 primary storage다.)
도서관 비유
- DB = 지하 서고 깊숙한 책장. 느리지만 모든 데이터가 다 있음
- 캐시 = 사서 책상 위의 "잘 나가는 책" 바구니. 빠르고 가까움
- 손님이 책을 찾으면 매번 지하 서고로 안 내려가고, 책상 위를 먼저 확인한다.
왜 필요한가
대부분의 시스템에서 비용은 균등하지 않다. 한 번 만든 결과를 다시 만드는 비용이 너무 크기 때문에 캐싱이 필요해진다. 비싼 작업의 예는 이렇다.
- DB 조인이 많은 무거운 쿼리
- 외부 API 호출 (네트워크 RTT + rate limit)
- 복잡한 연산 (집계, 직렬화, 암호화, 렌더링)
- 디스크/원격 스토리지 접근
캐싱이 효과적인 이유는 접근 패턴이 편향돼 있기 때문이다. 파레토(20% 데이터가 80% 요청). 모든 데이터가 균등하게 요청되면 캐시는 의미가 없다.
§2. 동작 원리: 지역성과 메모리 계층
캐싱이 통하는 근본 이유는 **두 가지 지역성(Locality)**이다.
- 시간 지역성(Temporal Locality) — 한 번 접근한 데이터는 곧 다시 접근될 가능성이 높다 (예: 인기 게시글, 로그인 유저 정보)
- 공간 지역성(Spatial Locality) — 어떤 데이터에 접근하면 그 근처도 곧 접근한다 (예: 게시글 목록 → 상세 진입)
컴퓨터는 본질적으로 속도·용량·가격이 반비례하는 계층 구조다.
| 계층 | 대략 지연시간 | 특징 |
|---|---|---|
| CPU 레지스터 / L1 | ~1ns | 매우 빠름, 매우 작음 |
| L2 / L3 | 수~수십 ns | |
| 메인 메모리 (RAM) | ~100ns | |
| 로컬 SSD | 수십~수백 μs | |
| 같은 DC 네트워크 (Redis 등) | 0.5~수 ms | |
| 디스크 / 원격 / 외부 API | 수~수백 ms | 느리고 큼 |
숫자는 정확한 벤치마크가 아니라 자릿수(order of magnitude) 감각으로 보면 된다. 핵심은 계층마다 대략 10~1000배씩 차이가 난다는 것이다.
캐싱은 결국 이 계층에서 더 위(빠른) 계층에 자주 쓰는 데이터를 끌어올려두는 것이다. CPU 캐시·Redis·CDN은 추상 수준만 다를 뿐 같은 원리다.
§3. 핵심 용어
- Cache Hit — 찾는 데이터가 캐시에 있음
- Cache Miss — 캐시에 없어 원본까지 가야 함
- Hit Ratio(적중률) =
hits / (hits + misses). 캐시 효과를 판단하는 1차 지표 - Cache Line / Entry — 캐시에 저장되는 단위
- Eviction(축출) — 공간 부족으로 기존 항목을 내보내는 것
- TTL(Time To Live) — 항목의 유효 기간
- Staleness — 캐시 값이 원본과 달라진 상태 (stale data)
- Warm-up / Cold Cache — 비어있는 초기 상태(cold)를 미리 채우는 것(warm-up)
토론 포인트 2: 적중률 99%인 캐시가 항상 좋은 캐시인가? 적중률만 높고 hit당 절약 비용이 작으면 무의미하다. 반대로 적중률 60%여도 miss 1건의 비용(외부 API 1초)이 크면 충분히 가치 있다. **"적중률 × hit당 절약 비용"**으로 봐야 한다.
§4. 캐시는 어디에나 있다: 계층별 예시
주니어가 "캐시 = Redis"라고 생각하기 쉬운데, 실제로는 요청 경로 곳곳에 깔려 있다.
① 클라이언트 / 프론트엔드
- 브라우저 캐시 — 이미지·JS·CSS를 로컬 디스크에 저장. 네이버 로고는 매번 다운로드하지 않음
- HTTP 캐시 — 서버가
Cache-Control: max-age=3600헤더로 "1시간 재사용 OK"를 알려줌.ETag·Last-Modified로 변경 검증
② 네트워크 / 인프라
- CDN (Cloudflare, CloudFront) — 엣지 서버가 원본 콘텐츠를 캐싱. 서울 서버 이미지를 미국 유저가 요청해도 미국 엣지에서 바로 서빙
③ 애플리케이션 / 백엔드
- 분산 캐시 — Redis, Memcached. RAM 기반이라 매우 빠르고, 모든 인스턴스가 같은 값을 공유
- 실무 예: 공지사항 목록, 실시간 인기 검색어, 세션/토큰, 랭킹
- 로컬(프로세스 내) 캐시 — Caffeine, Guava,
ConcurrentHashMap. 네트워크가 없어 가장 빠름. 단 인스턴스마다 따로 존재
④ 저장소 내부
- DB 버퍼 풀 / shared_buffers — DB가 디스크 페이지를 메모리에 캐싱
- OS Page Cache — 파일 시스템 읽기 캐싱 (Kafka 글의 zero-copy가 이걸 활용)
⑤ 하드웨어
- CPU L1/L2/L3 캐시 — 가장 원초적인 캐싱
토론 포인트 3: 로컬 캐시(Caffeine) vs 분산 캐시(Redis), 언제 무엇을?
- 로컬: 가장 빠름(네트워크 없음). 단 인스턴스마다 값이 다를 수 있고(정합성 어려움) 메모리가 중복된다.
- 분산: 모든 인스턴스가 같은 값을 공유. 단 네트워크 비용 + 단일 장애점 위험이 있다.
- 실무에서는 L1 로컬 + L2 Redis를 겹친 다층(multi-tier) 캐시도 흔하다.
§5. 읽기/쓰기 전략 (Caching Patterns)
가장 중요한 부분이자 토론거리가 많은 부분이다. 어디서 캐시를 채우고, 쓰기는 누가 책임지느냐의 차이다.
5-1. Cache-Aside (Look-Aside): 가장 흔함
앱이 캐시를 직접 관리한다.
읽기:
1) 캐시 조회
2) hit → 반환
3) miss → DB 조회 → 캐시에 저장 → 반환
쓰기:
1) DB 업데이트
2) 캐시 무효화(삭제) 또는 갱신
- 단순하고, 캐시 장애 시에도 DB로 정상 동작한다(캐시 의존성 낮음)
- miss 시 지연(3단계)이 있고, 정합성 관리를 앱이 책임진다
- Spring
@Cacheable의 기본 동작이 이 형태에 가깝다
5-2. Read-Through
앱은 캐시만 보고, 캐시 라이브러리가 miss 시 알아서 DB를 로딩한다.
- 흐름은 Cache-Aside와 비슷하나 로딩 책임이 캐시 계층에 있다
- 코드가 깔끔해지지만 캐시 추상화에 강하게 결합된다
5-3. Write-Through
쓰기 시 캐시와 DB에 동시(동기적)로 기록한다.
- 캐시가 항상 최신이라 정합성이 좋다
- 쓰기 지연이 증가하고, 안 읽힐 데이터까지 캐싱하는 낭비가 있다
5-4. Write-Back (Write-Behind)
쓰기를 일단 캐시에만 하고 DB는 나중에 비동기로 모아서 기록한다.
- 쓰기가 매우 빠르고, 쓰기 폭주를 흡수한다(버퍼링)
- 캐시 유실 시 데이터 손실 — 정합성이 가장 위험하다
- DB 버퍼 풀, 일부 메시지/로그 파이프라인이 이 방식이다
5-5. Write-Around
쓰기는 DB로만 하고 캐시는 안 건드린다. 다음 읽기 때 비로소 캐싱된다.
- 한 번 쓰고 잘 안 읽는 데이터에 적합하다
토론 포인트 4: 우리 서비스의 "조회수/좋아요"는? 쓰기 폭주 + 약간의 손실 허용이 가능하다면 Write-Back / 배치 집계가 후보다. 반대로 결제 잔액이라면? 절대 Write-Back을 쓰면 안 된다. Write-Through나 synchronous만 가능하다.
§6. 스케줄링 정책 (Eviction)
캐시 공간은 유한하므로 무엇을 버릴지 정해야 한다.
| 정책 | 기준 | 특징 |
|---|---|---|
| LRU (Least Recently Used) | 가장 오래 안 쓴 것 | 가장 흔함, 시간 지역성 활용 |
| LFU (Least Frequently Used) | 가장 적게 쓴 것 | 인기 데이터 유지에 강함. 과거 인기에 갇히는 단점 |
| FIFO | 먼저 들어온 것 | 단순. 지역성 무시 |
| TTL 기반 | 만료된 것 | 시간 기준. 정합성 도구로도 쓰임 |
| Random | 무작위 | 의외로 나쁘지 않고 매우 쌈 |
| W-TinyLFU | LFU+LRU 혼합 | Caffeine 기본. 현대적이고 적중률 우수 |
Redis는 maxmemory-policy로 allkeys-lru, allkeys-lfu, volatile-ttl 등을 설정한다.
세 정책이 같은 접근 패턴에서 서로 다른 항목을 버리는 걸 직접 보고 싶다면 Eviction 정책 시각화에서 LRU·LFU·FIFO를 나란히 돌려볼 수 있다.
토론 포인트 5: LRU의 약점은? Cache pollution이다. 스캔성 대량 조회(예: 야간 배치) 한 번에 인기 데이터가 전부 밀려난다. 그래서 W-TinyLFU 같은 현대적 정책이 등장했다.
§7. 캐싱이 어려운 이유: 장애 패턴
"There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton
캐싱의 난이도는 "넣기"가 아니라 언제 어떻게 무효화하고 방어하느냐에 있다.
아래 세 장애 패턴이 어떻게 트래픽을 DB로 몰아넣는지, 그리고 방어 기법이 그걸 어떻게 막는지는 장애 패턴 시각화에서 단계별로 직접 볼 수 있다.
7-1. 캐시 관통 (Cache Penetration)
존재하지 않는 데이터를 계속 조회하면 캐시가 항상 miss라 매번 DB로 직행한다.
- 악의적 공격(없는 ID 대량 요청)에 취약하다
- 대응: null도 짧은 TTL로 캐싱(예: "없음"을 30초간 기억) / Bloom Filter로 "확실히 없는" 키를 사전 차단
7-2. 캐시 눈사태 (Cache Avalanche)
대량의 키가 동시에 만료되거나 캐시 서버가 통째로 다운되면, 트래픽이 한꺼번에 DB로 몰려 DB가 과부하·장애에 빠진다.
- 대응: TTL에 랜덤 지터(jitter) 추가(예: 300초 ± 60초) / 다층 캐시(L1+L2) / 캐시 가용성 확보(클러스터, replica)
7-3. 캐시 스탬피드 / Thundering Herd (Hot Key)
인기 단일 키가 만료되는 순간, 수많은 요청이 동시에 miss가 나서 전부 같은 DB 쿼리를 동시에 던진다.
- 대응: 분산 락/뮤텍스(첫 요청만 DB 조회, 나머지는 대기 후 캐시 사용) / Refresh-Ahead(만료 전에 미리 갱신) / 확률적 조기 만료(만료 임박 시 일정 확률로 미리 갱신)
7-4. 정합성 (Consistency): 가장 근본적인 문제
캐시와 DB는 별개 저장소라 둘 사이에 반드시 시차가 생긴다. "DB 업데이트 → 캐시 삭제" 순서에서도 동시성 때문에 stale이 다시 캐싱될 수 있다.
대표적인 race condition은 이렇다.
A: DB read (old=1) ← A가 먼저 읽음
B: DB write (new=2)
B: cache delete
A: cache write(old=1) ← A가 오래된 값을 다시 박아버림 ❌
토론 포인트 6: 캐시를 "업데이트"할까, "삭제"할까? 보통 삭제(invalidate)가 안전하다. 업데이트는 위 race에 더 취약하고, 어차피 안 읽힐 값을 계산하는 낭비도 있다. 삭제 후 다음 읽기에서 자연스럽게 채우는 게 단순하고 견고하다.
§8. 정합성을 다루는 현실적 패턴
완벽한 정합성은 비싸다. 실무는 **"얼마나 stale을 허용할 수 있는가"**를 먼저 정한다.
- TTL을 짧게 — 가장 단순. "최대 N초 오래될 수 있음"을 명시적으로 허용
- 쓰기 시 캐시 삭제 (Cache-Aside invalidation) — 가장 흔한 기본형
- Write-Through — 정합성이 중요하고 쓰기 빈도가 낮을 때
- 지연 이중 삭제 (Delayed Double Delete) — 쓰기 시 삭제 + 짧은 지연 후 한 번 더 삭제. race로 다시 박힌 stale 제거
- 변경 이벤트 기반 무효화 — DB 변경 → 메시지/CDC(이벤트 드리븐·Debezium) → 캐시 무효화. 다중 인스턴스 로컬 캐시 동기화에 유용
- 버전/세대(generation) 키 — 키에 버전을 붙여 갱신 시 키 자체를 바꿔버림 (
product:v3:42)
핵심 사고방식: *"이 데이터가 1초 / 10초 / 1분 오래되면 사용자·비즈니스에 무슨 일이 생기는가?"*를 먼저 질문한다.
- 잔액·재고: 민감 → 짧은 TTL + Write-Through + 락
- 추천·랭킹·조회수: 관대 → 긴 TTL + Write-Back/배치 OK
- 프로필·공지: 중간 → Cache-Aside + 쓰기 시 삭제
§9. 언제 캐싱하나: 적합·부적합 상황
캐시는 모든 문제를 해결해주진 않는다. 캐시를 무조건 빠른 것으로 외운 채 모든 곳에 박으면 오히려 시스템이 무너질 수 있다.
9-1. 트레이드오프
캐시는 "속도"를 얻기 위해 "데이터 실시간 정확성"과 "메모리 비용·복잡도"를 맞바꾸는 트레이드오프 기술이다.
9-2. 장점: 무엇을 얻나
| 장점 | 설명 |
|---|---|
| 극적 latency 감소 | 디스크/네트워크를 안 거치고 RAM에서 처리 (ms → μs) |
| DB 부하 감소 | 캐시가 앞에서 막아주는 방패. 커넥션 풀 고갈 예방 |
| 컴퓨팅 비용 절감 | 무거운 집계/조인을 매번 계산하지 않고 한 번 계산 후 재사용 |
| 일시적 가용성 확보 | DB가 잠깐 죽어도 캐시에 남은 값으로 일부 서비스 지속 |
9-3. 단점: 무엇을 잃나
| 단점 | 설명 |
|---|---|
| 데이터 불일치 (가장 치명적) | 원본 변경 → 캐시 미반영 → stale 노출 |
| 메모리 비용·한계 | RAM은 SSD보다 비싸고 작음. 관리 못 하면 OOM |
| 시스템 복잡도 증가 | 무효화 로직·TTL·정합성 디버깅이 따라붙음 |
| Cold Start 위험 | 캐시 비었을 때 트래픽이 DB로 몰림 (스탬피드) |
9-4. 적합한 경우: "Read Heavy, Write Light"
캐시 효율이 극대화되는 건 자주 읽히지만 잘 안 변하는 데이터다.
| 데이터 유형 | 예시 | 왜 |
|---|---|---|
| 반복 조회 + 변경 적음 | 공지사항, FAQ, 카테고리, 배너 | 한 번 캐싱하면 hit율 압도적 |
| 연산 비용이 큰 결과 | 어제 베스트셀러, 시간 단위 통계 대시보드 | 무거운 집계를 한 번만 |
| 일시적 stale 허용 | 실시간 인기 검색어 (1~2분 오차 OK) | "관대한 정합성"이 캐시와 궁합 |
| 세션·인증 정보 | JWT, 로그인 세션, 블랙리스트 | 페이지마다 조회 + 짧은 TTL 자연스러움 |
9-5. 부적합한 경우: 캐시가 독이 되는 곳
물처럼 시시각각 변하거나, 절대적 정확성이 필요한 데이터다.
| 데이터 유형 | 예시 | 왜 안 되나 |
|---|---|---|
| Write Heavy | 배달 라이더 실시간 위치, 채팅 메시지 | 매번 무효화 → 캐시 오버헤드가 이득보다 큼 |
| 0원 오차 금지 (금융/결제) | 계좌 잔액, 주식 호가, 결제 승인 상태 | 캐시 불일치 = 잘못된 송금 = 대형 사고 |
| 일회성·극단적 개인화 | 마이페이지 "지난달 영수증" | 본인만 한 번 보고 끝 → hit 0, 메모리만 차지 |
| 보안 민감 정보 | 주민번호, 비밀번호, 카드 CVV | RAM 덤프 등 메모리 공격면이 더 큼 |
9-6. 결정 매트릭스 (2×2)
판단의 두 축은 읽기 빈도 × 쓰기/변경 빈도다.
쓰기/변경 적음 쓰기/변경 많음
┌───────────────────────┬───────────────────────┐
읽기 │ ⭐ 캐시 최적 │ ⚠️ 신중히 │
많음 │ │ │
│ 공지·FAQ·카테고리 │ 좋아요/조회수 │
│ 베스트셀러·랭킹 │ → Write-Back/배치 │
│ 세션·인증 │ (손실 허용 시만) │
├───────────────────────┼───────────────────────┤
읽기 │ ❓ 캐시 의미 적음 │ ❌ 캐시 금지 │
적음 │ │ │
│ 하루 1회 조회 정적 정보 │ 실시간 위치·채팅 │
│ (TTL 짧게 or 안 함) │ 잔액·결제 (정확성 절대)│
└───────────────────────┴───────────────────────┘
⭐는 무조건 캐시, ❌는 절대 금지다.
9-7. 결정 체크리스트
캐시를 도입하기 전 스스로 던질 질문 5개다.
- 읽기/쓰기 비율은? Read-heavy가 아니면 캐시의 효용이 작다.
- stale을 얼마나 허용하나? "초 단위도 안 됨" = 캐시 거의 불가, "분 단위 OK" = 자유.
- 캐시가 죽으면 시스템이 견디나? 못 견디면 그건 캐시가 아니라 primary store다.
- hit율을 어떻게 측정·모니터링할 것인가? 측정 못 하면 캐시 효과를 모른다.
- 장애 패턴(§7)을 어떻게 막을 것인가? 스탬피드·관통·눈사태 방어를 미리.
5개 중 2개 이상 "모르겠음"이면 아직 캐시를 도입할 때가 아니다.
§10. 실사용 코드 (Spring Boot / Kotlin + Redis)
10-1. Spring @Cacheable (Cache-Aside)
@Service
class ProductService(
private val productRepository: ProductRepository,
) {
// 조회: 캐시 miss일 때만 DB 접근 후 'products' 캐시에 저장
@Cacheable(cacheNames = ["products"], key = "#id")
fun getProduct(id: Long): ProductDto =
productRepository.findById(id)
.orElseThrow { ProductNotFoundException(id) }
.toDto()
// 갱신: DB 업데이트 후 캐시 항목 삭제(무효화)
@CacheEvict(cacheNames = ["products"], key = "#id")
@Transactional
fun updateProduct(id: Long, req: UpdateProductRequest) {
val product = productRepository.findById(id).orElseThrow()
product.update(req)
// @Transactional 커밋 후 캐시가 비워지는 타이밍 주의
}
}함정 2개:
@Cacheable은 프록시 기반 AOP라서 같은 클래스 내부 호출(self-invocation)에는 적용되지 않는다.@CacheEvict와@Transactional커밋 타이밍 차이로 stale이 생길 수 있어, 커밋 이후 무효화가 필요하면TransactionSynchronization또는@TransactionalEventListener(AFTER_COMMIT)를 고려한다.
10-2. Redis 직접 + 스탬피드 방어 (분산 락 + 지터)
fun getPopularRanking(): Ranking {
redisTemplate.opsForValue().get(KEY)?.let { return it } // ① 캐시 조회
// ② miss → 락 획득한 1개 요청만 DB 계산 (SET NX)
val locked = redisTemplate.opsForValue()
.setIfAbsent(LOCK_KEY, "1", Duration.ofSeconds(5)) == true
return if (locked) {
try {
val ranking = rankingRepository.calculateExpensiveRanking() // 무거운 연산
redisTemplate.opsForValue().set(KEY, ranking, ttlWithJitter())
ranking
} finally {
redisTemplate.delete(LOCK_KEY)
}
} else {
// ③ 락 못 잡은 요청은 잠깐 대기 후 캐시 재조회
Thread.sleep(50)
redisTemplate.opsForValue().get(KEY) ?: rankingRepository.calculateExpensiveRanking()
}
}
// TTL 눈사태 방지: 기본 TTL ± 랜덤 지터
fun ttlWithJitter(): Duration =
Duration.ofSeconds(300 + Random.nextLong(0, 60))10-3. HTTP / CDN 캐싱
Cache-Control: public, max-age=3600, stale-while-revalidate=60
ETag: "abc123"
- 정적 리소스나 공개 API 응답을 브라우저·CDN 계층에서 캐싱해 우리 서버 트래픽 자체를 줄인다
ETag+If-None-Match로 변경이 없으면304 Not Modified를 준다(본문 전송 생략 = 대역폭 절약)stale-while-revalidate는 만료돼도 짧은 시간 동안 stale 응답을 주면서 백그라운드로 갱신한다(스탬피드 자연 완화)
§11. 다른 주제와의 연결
이번 캐시 정리를 다른 스터디 주제와 묶어보면 이렇다.
- Kafka — 카프카의 sparse 인덱스는 mmap으로 OS page cache에 상주한다. 즉 인덱스 자체가 "캐시되도록" 설계됐다. 그리고 zero-copy(
sendfile)는 page cache에서 NIC로 직송하는, 캐시를 user-space를 거치지 않고 활용하는 극단의 사례다. OffsetStore도ConcurrentHashMapcache + Log append의 조합이라 본질이 캐싱과 같다. - 큐 — 큐의 "버퍼링"과 캐시의 "사본 보관"은 역할이 다르다. 큐는 처리 속도 차이를 흡수하고(시간축 분리), 캐시는 같은 데이터를 빠른 계층으로 끌어올린다(공간축 분리).
- MSA — 분산 환경에선 로컬 캐시 정합성이 어렵다. CDC 기반 무효화(이벤트 드리븐)가 자주 등장한다.
§12. 주니어와 토론할 질문 모음
설명 후 던지면 "암기"인지 "이해"인지가 갈리는 질문들이다.
개념·정합성
- 캐시와 일반 저장소(DB)의 본질적 차이는? (사본 vs 원본, 유실 허용 여부)
- 적중률이 높으면 무조건 좋은 캐시인가?
- "DB 업데이트 후 캐시 업데이트" vs "DB 업데이트 후 캐시 삭제" — 무엇이 더 안전하고 왜?
- 로컬 캐시를 여러 인스턴스에서 쓰면 생기는 문제는? 어떻게 동기화할까? (CDC, pub/sub 무효화)
결정·트레이드오프
- 좋아요/조회수 / 유저 프로필 / 결제 잔액 — 각각 어떤 캐싱 전략과 TTL이 맞을까? 왜?
- 당신이 담당한 기능을 §9-6 2×2 매트릭스에 매핑해보라. ⚠️/❓ 영역에 들어가는 게 있다면, 캐시 도입 여부를 어떻게 결정할까?
- §9-3 단점 4개 중 당신 서비스에 가장 큰 리스크는 무엇이라 생각하나? 어떻게 완화할까?
- "캐싱하면 빨라진다"가 항상 참인가? 캐싱이 오히려 손해인 경우는? (낮은 적중률, 무거운 직렬화 비용, 정합성 버그로 인한 장애 비용)
장애·운영
- 캐시 서버(Redis)가 통째로 죽으면 우리 시스템은 어떻게 되는가? (캐시 의존도 점검 → fallback 전략)
- 캐시 스탬피드를 막는 방법을 2가지 이상 말해보라.
- CDN의
stale-while-revalidate는 어떤 장애 패턴을 자연스럽게 완화하는가? - Bloom Filter는 false positive는 있지만 false negative는 없다. 이게 캐시 관통 방어에 왜 충분한가?
요약
캐싱은 느린 원본의 사본을 가까운 곳에 둬서 빠르게 만드는 기술이지만, 그 대가로 정합성과 추가적인 장애 표면(failure surface)을 떠안는 트레이드오프다.
그래서 캐시를 도입할 때는 "어떻게 캐싱할까"보다 "무엇을 캐싱하지 않을까", "얼마나 오래됨을 허용할까"를 먼저 따져봐야 한다.