정적자산 캐시정책 세우기(vercel.json)

문도연·2023년 12월 4일
2

같은글 노션링크 | 코드 변경 전후를 명확하게 보실수있습니다.

1. 캐시정책?

1.1 캐시정책 설정하는 이유

2.구현하기 전에 고민할 것

2.1 Vercel의 기본 캐시정책
2.2 적절한 캐시수명 정하기

3. 정적자산 파일별 캐시 헤더 설정하기 (vercel.json)

4. 결과물 및 마무리


1. 캐시정책?

유저가 같은 사이트를 자주 재방문한다고 치자. 매 요청마다 해당 사이트의 컨텐츠를 보기 위한 데이터를 서버로부터 다운받는다면 매번 통신비가 발생하고, 데이터를 전송 받는 시간이 소요될 것이다.

만약 사이트 UI가 자주 변경될 일이 없거나, 실시간 데이터를 제공하는 서비스가 아니라면, html, js, css, 이미지, 비디오 등의 정적 파일에 캐시 정책을 설정해 불필요한 네트워크 요청을 줄이고 좀 더 빠르게 유저에게 서비스를 보여줄 수 있게 된다.

1.1 캐시정책 설정하는 이유

본 프로젝트는 Vercel에 배포 중인데, Vercel은 무료플랜이어도 CDN 서비스를 이용할 수 있게 해준다. 현재는 기본캐시헤더가 적용돼 있는 상황인데, 본 프로젝트의 정적자산에 맞게 커스텀한 캐시정책을 설정해준다면 CDN 서비스를 더 잘 활용하게 되어 궁극적으로 유저에게 더 빠르게 서비스를 제공할 수 있을 것이다.

2.구현하기 전에 고민할 것

2.1 Vercel의 기본 캐시정책

현재 버셀이 내 프로젝트 정적자산을 브라우저에 응답할때 캐시헤더는 다음과 같다.

  1. html파일, manifest.json, 웹폰트, 이미지, 파비콘

    • public, max-age=0, must-revalidate
    • vercel 공식문서: Edge Network와 브라우저 모두에 캐시하지 않도록 지시하는 것입니다.
      • public: 공유 캐시(또는 중개 서버)에 저장해도 된다는 뜻
      • private : 브라우저같은 특정 사용자 환경에만 저장하라는 뜻
      • max-age=0 :캐시 유효기간이 0초, 응답받자마자 만료된 것으로 여겨진다
      • must-revalidate
        • 만료된 캐시만 서버에 확인 받도록해라
    • 해석: 브라우저야 이 파일은 응답받자마자 캐시만료되는 애니까 매번 본서버에 요청해야돼!
  2. js

    • s-maxage=31536000, immutable

      1. s-maxage: 공유캐시에만 적용되는 수명, 개인캐시에서는 무시된다.

      2. immutable: Cache-Control 익스텐션 중 하나

        • immutable 속성의 동작

          브라우저 캐시가 fresh(max-age 내) 상태일때, 페이지를 다시 로드해도 Conditional GET을 발생시키는 않고 무조건 fresh cache를 사용한다. 브라우저가 할일(조건부 검증 네트워크 요청)이 줄어들기 때문에 성능 최적화에 도움이 된다.

          1. 이 속성이 생긴이유
            • 불필요하게 발생하는 Conditional GET 요청은 서버로부터 대부분 304 응답을 얻어내며 이는 분명히 불필요한 RTT(패킷왕복시간→요청)를 발생시킨다.
            • 특히 크롬의 경우 다른 브라우저보다 많이 캐시 검증을 하기 때문에 더 많은 불필요한 Conditional GET 요청을 보내고 있었던 것이 Facebook에서 보고된 바 있다고한다
          2. 따라서, 페이지를 다시 로드하더라도 캐시 유효기간 내라면 재검증이라는 불필요한 네트워크 요청없이 바로 fresh cache(cache hit)를 사용할 수 있도록 하기 위해 제안된 것이 Immutable Extension입니다.
          3. 정리: 웹 브라우저는 max-age가 설정되어 있어도 페이지를 새로고침 할 때 Conditional GET(서버에 데이터가 변경되었는지 확인하는 요청; If-None-match or If-Modified-Since)을 보내 캐시 검증(cache revalidation)을 진행한다. 그러나 immutable은 캐시가 max-age 내에 있으면 페이지를 새로고침해도 파일이 변하지 않았을 것이라는 확신을 갖고 굳이 재검증 네트워크 요청을 보내지 않는다.
          4. 이점: 사용자가 어떠한 이유로 페이지를 새로고침하는 경우에 불필요하게 발생하는 요청을 막을 수 있게 됩니다. 특히, 이미지, 동영상, 폰트 등 웹 페이지에 있어 중요하게 사용되는 리소스는 캐시를 재사용(cache hit)하는 것이 서버 부하 측면은 물론 UX 측면에서도 매우 유리하게 작용하게 됩니다.
          5. 주의:무작위로 리소스를 Immutable로 지정해버리면 사용자는 예상했던 의도대로 브라우저가 작동하지 않는다고 생각하여 페이지를 계속해서 새로고침하는 경우가 발생할 수도 있다 즉, max-age와 함께 이 옵션이 제공된다 하더라도 리소스를 immutable로 지정하는 것은 큰 주의 및 고려가 필요하다.
          6. 그럼 업데이트는 언제되나?
            • 코드변경후 업데이트된 js 파일이 브라우저에 제공되게 하기 위한 처리
              • 1) js파일의 url을 변경 → 브라우저가 url 변경 확인 후 업데이트 된 파일을 가져오게됨
              • 2) 수동으로 캐시 무효화: CDN 이나 API 를 사용해서 캐시된 js파일을 수동으로 제거하거나 무효화할 수 있다. 이렇게 하면 CDN이 다음 요청시 서버에서 업데이트 된 파일을 가져오게 된다.
            • 즉, 코드 변경으로 js 파일이 업데이트 되는 경우에는 1) 또는 2)를 통해 적용되고, 헤더에는 똑같이 s-maxage=1년(초), immutable 을 주는 것같다.
      • 'immutable' 지시어는 리소스가 변경되지 않을 것임을 CDN과 브라우저에 알린다. 이는 s-maxage에 1년 동안기간 동안 파일이 동일하게 유지될 것으로 예상된다는 의미이다.
      • 이 캐시 헤더 구성을 사용하면 CDN과 브라우저는 JS 파일을 1년 동안 캐시하고 해당 기간 동안 변경되지 않는다고 가정한다.
    • 해석: 브라우저야. 캐시 유효기간 1년이고, 브라우저에 저장된 응답캐시가 1년이 아직 안됐으면, 조건부 검증 네트워크 요청할 필요 없이 바로 브라우저 캐시 (fresh cache임) 가져다 써라. 배포 등 코드가 업데이트되는 경우에는 캐시무효화를 통해 CDN과 클라이언트에 변경사항이 바로 반영되는듯 하다.

2.2 적절한 캐시수명 정하기

적절한 캐시설정은 성능최적화와 최신정보를 응답하는 것 사이의 균형을 찾는 것에서 비롯된다.

  1. 페이지가 제공하는 서비스의 종류에 따라
    • 짧은 캐시
      • 날씨, 주식가격과 같이 최신정보를 제공해야하는 서비스(정보가 계속해서 갱신되는)
    • 긴 캐시
      • 블로그 글
  2. 주요 사용자층
    • 주 사용자 층의 기기성능과 네트워크 환경이 양호한 편이라면 짧게 설정해도 문제없음
    • 양호하지 않다면, 요청을 매번 보내는 것이 부담이며 사용자 경험을 떨어뜨릴 수 있음 캐시응답을 토대로 서비스를 이용할 수 있게 해주자
  3. 파일마다 적절한 수명정하기
    • 페이지에 접속하면 가장먼저 요청을 보내는파일인 index.html 파일의 경우, 갱신되지 않으면 나머지 리소스가 변했더라도 최신정보를 받아올 수 있다. 짧게 혹은 no-cach로 설정해 캐시를 항상 검증하자
    • 로고, 이미지, 파비콘, 폰트 등 갱신될 가능성이 적은 파일은 수명을 길게 설정해 캐시를 활용하자

3. 정적자산별 캐시정책 세우기(vercel.json)

  1. index.html
    • public, max-age=0, must-revalidate 유지
      • 브라우저, 엣지서버 모두에 캐시되지 않고, 매번 본서버에 네트워트요청
      • 페이지에 접속하면 가장 먼저 요청을 보내는 파일인 index.html 파일이 갱신이 되지 않으면, 나머지 리소스가 변했더라도 최신의 정보를 받아올 수 없으므로, 매번 본서버에 요청하도록 설정한다.
  2. 웹폰트
    • 기존: public, max-age=0, must-revalidate
    • 변경: s-maxage=31536000, immutable 적용하기
      • 본 프로젝트의 폰트를 변경할 가능성이 매우 낮다. 경로 자체가 해당폰트의 이름을 가리키기도 해서 어차피 다른 폰트를 메인 폰트로 사용한다면 바뀐 폰트가 바로 반영 될것임
      • 페이지를 새로고침할때 웹폰트가 깜빡거리는 시간이 짧아질 것으로 예상된다
  3. manifest.json, logo192.png, logo512.png, favicon.ico
    • 기존: public, max-age=0, must-revalidate
    • 고민
      • 파일들 공통점: 파일이름 자체는 변치않고 내용물은 바뀔수 있다.
      • 캐싱은 이용하되, 업데이트는 바로 적용되게 하고싶다!, url 버젼*에 대해 신경쓰고싶지않다.
        • 리소스가 자주 변경되지만 URL이 동일한 경우 캐시가 만료될 때까지 사용자에게 업데이트된 콘텐츠가 표시되지 않을 수 있음
    • 변경: max-age=0, s-maxage=86400
      • 브라우저에 캐시하지 않도록 지시하며 Vercel의 Edge Network가 24시간동안 응답을 캐시하고 배포가 업데이트될 때 캐시를 무효화한다. ⇒ 캐싱은 활용하되, 유저에게 항상 최신 변경사항을 응답할 수 있다.
        • CDN은 요청을 받으면 캐시를 확인한다. 리소스가 CDN 캐시에 있고 만료되지 않은 경우(24시간 이내에) CDN은 원본 서버에 요청하지 않고 브라우저에 직접 리소스를 제공한다.
        • 리소스가 CDN 캐시에 없거나 CDN 캐시에서 만료된 경우 CDN은 원본 서버에서 해당 리소스를 가져와 다음 24시간 동안 캐시한다.
  4. js
    • s-maxage=31536000, immutable 유지
      • SPA 이므로 js 파일 크기가 큰 편이다. 따라서 캐싱을 이용하는게 유리하다.
      • 엣지서버에 캐싱해두고, 최초 요청이후 부터 브라우저는 캐시유효기간 내라면 조건부확인 요청(재검증) 없이 바로 로컬캐시를 가져다 쓴다.
      • js파일에 변경이 생기면 vercel은 업데이트를 반영해 배포하고 이를 cdn에 전달, 기존의 캐시를 무효화할것이며, cdn은 200 상태코드와, 캐시 헤더로 s-maxage=31536000, immutable를 브라우저에 응답.

vercel.json 작성코드

{
  "headers": [
    {
      "source": "/fonts/(.*)", // 웹폰트
      "headers": [
        {
          "key": "Cache-Control",
          "value": "s-maxage=31536000, immutable"
        }
      ]
    },
    {
      "source": "/manifest.json", 
      "headers": [
        {
          "key": "Cache-Control",
          "value": "max-age=0, s-maxage=86400"
        }
      ]
    },
    {
      "source": "/favicon.ico",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "max-age=0, s-maxage=86400"
        }
      ]
    },
    {
      "source": "/images/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "max-age=0, s-maxage=86400"
        }
      ]
    },
    ...
  ]
}

4. 결과물 및 마무리

  • 폰트: 새로고침시 깜빡하면서 기본글씨체(noto sans)가 보이던 시간이 짧아졌다.

  • favicon.ico (manifest.json, logo.png 도 동일한 케시헤더 적용)

참고

Cache-Control 확장인 Immutable을 이용한 캐시 최적화 👏🏻
성능 최적화 2 — 캐시 설정하기 👏🏻
알아둬야 할 HTTP 쿠키 & 캐시 헤더
https://vercel.com/docs/edge-network/headers#cdn-cache-control-priority
https://vercel.com/docs/edge-network/caching#cdn-cache-control
https://vercel.com/docs/projects/project-configuration#headers
학습 | 버셀 정적자산 캐시정책

profile
중요한건 꺾이지 않는 마음이 맞는 것 같습니다

1개의 댓글

comment-user-thumbnail
2024년 5월 28일

요즘 좀 전문성 높이고 싶어서 온갖 글 다 보는 중인데, 이거 보니까 전 아직도 갈 길이 머네요 ㅎㅎ… 부트캠프라도 들으면 더 빨리 늘 것 같아서 알아보는데 혹시 제로베이스 프론트엔드 이직반 아시나요?? (https://bit.ly/4e0hC9o) 여기 커리만 보면 괜찮은 것 같은데 궁금하네요.

답글 달기