HTTP 캐싱

이건회·2023년 7월 30일
0

네트워크

목록 보기
24/24

Http 캐시란?

http 캐시는 특정 응답의 요청을 어딘가에 저장해놨다가. 동일한 요청이 올 때 재사용 하는 것을 말한다.

응답을 재사용하면 서버까지 질의를 하는 과정을 거치지 않으니 클라이언트 측에서는 빠른 응답을, 서버 측에서는 부하의 감소를 기대할 수 있다.

urlresponse
https://hamad.com/index.html
https://hamad.com/style.cssbody {…}
https://hamad.com/script.jsfunction main () {…}

보통 http 캐시는 위와 같이 url을 키 값으로, 응답을 value로 하여 저장한다.

Http 캐시 종류

http 캐시는 크게 Private 캐시와 Shared 캐시로 구분할 수 있다.

Private Cache

쉽게 말하면 웹 브라우저 단에서 존재하는 캐시이다. 이것이 왜 private일까? 브라우저는 내 컴퓨터에만 있기 때문에 다른 사용자들이 접근할 수 없기 때문이다. 따라서 이곳에는 사용자의 개인화된 응답을 저장할 수 있는 것이다.

주의할 점은, 만약 서버의 응답에 “Authorization”헤더가 존재한다면 브라우저는 이 정보를 private 캐시에 저장하지 않는다.

Shared Cache

Shared Cache는 다시 Proxy와 Managed로 구분할 수 있다

Proxy Cache

클라이언트와 서버 사이에서 http 메시지를 전달하는 역할이다. 이 프록시 캐시는 개발자들이 직접 제어할 수 없고 http 오직 헤더를 통해서 캐시 정보를 전달하기만 할 뿐이다. 포워드 프록시에서 동작하는 캐시이다.

Managed Cache

개발자들이 제어할 수 있는 캐시이다. 리버스 프록시(nginx 등), 혹은 cdn(Aws CloudFront 등)을 의미한다. nginx에서 캐시 관련 설정을 직접 할 수 있으며, cdn도 제공 업체에 대시보드를 통해 설정할 수 있다.

캐시 유효기간

서버에서 언제까지 해당 캐시의 응답을 유효하다고 판단할지, 그 유효기간을 지정할 수 있다. Http 헤더의 Date에 응답 생성 시간을 적고, Cache-Control에 최대 유효시간 초를 설정한다.

HTTP/1.1 200 OK
Content-Type : ...
Content-Length : ...
Date : Tue, 30 Jul 2023 11:11:11 GMT
Cache-Control : max-age:604800

위와 같이 Cache-Control의 max-age:604800로 설정하여, 604800초, 즉 일 주일 동안 유효한 캐시를 설정해두었다.

유효기간이 지나기 전의 캐시 상태를 Fresh(신선함)라고 표현하며, 유효기간이 지난 캐시 상태를 Stale(오래됨, 만료됨)이라고 표현한다.

재검증

그런데, Stale한 캐시라고 해서 무조건 쓰레기통에 던져버리면 될까? 유효 기간이 지났어도 서버에서 갱신되지 않아 여전히 유효한 캐시들이 있을 텐데, 단순히 시간이 지났다고 날려버리고 다시 서버를 타면 그만큼의 불필요한 리소스가 낭비될 것이다. 따라서 유효시간이 지난 캐시가 아직 유효한지를 판단하는 검증이 필요하다. 이를 유효성 검증(validation), 혹은 재검증(revalidation)이라 한다.

재검증을 하는 방식은 “조건부 요청”을 통해서 진행한다. 날짜 기준으로만 확인하는 If-Modified-Since와 태그 값을 통해 확인하는 ETag/If-None-Match 방법이 존재한다.

If-Modified-Since

GET /index.html HTTP/ 1.1
Host : hamad.com
Accept : text/html
If-Modified-Since : Tue, 30 Jul 2023 21:00:00 GMT

만약 위와 같은 재검증 요청에 다음과 같은 200 OK 응답을 받았다면 캐시된 요청이 서버에서 갱신됐다는 뜻이다.

HTTP/1.1 200 OK
Content-Type : text/html
Content-Length : ...
Date : Tue, 30 Jul 2023 22:22:22 GMT
Last-Modified : Tue, 30 Jul 2023 22:00:00 GMT
Cache-Control : max-age:3600
<!doctype html>
...

Date는 응답이 생성된 시간이며, Last-Modified는 서버에서 해당 자원이 수정된 시간이다. 자원이 수정되었으므로 body에 html 응답 값이 새로 들어온다

브라우저는 응답받은 last-modified 헤더를 기준으로 유효 시간(3600초=1시간)이 지나면 다시 아래와 같은 get 요청을 통해 재검증을 한다. 아래의 GET 요청에서 브라우저가 보내는 If-Modified-Since 헤더 값은 이전 응답에서 받은 Last-Modified 헤더의 값이다.

GET /index.html HTTP/ 1.1
Host : hamad.com
Accept : text/html
If-Modified-Since : Tue, 30 Jul 2023 22:00:00 GMT

만약 다시 유효시간이 지나 위와 같은 재검증 요청을 했는데도, 캐시가 갱신되지 않았다면 304 Not Modified 응답을 받는다. 이 때는 캐시가 유효하므로 body에 아무 응답이 없다.

HTTP/1.1 304 Not Modified
Content-Type : text/html
Content-Length : ...
Date : Tue, 30 Jul 2023 23:22:22 GMT
Last-Modified : Tue, 30 Jul 2023 22:00:00 GMT
Cache-Control : max-age:3600

Last-Modified 값이 요청의 If-Modified-Since값과 동일하므로 갱신되지 않음을 확인할 수 있다. 304 응답은 body에 아무런 값이 없다.

그러나 If-Modified-Since는 초 단위까지는 비교가 가능하나, 밀리세컨드 단위까지는 비교가 불가능하다. 따라서 이후에 설명할 방법론에 비해 상대적으로 정교한 캐시 체크를 수행할 수 없다는 단점이 있다.

ETag/If-None-Match

따라서 해시 값의 태그번호를 헤더에 넣어 보내는 ETag/If-None-Match(쉽게 ETag) 방식이 존재한다.

브라우저가 요청 시 서버는 해당 자원 요청에 임의의 해시 값을 부여하고, 응답의 헤더 값의 “Etag” 필드에 넣어준다.

HTTP/1.1 200 OK
Content-Type : text/html
Content-Length : ...
Date : Tue, 30 Jul 2023 22:22:22 GMT
Etag : "ssdafjka" // 임의의 해시 태그값 부여
Cache-Control : max-age:3600
<!doctype html>
...

캐시 유효기간인 3600초 이후 재요청을 보내면 “If-None-Match” 헤더에 이전에 응답받은 해시값을 넣어 보낸다.

GET /index.html HTTP/ 1.1
Host : hamad.com
Accept : text/html
If-None-Match : "ssdafjka"

서버는 해시 값을 보고 해시값에 해당하는 자원을 확인하여, 이전과 같이 갱신되면 200, 갱신되지 않았으면 304를 응답한다.

태그 값을 활용하니 이제 시간과는 전혀 상관없이 갱신 여부를 체크할 수 있다.

강제 재검증(Force Revalidation)

강제 재검증은 항상 최신화를 하는 것이다. 아래와 같이 응답의 캐시 컨트롤 헤더에 no-cache가 부여되면, 일단 캐시에는 저장을 하나, 요청이 올 때마다 항상 서버에게 갱신 여부를 질의하고 200, 304 응답 여부에 따라 캐시의 사용 여부를 검증한다.

HTTP/1.1 200 OK
Content-Type : text/html
Content-Length : ...
Date : Tue, 30 Jul 2023 22:22:22 GMT
Last-Modified : Tue, 30 Jul 2023 22:00:00 GMT
Etag : "ssdafjka"
Cache-Control : no-cache
<!doctype html>
...

참고로 no-cache 이외에도 no-store 방식이 존재하는데, 일단 캐시를 저장하는 no-cache와 달리 no-store는 캐시 자체를 저장하지 않는다 한다.

궁금한 점

  • 캐시 유효시간 이내에 데이터가 갱신되면 어떻게 대처하나?
    -> 사실 이 부분은 유효시간을 잘못 잡은 개발자의 잘못이 아닐까…그러니까 배포 시에만 갱신될 확률이 높은 정적 파일은 유효시간을 상대적으로 길게 잡거나 가변적인 리소스는 유효시간을 극단적으로 적게 잡는 등 비즈니스에 대한 이해도 있어야할 것 같다.
    -> 그럼에도 불구하고 캐시 유효시간 이내에 데이터가 계속 수정될 경우, 아래에서 언급하는 no-cache를 사용하는 의미가 있을 것이다.
  • no-cache는 일단 캐싱을 하지만 요청이 올 때마다 검증을 하는데, 이러면 네트워크를 계속 타는데 캐싱의 의미가 있나? 그냥 no-store를 사용하면 되는 것 아닌가?
    -> 유효시간 이내 갱신될 확률이 높을 때, no-cache를 사용하면 계속 갱신여부를 확인할 수 있고, 미갱신시에는 body에 값을 가져올 필요가 없을 것이다. 결국 body에 값이 있으면 그만큼 리소스를 잡아먹게 되므로 no-cache로 유효성 검사를 계속 하더라도 리소스상 이득이 있다.
    -> 또 no-store를 사용하면 브라우저의 기능, 예를 들면 BFCache(뒤로가기/앞으로 가기 최적화)등의 캐시 기능을 사용하지 못할 수 있다고 한다. 아마 이러한 이점을 쓰기 위해서 no-store 대신 일단 캐싱을 하는 no-cache를 사용하는 듯
  • 캐시 관련 헤더가 없는 경우와 no-store의 차이가 존재 하는가
    -> Cache-Control 헤더가 존재하지 않으면, 기본적으로 브라우저나 서버가 지정한 기본 동작에 의해 캐시를 한다.(크롬의 경우 약 1년 정도 캐싱을 한다고 함)
  • 서버가 이태그 값을 어딘가에 저장하고 관리하는건가?
    -> 이태그 값은 저장한다기 보단, 리소스의 현재 상태를 기준으로 계산한다고 생각해야 한다. 예를 들어 서버가 파일의 속성, 즉 수정일과 변경 시간, 크기, 해시 함수 등을 기준으로 이태그 값을 만들어 판단하는 방식이다. 따라서 저장할 필요가 없다.

참고 : https://www.youtube.com/watch?v=UxNz_08oS4E

profile
하마드

0개의 댓글