Skip to content

HTTP 캐시 관련 헤더 정리

Published: at 오후 12:11

개요

웹 서비스를 빠르고 안정적으로 제공하기 위해 캐싱은 반드시 이해해야 할 기술이다. 잘 설계된 캐싱 전략은 서버 부하를 줄이고 사용자 경험을 크게 향상시킬 수 있다. 이 글에서는 『HTTP 완벽가이드』를 기준으로 HTTP 캐싱을 구성하는 핵심 요소들과, 프론트엔드 개발자가 실무에서 마주치는 다양한 상황에 적용할 수 있는 전략을 설명한다.

목차

캐시 제어의 핵심: HTTP 헤더

HTTP 캐싱은 헤더를 통해 동작한다. 응답 헤더와 요청 헤더를 조합하여, 클라이언트가 리소스를 저장할지, 재검증할지, 또는 매번 새로 요청할지를 판단하게 된다.

Cache-Control

HTTP/1.1부터 도입된 표준 캐시 제어 헤더이다. Expires 대신 사용되며 훨씬 정교한 제어가 가능하다.

ETag / If-None-Match

ETag은 리소스의 고유한 식별자 역할을 한다. 해시값 형태로 리소스 변경 여부를 추적할 수 있다. 클라이언트는 이전에 받은 ETag 값을 If-None-Match 요청 헤더에 담아 보내며, 서버는 리소스가 변경되지 않았다면 304 Not Modified로 응답한다. 이 방식은 정확한 캐시 검증을 가능하게 한다.

Last-Modified / If-Modified-Since

Last-Modified는 리소스의 최종 수정 시점을 나타낸다. 클라이언트는 해당 값을 If-Modified-Since 요청 헤더로 전송하며, 서버는 리소스가 그 이후로 수정되지 않았다면 304 Not Modified로 응답한다. 시간 단위로 동작하기 때문에 ETag보다 정밀도는 낮지만 구현이 간단하다.

블로그 서비스에서의 기능별 캐싱 전략 생각해보기

블로그 서비스를 개발한다고 가정하고 각 기능에 맞는 캐싱 전략을 판단해보려한다.

정적 리소스 (CSS, JS, 이미지 등)

빌드 시 해시를 포함한 파일명을 생성하여 URL이 변경되도록 구성한다. 브라우저는 해당 URL에 대해 Cache-Control: public, max-age=31536000, immutable 헤더를 통해 1년 동안 캐시한다. 이때 immutable은 리소스가 절대 변경되지 않음을 의미하며, 불필요한 재검증 요청을 차단할 수 있다.

블로그 글 본문

상대적으로 덜 자주 변경되지만, 변경 가능성이 있는 콘텐츠이다. Cache-Control: public, max-age=600과 함께 ETag 또는 Last-Modified 헤더를 활용하여 유효 시간이 지나면 서버 검증을 거쳐 캐시를 갱신하도록 한다.

관리자 페이지 및 글쓰기 화면

민감한 데이터를 포함하거나 실시간성이 필요한 페이지이다. 캐시 저장을 방지하기 위해 Cache-Control: no-store 헤더를 적용한다. 이는 로그인 페이지나 결제 페이지에도 동일하게 적용된다.

API 응답

실무에서 마주치는 캐싱 상황과 해결법

이미지가 변경되지 않는 문제

이미지를 변경했음에도 브라우저에서 이전 이미지를 계속 보여주는 경우가 있다. 이는 브라우저 캐시 때문이다. 해결 방법으로는 URL에 버전 쿼리 파라미터를 추가하거나(?v=2), 파일명을 해시 기반으로 변경하여 캐시 무효화를 유도한다.

HTML 문서 갱신 문제

새로운 배포 후에도 브라우저에서 구버전 HTML이 유지되는 경우, 브라우저가 HTML을 캐시하고 있기 때문이다. Cache-Control: no-cache, must-revalidate를 설정하여 서버 재검증을 강제해야 한다. 최신 HTML을 통해 최신 JS와 CSS가 로딩되므로 중요하다.

로그인 정보 노출 문제

공용 캐시가 설정된 경우, 다른 사용자의 데이터가 잘못 보여질 수 있다. 이를 방지하기 위해 로그인 후 응답에는 Cache-Control: private 또는 no-store를 반드시 적용해야 한다.

정리

캐시는 단순히 성능 향상 도구가 아니다. 제대로 설계하지 않으면 오히려 버그와 보안 문제가 발생할 수 있다. 정적 자원, HTML, API 응답 등 각 리소스의 특성을 고려한 전략적 캐싱이 필요하다.

참고자료

웹 서비스 캐시 똑똑하게 다루기