HTTP 캐싱 완벽 가이드: ETag, Cache-Control, CDN

Web2026-09-28TryQuickToolBox

웹사이트가 느리게 느껴지는 이유 (그리고 캐싱이 해결하는 방법)

이미지를 최적화하고, CSS를 압축하고, 서버까지 업그레이드했습니다. 그런데도 재방문 사용자는 여전히 느린 로딩을 경험하고, 오리진 서버는 대역폭을 낭비하고 있습니다. 원인은? 비효율적인 HTTP 캐싱입니다. 적절한 캐싱 헤더가 없으면 브라우저는 방문할 때마다 동일한 자산을 다시 다운로드하고, CDN은 제 역할을 하지 못합니다.

HTTP 캐싱은 가장 효과가 큰 성능 최적화 중 하나입니다. 지연 시간을 줄이고, 대역폭 비용을 절감하며, 오리진 서버의 부하를 줄입니다. 이 가이드에서는 핵심 메커니즘인 ETag, Cache-Control, 그리고 CDN이 어떻게 관련되는지 살펴봅니다. 올바르게 구현하는 실전 전략도 배우게 됩니다.

HTTP 캐싱 작동 방식: 전체 그림

브라우저가 리소스를 요청할 때, 오리진 서버에서 가져오거나 로컬에 저장된 복사본을 사용할 수 있습니다. HTTP 캐싱은 저장된 복사본이 언제 신선한지, 언제 재검증해야 하는지에 대한 규칙을 정의합니다.

크게 두 가지 유형의 캐싱이 있습니다:

둘 다 신선도와 재검증을 결정하기 위해 HTTP 헤더에 의존합니다. 가장 중요한 두 헤더는 Cache-Control과 ETag입니다.

Cache-Control: 신선도 규칙

Cache-Control은 캐싱 정책을 정의하는 기본 헤더입니다. 캐시에 응답을 어떻게 처리할지 알려주는 지시문 기반 헤더입니다.

주요 지시문

예: Cache-Control: public, max-age=31536000, immutable은 해시된 파일 이름을 가진 정적 자산에 이상적입니다.

ETag와 조건부 요청

ETag(Entity Tag)는 리소스의 특정 버전에 대한 식별자입니다. 리소스가 변경되면 ETag도 변경됩니다. 브라우저는 ETag를 사용하여 조건부 요청을 합니다: 저장된 ETag를 If-None-Match 헤더에 담아 보냅니다. 리소스가 변경되지 않았다면 서버는 304 Not Modified와 본문 없이 응답하여 대역폭을 절약합니다.

마찬가지로 Last-Modified는 If-Modified-Since와 함께 작동하지만, ETag가 더 정밀합니다(같은 초 내의 변경도 감지할 수 있음).

ETag 생성 방법

대부분의 웹 서버와 프레임워크는 ETag를 자동으로 생성합니다. 예를 들어 Express.js에서는 app.set('etag', 'strong')으로 활성화할 수 있습니다. Nginx에서는 정적 파일에 대해 ETag가 기본으로 켜져 있습니다.

강한 ETag(예: "abc123")는 바이트 단위 동일성을 보장합니다. 약한 ETag(예: W/"abc123")는 정확한 바이트가 아닌 의미적 동등성을 나타냅니다.

CDN 캐싱: 대규모 공유 캐시

CDN(Content Delivery Network)은 전 세계에 분산된 공유 캐시 역할을 합니다. 엣지 위치에 콘텐츠를 캐시하여 가까운 접속 지점에서 사용자에게 제공합니다. 이는 지연 시간을 줄이고 오리진 부하를 분산합니다.

CDN은 Cache-Control 헤더를 존중하지만, 자체 구성이 있는 경우가 많습니다. 주요 개념:

CDN을 사용할 때는 s-maxage와 함께 Cache-Control을 설정하여 브라우저 캐시와 별도로 공유 캐시 신선도를 제어하세요. 예: Cache-Control: public, max-age=600, s-maxage=3600은 브라우저는 10분, CDN은 1시간 동안 캐시함을 의미합니다.

캐싱 헤더 비교

헤더 목적 예시
Cache-Control 신선도 및 캐싱 규칙 정의 public, max-age=3600
ETag 리소스 버전의 고유 식별자 "abc123"
Last-Modified 마지막 수정 타임스탬프 Wed, 21 Oct 2025 07:28:00 GMT
Expires 레거시 절대 만료 날짜 Wed, 21 Oct 2025 07:28:00 GMT
Vary 캐싱에 영향을 주는 헤더 지정 Accept-Encoding

참고: Expires는 Cache-Control로 대체되었지만, 구형 클라이언트에서 여전히 사용됩니다.

웹 앱을 위한 실전 캐싱 전략

효과적인 캐싱을 구현하려면 다음 단계를 따르세요:

  1. 정적 자산에 핑거프린트 적용: 해시된 파일 이름(예: app.a1b2c3.js)을 사용하고 immutable과 함께 긴 max-age를 설정하세요. 파일이 변경되면 해시가 바뀌어 캐시가 무효화됩니다.
  2. HTML에 적절한 Cache-Control 설정: HTML은 보통 no-cache 또는 짧은 max-age로 설정하여 사용자가 빠르게 업데이트를 받도록 하세요. 재검증에는 ETag를 사용하세요.
  3. Vary: Accept-Encoding 사용: 압축 버전과 비압축 버전을 제공하는 경우, 캐시가 이를 별도로 저장하도록 보장합니다.
  4. s-maxage로 CDN 활용: 공유 캐시에는 더 긴 s-maxage를 설정하여 오리진 부하를 줄이고, 필요하면 브라우저 캐시는 더 짧게 유지하세요.
  5. 현명하게 무효화: 중요한 업데이트를 배포할 때 CDN 퍼지 API를 사용하세요. 정적 자산은 핑거프린팅으로 퍼지 필요성을 피할 수 있습니다.
  6. 캐시 적중률 모니터링: CDN 분석을 사용하여 캐시가 효과적인지 확인하세요. 낮은 적중률은 헤더 구성 오류를 의미합니다.

흔한 실수와 예방법

캐싱 설정 테스트

브라우저 DevTools(네트워크 탭)를 사용하여 응답 헤더를 검사하고 리소스가 캐시에서 제공되는지 확인하세요("(from disk cache)" 또는 "(from memory cache)" 확인). CDN 동작은 curl -I로 X-Cache 또는 CF-Cache-Status 같은 헤더를 확인하세요. WebPageTest 같은 도구는 여러 방문에 걸친 캐싱을 시각화할 수 있습니다.

FAQ

ETag와 Last-Modified의 차이점은 무엇인가요?

ETag는 리소스가 변경될 때 바뀌는 불투명한 식별자이고, Last-Modified는 타임스탬프입니다. ETag는 같은 초 내의 변경도 감지할 수 있고 시계 동기화에 의존하지 않아 더 정밀합니다.

no-cache와 no-store는 언제 사용해야 하나요?

캐시가 응답을 저장하되 사용 전에 매번 오리진과 재검증하길 원할 때 no-cache를 사용하세요. 어떤 캐시도 디스크나 메모리에 기록해서는 안 되는 민감한 데이터에는 no-store를 사용하세요.

CDN은 캐시 무효화를 어떻게 처리하나요?

CDN은 특정 URL이나 전체 디렉터리를 엣지 캐시에서 제거할 수 있는 퍼지 API를 제공합니다. 일부는 콘텐츠를 만료로 표시하고 다음 요청 시 재검증하는 소프트 퍼지도 지원합니다. 자산 핑거프린팅이 퍼지보다 더 효율적인 경우가 많습니다.

결론

ETag, Cache-Control, CDN을 활용한 HTTP 캐싱을 마스터하는 것은 빠르고 확장 가능한 웹 애플리케이션을 구축하는 데 필수적입니다. 정적 자산과 HTML에 적절한 헤더를 설정하는 것부터 시작하고, s-maxage로 CDN 공유 캐시를 활용하며, 항상 구성을 테스트하세요. 캐싱 헤더의 작은 변화가 상당한 성능 향상을 가져올 수 있습니다.

서버 로그를 빠르게 분석하여 캐시 적중률을 확인해야 하나요? Nginx Log Analyzer로 액세스 로그를 파싱하고 시각화해 보세요.