웹 캐시 전략

웹 캐시란?

캐시란 데이터에 빠르게 접근하기 위해 자주 사용되는 데이터나 값을 미리 복사해 놓는 임시 장소를 의미한다.

웹 캐시란 브라우저가 서버에서 받아온 리소스를 특정 위치에 저장해 놓는 것이다.

이후 요청을 하면, 해당 리소스가 있는지 확인하고 재사용하는 방법이다.

 

캐시는 Http 헤더의 Cache-Control로 설정할 수 있다.

 

⭐️ Cache-Control 옵션

  • no-store : 캐시에 리소스를 저장하지 않는다.
  • no-cache : 캐시 만료기간을 신경쓰지 않고 항상 서버에 리소스 재검사를 요청한다.
  • must-revalidate : 캐시 만료기간이 지났을 때만 서버에 리소스 재검사를 요청한다.
  • public : 해당 리소스를 캐시에 저장한다.
  • private : 해당 리소스를 캐시에 저장하지 않는다. 개인정보성 리소스나 보안이 필요한 리소스의 경우에 사용한다.
  • max-age : 캐시의 만료기간(초단위)을 설정한다.
재검사 : 캐시에 있는 데이터가 최신 데이터인지 확인하는 작업. 최신 데이터라면 304 Not Modified 응답.

웹 캐시 장점

  • 성능 향상
    이전에 받아왔던 데이터나 계산 결과를 빠르게 반환할 수 있다.
  • 네트워크 트래픽 감소 및 서버 부하 감소
    클라이언트가 요청한 동일한 데이터를 서버에서 반복적으로 불러오지 않는다.
  • 응답 속도 향상
    서버에서 응답받는 것보다 캐시에서 가져오는 것이 훨씬 빠르다.
  • 비용 절감
    네트워크 트래픽 및 서버 부하 감소로 인해 비용이 절감된다.
  • 사용자 경험 향상
    빠른 응답 속도로 사용자 경험을 향상 시킨다.

동작 방식

브라우저가 지금까지 요청한 적 없는 리소스를 가져오려한다면 완전한 HTTP 요청/응답을 주고받는다.

이후에는 HTTP 응답에 포함된 Cache-Control 헤더에 따라 리소스의 생명주기가 결정된다.

 

  • 캐시의 유효 기간 지나지 않음
    브라우저는 서버에 요청을 보내지 않고 캐시를 읽어와서 사용한다.
  • 캐시의 유효 기간이 지남
    조건부 요청을 통해 캐시가 유효한 지 재검사를 수행한다.
    • 재검사 결과 브라우저가 가지고 있는 캐시가 유효하다면(최신 데이터라면),
      [304 Not Modified] 응답을 받는다.
      304 응답은 HTTP 본문을 포함하지 않기 때문에 매우 빠르다.
    • 재검사 결과 캐시가 유효하지 않다면,
      [200 OK]와 함께 최신 데이터를 응답 받는다.

캐시 유효성 검증 및 조건부 요청

캐시의 유효 기간이 지났다면, 캐시가 유효한지를 재검사 해야한다.

실제 원본 데이터가 수정되었을때만 서버로 요청을 보내서 리소스를 새로 받아오는 것이다.

이런 과정을 캐시 유효성 검증 및 조건부 요청이라 부르고, 두 가지의 방법이 있다.

 

1. Last-Modified / If-Modified-Since

리소스의 마지막 갱신 시각으로 검증한다.

 

최초 요청 시, 응답 값의 Last-Modified 라는 헤더에 마지막으로 수정된 날짜가 포함되어 넘어온다.

이후 캐시의 유효 기간이 지나서 요청을 보낼 때, 

Last-Modified 값을 If-Modified-Since 라는 요청 헤더에 넣어서 서버로 요청을 보낸다.

 

리소스가 변경되지 않았다면 [304 Not Modified] 응답을 받을 수 있다.

 

2. ETag / If-None-Match

리소스의 식별자를 기준으로 검증한다.

위의 방식은 밀리 세컨드 단위로 더 섬세하게 시각을 설정할 수 없는 한계점이 존재한다.

ETag란 특정 버전의 리소스를 식별하기 위해 사용하는 식별자로 Last-Modified 방식의 단점을 극복할 수 있다.

 

최초 요청 시, 응답 값의 ETag 라는 헤더에 리소스의 현재 버전에 대한 식별자가 포함되어 넘어온다.

이후 캐시의 유효 기간이 지나서 요청을 보낼 때, 

If-None-Match 라는 헤더에 저장해둔 ETag 값을 넣어서 보낸다.

 

마찬가지로 리소스가 변경되지 않았다면 [304 Not Modified] 응답을 받을 수 있다.


캐시 무효화

파일을 수정하고 재배포 했지만 파일명이 동일해서

브라우저에서 변경 여부를 알아채지 못해서 캐싱된 파일을 가져오는 문제가 발생할 수 있다.

 

이런 문제를 해결하기 위해, 리소스 요청 시 버전 식별자를 사용해서 새로운 리소스가 있음을 브라우저에게 알려주는 방법을

캐시 무효화(Cache Busting)이라고 한다.

 

1. 파일 이름에 식별자 추가

//	버전 사용
<script type="text/javascript" src="./fileName.v1.js"></script>

//	timestamp 사용 
<script type="text/javascript" src="./fileName.20241009011823.js"></script>

 

2. 파일 경로에 식별자 추가

//	버전 사용
<script type="text/javascript" src="v1/fileName.js"></script>

//	timestamp 사용
<script type="text/javascript" src="20241009011823/fileName.js"></script>

 

3. 쿼리 스트링을 사용하여 식별자 추가

//	버전 사용
<script type="text/javascript" src="./fileName.js?v=1"></script>

//	timestamp 사용
<script type="text/javascript" src="./fileName.js?timestamp=20241009011823"></script>

 

위의 예시들처럼 버전을 붙이거나 시간을 붙임으로써 파일을 수정했음을 명시한다.


stale-while-revalidate

stale이란 '신선하지 않은, 오래된'이라는 의미를 가진다.

stale-while-revalidate를 직역하자면, ?재검증하는동안 오래된?이라는 의미이다.

즉, 데이터를 재검증하는 동안 오래된 데이터를 사용한다고 이해할 수 있다.

 

stale-while-revalidate는 캐싱된 데이터를 즉시 로드하는 즉시성

갱신된 최신 데이터가 향후에 사용되도록 보장하는 최신성 사이의 균형을 유지하는데 도움을 준다.

 

stale-while-revalidate는 max-age와 함께 Cache-Control 응답 헤더에 사용한다.

max-age와 stale-while-revalidate에 설정된 시간안의 반복된 요청은 캐싱된 데이터를 사용한다.

하지만 max-age는 지났지만, stale-while-revalidate에 설정한 시간은 지나지 않았다면

백그라운드에서 재검증 과정을 수행한 후, 최신 응답으로 갱신하는 방식이다. 

 

예시를 보자.

Cache-Control : max-age=1, stale-while-revalidate=59

1. 0-1초에 요청이 반복

캐싱된 응답 = 최신 응답, 재검증 없이 캐싱된 응답을 사용한다.

 

2. 1-60초에 요청이 반복

캐싱된 응답 = 오래된 응답, 하지만 캐싱된 응답을 사용한다.

동시에 백그라운드에서 재검증 요청을 수행해서 최신 응답으로 갱신한다.

따라서 다음 응답으로는 최신 응답을 사용할 수 있다.

 

3. 60초 이상 지난 후 요청이 반복

캐싱된 응답을 사용하지 않는다.

서버에 요청을 보내 새로운 응답을 받아와서 사용하고 캐싱한다.


휴리스틱 캐싱

문득 프로젝트를 하면서 생겼던 문제가 생각이 났다.

사이트를 재배포했을때 바뀐 데이터가 아닌 이전 데이터로 나왔었다.

아마 휴리스틱 캐싱 문제였지 않을까라는 추측이 된다.

 

🤔 휴리스틱 캐싱이란?

만약 Cache-Control 또는 Expires 헤더를 모두 기입하지 않으면 

브라우저에서 어림잡아서 캐시하도록 동작하는 것이다.

 

=> 해결방법은 헤더에 캐시를 사용하지 않는다고 명시를 하는 것이다!

 

'WEB' 카테고리의 다른 글

OAuth 2.0, OIDC(OpenID Connect)  (0) 2024.10.20
Web View  (2) 2024.10.01
SWC(Speedy Web Compiler)  (1) 2024.09.30
Web Worker  (0) 2024.09.21
타입스크립트를 사용하는 이유  (0) 2024.09.06