이 글의 원본은 https://blog.jxck.io/entries/2016-07-12/cache-control-immutable.html(일본어)이며 번역과 약간의 수정을 진행했습니다.


소개

웹 브라우저는 max-age가 설정되어 있어도 페이지를 새로고침 할 때 Conditional GET(서버에 데이터가 변경되었는지 확인하는 요청; If-None-match or If-Modified-Since)을 보내 캐시 검증(cache revalidation)을 진행합니다. Extension Cache-Control directives로 제안된 immutable은 캐시가 max-age 내에 있으면 페이지를 새로고침해도 파일이 변하지 않았을 것이라는 확신을 갖고 이러한 요청을 보내지 않도록 합니다.

Cache-Control

Cache-Control은 불필요한 클라이언트의 서버 요청을 최소화시켜 서버 입장에서는 부하를 감소시키고 클라이언트 입장에서는 페이지를 더 빠르게 로드하고 트래픽을 줄일 수 있도록 해주는 중요한 헤더입니다. max-age 값으로 리소스가 신선(fresh)하다고 판단할 시간을 정하며 이 시간 내에서는 다음과 같이 작동하도록 되어 있습니다:

  • 링크를 클릭하거나 페이지 리디렉션으로 도달한 경우: fresh cache 사용(cache hit)
  • 페이지를 새로고침(F5, Cmd + R)한 경우: fresh cache에 대한 Conditional GET 실행
  • 강제 새로고침(Shift + Reload)한 경우: fresh cache 여부와 상관없이 GET 실행

문제점

웹 브라우저 구현상의 이유로 불필요한 Conditional GET 요청이 꽤 많이 발생합니다. 특히 크롬의 경우 다른 브라우저보다 많이 캐시 검즘을 하기 때문에 더 많은 불필요한 Conditional GET 요청을 보내고 있었던 것이 Facebook에서 보고된 바 있습니다:

불필요하게 발생하는 Conditional GET 요청은 서버로부터 대부분 304 응답을 얻어내며 이는 분명히 불필요한 RTT를 발생시킵니다. 따라서 캐시에 대한 스펙이 완전히 정립되기까지 브라우저 구현에 의존하지 않고 페이지를 다시 로드하더라도 fresh cache(cache hit)를 사용할 수 있도록 하기 위해 제안된 것이 Immutable Extension입니다.

Immutable Extension

Immutable Extension은 Cache-Control 확장 중 하나입니다. 다음과 같이 사용할 수 있으며, 브라우저 캐시가 fresh(max-age 내) 상태이면 페이지를 다시 로드하면 Conditional GET을 발생시키는 대신 무조건 fresh cache를 사용합니다.

Cache-Control: max-age=10000, immutable

이에 따라 사용자가 어떠한 이유로 페이지를 새로고침하는 경우에 불필요하게 발생하는 요청을 막을 수 있게 됩니다. 특히, 이미지, 동영상, 폰트 등 웹 페이지에 있어 중요하게 사용되는 리소스는 캐시 재사용(cache hit)이 서버 부하 측면은 물론 UX 측면에서도 매우 유리하게 작용하게 됩니다.

현재 글을 쓰는 시점에서 다음 브라우저들이 immutable extension을 지원하고 있습니다:

참고: immutable extension은 https 통신을 통해서만 지원됩니다.

Demo

Cache-Control: immutable을 설정한 이미지와 설정하지 않은 이미지를 로드하는 페이지를 만들었습니다. 직접 들어가서 새로고침하면서 확인이 가능합니다.

유저의 새로고침

다음과 같은 이유에서 사용자들이 페이지를 새로고침 할 수도 있습니다:

  • 새로운 내용이 올라왔는지 확인하고 싶은 경우 (F5 연타)
  • 버그로 인해 페이지가 제대로 렌더되지 않은 경우
  • 네트워크 문제로 브라우저에서 콘텐츠를 제대로 내려받지 못한 경우
  • 어떤 이유에서 페이지가 사용자에게 새로고침을 유도하는 경우 (희소)

1번의 경우 가장 많은 경우이고 이러한 경우는 대부분 사용자가 수동으로 페이지를 새로고침해서 업데이트된 내용을 확인하게끔 페이지를 잘못 설계한 영향이 큽니다.

때문에 원래 사용자가 직접 새로고침을 하도록 하는 것 자체가 사이트 설계 자체 문제로 여겨질 수 있지만, 네트워크 프록시 또는 브라우저 확장 및 기타 이유로 사용자가 페이지를 새로고침하는 경우도 적지않게 존재할 것입니다. 이 경우에 사용자가 페이지를 새로고침하는 것은 서버에 fresh한 새로운 콘텐츠를 요구하고자 하는 의도가 드러납니다.

따라서 무작위로 리소스를 Immutable로 지정해버리면 사용자는 예상했던 의도대로 브라우저가 작동하지 않는다고 생각하여 페이지를 계속해서 새로고침하는 경우가 발생할 수도 있습니다. 즉, max-age와 함께 이 옵션이 제공된다 하더라도 리소스를 immutable로 지정하는 것은 큰 주의 및 고려가 필요하다고 생각합니다.

웹 폰트에 적용

대부분의 웹 사이트에서 가장 크게 작용될 수 있는 부분은 바로 웹 폰트입니다. 웹 폰트는 경로 자체가 해당 폰트의 이름을 가리키고 있는 경우가 대부분이며 파일 자체가 변경될 경우가 매우 적기 때문입니다.

또한 대부분 웹 폰트로 인해 페이지가 깜빡거리거나 로드하는데 오래 걸리는 경우가 많은데 Conditional GET으로 302 응답이 돌아온다고 하더라도 1-RTT가 발생하기 때문에 항상 로컬 캐시를 사용하는 쪽으로 유도하는 것이 좋습니다.