Vite로 빌드한 SPA를 배포한 뒤 Chrome Network 패널을 확인해 보면 어떤 요청은 (메모리 캐시), 어떤 요청은 (디스크 캐시), 또 어떤 요청은 304로 표시된다. 캐시 동작에도 여러방식이 있다.
처음엔 이미지나 외부 라이브러리 같은 리소스 종류에 따라 자동으로 나뉘는 줄 알았다. 실제로는 서버가 내려주는 Cache-Control 헤더나 캐시의 신선도, ETag 값에 따라 브라우저가 '즉시 재사용'과 '서버 재검증' 중 선택하게끔 되어 있다.

Chrome에서 동일한 폰트 리소스를 다시 요청했을 때 브라우저가 메모리 캐시에서 가져온 것이다. 200과 함께 (메모리 캐시) 문구가 있는데 파일을 다시 수신했다는 뜻이 아닌 브라우저에서 가져다 썼다는 것이다.
개발자가 제어할 수 있는 부분은 서버 응답을 캐시해도 되는지, 얼마나 오래 신선함이 유지되는지, 재사용 전 서버에 변경 사항을 조회해야 하는지 정도다. 이 명세는 응답에 얹어 내려주는 HTTP 응답 헤더로 결정된다.
캐시 유지 기간 max-age
Cache-Control: public, max-age=3600
max-age 유효기간이 만료되기 전까지 브라우저는 동일 URL 요청에 대해 기존 캐시 응답을 사용한다. 브라우저는 최초 응답 수신 시점부터 1시간 동안 해당 리소스를 캐싱해서 사용한다는 것이다. 이때 Network 패널에는 (메모리 캐시) 또는 (디스크 캐시) 표기가 뜨고, 서버로는 요청이 발송되지 않는다.
만료된 캐시는 ETag로 다시 확인할 수 있다
캐시의 유효기간이 지났다고 해서 매번 새로 다운로드하지는 않는다. 서버가 최초 응답 시 ETag 또는 Last-Modified 헤더를 함께 줬고 브라우저가 갖고 있으면, 변경 여부만 간단하게 확인할 수 있다.
GET /assets/index-a1b2c3.js HTTP/1.1
If-None-Match: "a1b2c3"
HTTP/1.1 304 Not Modified
ETag: "a1b2c3"
Cache-Control: no-cache
서버의 현재 리소스 ETag가 브라우저가 보낸 If-None-Match 값과 일치하면 서버는 파일 본문을 제외한 304 Not Modified 응답만을 반환한다. 브라우저는 이미 갖고 있는 게 유효하다고 판단하고 계속 사용한다. 파일이 수정되어 ETag가 변경되었거나 시각이 갱신되었다면 서버는 새 파일과 함께 200 OK 를 응답한다.
no-cache와 no-store는 목적이 다르다
no-cache와 no-store 가 같은줄 알았는데 아니었다.
| 지시자 | 캐시 저장 | 재사용 전 동작 | 적합한 사용 환경 |
|---|---|---|---|
no-cache | 저장 가능 | 매번 서버에 304 재검증을 수행 | 항상 최신 조회가 필요하지만 304 절감 효과는 챙기고 싶을 때 |
no-store | 캐시 저장 안함 | 검증하지 않고 새로 요청 | 개인정보나 금융 데이터, 일회성 토큰 등 로컬 저장 자체가 금지된 응답 |
배포 직후 최신 주소와 HTML을 보장하려는 목적이라면 no-cache를 설정하는 편이 올바르다. no-store는 이미 브라우저에 저장되어 있는 동일 URL의 캐시를 삭제해 주는 명령이 아니며, 브라우저의 전후 이동(bfcache) 성능이나 오프라인 브라우징 이점을 쇄신할 수 있다.
SPA 에서 index.html
index.html
├── /assets/index-a1b2c3.js
└── /assets/index-d4e5f6.css
Vite 를 사용하면 번들링된 파일들에 내용 기반 해시가 자동으로 덧붙여진다. 리소스 코드가 변경되면 파일 URL 자체가 새로 생성되므로, 이전 파일에 나이를 길게 설정해 두어도 새 배포 시 기존 파일을 덮어써서 생기는 문제가 없다.
따라서 index.html과 /assets/ 하위 해시 에셋의 캐시 정책을 서로 다르게 가져가는 구조가 안전하다.
S3와 CloudFront 배포 환경에서 진입점과 해시 에셋의 캐시 정책을 분리하는 방법은 S3와 CloudFront에서 SPA 캐시를 분리해 배포 오류 막기S3와 CloudFront에서 SPA 캐시를 분리해 배포 오류 막기S3와 CloudFront로 SPA를 배포할 때 index.html·버전 파일·해시 에셋의 캐시 규칙을 분리해 구버전 번들 404와 업데이트 루프를 막는 방법을 정리한다. 글에서 다룬다.
# index.html: 배포 때마다 새로운 해시 에셋 주소를 확인하도록 유도한다.
Cache-Control: no-cache
# /assets/*: URL에 버전 해시가 포함되어 있으므로 불변(immutable) 캐시를 적용한다.
Cache-Control: public, max-age=31536000, immutable
index.html은 매번 서버에 재검증을 요청해야 한다. 파일명이 동일하지만 진입점이기 때문에, 이 파일을 캐싱해서 그대로 사용하면 모든 것이 예전 파일을 사용하게 된다. index-a1b2c3.js 처럼 해시가 박힌 파일은 index.html 만 최신이라면 사용하는 URL이 변경되어 연쇄적으로 새로운 파일을 요청하게 된다. max-age=31536000, immutable을 적용해 최대 1년간 네트워크 재요청 없이 캐시를 재사용하도록 만든다.
댓글 0개