리소스별 정책을 서버가 아니라 리소스에 매핑하세요
헤더를 변경하기 전에 대표적인 URL을 인벤토리로 만드세요: 홈 페이지, 콘텐츠 페이지, 애플리케이션 셸, CSS, JavaScript, 이미지, 폰트, API, 로그인, 계정, 결제, 다운로드 등. 각 항목에 대해 응답이 공개(public)인지 개인화(personalized)인지, 바이트가 바뀔 때 URL이 변경되는지, 그리고 수정이 방문자에게 얼마나 빨리 도달해야 하는지를 기록하세요.
그 인벤토리로 정책 매트릭스를 만드세요. 콘텐츠-주소화(content-addressed)된 공개 자산은 안정된 주소의 HTML과 다른 규칙이 필요합니다. Cache-Control이 없는 응답도 휴리스틱하게 캐시될 수 있으므로 명시적 규칙이 테스트하기 더 쉽습니다.
저장된 응답을 재사용할 수 있는 기간; max-age가 초 단위로 제공합니다.
Cache-Control 용어를 정확하게 사용하세요
Cache-Control은 브라우저와 공유 캐시에게 응답을 어떻게 저장하고 재사용할지 알려줍니다. max-age=N은 fresh한 동안 재사용을 허용합니다. s-maxage=N은 공유 캐시의 freshness를 설정하고 그곳에서 max-age를 덮어쓰지만, 브라우저는 s-maxage를 무시합니다. public은 모든 캐시를 허용하고, private은 브라우저 캐싱을 허용하되 공유 캐시 저장을 막습니다.
no-cache는 저장은 허용하지만 재사용 전에 오리진 검증을 요구합니다. no-store는 캐시가 응답을 저장해서는 안 된다는 뜻이지만 완전한 프라이버시 제어는 아닙니다. 만료 후 오래된 콘텐츠를 제공하면 안 되는 경우 must-revalidate를 사용하세요; 이러면 오리진이 사용할 수 없을 때 504를 반환하고 오래된 대체물을 제공하지 않도록 할 수 있습니다.
no-store는 저장을 완전히 제거합니다; 실제로 저장해서는 안 되는 응답에만 사용하세요.
불변(immutable) 자산은 긴 수명을 부여하기 전에 버전화하세요
버전화된 자산 URL은 콘텐츠 지문(content fingerprint)이나 릴리스 버전을 포함합니다(예: app.4f2c.js). 자산 바이트가 변경되면 다른 URL을 발행하고 HTML 참조를 업데이트하세요. 캐시는 새로운 리소스로 인식하므로 모든 브라우저에 invalidation이 도달하지 않아도 방문자가 새 자산을 가져옵니다.
표현이 freshness 수명 동안 변경되지 않을 것이 확실하면 Cache-Control: max-age=31536000, immutable 패턴이 실용적입니다. immutable은 이를 지원하는 클라이언트에게 fresh한 동안 조건부 재검증을 하지 말라고 알립니다. 같은 URL에서 변경 가능한 파일에 1년짜리 max-age나 immutable을 적용하지 마세요. 캐시된 HTML이 여전히 그 파일을 참조할 수 있으므로 이전 지문(fingerprinted) 자산을 유지하세요.
HTML이 자산 URL을 선택하므로 릴리스 순서 조정은 참조된 세대를 유지해야 합니다.
HTML은 긴 캐싱 대신 검증으로 최신 상태를 유지하세요
HTML은 보통 사람이 접근하는 안정적인 URL을 가지며 콘텐츠, 자산 참조, 기능 플래그 또는 보안 공지로 변경됩니다. 보수적인 공개 HTML 패턴은 플랫폼이 올바르게 생성할 수 있다면 Cache-Control: no-cache와 ETag 및 Last-Modified를 함께 사용하는 것입니다. 브라우저는 본문을 저장하지만 재사용 전에 조건부 요청을 수행하고, 변경이 없으면 서버는 바디 없이 304 Not Modified를 반환할 수 있습니다.
ETag는 리소스 버전을 식별합니다. stale한 응답에서 브라우저는 ETag를 If-None-Match에 포함하여 보낼 수 있고, 일치하면 서버는 304를 응답하고, 그렇지 않으면 새로운 표현을 전송합니다. RFC 권고는 가능하면 200 응답에 ETag와 Last-Modified를 모두 포함할 것을 권장합니다. no-cache는 브라우저의 Back 또는 Forward 네비게이션에서 검증을 보장하지 않습니다. 브라우저는 세션 히스토리 스냅샷을 복원할 수 있습니다.
ETag는 표현이 변경될 때 반드시 변경돼야 합니다; 오리진과 배포 환경 전반에서 테스트하세요.
공개 전달을 개인화·민감 응답과 분리하세요
CDN과 같은 공유 캐시는 여러 사용자에게 같은 응답을 재사용할 수 있습니다. 계정별 HTML이나 API 데이터를 사용자들 사이에서 재사용하게 해서는 안 됩니다. 브라우저에 남을 수 있는 개인화된 콘텐츠에는 공유 저장을 금지하고 검증을 요구하는 private, no-cache가 합리적인 시작점입니다. Set-Cookie 헤더만으로는 캐싱을 중지시키지 않습니다.
저장되어서는 안 되는 응답에는 no-store를 사용하고 애플리케이션 코드, CDN 규칙, 서비스 워커, 브라우저 저장소, 로그, 오류 페이지를 검토하세요. 헤더만으로는 프라이버시, 규정준수, 결제 보안 프로그램을 대체할 수 없습니다. 민감한 흐름에는 자격을 갖춘 보안, 개인정보, 결제 검토자를 참여시키세요.
따뜻한(warm) 브라우저 및 CDN 캐시 상태로 로그인, 로그아웃, 계정 전환, 권한 변경을 테스트하세요.
캐시 변경은 되돌릴 수 있는 릴리스로 배포하세요
비교되지 않은(uncached) 요청과 반복 요청에 대한 기준 헤더 및 본문 동작을 캡처하세요. 스테이징에서 먼저 버전화된 자산을 배포한 뒤 이를 참조하는 HTML을 배포하고, 오래된 캐시된 HTML이 새 오리진과 어떻게 상호작용하는지 테스트하세요. 직접 오리진 경로와 CDN 경로가 모두 존재한다면 둘 다 확인하세요. 브라우저 개발자 도구(DevTools) Network 탭의 헤더는 의도한 정책과 반복 동작을 보여야 합니다.
릴리스 매니페스트에 자산 URL, 헤더 규칙, 구성 버전을 기록하세요. 릴리스가 호환되지 않는 클라이언트를 조합하면 호환 가능한 HTML과 애플리케이션 동작을 복원하되, 공개된 지문(fingerprinted) 파일은 덮어쓰지 마세요. HTML을 알려진 정상 세대로 다시 가리키고, 캐시된 HTML이 요청할 수 있는 동안 두 세대를 모두 유지하세요. 공유 캐시 무효화(purge)는 브라우저가 fresh한 응답을 폐기하도록 강제할 수 없습니다.
헤더 테스트 실패, 자산 404, 버전 불일치를 다음 릴리스 체크리스트의 수정 항목으로 만드세요.
