HTTP 캐시 기초: Cache-Control·ETag·304로 브라우저 캐시 읽기

··설명 약 10분·작성: 해비·블로그 소개

HTTP 캐시에서 no-cache·no-store·ETag·304는 어떻게 다르게 작동하나요?

HTTP 캐시는 이전 응답을 재사용하거나 변경 여부만 확인해 전송을 줄입니다. Cache-Control의 규칙과 ETag·304의 재검증을 나눠 읽으세요. Network 헤더를 비교하며 저장 금지가 아닌 no-cache와 no-store를 구분합니다.

공식 문서 참고: MDN: Cache-Control·ETag와 304 재검증

HTTP 캐시가 필요한 이유

웹페이지는 HTML뿐 아니라 CSS·JavaScript·이미지·폰트·API 응답 등 많은 데이터를 요청합니다.

변하지 않은 데이터를 매번 다시 내려받으면 다음 비용이 생깁니다.

네트워크 전송 증가
서버 요청 증가
페이지 로딩 시간 증가
사용자 데이터 사용량 증가

그래서 HTTP는 응답을 일정 시간 재사용하거나 서버에 변경 여부만 확인하는 규칙을 제공합니다.

캐시는 빠르게 만드는 기능이지만 잘못 이해하면 수정한 파일이 반영되지 않는 것처럼 보이는 문제도 만들 수 있습니다.

fresh와 stale

캐시된 응답이 아직 유효한 상태를 fresh라고 표현합니다.

유효 기간이 지나 다시 확인이 필요한 상태는 stale이라고 표현합니다.

예를 들어 서버가 다음 Header를 보냈다고 가정합니다.

Cache-Control: max-age=60

max-age=60은 응답의 나이가 60초 미만인 동안 fresh로 볼 수 있다는 뜻입니다. 응답 생성 시각과 Age 헤더 등을 반영하므로 브라우저에 저장한 시점부터 항상 60초가 새로 시작되는 것은 아닙니다.

이 기간에는 네트워크 요청 없이 캐시를 사용할 수도 있습니다.

60초가 지나면 stale 상태가 되고 서버에 재검증할 수 있습니다.

실제 동작은 다른 Cache-Control 지시어와 브라우저 정책에 따라 달라질 수 있습니다.

Cache-Control 읽기

max-age

응답을 얼마 동안 fresh로 볼지 초 단위로 나타냅니다.

Cache-Control: max-age=3600

3600초 동안 재사용할 수 있다는 의미입니다.

no-cache

이름 때문에 캐시를 저장하지 말라는 뜻으로 오해하기 쉽습니다.

no-cache는 저장 자체를 무조건 금지한다는 뜻이 아니라 재사용하기 전에 서버에 재검증하도록 요구하는 의미로 사용됩니다.

Cache-Control: no-cache

no-store

응답을 저장하지 말라는 지시입니다.

Cache-Control: no-store

민감한 응답이나 저장 자체를 원하지 않는 데이터에서 고려할 수 있습니다.

public과 private

공유 캐시가 저장할 수 있는지와 관련된 지시어입니다.

브라우저 개인 캐시만 허용해야 하는 사용자별 응답에서는 private을 사용할 수 있습니다.

이번 과정에서는 세부 프록시 캐시보다 브라우저 Network에서 Header를 읽는 데 집중합니다.

ETag와 304 재검증

ETag는 서버가 응답 버전을 식별하기 위해 제공할 수 있는 값입니다.

서버 응답 예시입니다.

ETag: "task-list-v3"

캐시가 stale이 된 뒤 브라우저가 다시 요청할 때 이전 ETag를 함께 보낼 수 있습니다.

If-None-Match: "task-list-v3"

서버가 현재 데이터도 같은 버전이라고 판단하면 전체 응답 본문을 다시 보내지 않고 304를 응답할 수 있습니다.

브라우저
→ If-None-Match
→ 서버

서버
→ 변경 없음
→ 304 Not Modified
→ 브라우저가 기존 캐시 사용

304는 실패가 아닙니다.

오히려 기존 데이터가 여전히 유효하다는 확인 결과입니다.

Last-Modified와 If-Modified-Since

ETag 외에 수정 시각 기반 재검증도 사용할 수 있습니다.

서버 응답입니다.

Last-Modified: Wed, 16 Sep 2026 10:00:00 GMT

이후 요청에서 브라우저는 다음 Header를 보낼 수 있습니다.

If-Modified-Since: Wed, 16 Sep 2026 10:00:00 GMT

서버가 변경되지 않았다고 판단하면 304를 반환할 수 있습니다.

ETag와 Last-Modified는 서버 구성에 따라 함께 사용되거나 하나만 사용될 수 있습니다.

DevTools에서 캐시 확인하기

Network에서 같은 페이지를 두 번 새로고침해 비교해봅니다.

확인할 항목은 다음과 같습니다.

Status
Size
Time
Cache-Control
ETag
Last-Modified
If-None-Match
If-Modified-Since

브라우저에 따라 Size 열에 memory cache나 disk cache처럼 보일 수 있습니다.

이 표시는 구현과 버전에 따라 달라질 수 있으므로 정확한 저장 위치를 암기하기보다 네트워크 전송이 실제로 발생했는지와 응답 Header를 함께 봅니다.

Disable cache 사용 시 주의

DevTools Network에는 Disable cache 옵션이 있습니다.

개발 중 캐시 영향 없이 새 응답을 확인할 때 유용합니다.

하지만 이 옵션을 켜 둔 상태에서 테스트하면 실제 사용자의 캐시 동작과 달라질 수 있습니다.

개발 중 문제 재현
→ Disable cache 상태 확인

실제 캐시 동작 확인
→ Disable cache 해제 후 다시 테스트

강력 새로고침 역시 일반 새로고침과 캐시 사용 방식이 다를 수 있습니다.

따라서 캐시 문제를 확인할 때 어떤 조건으로 새로고침했는지 기록하는 것이 좋습니다.

캐시 때문에 화면이 오래된 경우

배포 후 JavaScript 파일을 수정했는데 이전 화면이 보인다고 가정하겠습니다.

무조건 서버 배포 실패로 단정하지 않습니다.

먼저 Network에서 실제로 어떤 파일을 받았는지 확인합니다.

1. 요청 URL 확인
2. Status 확인
3. Cache-Control 확인
4. ETag 또는 Last-Modified 확인
5. 응답 파일이 최신 내용인지 확인
6. Disable cache 상태에서 비교

정적 자산에 긴 캐시를 적용하는 서비스는 파일 이름에 해시를 포함해 내용이 바뀔 때 URL 자체가 달라지도록 구성하는 경우가 많습니다.

app.a1b2c3.js
→ 코드 변경
→ app.d4e5f6.js

이 방식은 긴 캐시와 새 배포를 함께 운영하기 쉽게 합니다.

다른 캐시와 구분하기

프론트엔드 개발에서는 여러 종류의 캐시를 만납니다.

브라우저 HTTP 캐시
CDN 캐시
Service Worker 캐시
Next.js 데이터 캐시
TanStack Query 캐시
애플리케이션 메모리 캐시

이들은 모두 같은 캐시가 아닙니다.

이번 글에서 다루는 것은 HTTP 응답 Header와 연결되는 브라우저 캐시입니다.

Next.js의 fetch cache나 TanStack Query의 staleTime을 HTTP Cache-Control과 같은 옵션으로 생각하면 안 됩니다.

각 계층마다 저장 위치와 무효화 기준이 다릅니다.

자주 하는 오해

no-cache면 아무것도 저장하지 않습니다

아닙니다.

저장할 수 있지만 재사용 전에 서버에 재검증하도록 요구하는 의미입니다.

저장을 막는 목적에는 no-store가 더 직접적입니다.

304는 오류입니다

아닙니다.

서버가 기존 캐시를 계속 사용해도 된다고 알려주는 정상적인 응답입니다.

브라우저 새로고침은 항상 서버에서 새 파일을 받습니다

새로고침 방식과 캐시 Header에 따라 달라질 수 있습니다.

DevTools에서 Disable cache를 켜고 테스트하면 실제 사용자와 같습니다

아닙니다.

캐시를 우회한 개발 환경이므로 실제 사용자 조건과 다를 수 있습니다.

Next.js 캐시와 브라우저 캐시는 같은 기능입니다

아닙니다.

서로 다른 계층에서 동작합니다.

독자 질문과 실전 확인

독자 질문

배포한 CSS가 이전 모습으로 보일 때 304는 오류일까요, 재사용 신호일까요?

웹 개발 사례

TaskFlow의 style.css가 오래돼 보이면 Network에서 실제 요청 URL과 Cache-Control·ETag·Status·Size를 기록합니다. 파일명이 해시로 바뀌었는지, Service Worker 등 별도 계층이 있는지도 분리합니다.

직접 확인할 문제

두 번째 요청에 If-None-Match가 있고 응답이 304인 상황과, Cache-Control: no-store인 상황을 비교하세요. 각 경우 브라우저가 저장본을 사용할 수 있는지, 어떤 추가 증거가 필요한지 적어 보세요.

예상 결과와 판단

304는 서버가 조건부 요청에 대해 기존 표현을 재사용할 수 있다고 알리는 정상 응답입니다. no-store는 응답 저장을 제한합니다. no-cache는 저장 금지가 아니라 사용 전 재검증 요구이므로 둘을 혼동하지 않습니다.

검증 범위

가상의 응답 헤더 비교입니다. 실제 배포·CDN·Service Worker·브라우저 캐시 동작은 확인하지 않았습니다. DevTools의 Disable cache 사용 중 관찰 결과를 일반 사용자 상태로 일반화하지 마세요.

다음 학습: 6회차에서는 브라우저와 서버의 메모리·저장소가 어디에 있는지 구분합니다.

학습 완료 체크리스트

  • HTTP 캐시가 필요한 이유를 설명할 수 있습니다.
  • fresh와 stale을 구분할 수 있습니다.
  • max-age·no-cache·no-store 차이를 설명할 수 있습니다.
  • ETag와 If-None-Match 관계를 이해했습니다.
  • 304가 오류가 아니라 재검증 결과라는 것을 설명할 수 있습니다.
  • DevTools에서 캐시 관련 Header를 찾을 수 있습니다.
  • 브라우저 HTTP 캐시와 프레임워크 캐시를 구분할 수 있습니다.

선택 실습: 같은 프로젝트로 연습하기

이 선택 실습은 TaskFlow의 요청 흐름을 설명하는 분석 노트를 만드는 연습입니다. 실행 앱을 새로 만드는 과정은 아닙니다.

HTTP 요청 전에 캐시 판단 단계를 추가합니다.

TaskFlow 요청 흐름 분석 노트

이전 체크포인트

cs-step-04-web-security

현재 상태

지금까지 TaskFlow 요청 흐름은 네트워크·인증·보안 경계를 추적합니다.

하지만 같은 요청을 다시 했을 때 실제 서버 응답을 새로 받는지 여부는 기록되어 있지 않습니다.

이번 목표

HTTP 요청 전에 캐시 판단 단계를 추가합니다.

요청할 리소스 확인
↓
사용 가능한 fresh cache 존재?
├─ 예 → 캐시 사용 가능
└─ 아니오 → 서버 재검증 또는 새 요청
              ↓
             304 또는 200

관찰 실습

TaskFlow의 HTML·CSS·JavaScript·이미지 요청 중 하나를 선택합니다.

첫 요청과 두 번째 요청을 비교합니다.

첫 요청 Status:
두 번째 요청 Status:
Cache-Control:
ETag:
Last-Modified:
Size 변화:

Before

이전 화면이 보임
→ 배포 실패라고 바로 판단

After

이전 화면이 보임
→ 실제 요청 URL 확인
→ Cache-Control 확인
→ 304 또는 캐시 사용 여부 확인
→ 서버 파일과 브라우저 캐시를 분리해서 판단

현재 확장 단계

✓ 1. 브라우저·HTTP 요청/응답
✓ 2. DNS·TCP·TLS
✓ 3. 쿠키·세션·토큰·CORS
✓ 4. CSRF·XSS
→ 5. HTTP 캐시
○ 6. 프로세스·스레드·메모리
○ 7. 통합 장애 진단

완료 체크포인트

cs-step-05-http-cache

다음 학습

네트워크 요청뿐 아니라 브라우저와 서버가 각각 어디에서 실행되고 어떤 상태를 메모리에 가지고 있는지도 문제 진단에 중요합니다.

다음 글에서는 프로세스·스레드·메모리의 최소 개념을 이용해 브라우저 상태와 서버 상태를 구분합니다.

다음 글: 프로세스·스레드·메모리 기초: 브라우저 탭과 서버 실행 구분하기

글 소개·학습 준비·전체 목차
HTTP 캐시와 재검증을 표현한 문서 보관함·시계·새로고침 기호

CS 기초 · 5 / 7 · 필수 학습 · HTTP 캐시

같은 파일을 다시 요청할 때 서버에서 매번 전체 데이터를 내려받는다면 불필요한 네트워크 비용이 늘어납니다.

브라우저와 서버는 HTTP 캐시 규칙을 이용해 이전 응답을 재사용하거나, 변경되었는지만 확인한 뒤 기존 데이터를 사용할 수 있습니다.

이번 글에서는 Cache-Control·ETag·304를 중심으로 브라우저 Network에서 캐시 흐름을 읽는 방법을 학습합니다.

이번 글에서 배우는 내용

  • HTTP 캐시가 필요한 이유
  • fresh와 stale의 기본 개념
  • Cache-Control max-age
  • no-cache와 no-store 차이
  • ETag와 If-None-Match
  • 304 Not Modified의 의미
  • Last-Modified와 If-Modified-Since
  • DevTools에서 캐시 관련 문제를 확인하는 방법
  • 브라우저 캐시와 프레임워크 캐시의 차이

선수 지식

  • HTTP 요청과 응답
  • Request Headers와 Response Headers
  • 200·304 상태 코드
  • 브라우저 DevTools Network 사용법

목차

  1. HTTP 캐시가 필요한 이유
  2. fresh와 stale
  3. Cache-Control 읽기
  4. ETag와 304 재검증
  5. Last-Modified와 If-Modified-Since
  6. DevTools에서 캐시 확인하기
  7. Disable cache 사용 시 주의
  8. 캐시 때문에 화면이 오래된 경우
  9. 다른 캐시와 구분하기
  10. 자주 하는 오해
  11. 독자 질문과 실전 확인
  12. 학습 완료 체크리스트
  13. TaskFlow 요청 흐름 분석 노트
  14. 현재 확장 단계
  15. 다음 학습

이 글이 도움이 되었나요?

조회 중

읽음 기록

읽음 — / 전체 7개 · 필수 7개

이 글을 쓴 해비

웹사이트를 제작·운영하고, 개인 서비스와 자동화·AI 도구를 만듭니다. 직접 만들며 배운 내용과 문제 해결 과정을 BlogFlow에 기록합니다.

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기