장쫄깃 기술블로그

[Network] HTTP 캐시 (캐시란, Cache-Control, 유효성 검증 및 조건부 사용) 본문

ETC/Network

[Network] HTTP 캐시 (캐시란, Cache-Control, 유효성 검증 및 조건부 사용)

장쫄깃 2022. 9. 12. 16:23
728x90

캐시(Cache) 란?


컴퓨터 공학 전반에서 이야기되는 캐시는 자주 사용되는 데이터를 임시로 복사해두는 임의의 장소를 의미한다. 그리고 데이터를 캐시에 저장하는 행위를 캐싱이라고 한다. 일반적으로 캐싱은 캐시에 저장된 데이터에 접근하는 시간에 비해 원본 데이터에 접근하는 시간이 오래 걸리는 경우 사용한다.

 

 

HTTP 캐시


앞서 설명했듯이 캐시는 자주 사용하는 데이터에 더 빠르게 접근하기 위해 사용한다. 데이터 접근을 위해 네트워크를 사용해야 하는 웹 환경에서도 캐시는 유용하게 사용된다. HTTP 캐싱을 활용하면 웹 사이트의 로딩 시간을 개선할 수 있다.

 

특히 자주 변하지 않는 정적 파일(js, css, 이미지 등)들을 캐시를 사용하지 않으면, 요청마다 새롭게 다운로드 해야 한다. 이는 불필요한 네트워크 비용을 발생시키고, 서버에 추가적인 부담을 준다. 때문에 웹 페이지의 느린 로딩 속도로 직결되고, 좋지 않은 사용자 경험을 주게 된다.

 

 

HTTP 캐시 종류


HTTP 캐시 종류는 MDN 문서를 기준으로 구분하였다.

 

Private Cache

웹 브라우저에 저장되는 캐시로, 다른 사람이 접근할 수 없다. 단 서버 응답에 Authorization 헤더가 포함되어 있다면 Private Cache에 저장되지 않는다.

 

Shared Cache

웹 브라우저와 서버 사이에서 동작하는 캐시이다. Proxy Cache와 Managed Cache로 나뉜다.

  • Proxy Cache
    • (포워드) 프록시에서 동작하는 캐시
  • Managed Cache
    • AWS Cloudfront 와 같은 CDN 서비스나 리버스 프록시에서 동작하는 캐시
    • 관리자 패널에서 직접 캐시에 대한 설정을 관리하거나 리버스 프록시 설정에서 관리

 

포워드 프록시와 리버스 프록시에 대한 자세한 내용은 해당 글을 참고하면 된다.

링크 : https://jangjjolkit.tistory.com/47

 

[Network] 포워드 프록시와 리버스 프록시

프록시(Proxy) 란? 프록시는 클라이언트와 서버 사이에 위치한 중계 서버로, 통신을 대신 수행하는 대리자 역할을 한다. 프록시가 없다면 클라이언트는 서버와 직접 통신한다. 반면, 클라이언트와

jangjjolkit.tistory.com

 

 

캐시 유효기간


Cache-Control은 HTTP에서 캐시 메커니즘을 지정하기 위해 사용되는 헤더이다. max-age라는 디렉티브를 사용하면 캐시의 최대 수명을 설정할 수 있다. max-age는 초 단위이다.

 

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: max-age=3600
Content-Length: 157

 

웹 브라우저가 특정 리소스에 최초 요청을 했을 때, 서버는 Cache-Control 헤더가 포함된 응답을 보낸다. 위 응답을 받은 브라우저는 응답 결과를 3600초(1시간)동안 캐시에 저장한다.

 

이후 웹 브라우저가 같은 리소스에 요청을 보내면, 실제 웹 서버에 요청을 보내는 것이 아니라 캐시에 저장된 사본 데이터를 사용자에게 제공한다.

 

max-age 디렉티브에 명시한 캐시 유효 시간 이후에 동일한 리소스를 요청하면, 다시 실제 웹 서버에 리소스를 요청한다. 이 때, 캐시된 데이터가 지정된 유효기간을 지나지 않아 아직 유효한 경우를 신선하다(fresh)라고 하며, 유효기간이 초과된 경우를 신선하지 않다(stale)라고 한다.

 

 

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


캐시의 유효기간이 지난 다음 다시 서버에 리소스를 요청하면 둘 중 하나일 것이다. 이전과 다르지 않은 동일한 데이터를 받고나, 새롭게 갱신된 데이터를 받거나이다. 이 때, 전자의 경우에는 의미없는 트래픽을 낭비한 것이 된다.

 

이런 트래픽 낭비를 줄이기 위해 실제 원본 데이터가 수정되었을 때만 리소스를 내려받는 것이 바람직하다. 이런 과정을 캐시 유효성 검증(validation)조건부 요청(conditional request)이라고 한다.

 

이 방식은 크게 2가지로 나뉜다.

  1. Last-Modified 와 If-Modified-Since : 리소스의 마지막 갱신 시각으로 검증
  2. ETag 와 If-None-Match : 리소스의 식별자를 기준으로 검증

 

Last-Modified / If-Modified-Since

최초 요청 시 응답

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: max-age=3600
Last-Modified: Sat, 03 Sep 2022 00:00:00 GMT
Content-Length: 157

<!DOCTYPE HTML>
<html>
...

이전에 설명했던 응답과 동일하다. 단, 여기에 Last-Modified라는 헤더가 추가되었다. 이 헤더는 요청한 리소스가 마지막으로 수정된 일자를 나타낸다. 브라우저는 이 Last-Modified를 저장해둔다.

 

두번째 요청

GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-Modified-Since: Sat, 03 Sep 2022 00:00:00 GMT

캐시 기간이 초과되어 두번째 요청 시 브라우저는 저장해둔 Last-Modified 값을 If-Modified-Since 헤더에 넣어 서버로 요청을 보낸다.

 

리소스가 변경되지 않은 경우

HTTP/1.1 304 Not Modified
Content-Type: text/html
Cache-Control: max-age=3600
Last-Modified: Sat, 03 Sep 2022 00:00:00 GMT

만약 위와 같이 조건부 요청을 보냈을 때, 원본 리소스에 변경이 없었다면, 서버는 위와 같이 304 Not Modified라는 상태 코드로 응답한다.

304 Not Modified
이것은 캐시를 목적으로 사용된다. 이것은 클라이언트에게 응답이 수정되지 않았음을 알려주며, 그러므로 클라이언트는 계속해서 응답의 캐시된 버전을 사용할 수 있다.

이 응답에는 Response Body가 없기 때문에 트래픽을 아낄 수 있다.

 

ETag / If-None-Match

앞서 소개한 방법은 밀리 세컨드 단위로 시간을 설정할 수 없다는 단점이 존재한다. ETag란 특정 버전의 리소스를 식별하기 위해 사용되는 식별자이다. 이를 통해 Last-Modified / If-Modified-Since의 단점을 극복할 수 있다.

 

최초 요청 시 응답

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: max-age=3600
ETag: "aaaa"
Content-Length: 157

<!DOCTYPE HTML>
<html>
...

리소스에 대한 최초 요청 시 ETag라는 응답 헤더가 돌아온다. 요청한 리소스의 현재 식별자 정보이다. 브라우저는 이 ETag 값을 저장한다.

 

두번째 요청

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

이후 캐시 기간이 초과되어 리소스에 대해 재요청 시 If-None-Match라는 헤더에 저장해둔 ETag 값을 넣어서 보낸다.

 

리소스가 변경되지 않은 경우

HTTP/1.1 304 Not Modified
Content-Type: text/html
Cache-Control: max-age=3600
Last-Modified: "aaaa"

마찬가지로 리소스가 변경되지 않은 경우 서버는 Response Body를 제외하고, 304 Not Modified로 응답한다.

 

 

no-cache, no-store


유저에게 항상 최신 버전의 리소스만을 제공하고 싶을 경우가 있을 것이다. 이 때, 항상 최신 버전의 리소스를 캐시하거나, 혹은 아예 캐싱을 하지 않는 방법이 있다.

 

이런 옵션을 위해서 웹 브라우저가 요청을 보낼 때 Cache-Control헤더에 no-cache 혹은 no-store 디렉티브를 포함할 수 있다.

 

Cache-Control은 요청과 응답 양쪽에서 사용되는 헤더이다.

 

no-cache

리소스에 대한 캐시를 생성하지만, 리소스를 요청할 때 서버에 항상 캐시 유효성 검증을 하는 옵션이다. 캐시를 아예 생성하지 않는 옵션이 아니다.

 

no-store

리소스에 대한 캐시를 생성하지 말라는 가장 강력한 Cache-Control 디렉티브이다. 저장하면 안되는 민감한 정보일 때 사용한다.

 

 

private vs public


Cache-Control 헤더의 private 또는 public 디렉티브를 사용하여 캐시 허용 범위를 지정할 수 있다.

 

public 디렉티브를 사용하면 Shared Cache 에서도 캐싱을 허용하고, private 디렉티브를 사용하면 사용자 브라우저에게만 캐싱을 허용한다.

 

Cache-Control: max-age=3600, private;

 

 


참고

https://hudi.blog/http-cache/

728x90