Network

HTTP cache(with. MDN)

Creative_Lee 2023. 9. 24. 14:26

이번 포스팅에서는 HTTP cache에 대해 알아보겠습니다.

포스팅의 전반적인 내용은 MDN - HTTP caching 문서 기반입니다.

다루지 않은 디렉티브나 내용에 대해 더 자세하게 알아보고 싶다면 문서를 참고하세요!

 

HTTP caching - HTTP | MDN

The HTTP cache stores a response associated with a request and reuses the stored response for subsequent requests.

developer.mozilla.org


캐시(cache)란?

자주 사용되는 자원(데이터, 값)을 임시로 복사해서 보관하는 장소를 말합니다.
기존 자원에 접근하는 시간 혹은 다시 계산하는 시간 절약하고자 사용합니다.


HTTP cache

cache는 웹 통신 과정에서도 사용합니다.

프로젝트 성향에 맞게 JS, CSS, image와 같은 파일들을 cache해서 불필요한 통신 리소스를 줄일 수 있습니다.   

 

통신 시간의 절약은, 곧 빠른 페이지 로딩으로 이어지고 사용자 경험을 향상시킵니다!


1. HTTP cache의 종류 

HTTP cache는 크게 private, shared 2가지로 나뉩니다.

1-1. private cache(비공개 캐시)

클라이언트에 (일반적으로 웹 브라우저)에 저장되는 캐시입니다.  
다른 클라이언트와 공유되지 않으므로, 사용자의 개인 응답을 저장할 수 있습니다.

 

 

1-2. shared cache(공유 캐시)

클라이언트와 서버(origin) 사이에 저장되는 캐시입니다.

shared cache는 다시 Proxy Cache와 Managed Cache 2가지로 나뉩니다.

 

  • Proxy Cache(프록시 캐시)
    클라이언트의 바로 다음단에 위치하는 포워드 프록시에 저장되는 캐시입니다.
    HTTPS가 점차 대중화 되면서 캐시의 역할보다는 필터링, 익명 접근, 접근 제한 등의 역할로 사용되고 있습니다.
    (종단간 암호화를 보장하는 HTTPS 특성 상 포워드 프록시에서 그 내용에 접근하기 어렵기 때문입니다.)
  • Managed Cache(관리형 캐시)
    오늘 포스팅의 주인공인 CDN 서비스 그리고 리버스 프록시, 캐시 API와 결합된 서비스 워커와 같은 환경에 저장되는 캐시입니다.
    관리형 캐시 서비스는 대부분 캐시 제어 헤더(Cache-Control)와 자체 구성 파일 또는 대시보드로 캐시의 동작을 제어하는 기능을 제공합니다.

2. HTTP cache의 유효 기간

저장된 HTTP cache는 Fresh, Stale 상태로 나뉩니다.

  • Fresh - 일반적으로 응답이 아직 유효하며 재사용할 수 있음을 의미합니다.
  • Stale - 응답이 만료되었음을 의미합니다.

2-1. Cache-Control header

Cache-Control은 HTTP 에서 캐시 메커니즘을 지정하기 위해 사용되는 헤더입니다.

Cache-Control 헤더에 작성하는 지시문을 디렉티브(directive) 라고 합니다.

 

 

*HTTP는 가능한 한 많이 캐시하도록 설계되었기 때문에 Cache-Control 헤더가 없더라도 캐싱을 합니다. 

이를 휴리스틱 캐싱이라고 합니다.

 


2-1-1. max-age, s-maxage 디렉티브와 age 

max-age는 캐시가 유지될 최대 시간을,  s-maxage는 shared cache(공유 캐시)에 유지될 최대 시간을 의미합니다.

age는 응답이 생성된 후 경과된 시간을 의미합니다.

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=604800
Age: 86400

<!doctype html>
…

위 예제에서 응답에 대한 캐싱은 604,800초 동안 유지되며, 응답이 생성된지 86,400초가 경과했음을 의미합니다. 

max-age에 명시한 시간내에 중복된 요청에 대해서는 캐시에 저장된 응답을 제공합니다. - Fresh

age가 max-age를 넘어선 후 부터는 origin(원본서버)에 요청합니다. - Stale

 

 

*HTTP/1.0 에서는 Expires 헤더를 사용하기도 했습니다.

Expires: Tue, 28 Feb 2022 22:22:22 GMT

하지만 위 시간 형식은 의도치 못한 버그가 많았고, HTTP/1.1 에서는 max-age 디렉티브가 채택되었습니다.
Expires 헤더는 사용하지 않는 것이 좋겠습니다. 


2-2. Validation 

cache가 Stale 하다면 origin으로 요청을 해야합니다. 

이때 origin에서 받아올 데이터가 이전과 변함이 없다면, 같은 데이터를 또 받아오는 것은 낭비가 될 수 있겠죠.

 

이러한 이유로 Stale한 캐시는 즉시 폐기되지 않습니다.

HTTP는 origin에 요청할 때 Stale한 캐시를 그대로 갱신하여 사용할지, 새로운 응답으로 갱신할지 검증하는 메커니즘이 있는데

이를 revalidate 라고 부르고 conditional requests 과정을 통해 진행됩니다. (캐시 유효성 재검증 및 조건부 요청) 

 

캐시 유효성 재검증 방식은 크게 2가지로 나뉩니다.

  • Last-ModifiedIf-Modified-Since - 리소스의 마지막 변경 시간으로 검증하는 방법   
  • ETagIf-None-Match - 리소스 식별자로 검증하는 방법

 

위 2가지 방식 모두

리소스가 변경 되지 않았다면, 304 Not-Modified 상태 코드와 함께 Stale한 캐시를 그대로 갱신하여 재사용 합니다. 

변경이 없는 사실만을 전달하면 되므로, 응답 body가 없어 전송 크기 자체가 매우 작고 빠릅니다.

변경 되었다면, 200 상태 코드와 함께 새로운 응답으로 캐시를 갱신합니다. 


2-2-1. Last-Modified / If-Modified-Since  방식

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600

<!doctype html>
…

리소스 응답에 Last-modified 헤더가 포함되고, 마지막 변경 시간이 담겨있습니다.

 

GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-Modified-Since: Tue, 22 Feb 2022 22:00:00 GMT

이후 Stale cache에 대한 재검증 시

이전 응답으로 받은 Last-modified 헤더의 마지막 변경 시간을 If-Modified-Since 헤더에 담아 리소스의 변경을 검증합니다. 

 

파일의 수정 시간은 비교적 쉽게 얻을 수 있기에 간단하게 재검증을 구현할 수 있습니다.

정적 파일을 제공하는 경우에 유용하죠.

그러나 1초 미만의 단위로 조정이 불가능 하고, 주석/개행의 수정과 같이 유의미한 변경이 아닌 경우에도 변경으로 취급하기에 불편함이 있습니다.  


2-2-2. ETag / If-None-Match 방식

리소스 식별자 기반의 재검증 방식이 위와 같은 불편함을 해결해 줄 수 있습니다.

 

ETag 응답 헤더에 포함되는 식별자는 서버에서 생성하는 임의의 값입니다.

이 값의 형식에는 제한이 없어, 자유롭게 생성 가능합니다.

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
ETag: "33a64df5"
Cache-Control: max-age=3600

<!doctype html>
…

리소스 응답에 Etag 헤더가 포함되고, 식별자가 담겨있습니다.

 

GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-None-Match: "33a64df5"

이후 Stale cache에 대한 재검증 시

이전 응답으로 받은 Etag 헤더의 식별자를 If-None-Match 헤더에 담아 리소스의 변경을 검증합니다. 


2-3. no-cache / no-store

Cache-Control의 디렉티브

  • no-cache - 캐시를 사용하되, 매번 재검증을 통해 Origin의 신선한 리소스를 가져오도록 합니다.
  • no-store - 캐시를 사용하지 않습니다.

2-4. private / public

Cache-Control의 디렉티브

  • private - 브라우저에만 캐싱을 허용합니다.
  • pubilc - 공유캐시(Shared cache)에서도 캐싱을 허용합니다.

 

 

 

 

네트워크 재밌자네?