본문 바로가기
WEB,WAS

웹에서 사용하는 캐시 총정리 — 브라우저부터 서버까지 13가지

by Rainbound-IT 2026. 2. 11.
반응형

 

배포했는데 반영이 안 된다? 강력 새로고침(Ctrl+Shift+R)으로도 안 풀린다? 웹 캐시가 어디서 어떻게 동작하는지 전체 그림을 알면, 원인을 빠르게 찾을 수 있다.

 


요청이 서버에 도달하기까지 거치는 캐시 계층

사용자가 URL을 입력하고 Enter를 누르면, 요청은 여러 캐시 계층을 순서대로 거친다. 어딘가에서 캐시 HIT가 발생하면 그 아래 계층은 실행되지 않는다.

 

[ 클라이언트 영역 ]

순서 캐시 설명
1 Memory Cache 가장 빠름 (RAM)
2 Disk Cache (HTTP Cache) 가장 흔한 캐시
3 Service Worker Cache 개발자가 직접 제어
4 Back-Forward Cache 뒤로가기 전용
5 Application Cache JS 런타임 (프레임워크, Storage)

 

[ 네트워크 연결/정책 캐시 ]

순서 캐시 설명
6 DNS Cache 도메인 → IP 변환 결과 캐시
7 TLS Session Cache HTTPS 핸드셰이크 결과 재사용
8 CORS Preflight Cache OPTIONS 요청 결과 캐시
9 HSTS Cache HTTP → HTTPS 강제 전환 정책 캐시

 

[ 서버 / 인프라 영역 ]

순서 캐시 설명
10 CDN Cache Edge 서버 캐시
11 Reverse Proxy Cache Origin 앞단 캐시
12 Server Application Cache Redis, Memcached
13 Database Cache Buffer Pool, Query Cache

1. 브라우저 캐시

1-1. Memory Cache

탭 프로세스의 RAM에 저장되는 캐시. 같은 페이지에서 이미 로드한 리소스를 다시 요청하면 네트워크 없이 즉시 반환한다.

  • 저장 위치: 탭 프로세스 메모리 (RAM)
  • 생명주기: 탭을 닫으면 소멸
  • 대상: 이미 로드된 이미지, JS, CSS, preload 리소스
  • 개발자 제어: 불가 (브라우저 자동 관리)

개발자 도구 Network 탭의 Size 열에 (memory cache)로 표시된다.

1-2. Disk Cache (HTTP Cache)

가장 핵심적인 캐시. 서버 응답을 디스크에 저장하고, Cache-Control 헤더에 따라 재사용한다.

  • 저장 위치: 디스크 (파일 시스템)
  • 생명주기: Cache-Control, Expires 헤더에 따라 결정
  • 대상: HTTP 응답 전체 (HTML, JS, CSS, 이미지, 폰트, API 응답)

Cache-Control 주요 디렉티브

 

디렉티브 의미
max-age=31536000 1년간 캐시 사용 (재검증 없이)
no-cache 캐시 저장은 하지만, 사용 전 매번 서버에 재검증
no-store 절대 캐시하지 않음
private 브라우저만 캐시 가능 (CDN은 캐시 금지)
public CDN 포함 어디서든 캐시 가능
must-revalidate 만료 후 반드시 서버에 검증
s-maxage=3600 CDN/프록시 전용 캐시 시간 (브라우저는 무시)
stale-while-revalidate=60 만료된 캐시를 일단 사용하면서 백그라운드 갱신
immutable max-age 기간 내 재검증 자체를 안 함

no-cache vs no-store 차이가 중요하다.

  • no-cache: "저장해도 되지만, 쓰기 전에 서버한테 물어봐" → 304 가능
  • no-store: "아예 저장하지 마" → 매번 전체 응답을 받아야 함

ETag를 이용한 재검증 흐름

첫 요청:

  • 브라우저 → GET /api/data
  • 서버 ← 200 OK + ETag: "abc123" + 본문

재요청 (캐시 만료 후):

  • 브라우저 → GET /api/data + If-None-Match: "abc123"
  • 변경 없으면 → 서버 ← 304 Not Modified (본문 없음, 빠름!)
  • 변경 있으면 → 서버 ← 200 OK + ETag: "def456" + 새 본문

304 응답은 본문이 없어서 수 바이트만 전송된다. no-store와 비교하면 대역폭 차이가 크다.

주의: 강력 새로고침은 만능이 아니다

강력 새로고침(Ctrl+Shift+R)은 브라우저가 직접 로드하는 리소스만 캐시를 우회한다.

  • O 우회됨 — HTML 문서, <script>, <link>, <img> 
  • X 안 됨 — JavaScript 코드 내 fetch() 호출
  • X 안 됨 — JavaScript 코드 내 dynamic import()

React, Next.js 같은 SPA/SSR 프레임워크는 페이지 로드 후 JS에서 fetch()로 데이터를 가져오는 경우가 많다. 이 요청들은 강력 새로고침의 영향을 받지 않아서, 디스크 캐시에 남은 이전 응답을 그대로 사용할 수 있다.

1-3. Service Worker Cache

브라우저와 서버 사이에 끼어드는 프록시. 개발자가 JS 코드로 캐시를 완전히 제어한다.

  • 저장 위치: Cache Storage API (디스크)
  • 생명주기: SW 코드가 명시적으로 삭제할 때까지 영구
  • 제어: caches.open(), cache.put(), cache.delete()

 

캐시 전략 패턴

전략 동작 적 합한 대상
Cache First 캐시 있으면 사용, 없으면 네트워크 정적 리소스 (JS, CSS, 이미지)
Network First 네트워크 우선, 실패 시 캐시 API 데이터, 실시간 콘텐츠
Stale While Revalidate 캐시 즉시 반환 + 백그라운드 갱신 뉴스, 피드, 준실시간 데이터
Cache Only 캐시만 사용 완전 오프라인 앱
Network Only 네트워크만 사용 결제, 인증 등

강력 새로고침으로도 안 풀리는 이유

 

상황요청  흐름
일반 요청 브라우저 → SW 가로챔 → Cache Storage → 응답
강력 새로고침 브라우저 → SW 가로챔 → Cache Storage → 응답 (여전히 SW 경유!)
기록 삭제 후 브라우저 → (SW 없음) → 서버 → 응답

SW는 브라우저와 서버 사이의 프록시처럼 동작하기 때문에, 강력 새로고침이 HTTP 캐시를 우회해도 SW 레이어는 여전히 통과한다. "인터넷 기록 삭제"를 해야 SW 등록 자체가 해제된다.

 

1-4. Back-Forward Cache (bfcache)

뒤로가기/앞으로가기 시 페이지를 즉시 복원하는 캐시. 네트워크 요청 없이 JS 상태, DOM, 스크롤 위치까지 모두 보존한다.

페이지 A에서 페이지 B로 이동한 후 뒤로가기를 누르면, bfcache에서 페이지 A를 즉시 복원한다. JS 변수값, 폼 입력값, 스크롤 위치가 모두 유지된다.

bfcache가 비활성화되는 조건:

  • Cache-Control: no-store 헤더가 있는 페이지
  • unload 이벤트 리스너가 등록된 페이지
  • 열린 WebSocket/WebRTC 연결이 있는 페이지

 

1-5. Application Cache (JS 런타임)

JavaScript 코드 레벨에서 관리하는 캐시들이다.

 

Framework 캐시

프레임워크 캐시  종류 저장 위치 제어 방법
Next.js App Router Router Cache 인메모리 staleTimes, router.refresh()
Next.js Full Route Cache 서버 (빌드 타임) revalidate, dynamic
Next.js Data Cache 서버 revalidateTag(), revalidatePath()
React Query Query Cache 인메모리 staleTime, gcTime
SWR Cache 인메모리 revalidateOnFocus
Apollo Client Normalized Cache 인메모리 fetchPolicy

Next.js App Router의 4가지 캐시 계층

  1. Request Memoization — 같은 렌더링 사이클 내 동일 fetch() 자동 중복 제거
  2. Data Cache — fetch() 응답을 서버에 영구 캐시, revalidate로 갱신
  3. Full Route Cache — 빌드 타임에 정적 렌더링된 HTML + RSC payload
  4. Router Cache — 방문한 경로의 RSC payload를 브라우저 인메모리에 캐시

Web Storage

종류 용량 생명주기 범위
localStorage ~5-10MB 영구 (명시적 삭제 전까지) 동일 origin 전체
sessionStorage ~5-10MB 탭 닫으면 소멸 해당 탭만
IndexedDB 수백 MB ~ GB 영구 동일 origin
Cookie ~4KB/개 Expires/Max-Age 또는 세션 도메인+경로

2. 네트워크 캐시

2-1. DNS Cache

도메인 이름 → IP 주소 변환 결과를 캐시한다. 여러 단계에 걸쳐 캐시가 존재한다.

  1. 브라우저 DNS 캐시 — 수 분간 유지
  2. OS DNS 캐시 — 수 분 ~ 수 시간
  3. 공유기/라우터 DNS 캐시
  4. ISP DNS 서버 캐시
  5. 권한 DNS 서버 — TTL 기반
  • 제어: DNS 레코드의 TTL 
  • 확인: Chrome chrome://net-internals/#dns
  • 초기화: ipconfig /flushdns (Windows) / dscacheutil -flushcache (macOS)
  • 주의: TTL을 짧게 설정해도 ISP가 무시할 수 있음

DNS 변경(예: 서버 이전) 후 "반영이 안 된다"는 문제의 대부분은 DNS 캐시 때문이다.

2-2. TLS Session Cache

HTTPS 연결의 핸드셰이크 비용을 줄이기 위한 캐시다.

 

연결 상황흐름 비용
첫 연결 TCP 3-way → TLS Full Handshake → 데이터 2-RTT
재연결 TCP 3-way → TLS Session Resume → 데이터 1-RTT
TLS 1.3 재연결 TCP 3-way → 0-RTT Resume → 데이터 첫 패킷에 데이터 포함

2-3. CORS Preflight Cache

Cross-Origin 요청 시 브라우저가 보내는 OPTIONS 요청(Preflight)의 결과를 캐시한다.

첫 Cross-Origin 요청:

  • 브라우저 → OPTIONS /api (Preflight)
  • 서버 ← 200 OK + Access-Control-Max-Age: 86400

이후 24시간 동안:

  • 브라우저 → POST /api (Preflight 생략, 캐시 사용)

Access-Control-Max-Age 헤더로 캐시 시간을 제어한다. 설정하지 않으면 Chrome은 기본 2시간(7200초) 캐시한다.

2-4. HSTS Cache

Strict-Transport-Security 헤더를 받은 도메인에 대해, 이후 요청을 브라우저 레벨에서 자동으로 HTTPS로 업그레이드한다. 네트워크 요청 없이 브라우저가 직접 처리한다.

헤더 예시: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

  • 1년간 이 도메인 + 서브도메인 모두 HTTPS 강제
  • preload 옵션을 설정하면 브라우저 사전 목록에 등록되어 첫 방문부터 HTTPS 적용

확인: Chrome chrome://net-internals/#hsts


3. 서버/인프라 캐시

3-1. CDN Cache

사용자와 가까운 Edge Location에 콘텐츠를 캐시한다. 원본 서버(Origin)까지 요청이 가지 않아도 된다.

  • 제어: Cache-Control 헤더 + CDN 자체 설정
  • 확인: 응답 헤더 X-Cache: Hit from cloudfront
  • 무효화: Invalidation 요청
Cache-Control 값 CDN 동작
public, max-age=86400 Edge에 24시간 캐시
public, s-maxage=3600 Edge에 1시간 캐시 (브라우저에는 max-age 적용)
private Edge 캐시 안 함
no-store Edge 캐시 안 함

CloudFront Invalidation:

aws cloudfront create-invalidation --distribution-id E123 --paths "/*"

전파에 수 분이 소요되며, 월 1,000건까지 무료다.

3-2. Reverse Proxy Cache

Origin 서버 앞단에 위치하는 프록시(Nginx, Varnish 등)가 응답을 캐시한다.

# Nginx 프록시 캐시 설정 예시
proxy_cache_path /tmp/cache levels=1:2 keys_zone=my_cache:10m max_size=1g;

location /api/ {
    proxy_cache my_cache;
    proxy_cache_valid 200 10m;      # 200 응답 10분 캐시
    proxy_cache_valid 404 1m;       # 404 응답 1분 캐시
    proxy_cache_bypass $http_cache_control;
    add_header X-Cache-Status $upstream_cache_status;
}

CDN과 비슷하지만, 단일 위치에서 더 세밀한 캐시 규칙을 적용할 수 있다.

3-3. Server Application Cache

애플리케이션 코드에서 직접 관리하는 캐시다.

종류 특징 용도
Redis 인메모리, 분산 가능, TTL, 다양한 자료구조 세션, API 응답, 계산 결과
Memcached 인메모리, 멀티스레드, 단순 K-V 대규모 단순 캐시
In-Process (Map/LRU) 프로세스 메모리, 재시작 시 소멸 프로세스 내 임시 캐시

 

3-4. Database Cache

종류 설명
Buffer Pool 자주 접근하는 데이터 페이지를 메모리에 유지 (InnoDB)
Query Cache 동일 쿼리 결과 캐시 (MySQL 8.0에서 제거됨)
Prepared Statement Cache 파싱된 SQL 실행 계획 캐시
Connection Pool DB 연결 재사용 (캐시는 아니지만 유사 효과)
Materialized View 쿼리 결과를 테이블로 저장 (수동/주기적 갱신)

전체 요약 비교표

 

계층 캐시 Ctrl+Shift+R 기록 삭제 제어 방법
브라우저 Memory Cache O O 불가 (자동)
브라우저 Disk Cache (직접 요청) O O Cache-Control 헤더
브라우저 Disk Cache (JS fetch) X O fetch options, 서버 헤더
브라우저 Service Worker X O SW 코드
브라우저 bfcache O O Cache-Control: no-store
JS 런타임 Framework Cache O O 프레임워크 설정
JS 런타임 Web Storage X O 코드에서 직접 관리
네트워크 DNS Cache X X TTL, flushdns
네트워크 TLS Session X X 서버 설정
네트워크 CORS Preflight O O Access-Control-Max-Age
네트워크 HSTS X 부분적 max-age
인프라 CDN X X Invalidation, Cache-Control
인프라 Reverse Proxy X X 서버 설정
서버 App Cache (Redis 등) X X TTL, 수동 삭제
서버 DB Cache X X DB 설정

핵심 포인트: 강력 새로고침(Ctrl+Shift+R)은 "브라우저가 직접 요청하는 리소스"만 우회한다. JS fetch(), Service Worker, Web Storage, 그리고 네트워크/서버 측 캐시는 전혀 영향을 받지 않는다.


캐시 문제 디버깅 가이드

브라우저 개발자 도구 (F12)

Network 탭:

  • Size 열: (memory cache), (disk cache), (ServiceWorker) 표시로 어느 캐시에서 왔는지 확인
  • Disable cache 체크박스: 개발 중 디스크 캐시 비활성화

Application 탭:

  • Cache Storage: Service Worker 캐시 내용 확인/삭제
  • Service Workers: 등록된 SW 확인/해제
  • Storage: localStorage, sessionStorage, IndexedDB, Cookies 확인/삭제

서버 응답 헤더에서 확인할 것

 

헤더 의미
Cache-Control 캐시 정책 (가장 중요)
ETag 리소스 버전 식별자
Last-Modified 마지막 수정 시각
Vary 캐시 키 분기 기준 (Accept-Encoding, Cookie 등)
Age CDN에서 캐시된 후 경과 시간 (초)
X-Cache CDN 캐시 HIT/MISS (CloudFront)
CF-Cache-Status Cloudflare 캐시 상태

Chrome 내부 진단 도구

 

URL 용도
chrome://net-internals/#dns DNS 캐시 확인/초기화
chrome://net-internals/#hsts HSTS 캐시 확인/삭제
chrome://serviceworker-internals 등록된 모든 Service Worker 확인/해제

마무리

"캐시 때문에 안 된다"는 말을 할 때, 그 캐시가 어느 계층의 캐시인지를 특정하는 것이 문제 해결의 시작이다.

  • 배포 후 반영 안 됨 → Disk Cache(JS fetch), CDN Cache, Service Worker 순서로 의심
  • DNS 변경 후 안 됨 → DNS Cache (브라우저 → OS → ISP)
  • 뒤로가기 시 이전 상태 → bfcache
  • API 응답이 안 바뀜 → Framework Cache(React Query 등), Redis, Reverse Proxy
  • 강력 새로고침으로 안 풀림 → Service Worker 또는 JS fetch() 디스크 캐시

각 캐시 계층의 특성을 이해하면 "일단 캐시 비워봐"가 아닌, 정확한 원인 진단과 해결이 가능해진다.

반응형

댓글