[WEB] 웹 캐시로 무엇을 할 수 있을까?


CS가 들어오면 가장 자주 하는 답변, ‘시크릿 모드로 접속해보실래요?’, ‘캐시 비우기 및 강새(강력 새로고침) 해보세요.’ 이다. 왜 이게 의미가 있는지 또 어떻게 동작하는지 알아보자.

가장 최초의 HTTP요청일 경우 우리가 생각하는 일반적인 서버와의 통신을 통해 리소스를 받게 되지만, 이후부터는 리소스를 캐싱할 수 있다. HTTP요청의 Cache-Control 헤더를 통해 리소스의 유효기간 등을 설정하게 되는 것이다.

Cache-Control 헤더

max-age

말 그대로 리소스의 캐싱 유효시간을 설정할 수 있다. 재요청이 max-age 이내에 들어오게 되면 서버를 찌르지 않고 메모리에서 캐시를 읽어온다.

  • 이 때 한 번 브라우저에 캐시가 저장되면 만료되기 전까지 계속해서 남아있게 된다.

Expires 헤더로 만료 기간을 지정할 수도 있다. 다만 두 헤더가 모두 있을 경우 캐시 서버는 Expires 헤더를 무시한다.

브라우저에 캐시가 저장되는 곳은 어딘데? feat. chrome

브라우저가 캐시를 저장하는 곳은 사용자 기기 내의 숨겨진 캐시 폴더이다. 실제로 보기 위해서는 chromeCacheView를 설치해야 하는 것으로 알고있다. (또는 크롬 익스텐션 웹 캐시 뷰어를 설치하고 특정 페이지에서 우클릭을 통해 확인할 수도 있다.) 이를 통해 캐싱된 이미지, css, js 등의 정적 리소스들을 읽을 수 있다.

~/Library/Caches

cache_directory

web-cache-viewer

webCacheView를 통해 확인해 본 내 깃허브 프로필 페이지의 캐시 - 어제 캐싱된 내역을 사용하고 있다 (오늘 6/27일임!)

그러면 유효 기간이 만료되면 캐시가 삭제되나?

그렇게 쉽게 사라지는 놈이 아니다. 유효 기간이 만료되었다면 검증을 통해 캐시를 재사용할 수 있는지 확인한다.

304 Not Modified

재검증 결과 캐시가 유효하다면 응답으로 304 Not Modified 를 받게되고(브라우저가 가지고 있던 값을 그대로 사용할 수 있기 때문에 HTTP Body없이 Header만 전송하게 된다.), 브라우저는 가지고 있던 캐시를 읽어서 사용한다.

조건부 요청

위에서 말한 검증을 할 수 있는 방법이다. 캐시가 유효할 수 있는 경우는 무엇일까?

유효 기간이 지났지만 여전히 응답 데이터가 변하지 않은 경우일 것이다.

  • If-Modified-Since
    • Last-Modified 검증 헤더를 설정했을 경우, 캐시된 데이터의 Last-Modified 와 현재 요청의 Last-Modified 시간을 비교하여 수정되었는지 확인
    • 조건을 만족하면 (== 수정되었다면) 200을, 아니면 304를 내려줌

위 방법을 사용했을 때 불편한 점은 없을까?

  • 날짜 기반 로직을 사용하기 때문에, 동일한 값으로 데이터를 수정했더라도 수정 날짜가 바뀌어버림
  • (css, js와 같은 코드들의 경우) 공백이나 주석과 같이 의미없는 변경점이 있을 경우에도 수정 날짜가 바뀌어 버림

이러한 불편함을 개선한 것이 당연히 있다.

  • If-None-Match
    • 캐시 데이터에 고유한 버전 이름인 ETag 를 달아둠
    • 데이터가 변경될 때 ETag 를 업데이트
    • ETag 가 다르면 200을, 아니면 304를 내려주어 의미있는 데이터 변경이 있을 때만 새로운 데이터를 받을 수 있음

캐싱 안하고 싶어! max-age=0하면 되나?

일반적인 경우에는 max-age=0이므로 매 요청 시 서버로 재검증 요청을 보내게 되지만, 일부 모바일 브라우저에서는 웹 브라우저를 종료하기 전까지는 캐시된 리소스를 계속 사용하는 경우가 있다고 한다. (성능 최적화를 위해서)

그럼 방법이 없는가? 아니다. no-cacheno-store를 통해 해결할 수 있다. (요청과 응답에 모두 사용할 수 있는 경우만 정리했다. 응답 헤더에만 사용할 수 있는 must-revalidate (캐싱된 리소스여도 무조건 검증을 거쳐야 함) 같은 것도 있다.)

  • no-cache
    • max-age=0과 같은 의미를 지닌다. 캐시에 저장은 하지만 매 요청 시 서버의 재검증이 필요하다.
  • no-store
    • 말 그대로 캐시를 저장조차 하면 안되는 경우에 사용한다.

CDN과 같은 중간(프록시) 서버를 사용하고 있을 때

이러한 중간 서버들을 사용하는 경우, 캐싱는 여러 곳에서 발생할 수 있다.

서버CDN(서버 응답 캐싱) → 브라우저(CDN 응답 캐싱)

CDN Invalidation

캐시를 무효화 하기 위해 CDN Invalidation을 수행하는 것은 위에서 볼 수 있듯이 CDN이 캐싱하고 있는 서버 응답 캐싱을 무효화 하는 것이기 때문에 브라우저 캐시까지 삭제되는 것은 아니다.

중간 서버의 캐싱 여부를 지정할 수 있는 항목들

  • public
    • 말 그대로 제한을 두지 않고 중간 서버에서도 캐시를 저장할 수 있음
  • private
    • 리소스를 요청한 최초 사용자(끝에 있는 사용자 브라우저)만 캐시를 저장할 수 있음
  • s-maxage
    • 중간 서버에서 사용할 수 있는 max-age
  • proxy-revalidate
    • 앞서 잠깐 설명했던 must-revalidate 와 같이 응답에서만 사용할 수 있음
    • 중간에 있는 프록시 서버들(사용자 브라우저는 해당 x)에 대해서만 캐싱된 리소스가 있어도 서버 검증이 필요함

실무에서의 응용

참고했던 토스 기술 블로그의 내용을 참고해 실무에서 쓰일 수 있는 구조를 정리해보자.

배포 시에만 변경되고 CDN에 저장하여 사용할 수 있는 index.html과 같은 파일

  • max-age=0
    • 파일을 가져올 때마다 서버에 검증 요청을 보내게 되고, 변경점이 있다면(== 배포) 새로운 파일을 내려받는다.
  • s-maxage=31536000
    • CDN은 해당 파일에 대한 캐시를 계속 가지고 있도록 하되, 배포가 있을 경우에 CDN Invalidation을 통해 CDN의 캐시를 무효화 하고 새로운 파일을 저장하고 있도록 한다.

빌드 시마다 새롭게 생성되는 js, css 파일

  • 해시와 같은 값을 url 앞에 붙여 빌드 결과물마다 고유 url을 가질 수 있도록 설정
    • 같은 url에 대해 내용이 변경될 일은 없다.
    • ETag를 사용할 수도 있을 것 같다?
  • max-age=31536000
    • 따라서 같은 url에 대해서는 내용이 변경될 여지가 없으므로 계속 유지한다.

ref.




# 카테고리