웹 페이지를 배포를 하다보면 자동으로 업데이트가 안됨을 확인할 수 있다. 그래서 각각의 사용자의 웹 페이지에 있는 캐시를 삭제하고 나서 웹 페이지가 업데이트됨을 확인할 수 있다.
그렇게 하면 각각의 사용자에게 캐시를 삭제하는 것을 요청해야 하는데, 이는 너무 비효율적이다. 그래서 이 글을 쓰게 되었다.
캐시의 생명 주기
HTTP에서 리소스(Resource)란 웹 브라우저가 HTTP 요청으로 가져올 수 있는 모든 종류의 파일을 말한다. 대표적으로 HTML, CSS, JS, 이미지, 비디오 파일 등이 이에 해당한다. HTTP 응답에 포함된 Cache-Control 헤더에 따라 받은 리소스의 생명 주기가 결정된다.
캐시의 유효 기간: max-age
서버의 Cache-Control 헤더의 값으로 max-age=<seconds> 값을 지정하면, 이 리소스의 캐시가 유효한 시간은 <seconds> 초가 된다.
캐시의 유효 기간이 지나기 전
한 번 받아온 리소스의 유효 기간이 지나기 전이라면, 브라우저는 서버에 요청을 보내지 않는다. 그리고 디스크 또는 메모리에서만 캐시를 읽어와 계속 사용된다. 즉 에서 필요한 데이터나 값을 저장해놨다가 다시 사용한다.
처음 접속한 페이지를 불러올 때 서버로부터 js 파일을 받고 사용한다. 다시 페이지에 들어가js파일을 또 받으면 그만큼의 시간이 소요되기 때문에, 캐싱된 js파일을 사용한다. 그렇기 때문에 더 빠르게 페이지를 렌더링할 수 있다. 매번 컨텐츠를 요청하여 받는 것이 아니라, 특정 위치에서 불러와 페이지 응답시간을 줄이고, 서버 트래픽 감소 효과를 얻는다.
캐시가 일어나는 과정
내 경우에는 nginx를 사용하고 있는데, response headers를 확인해보자.
nginx 웹 서버의 기본 cache 설정은 24시간이다.
특정 요청에 대한 응답 리스폰스 헤더를 열어보면 last-modified값과 date값이 있는 것을 확인할 수 있다.
- last-modified : nginx가 요청된 파일에 대한 최신본을 확인한 시간
- date : 실제 응답을 전달한 시간
→ 두 시간의 차이가 크지 않으므로, 캐시가 일어나게 된다.
리소스가 가지는 Cache-Control 헤더 값이 있는데, 만약 max-age=31536000이라면, 이 리소스는 1년(31,536,000초)동안 캐시할 수 있다. 유효한 캐시가 메모리에 남아 있기 때문에, 서버에 요청을 보내지 않는다.
아까 위에서 설명한 것처럼 한번 브라우저에 캐시가 저장되면 만료될 때까지 캐시는 계속 브라우저에 남아 있게 된다. 때문에 CDN Invalidation을 포함한 서버의 어떤 작업이 있어도 브라우저의 유효한 캐시를 지우기는 어렵다.
캐시의 유효 기간이 지난 이후
브라우저는 서버에 조건부 요청(Conditional request)을 통해 캐시가 유효한지 재검증을 수행하게 된다. 재검증 결과 브라우저가 가지고 있는 캐시가 유효하다면, 서버는 304 Not Modified 요청을 내려주게 된다. 304 Not Modified 응답은 HTTP 본문을 포함하지 않기 때문에 js파일을 매우 빠르게 내려받을 수 있다.
재검증 결과 캐시가 유효하지 않으면, 서버는 200 OK 응답 또는 적합한 상태 코드를 내려준다. 추가로 HTTP 요청을 보낼 필요 없이 바로 최신 값을 내려받을 수 있다.
정적 파일 캐싱하기
내 경우에는 프론트 파일을 react로 작성하였다. react로 코딩하게 되면 프로젝트를 생성할 떄 Create React App(CRA)로 생성한다. CRA를 사용하게 되면, js파일을 build를 거쳐 배포를 하게 된다. 이렇게 되면, 각 파일이름에 파일 내용의 고유한 해시가 포함되고, 정적 파일로 배포를 하게 된다. 파일 이름의 이 해시는 장기 캐싱 기술을 가능하게 된다.
아래는 js파일, 즉 배포시 청크파일로 변하여 웹페이지에 배포되게 된다. 아래와 같은 형식으로 청크파일이 되게 된다.
main.[hash].chunk.js
[number].[hash].chunk.js
그렇게 되면, 몇 번을 빌드해도 파일의 해쉬값이 동일하게 된다. 몇 번을 빌드해도 저 해쉬 값이 동일하게 되면, 배포가 이뤄져도 웹서버가 기존 캐시를 날리고 최신 파일로 교체해야 하는지 알 수 없게 된다.
webpack 설정 바꾸기
CRA는 항상 빌드 시 동일한 이름으로 정적 파일 이름을 짓는다.
react-app-rewired라이브러리를 사용해서 webpack의 설정을 바꿔준다. 그래서 파일의 해쉬 값이 달라지게 되면, js파일을 최신 파일로 교체하게 된다. 이 후, 업데이트하게 되면 자동으로 업데이트하게 된다.
react-app-rewired 사용하기
: CRA 환경의 웹팩 설정을 커스터마이징 할 수 있게 도와주는 라이브러리
npm install react-app-rewired
위와 같이 라이브러리를 설치해준다.
그리고 config-overrides.js을 만들어준다. 해당 파일은 루트 폴더에 생성해준다.
module.exports = {
webpack: function (config, env) {
config.output.filename = 'static/js/[name].[hash:8].js';
config.output.chunkFilename = 'static/js/[name].[hash:8].chunk.js';
return config;
},
};
그리고 package.json에서 build 명령어를 react-app-rewired build 로바꿔주면 된다.
"scripts": {
"start": "react-scripts start",
"build": "react-app-rewired build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
이러한 과정을 마치면 CRA로 만들어진 프로젝트는 빌드될 때마다 새로운 js 파일 이름을 가지게 된다. 만약 내용이 바뀔 여지가 없는 리소스는 max-age를 최대치인 31536000 으로 주면 된다.
🔗 참고
https://toss.tech/article/smart-web-service-cache
https://velog.io/@cksrb63/웹-캐시-잘-다루기