Cache-Control: max-age=3, must-revalidate라는 설정을 살펴봤기 때문에, 잊기 전에 내 기준으로 정리해 둔다.
이 설정은 “아주 짧은 시간인 3초 동안만 캐시를 쓰고, 만료되면 반드시 원본 서버에 확인한다”는 동작을 의미한다. 흔히 마이크로 캐싱이라고 부르며, 실시간성은 어느 정도 유지하면서도 갑작스러운 트래픽 집중으로부터 서버를 보호할 때 자주 쓰인다.
각 디렉티브의 의미
먼저 각 디렉티브가 무엇을 뜻하는지 분리해서 보면 이해가 쉽다.
max-age=3
브라우저나 CDN 같은 캐시에 **“이 콘텐츠는 3초 동안만 신선하다”**고 전달한다. 이 3초 동안은 원본 서버로 요청이 가지 않고, 캐시된 응답이 즉시 반환된다.
must-revalidate
3초가 지나 캐시가 만료되면, 캐시는 반드시 원본 서버에 재검증을 요청하고, 허가를 받기 전에는 오래된 응답을 반환해서는 안 된다는 뜻이다.
이 디렉티브가 없으면 일부 캐시 서버나 설정은 원본 서버가 내려갔을 때 예전 응답을 임시로 반환할 수 있다. must-revalidate는 그런 동작을 명확히 금지한다.
시간 순서대로 보는 동작 흐름
사용자가 페이지에 접근할 때 어떤 일이 일어나는지 따라가 보면 이해가 빠르다.
| 시점 | 사용자 행동 | 동작 | 상태 코드 |
|---|---|---|---|
| 첫 요청 | 페이지 접근 | 원본 서버에서 데이터를 가져와 표시 | 200 OK |
| 직후 ~ 3초 이내 | 새로고침 / 이동 | 원본 서버로 요청이 가지 않는다. 브라우저나 CDN 캐시가 응답을 그대로 반환 | 200 (from cache) |
| 3초 경과 후 | 새로고침 / 이동 | 캐시가 만료된 것으로 판단된다. 브라우저가 원본 서버에 조건부 요청을 보낸다 | 네트워크 통신 발생 |
| → 변경 없음 | — | 서버가 변경되지 않았다고 응답한다. 기존 캐시는 다시 3초 동안 유효해진다. 본문 다운로드는 발생하지 않는다 | 304 Not Modified |
| → 변경 있음 | — | 서버가 새 콘텐츠를 반환한다 | 200 OK |
핵심은 3초가 지난 뒤 304 Not Modified가 돌아오는 경우다. 콘텐츠 본문을 다시 내려받지 않기 때문에 대역폭과 지연을 모두 줄일 수 있다.
이 설정의 장점
이 헤더는 신선도가 중요하지만 트래픽도 매우 많은 환경에서 특히 강하게 작동한다. 기억해 둘 만한 장점은 세 가지다.
원본 서버 부하를 크게 줄인다
예를 들어 초당 1,000 요청이 들어오는 사이트가 있다고 하자. max-age=3가 있으면, 특히 CDN이 있을 때 대부분의 요청은 캐시 계층에서 처리되고 원본 서버는 훨씬 적은 빈도로만 검증 요청을 받는다.
단 3초의 캐시라도 고트래픽 환경에서는 큰 차이를 만든다.
거의 실시간에 가까운 반영
새 정보는 늦어도 3초 안에는 반영된다. 완전한 실시간은 아니지만, 많은 서비스에서는 충분히 허용 가능한 수준이다.
오래된 정보 노출을 막는다
must-revalidate 덕분에 캐시가 만료된 뒤에도 클라이언트가 몰래 예전 데이터를 계속 보여주는 일을 막을 수 있다. 정확성이 중요한 화면이라면 중요하다.
구체적인 사용 사례
어떤 상황에 잘 맞는지 직접 생각해 본 내용을 적어 둔다.
잘 맞는 경우
- 뉴스 사이트 메인 페이지나 목록 페이지: 새 글이 빨리 반영되어야 하지만 몇 초 정도의 지연은 허용 가능하고, 트래픽 집중도 자주 일어난다
- 이커머스 재고 표시: 몇 초 늦게 보이는 것은 괜찮지만 품절 상품이 구매 가능한 것처럼 보이면 곤란하다.
must-revalidate가 오래된 재고 정보 노출을 줄여 준다 - 이벤트나 캠페인 랜딩 페이지: 공개 직후 SNS 등을 통해 트래픽이 몰릴 수 있다. 마이크로 캐싱으로 초기 파동을 흡수하면서도 업데이트는 빠르게 반영할 수 있다
- 대시보드 요약 화면: 실시간 그래프만큼 즉각적일 필요는 없지만 최신 KPI는 반영하고 싶을 때. 폴링 간격이 3초 이상이면 잘 맞는다
- 목록 조회형 API 응답: 이런 헤더를 붙이면 CDN 레이어에서 응답을 캐시해 원본 서버 부하를 줄일 수 있다
잘 맞지 않는 경우
- CSS/JS/이미지 같은 정적 자산: 자주 바뀌지 않는다. 파일 해시 기반 이름 +
max-age=31536000,immutable가 더 적절하다 - 블로그 글 상세 페이지: 공개 후 자주 수정되지 않는 경우가 많아서 더 긴
max-age가 일반적으로 낫다 - 실시간 채팅이나 WebSocket 통신: 애초에 HTTP 캐시 대상이 아니고, 3초 지연조차 허용하기 어렵다
- 사용자별로 달라지는 콘텐츠: 마이페이지처럼 사용자별 응답은 공유 CDN 캐시에 잘 맞지 않는다. 필요하면
private같은 디렉티브를 함께 검토해야 한다
주의할 점
놓치기 쉬운 트레이드오프도 있다.
3초마다 다시 통신이 생긴다
3초가 지나면 조건부 요청이 반드시 발생한다. 더 긴 max-age 설정에 비해 네트워크 지연의 영향을 더 받는다. 느린 회선에서 UX를 한 번 생각해 볼 필요가 있다.
오프라인일 때의 동작
must-revalidate가 있기 때문에 네트워크가 끊기거나 원본 서버가 내려가 있으면, 만료된 캐시가 남아 있어도 에러가 날 가능성이 높다. 즉 “신선함을 확인할 수 없으면 오래된 것을 보여주기보다 실패한다”는 정책이다.
이게 너무 엄격하다면 stale-while-revalidate나 stale-if-error 같은 디렉티브 조합을 검토하는 편이 낫다.
정리
Cache-Control: max-age=3, must-revalidate는 신선도는 중요하지만 원본 서버를 무너뜨리고 싶지 않을 때 쓰는 공격적인 균형점이다.
“마이크로 캐싱”이라는 이름은 거창하지만 실제로 하는 일은 몇 초만 캐시하는 것이다. 고트래픽 환경에서는 그 몇 초가 큰 차이를 만든다.
적용하기 전에 대상이 정말 이 패턴에 맞는지 확인하는 편이 좋다. 정적 자산이나 거의 바뀌지 않는 페이지에 적용하면 불필요한 재검증 요청만 늘어난다.
hsb.horse