HTTP 캐시.
masonfe-hyunsu
🧵 1. 개요.

이미지를 새로고침할 때마다 원본을 다시 받아 오면, 체감은 느리고 서버만 바빠진다.
이 포스팅에서는 HTTP 캐시(자주 쓰는 응답을 브라우저 쪽에 잠시 남겨 두는 방식)랑 조건부 요청(바뀌었는지 확인만 하고 본문은 아끼는 요청)을, 프론트 실무에서 자주 보는 장면이랑 같이 정리해 본다.
문서형 계층 정리는 캐싱 전략 쪽을 보면 되고, 여기서는 브라우저가 헤더를 보고 움직이는 흐름에 초점을 둔다.
📦 2. 캐시를 쓰는 이유.
캐시는 거창한 게 아니다. 자주 쓰는 값을 가까운 곳에 복사해 두는 임시 자리다.
없는 쪽은 요청마다 서버까지 간다. 있는 쪽은 서버가 Cache-Control 같은 헤더로 “이만큼은 네가 갖고 있어도 된다”고 알려 주고, 다음엔 로컬부터 본다.

프론트에서 체감이 큰 건 보통 이미지, 폰트, JS/CSS 번들이다.
API JSON도 캐시할 수는 있지만, 로그인 사용자 데이터면 이야기가 달라진다. 그때는 아끼는 것보다 잘못된 값을 보여 주는 비용이 더 크다.
⏱️ 3. max-age와 로컬 캐시.
첫 응답에 유효 시간이 실려 온다. 대표가 Cache-Control: max-age=…다.
max-age 안: 서버에 안 물어보고 로컬 캐시를 쓴다
max-age 밖: “아직 같아?”를 서버에 물어본다
그래서 정적 파일 파일명에 해시가 붙는 구조(app.a1b2c3.js)랑 잘 맞는다. 내용이 바뀌면 URL이 바뀌니까, 예전 파일은 길게 묶어도 된다.
실무에선 이런 식으로 나누는 경우가 많다.
해시 붙은 정적 asset:
max-age를 아주 길게, 가능하면immutableHTML 문서: 짧게 두거나, 배포 파이프라인에서 무효화
민감 API: 남기면 안 되면
no-store, 브라우저만 허용하고 CDN엔 안 올리려면private
블로그 배너처럼 경로가 고정된 이미지(./banner.png)는 해시가 없다. 길게 묶으면 배포 후에도 예전 그림이 남을 수 있어서, TTL(유효 시간)이랑 배포 전략을 같이 봐야 한다.
🔁 4. 만료 후의 검증.
유효 시간이 끝났다고 해서 매번 통째로 다시 받는 건 아니다.
서버에 다시 물을 때 두 갈래가 나온다. 파일이 바뀌었거나, 안 바뀌었거나.
안 바뀌었으면 304 Not Modified로 헤더만 주고다. Body는 없다. 네트워크는 한 번 타지만, 무거운 본문은 안 온다.
검증에 쓰는 헤더는 크게 두 쌍이다.
날짜 기반:
Last-Modified↔If-Modified-Since태그 기반:
ETag↔If-None-Match
날짜 기반은 단순하다. 다만 내용은 그대로인데 수정일만 바뀌는 경우가 있다. 그때는 검증이 어긋날 수 있다.
ETag는 응답에 태그(해시인 경우가 많다)를 달아 둔다. 요청의 서버의 값이 같으면 유지, 다르면 바뀐 걸로 본다.

max-age가 “얼마나 묻지 않고 쓸지”라면, 조건부 요청은 “만료된 뒤 얼마나 싸게 확인할지”다.
🛠️ 5. 실무에서 자주 보는 장면.
블로그 목록의 배너 이미지.
이 블로그처럼 포스트 카드에 배너가 붙는 목록은, 한 화면에서 이미지가 한꺼번에 나간다.
Posts 탭을 오가거나 상세를 들어갔다 나오면 같은 /posts/{slug}/banner.png를 다시 보게 된다. 헤더가 재사용을 허용하지 않으면, 스크롤만 해도 원본을 또 받게 된다.
경로가 고정된 배너는 해시 URL이 아니라서, max-age만 길게 두면 배포 후에도 예전 그림이 남을 수 있다. 그래서 적당한 TTL + 304 검증이랑, 배너를 바꿀 때 파일명·쿼리를 같이 바꾸는 쪽을 같이 본다. <img> 최적화보다 응답 헤더가 허용하는 재사용이 먼저인 건 여기도 같다.
해시 번들과 배포.
Next나 Vite로 빌드하면 JS/CSS에 해시가 붙는다. 그래서 프로덕션에서는 정적 경로를 길게 캐시해도, 새 배포 사용자는 새 URL을 받는다.
반대로 HTML을 길게 묶어 두면, 예전 HTML이 예전 번들을 가리키는 조합이 남을 수 있다. 문서 TTL과 asset TTL을 다르게 두는 이유가 여기 있다.
로그인 API, 개인화 응답.
/api/auth/me 같은 건 브라우저나 CDN에 남기면 안 된다.
캐시가 “빠르다”고 무조건 좋은 게 아니다. 누가 봐도 되는 정적 리소스에 쓰고, 사람마다 다른 응답에는 일부러 끈다.
DevTools로 확인.
Network 탭에서 disk cache / 304 / 200을 보면 감이 빨리 온다.
첫 방문:
200+ 헤더에Cache-Control,ETag또는Last-Modifiedmax-age 안 재방문:
(disk cache)류로 서버 왕복 없음만료 후 동일 리소스:
304, Size가 헤더 수준
캐시/304를 볼 때는 Disable cache를 끄고, 강제 새로고침이 필요할 때만 켠다. 켠 채로 성능을 재면 실사용자랑 다른 그림이 나온다.
📐 6. 선택 기준.
정리하면 질문이 몇 개로 줄어든다.
바뀌는 빈도: 거의 안 바뀌면 길게, 자주 바뀌면 짧게 또는 검증 위주
URL이 내용을 가리키나: 해시가 있으면 공격적으로, 고정 경로면 보수적으로
공유해도 되나: 개인 응답이면
private, 민감해서 저장 자체가 싫으면no-store틀리면 티 나나: 이미지가 하루 늦게 바뀌는 것과, 결제 금액이 어제 값인 것은 비용이 다르다
완벽히 맞추려다 헤더만 복잡해지는 경우도 많다. 일단 정적 자산부터 길고 안전하게 묶고, 나머지는 짧은 TTL + 304로 버티는 쪽이 운영이 편했다.
댓글