2. 웹개발/웹개발 기본

웹 Cache(웹 브라우저 캐시) & 헤더 다루기

갓대희 2023. 8. 9. 18:00
728x90

안녕하세요. 갓대희 입니다. 이번 포스팅은 [  웹 Cache(웹 브라우저 캐시) & 헤더 다루기 ] 입니다. : ) 

 

웹브라우저는 서버와 HTTP 프로토콜을 통해 리소스를 서버에게 요청을 하여 가져온다.

 - 리소스(resource) : 웹 브라우저가 HTTP 요청을 통해 가져올 수 있는 모든 파일

   ex) html, css, js, image, video 등

 

 - 클라이언트는 웹브라우저를 통해 요청하여 네트워크를 거치는 시간, 서버는 이 요청을 처리하는데 시간이 소비되는데 

이러한 통신 과정이 똑같은 데이터 요청임에도 매번 발생하게 되면 비효율 적일 수 있다. 

따라서 이러한 비효율 적인 부분을 해소하기 위한 해결책으로
웹브라우저에도 캐시의 개념을 적용하게 되었다.

이 HTTP캐시를 다루기 위해 알아야 할 것이 HTTP에서 제공하는 헤더(Headers)인 Cache-Control 이다.

오늘은 직접 CDN 설정을 통해 HTTP 캐시 제어와 관련하여 알아 보려고 한다.

 

0. 캐시의 종류

 - 캐시는 클라이언트에 보관할 수도 있고,  클라이언트와 서버 사이에 존재하는 프록시 서버 등의 중개 서버에서 보관할 수도 있다. 

1) 클라이언트 측이 보관하는 것이 개인 캐시 (사설 캐시, 브라우저 캐시, private cache, browser cache) 이다. (Chrome 기준 disk cache, memory cache)

 

2) 서버에서 보관하는 것이 공용 캐시 (공유 캐시, shared cache, public cache)이다. (대표적인 예시로는 CDN)

 - HTTP 헤더를 통해 Private 캐시, Public 캐시 별 설정을 의도적으로 가져갈 수 있다.

ex) 보안상 Public 캐시에 저장하지 않아야되는 케이스, 주기를 다르게 가져가는 케이스 등 

 

1. 캐시 제어 헤더 종류

1. Cache-Control 헤더

 - HTTP/1.1부터 명시적으로 캐시를 제어할 수 있는 헤더를 추가했는데 그것이 cache-control 헤더 이다.

 - 캐시의 유효 시간(생명 주기)과 관련된 내용을 명시하는 헤더

 

헤더 값, 파라미터 종류

참고) https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

브라우저 호환성도 혹시 모르니 체크하고 넘어 간다.

 

1. no-cache

 - request 헤더 : 캐싱된 응답을 받지 않고 이 요청을 원본 서버에 그대로 전달 해 원본 서버로부터 항상 최신의 응답을 받겠다.

 - response 헤더 : 캐싱된 데이터를 항상 유효한지 확인하도록 강제하는 설정이다.

이름만 보고 판단하면 잘못 판단할 수 있다. 

캐싱을 하긴 하지만 원본 서버에는 항상 갔다 오게된다. 캐싱을 안하는건 아니다.
실제로 아무 캐시도 저장하지 않도록 하기 위해서는 no-store로 설정해주어야 한다.

 

2. no-store

 - request, response 모두 동일하게 캐시를 사용하지 않겠다 라는 의미로 사용한다.

 - no-store는 로컬에 저장하는 것 자체를 금지한다.

 - 데이터에 민감한 정보가 포함되어 있어 저장 불가 하도록 처리할때 사용한다.

 

3. max-age

 - 캐시 유효 시간, 초 단위 설정

 - 예를 들어 max-age=60 은 1분간 캐싱한다는 내용이다.

 - 다만, max-age 60초가 지났다고 해서 해당 리소스에 대한 캐시를 완전 버리지 않는다. 

   서버에 한번 물어보고(조건부 요청), 리소스 변경이 없다면 해당 캐싱된 리소스를 그대로 사용한다.

 - 흔히, 캐싱되지 않아야 하는 민감한 리소스에 대해 응답헤더에 Cache-Control:max-age=0을 사용한다.

 

4. Age

 - Origin Server 의 응답이 프록시 캐시 서버에 머문 시간, 초 단위(sec).

 

5. expire

 - max-age는 유효기간을 초단위로 설정 하였지만 expire는 만료일자를 지정한다. 정확한 날짜를 지정하여야 한다.

 - Cache-Control 헤더에도 max-age로 유효 시간을 명시하는 것이 더 추천되기 때문에, 현재는 사용이 권장 되지 않고 하위 호환을 위해 사용된다.

 - 만일 max-age와 동시에 사용되면 Expires는 무시된다.

 

6. public

 - 응답 헤더에 사용할 수 있다. 

 - 모든 캐시 서버에 캐시될 수 있고 사용자 제한 없이 모든 사용자에게 응답이 전달될 수 있다.

 

7. private

 - public 캐시에 저장 불가

 - 최종 사용자 개인의 브라우저 환경에서만 캐싱을 하고, 외부 캐시서버(CDN 등의 범용 캐시 서버)에서는 캐싱할 수 없다.

 ex) 로그인을 해서 내 정보가 들어있는 페이지 접근시, 내 브라우저가 아닌 만약 다른 외부서버에까지 내 정보가 캐싱되는 경우를 방지해 준다. 

응답 메시지를 어디에 캐시할 것인지 지정하기 위해 사용할 뿐, 그 자체로 개인정보를 보호하는 장치가 되지는 않는다. 

 

 8. s-maxage

 - 공유 캐시에 해당하는 캐시에만 적용되는 수명이며, 개인 캐시에서는 무시. 

 - 헤더의 age 필드와 비교해서 캐시의 유효성을 판단한다.

 

9. must-revalidate, proxy-revalidate

 - must-revalidate : 캐시 만료후 최초 조회시 Origin Server 에 검증

 - proxy-revalidate : 위의 내용을 공유(public) 캐시에만 적용한다.

 

10. ETag

 - HTTP 응답 헤더로 리소스의 특정 버전에 대한 식별자라고 하며, 원본 서버가 리소스를 식별하기 위해 부여하는 고유 번호 이다.
 - ETag는 임의의 문자들이 따옴표 안에 포함되도록 하며, 이 값은 원본서버에서 결정한다.

 

11. Pragma

 - HTTP/1.0 하위 호환을 위해 사용하는 캐시 제어 헤더 이다.
 - Cache-Control과 동일한 역할을 수행하지만 권장되지 않는다.

 

ETC. 크롬 브라우저를 기준으로 웹 브라우저는 크게 2가지 방법으로 캐싱을 한다. (브라우저 자체 알고리즘에 따름)

1. 메모리캐시 : RAM에 데이터를 저장해놓는 방식
2. 디스크 캐시 : 하드 디스크에 파일로 저장해놓고 파일을 읽어서 저장하는 방식

 

2.  실제  CDN 설정을 통한 사례 확인

시나리오1)  기본 동작

 

▶ 테스트 환경

 - AWS S3를 통해 정적 웹 사이트 호스팅 하여 해당 서버가 오리진(Origin)서버 이다. (해당 서버는 미국에 있다.)

 

 - React 정적 웹 사이트를 호스팅 한 뒤 접속해보면,

   다음과 같이 상태(status)는 200 응답을 받으며, 새로고침 등 매번 접속시 contents들을 다운로드 하는것이 기본 동작.
   (특정 브라우저, 특정 옵션등을 특수 케이스들은 무시)

 

▶ 결과 확인 

 

※ 위의 구조를 다음과 같이 다음 도식으로 표현해 보자.

1) 단점

- 이미지 응답이(HTTP 헤더 + 바디) 1.0M 라고 했을때 똑같은 이미지를 요청한다면, 서버에서는 매번 1.0M 용량의 데이터로 응답해야 한다.

- 용량이 크면 클 수록 통신 비용이 커지게 되고 로딩속도가 느려지게 된다. (매번 10초)

 

2) 해소방법

 - 이를 해소하기위한 방법 중 하나로 중간에 프록시 캐시를 둘 수 있다.

 - 다음과 같이 구성하면 Client는 프록시 캐시 서버에 요청하면 되고,

   프록시 캐시 서버는 오리진 서버에 요청한 자원이 있다면, 해당 자원을 갖고 응답해주면 훨씬 빨라질 수 있을 것이다.

   (클라이언트 입장에서는 프록시 캐시 서버를 바라보게 되고, 10초 > 1초로 개선되는 효과)

 

시나리오2)  CDN 적용 및 Cache-Control 메타 적용

 

▶ 테스트 환경

1) AWS S3를 통해 정적 웹 사이트 호스팅 하여 해당 서버가 오리진(Origin)서버로 두고

2) AWS CloudFront(CDN)를 통해 배포하고, Client에게 제공 하려고 한다.

 

▶ 설정

1. index.html

 - max-age=0
 - 기대동작 : index.html만 노 캐싱 처리

 

2. index.html을 제외한 모든 resource에 대한 설정

 - max-age=60
 - 기대동작 : index를 제외한 컨텐츠에 대해 60초 캐싱

▶ 결과 확인

1. CDN을 통해 최초 접속 (이미 한번 접속했었기 때문에, 캐시, 쿠키 등 비우기 이후 깨끗한 상태에서 수행)

 - Origin 서버 직접 접속한 것과 동일한 결과

 

2. 재 접속

Case 1) 캐시의 유효 기간이 지나기 전 접속

 - index.html을 제외하고 60초 캐시

 - api, 크롬 plugin.js 에 대해서는 회색 음영으로 제외 하였다.

 - 상세 확인

 

 - 한번 받아온 리소스의 유효 기간(위의 예시로 60초)이 지나기 전 요청을 하게 되면, 
   브라우저는 서버에 요청을 보내지 않고 메모리 or 디스크 캐시를 통해 읽어와 사용하게 된다.

   ex) 상기 응답 헤더의 Age: 8


 - 즉 Response Headers의 Age= 8(캐싱된지 8초), 60초가 지나기 전 이기 떄문에 disk or memory caching 된 자원(private 캐시)을 사용 하였다.

 

※ 이때 저장되고 사용한 디스크 캐시, 메모리 캐시를 private 캐시라고 한다.

※ 이중에서도 memory cache가 더 빠른것을 볼 수 있다.

 - 간단히만 알아보자면

(디스크캐시)는 하드디스크에 접근하는 시간을 개선하기위해 램(RAM)에서 가져오며, 

(메모리캐시)는 더 빨리 가져오기 위해 램에 접근하지 않고 CPU 칩 안에 있는 (L1, L2, L3)같은 메모리에서 가져오게 된다.

 

Case 2) 캐시의 유효 기간이 지난 후(60초 이후) 접속

 - api, 크롬 plugin.js 에 대해서는 회색 음영으로 제외 하였다.

 - 304 Not Modified로 응답 주게 된다. (200 ok status로 재 전송을 생각 하였을 수도 있다.) 

 

※ 참고 

 - 현재 테스트 중인 CDN은 AWS의 Cloud-Front로  X-Cache 헤더가 “Hit from cloudfront” 또는 “RefreshHit from cloudfront”인 경우 엣지 로케이션의 캐시에서 요청이 처리된 것이라고 볼 수 있다.

 - Clout-Front에서 제공해주는 특수한 header에 대한 내용은 향후 AWS에 따로 자세히 알아 보고 넘어 가도록 한다.

https://catalog.us-east-1.prod.workshops.aws/workshops/4557215e-2a5c-4522-a69b-8d058aba088c/ko-KR/cache-control/ttl-control-from-origin#:-s3-ttl

 

※ 나의 경우 잘못된 의식의 흐름

 - 캐시의 유효기간이 지나게 되면 캐시가 사라지게 되지 않을까? 
 - 유효기간 지난 후 다시 해당 웹 페이지를 호출하면 status가 200으로 다시 resource를 당겨오지 않을까?


※ 지금 내가 속해있는 사이트의 경우 Default Cache 유효기간을 1년으로 검토하고 있다.

 - 이때 조심하거나, 우려해볼만 사항이 뭐가 있을까?

 - 이 경우 일반적인 상황에서는 한번 캐싱된 이후 약 1년간은 서버를 찌를일이 없이, 캐싱된 자원을 사용한다고 볼 수 있겠다.

 - 이러한 특성(디스크 캐시, 메모리 캐시를 타서 서버에 요청조차 하지 않는 상황을 가정)때문에 해당 유효 기간이 지나기 전이라면, CDN Invalidation, purge 를 했다고 하더라도 private 캐시를 사용하고, 서버로 요청조차 날리지 않기 때문에,

고객들의 브라우저, 즉 남의 PC에 있는 브라우저 내 유효한 캐시를 지우긴 어렵게 된다.
  ( 잘못된 캐싱 전략을 세우는 경우 CS센터를 통해 강제로 고객들에게 캐시 초기화를 하도록 유도해버리는 상황이 발생하기도 한다. )

 

▶ 결론적으로 나의 추측은 틀렸다.

 - 캐시의 유효기간이 지나도 캐시는 사라지지 않고, 

   대신! 브라우저는 조건부 요청을 날리고, 서버에 캐시가 유효한지 재 검증 한다.


 - 검증 결과 브라우저가 갖고 있는 캐시가 유효한 경우, 

   서버는 Response Header를 통해 304 Not Modified 상태를 내려준다. 

   304 Not Modified 응답은 본문을 포함하지 않기 떄문에, 매우 빠른 응답을 내려 받을 수 있다.

 

▶ 304 Not Modified

 - 변경이 없는 경우 서버는 304(not-modified)코드만 본문 없이 보내기 때문에 서버와 네트워크의 자원낭비를 방지할 수 있다.

ex) 200(OK) : 4.7kb   vs   304(Not Modified) 313B

※ 이때 저장되고 사용한 캐시를 public 캐시라고 한다.

 

▶ 이런 private 캐시, public 캐시의 개념을 이해 하였다면, 의도를 갖고 설정 가능 하다.

ex) cache-control: s-maxage=60, max-age=0

 - CDN에서는 60초 동안 캐시되지만 브라우저에서는 매번 재검증 요청을 보내도록 설정 하였다.

 - (캐시, 쿠키 비운 이후) 최초 접속 이후, 60초 이내에 재접속시 아까와 같은 경우 privateCache를 탔지만,

   이번엔 모두 304(NotModified) 되는것을 볼 수 있다.

(응답 헤더의 Age: 11로 60초 이내지만, 304로 CDN 확인 처리)

 

캐시 검증 동작

 - 그럼 캐시 동작은 어떻게 검증하는 것일까?

 

1. 검증 우선순위

 - Last-ModifiedEtag가 같이 있다면, Etag가 우선순위가 높다.

 - ExpiresCache-Control의 경우는 Cache-Control의 설정된 정책이 우선순위가 높다.

 

2. If-None-Match 헤더 (If-Match의 반대)

 - ETag 헤더 값을 활용

 - 서버의 데이터 Etag와 캐시 데이터의 Etag를 비교하여 데이터 수정 여부를 확인하기 위해 사용한다.

case1) 같은경우 : 304 Not Modified
case2) 다른경우 : 200 OK 

 

3. If-Modified-Since 헤더 (IF-Unmodified-Since의 반대)

 - Last-Modified 헤더 값을 활용

 - 서버의 데이터 최종 수정 시각과 캐시 데이터의 최종 수정 시각을 비교하여 데이터 수정 여부를 확인하기 위해 사용한다.
   (캐시에 최종 수정일 정보(Last-Modified)가 있다면, 클라이언트는 요청 메세지에 if-modified-since 헤더에 해당 날짜를 담아서 서버에 보낸다.)

case1) 같은경우 : 304 Not Modified (클라이언트는 응답 결과를 캐시에 저장할 때 데이터 최종 수정일도 저장, 캐시 유효 기간을 갱신)

case2) 다른경우 : 200 OK

 

ETC. 검증 우선순위 비교 예시

 Last-Modified와 Etag가 같이 있다면, Etag가 우선순위가 높다.

 

4. Expires
 - Expires 헤더 값을 활용
case1) 기간 내라면 서버를 거치지 않고 캐쉬에서 페이지를 로드 한다.
case2) 기간이 만료되었다면 validation작업을 수행 한다.

 

캐시 무효화 동작

 - 나와 같은 경우 index.html은 캐시를 제외 하려고 하였다.
 - 캐시를 적용하지 않도록 설정하는 방법은 정확히 어떤게 있을까?
 - 위에서 한 것과 같이 max-age=0으로만 설정하면 부족할까?

위에서 가볍게 각각의 헤더에 대해 살펴보긴했지만, 다시 자세히 확인 해보자.

1) 방법1 : Cache-Control: no-cache, Cache-Control: max-age=0 ( 0초 동안 캐시 )
 - 직관적으로 이름만 봤을땐 위의 no-cache가 캐시를 사용하지 않는다는것 같지만 아니라는점을 명심하자.
 - 캐시해도 되지만 항상 오리진 서버에 검증하고 사용해야 한다.(결국 max-age=0 과 동일한 의미가 된다.)
 - 서버로 부터 304 not modified 응답을 받으면 캐시를 사용 한다.

 

2) 방법2 : Cache-Control: no-store
 - no-cache와 다르게 실제 캐시를 사용하지 말라는 의도에 가장 적합 하다.
 - 특정 페이지, resource등의 자원은 저장하지 말라는 의미
 - 위의 no-cache, max-age 보다 엄격하게 캐시를 완전히 비활성화 한다.


3) 방법3 : Cache-Control: must-revalidate
 - 캐시만료 전에는 캐시를 사용하며, 캐시가 만료된 후 원서버에 검증하게 한다.

 

4) 방법4 : Pragma: no-cache
 - 해당 방법은 HTTP 1.0 하위 호활을 위해 사용한다.
 
※ 이를 환경에 따라선 무효화를 위해 권고하는 내용을 참고하면 이렇다.
1) HTTP/1.0 및 IE6 환경
Pragma: no-cache
Expires: 0

2) HTTP/1.1 환경
Cache-Control: no-store, must-revalidate

3) HTTP/2.0 에서의 타 Site에서 활용하고 있는 headers
 - Amazon.com.au

Cache-Control: no-cache
pragma: no-cache
expires: -1

 - Nab.com.au

Cache-Control: max-age=0, no-cache, no-store
pragma: no-cache
expires: Thu, 01 Jan 1970 00:00:00 GMT

 

 - Alibaba.com

Cache-Control: no-cache
Cache-Control: no-store
pragma: no-cache


 - HSBC online bank

Cache-Control:max-age=1,s-maxage=0,no-store,must-revalidate,private Cache-Control:post-check=0, pre-check=0
pragma: no-cache
expires: Sat, 06 May 1995 12:00:00 GMT

 

※ 무효화 관련 내용 참고

https://fred-gu.medium.com/fix-the-annoying-web-page-caching-issue-permanently-5d16527d0b5a

https://stackoverflow.com/questions/49547/how-do-we-control-web-page-caching-across-all-browsers

 

 

300x250