Cache와 Cache Busting

2025년 08월 08일

TOC

Web

웹 개발을 하다 보면 배포 후에도 변경 사항이 반영되지 않는 문제를 겪을 때가 있다. 이 문제의 주범은 보통 캐시(Cache)일 것이다. 캐시는 성능 향상에 필수적인 기술이지만, 때때로 변경된 최신 리소스를 받아오지 못하게 하여 불편을 초래한다. Cache Busting은 파일의 고유 식별자를 이용해 브라우저에 "이건 새 버전이야"라고 알려주는 기법으로, 이러한 문제를 해결한다.

Cache(캐시)란?

웹 캐시는 자주 요청되는 웹 리소스(HTML, CSS, JS, 이미지, API 응답 등)를 임시 저장해 두었다가, 동일한 요청이 들어오면 서버 대신 저장된 데이터를 제공하는 기술이다. 한 번 받은 리소스를 다시 내려받을 필요가 없으므로 서버 부하를 줄이고, 응답 속도를 높이며, 네트워크 트래픽을 절약할 수 있다.

캐시의 사용은 아래와 같은 효과를 기대할 수 있다.

  • 불필요한 데이터 전송 감소 → 네트워크 비용 절감
  • 네트워크 병목(Bottleneck) 완화 → 대역폭 증설 없이 빠른 로딩
  • 원 서버 부하 감소 → 요청 처리 효율 향상
  • 지연(Latency) 감소 → 물리적 거리로 인한 응답 지연 최소화

캐시는 웹 브라우저, 프록시 서버, CDN, 애플리케이션 서버 등 다양한 위치에 존재할 수 있다. 캐시 데이터는 보통 **유효 기간(Expire Time)**이나 **검증 메커니즘(Validation)**을 기반으로 관리되며, HTTP 헤더를 통해 캐시 정책을 제어한다. 주요 캐시 유형은 다음과 같다.

  • 브라우저 캐시: 사용자의 로컬 저장소에 리소스를 저장 (예: Chrome, Firefox)
  • 프록시 캐시: 사용자와 서버 사이의 중간 서버에서 캐싱 (예: 회사 내부 프록시, ISP 캐시)
  • CDN 캐시: 전 세계에 분산된 서버에서 캐싱, 가까운 서버에서 제공 (예: Cloudflare, Akamai)
  • 애플리케이션 캐시: 서버 내부에서 데이터/쿼리 결과를 캐싱 (예: Redis, Memcached)

HTTP 캐시 제어와 동작 원리

웹 애플리케이션에서 HTTP 캐시는 네트워크 트래픽을 줄이고, 페이지 로딩 속도를 개선하며, 서버 부하를 줄이는 핵심 최적화 기술이다. 서버와 클라이언트(또는 중간 프록시) 간 데이터를 주고받을 때, 이미 받은 리소스를 재사용할 수 있도록 해주는 역할을 한다.

캐시는 브라우저, 프록시 서버, CDN 등 여러 위치에서 동작할 수 있으며, 캐싱 전략을 적절히 설정하면 대규모 트래픽 처리 성능과 사용자 경험을 동시에 개선할 수 있다.

1. 캐시 제어를 위한 주요 HTTP 응답 헤더

Cache-Control

HTTP/1.1에서 캐시 제어의 핵심 헤더이며, 다양한 옵션으로 캐시 동작을 제어한다.

지시어 설명
max-age=초 리소스를 캐시에 저장할 최대 시간(초)
no-cache 캐시에 저장은 가능하지만, 사용 전 반드시 서버에 재검사 요청
no-store 캐시에 전혀 저장하지 않음
must-revalidate 만료된 캐시는 반드시 서버 재검사 후 사용
public 모든 캐시(브라우저, 프록시)에서 저장 가능
private 개인 브라우저 캐시에만 저장 가능

예시:

Cache-Control: public, max-age=3600, must-revalidate

Expires

HTTP/1.0에서 사용되던 캐시 만료 방식으로, 특정 절대 시각을 지정한다. 하지만 서버와 클라이언트의 시간 차이 문제로 인해 Cache-Control: max-age 방식이 더 권장된다.

예시:

Expires: Wed, 21 Oct 2025 07:28:00 GMT

ETag

서버가 리소스의 고유 식별자를 생성해 제공하는 방식이다. 리소스 내용이 변경되면 ETag 값도 변경되므로, 변경 여부 확인에 사용된다.

  • 브라우저는 요청 시 If-None-Match 헤더로 이전 ETag 값을 서버에 전달한다.
  • 서버는 값이 동일하면 304 Not Modified를 반환하고, 캐시된 데이터를 사용하도록 한다.

Last-Modified

리소스가 마지막으로 수정된 시간을 나타낸다. 브라우저는 If-Modified-Since 헤더로 해당 시각을 서버에 전달하여, 변경된 경우에만 새 데이터를 요청한다.

2. HTTP 캐시 동작 흐름

  1. 첫 요청

    • 브라우저 → 서버: 리소스 요청
    • 서버 → 브라우저: 응답 + 캐시 제어 헤더(Cache-Control, ETag 등)
    • 브라우저: 로컬 캐시에 저장
  2. 유효 기간 내 요청 (Fresh)

    • 브라우저: 로컬 캐시에서 즉시 응답 반환 (네트워크 요청 없음)
    • 장점: 속도 개선, 서버 부하 감소, 네트워크 절약
  3. 만료 후 요청 (Stale)

    • 브라우저 → 서버: 조건부 요청(If-None-Match, If-Modified-Since)
    • 서버: 변경 없음 → 304 Not Modified 응답
    • 브라우저: 기존 캐시 재사용

3. 캐시 적중과 부적중

  • Cache Hit (적중): 요청에 맞는 유효한 사본이 캐시에 있어 즉시 반환되는 경우
  • Cache Miss (부적중): 캐시에 해당 사본이 없거나 만료되어 서버로 요청하는 경우

4. 재검사(Revalidation)

캐시된 리소스가 변경되지 않았음을 확인하는 과정이다. ETag 또는 Last-Modified 값을 이용해 서버에 변경 여부를 물어보고, 변경이 없으면 캐시를 재사용한다.

  • 장점: 불필요한 데이터 다운로드 방지
  • 단점: 네트워크 요청은 여전히 발생

5. 캐시 토폴로지 (Cache Topology)

유형 설명 예시
Private Cache 특정 사용자만 사용하는 캐시 브라우저 캐시
Public Cache 여러 사용자가 공유하는 캐시 프록시 서버, CDN 캐시

6. 캐시 처리 단계 (Detailed Flow)

캐시 서버(또는 브라우저 캐시)는 요청을 처리할 때 다음 단계를 거친다.

  1. 요청 수신

    • 클라이언트(브라우저)로부터 HTTP 요청을 받음
  2. 메시지 파싱

    • 요청 라인, URL, 헤더, 쿠키 등의 정보를 분석
  3. 로컬 사본 검색

    • 캐시 저장소에서 요청 URL과 매칭되는 리소스를 탐색
  4. 신선도 검사 (Freshness Check)

    • Cache-Control: max-age, Expires 값 기반으로 만료 여부 판단
  5. 재검사 필요 시 서버 요청

    • ETag 또는 Last-Modified 기반 조건부 요청 전송
  6. 응답 생성

    • 캐시 적중 시 로컬에서 즉시 응답
    • 부적중 시 서버 응답을 받아 캐시에 저장 후 클라이언트에 전달
  7. 응답 전송

    • 브라우저 또는 프록시에 최종 응답 반환
  8. 로깅

    • 요청, 응답, 캐시 상태(HIT/MISS) 기록

HTTP 캐시는 성능 최적화와 서버 부하 감소를 동시에 달성하는 핵심 기술이다. 적절한 캐시 정책을 설계하면, 트래픽 절감, 사용자 경험 개선, 서버 안정성 향상까지 가능하다. 특히 Cache-Control, ETag, Last-Modified와 같은 헤더를 활용하면 캐시를 정교하게 제어할 수 있다.

Cache Busting

웹 애플리케이션에서 정적 파일(예: CSS, JavaScript, 이미지)은 보통 오랫동안 캐시에 보관되도록 설정한다. 이렇게 하면 서버 부하를 줄이고 속도를 높일 수 있지만, 문제는 파일이 변경되었는데도 브라우저가 기존 캐시된 파일을 계속 사용하는 경우다.

이 문제를 해결하는 기법이 바로 Cache Busting이다. 즉, 파일의 이름 또는 경로를 변경해 브라우저가 새 파일로 인식하도록 만드는 전략이다.

1. SPA(Single Page Application)와 캐시 문제

React, Vue, Angular 같은 SPA에서는 HTML 파일 하나(index.html)가 앱의 뼈대 역할을 하고, 여기에 연결된 번들된 JS·CSS가 실제 UI를 구성한다.

문제는:

  • index.html은 변경이 적지만,
  • 번들된 JS·CSS는 개발 과정에서 자주 변경된다.
  • 그런데 캐시 만료 설정이 길게 되어 있으면, 사용자가 오래된 JS를 계속 로딩해 새 기능·버그 수정이 반영되지 않는 상황이 발생한다.

예:

  1. 서버에 새로운 main.js 배포
  2. 사용자는 여전히 캐시된 main.js 사용
  3. UI나 기능이 정상 동작하지 않거나, 배포된 버그 수정이 반영되지 않음

2. React/Webpack 환경에서 Cache Busting 적용

Webpack은 빌드할 때 contenthash 옵션을 사용하면 파일 내용이 변경될 때마다 해시값이 달라진다. 이 덕분에 브라우저는 파일이 변경될 때마다 새로운 파일로 인식하게 된다.

Webpack 설정 예시

// webpack.config.js
module.exports = {
  output: {
    filename: "[name].[contenthash:8].js",
    chunkFilename: "[name].[contenthash:8].chunk.js",
  },
}
  • contenthash:8 → 파일 내용 기반 해시의 앞 8자리 사용
  • 파일 내용이 같으면 동일한 해시를 유지 → 불필요한 캐시 무효화 방지
  • 파일 내용이 변경되면 해시가 변경되어 새 파일로 인식

CRA(Create React App) + craco 적용 예시

// craco.config.js
module.exports = {
  webpack: {
    configure: (webpackConfig) => {
      webpackConfig.output = {
        ...webpackConfig.output,
        filename: "static/js/[name].[contenthash:8].js",
        chunkFilename: "static/js/[name].[contenthash:8].chunk.js",
      }
      return webpackConfig
    },
  },
}
  • CRA 기본 설정은 이미 해시를 사용하지만, 필요 시 craco로 세부 조정 가능

3. Nginx 캐시 정책 설정

Cache Busting을 적용했다면, 서버 쪽에서는 정적 파일에 긴 캐시 유효기간을 설정하는 것이 이상적이다. 왜냐하면 파일명이 변경되므로, 오래된 파일이 캐시에 남아 있어도 새 요청에는 영향을 주지 않기 때문이다.

server {
  location ~* \.(?:css|js)$ {
    expires 1y;   # 1년 캐시
    access_log off;
    add_header Cache-Control "public";
  }
}
  • expires 1y : 브라우저에 1년간 캐시 유지 지시
  • Cache-Control: public : 모든 캐시(브라우저, 프록시, CDN)에서 저장 가능
  • access_log off : 정적 리소스 요청은 로그를 남기지 않아 서버 부하 감소

4. 적용 시 주의사항

  • 권장

    • max-age + ETag 또는 Last-Modified 병행
    • 빌드 자동화에서 파일 fingerprinting(해시) 적용
    • Nginx/Apache/CDN에 적절한 캐시 정책 설정
  • 지양

    • HTML <meta> 태그로 캐시 제어 (HTTP 헤더보다 우선순위 낮음)
    • Query String 방식 Cache Busting (프록시·CDN 캐싱 이슈 가능)

Cache Busting은 정적 리소스 변경 사항이 즉시 반영되도록 보장한다. 특히 SPA 환경에서는 필수적으로 적용해야 하며, Webpack의 contenthash나 빌드 해시 전략과 함께 긴 캐시 만료 정책을 결합하면 성능과 안정성을 동시에 확보할 수 있다.

Reference

HTTP 완벽가이드

What Is Cache Busting?

A Web Developer's Guide to Browser Caching

Strategies for Cache Busting CSS

WordPress Cache Busting

[Web] Cache와 Cache Busting이란?

React - 성능 최적화 (Cache Busting)

Written by

yhuj79

🌱 Junior Developer