포스트

[Network] HTTP (4-3) - Proxy 캐시와 HTTP 캐시 무효화

Computer Science / Network

Proxy 캐시와 HTTP 캐시 무효화에 대한 개념 정리

HTTP와 웹 브라우저의 캐시 검증과 조건부 요청에 대한 자세한 내용은 #HTTP (4-2) - HTTP 캐시 검증 & 조건부 요청 게시글을 참고

프록시(Proxy) 캐시

프록시는 클라이언트와 본 서버를 중계하는 중간에 위치한 서버 대리자로서, 클라이언트의 요청을 대신 받고 본 서버에 해당 요청을 대신 전달해주는 역할을 한다.

이 프록시를 캐시 서버로서 이용할 수 있는데, 예를 들어서 한국에 있는 클라이언트에서 이미지가 필요한 상황이고, 해당 이미지의 본 서버가 미국에 있다고 가정해보자.

한국 서버에서 미국 서버까지 직접 접근하여 이미지를 가져오는데는 약 0.5초가 걸린다고 하면, 한국에 위치한 컴퓨터들은 모두 0.5초 가량을 기다려야 해당 이미지를 받을 수 있게 된다.

image_31

위와 같은 현상을 해결하기 위해 프록시 캐시를 도입하여 사용한다고 보면 된다.

한국에 프록시 캐시 서버를 따로 두고, 최초의 요청에만 미국 서버까지 가서 이미지 리소스를 받아오고 프록시 캐시 서버에 저장한다.

그러면 한국의 클라이언트는 리소스를 가져올 때 프록시 캐시 서버로부터 가져오면 된다.

image_32

특히 여러 사람들이 찾는 자료일수록 이미 캐시에 등록되어있을 가능성이 크기에 효과적으로 빠른 속도로 자료를 가져올 수 있다.

유튜브에서 고용량의 영상을 빠르게 볼 수 있는 이유도 이에 해당한다.

즉, 같은 국내에 캐시된 데이터가 있는 덕분에 본 서버에 접근하는 것보다 훨씬 빠른 속도에 자료를 가져올 수 있다는 것이다.

image_33

참고로 클라이언트에서 사용되고 저장되는 캐시를 private 캐시라고 하며, 프록시 캐시 서버의 캐시를 public 캐시라고 한다.

웹 브라우저 캐시와 프록시 서버 캐시가 분리되어 운용되는 만큼, 위에서 설명한 Cache-Control HTTP 헤더도 프록시 전용으로 캐시를 설정해야 한다.

프록시 캐시 헤더

image_34 CDN에서는 1년 동안 캐시되지만, 브라우저에서는 매번 재검증 요청을 보내도록 설정

  • Cache-Control: private
    응답이 해당 사용자만을 위한 것으로 private 캐시에 저장해야 한다. (default)
  • Cache-Control: public
    응답이 public 캐시에 저장되어야 한다.
  • Cache-Control: s-maxage
    프록시 캐시에만 적용되는 max-age
  • Age: 60
    HTTP 헤더로써, 오리진 서버에서 응답 프록시 캐시 내에 머문 시간이다.(초 단위)

HTTP 캐시 무효화

캐시 무효화(Cache Busting)는 말 그대로 웹 브라우저의 캐시를 완전히 제거해 버리는 것을 말한다.

이러한 기술이 필요한 이유는 웹 브라우저들이 GET 요청을 받을 경우에 별도의 캐시 헤더 없이도 자기 마음대로 최적화를 한다고 임의로 캐싱을 해 버리기 때문이다.

또한 리소스의 캐시 유효 기간을 길게 설정해버리는 바람에 리소스의 업데이트가 필요할 경우, 캐시 저장소의 복사본을 갱신해줘야 하는데, 기본적으로 브라우저는 캐시 유효 기간이 끝나야 캐시 유효성 검증을 서버에게 요청하기 때문에 어떠한 방법이 없다.

이런 문제를 해결하기 위해 캐시 무효화 전략을 사용한다고 보면 된다.

---
title: Cache-Control의 흐름도
---
flowchart TD
  A(응답을 캐싱 할 것인가?) --<ins class="red" style="padding: 5px">NO</ins>--> B(no-store<br>캐싱 안함)
  A --<ins class="blue" style="padding: 5px">YES</ins>--> C(캐시 사용 전에 <ins>캐시 유효성 검증</ins>을 할 것인가?)
  C --<ins class="red" style="padding: 5px">NO</ins>--> D(프록시 캐시를 사용하는가?)
  C --<ins class="blue" style="padding: 5px">YES</ins>--> E(no-cache<br><ins>매번 서버에서 캐시 유효성 검증</ins>을 하고 캐시 사용)
  E --> D
  D --<ins class="blue" style="padding: 5px">YES</ins>--> F(public<br><ins>프록시 캐시에 저장</ins>)
  D --<ins class="red" style="padding: 5px">NO</ins>--> G(private<br><ins>클라이언트 캐시에 저장</ins>)
  G --> H(캐시 유효 기간을 설정하는가?)
  F --> H
  H --> I(max-age= ∙∙∙)
  I --> J(Add ETag Header)

캐시 무효화 헤더

만약 캐시를 사용해서는 안되는 페이지가 존재한다면, 다음과 같이 Cache-Control 헤더에 파라미터들을 설정해줘야 한다.

1
2
3
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
  • Cache-Control: no-cache
    데이터는 캐시해도 되지만, 항상 본 서버에 검증하고 사용해야 한다는 의미로, max-age=0과 동일한 뜻이다.
    즉, 서버로부터 304 응답을 받아야 캐시에서 가져온다는 의미로, 비록 네트워크 트래픽이 발생하지만 헤더 메시지만 응답받기 때문에 네트워크 다운로드량은 적다.
    no-cache라는 명칭 때문에 캐시를 사용하지 않는다고 생각할 수 있지만, 이 단어의 의미는 본래 캐시 유효 기간이 남아있으면 무조건 캐시 저장소를 조회하지만, 그러지 말고 무조건 서버에 검증을 받으라는 의미이다.
  • Cache-Control: no-store
    데이터에 민감한 정보가 있기에 저장하면 안된다는 의미이다.
    메모리에서 사용하고 최대한 빠르게 삭제한다.
  • Cache-Control: must-revalidate
    캐시 만료 후 최초 조회 시, 본 서버에 검증해야 할 때 설정한다.
    본 서버에 접근 실패 시, 반드시 504(Gateway Timeout) 오류가 발생하도록 해야 한다.
    만일 캐시 유효 시간 내에 있다면, 캐시를 사용한다.
  • Pragma: no-cache
    HTTP/1.0 하위 호환용으로 사용한다.

간혹, 몇몇 웹 페이지에서는 캐시 무효화 우회 로직으로 Cache-Control 헤더에 max-age=0으로 설정해 놓는다.

캐시 유효 시간을 0으로 설정해 놓으면, 매번 리소스를 요청할 때마다 서버에 재검증 요청을 보내게 된다.

하지만, 몇몇 일부 모바일 브라우저의 경우 네트워크 요청을 아끼고 사용자에게 빠른 웹 경험을 제공하기 위해 웹 브라우저를 껐다 켜기 전까지 리소스가 만료되지 않도록 하는 경우도 존재한다.

따라서 max-age=0을 사용하기 보다는 좀 더 명확한 no-store 파라미터를 사용하기를 권장한다.

no-cache VS must-revalidate

캐시 무효화 헤더를 설정할 때, 보통 no-cachemust-revalidate를 같이 설정하는 편이다.

must-revalidate를 사용하는 이유는 no-cache에 의해 본 서버(Origin Server)에 검증 요청을 보내는 도중에 본 서버와 프록시 캐시 서버(Proxy Cache Server)의 연결이 끊어져 검증이 불가능할 경우에 504 Gateway Timeout 오류를 발생시키기 위함이다.

몇몇 프록시 캐시 서버에서는 본 서버에 접근이 불가능할 경우에 검증을 거치지 않고 이전의 캐시 데이터를 반환하기 때문이다.

예를 들어, 통장 잔고와 같은 중요 데이터의 경우, Origin Server와의 연결이 불가능하다고 해서 변경 전의 데이터를 반환하면 절대로 안된다.

이와 같은 이유 때문에 must-revalidate를 활용해서 일부러 5XX 오류를 발생시키는 전략을 세운다고 보면 된다.

no-cache의 동작 (기본)

image_36

  1. 클라이언트는 ETagno-cache 설정을 하고 서버에게 요청한다.
  2. 중간에 프록시 캐시 서버가 받게 되고, no-cache 설정이 되어있기 때문에, 다시 본 서버에 요청한다.
  3. 본 서버에서 ETag로 검증한다.
  4. 프록시에게 304 응답을 준다.
  5. 프록시는 다시 클라이언트에게 그대로 응답한다.
  6. 클라이언트는 캐시를 재사용한다.

no-cache의 동작 (본 서버 문제 발생 시)

image_37

  1. 클라이언트는 ETagno-cache 설정을 하고 서버에게 요청한다.
  2. 중간에 프록시 캐시 서버에서 다시 본 서버에 요청하는데, 어떠한 이유로 네트워크가 단절되어 접근이 안된다.
  3. 그러면 no-cache에서는 응답으로 오류가 아닌, 오래된 데이터라도 보여준다는 개념으로 200 OK 상태 코드로 응답을 한다.
  4. 본 서버의 처리 상태는 모른 채로 클라이언트는 오래된 데이터를 소비자에게 제공하게 되고, 결국에는 서비스에 차질이 생기게 된다.

must-revalidate의 동작

위와 같이 no-cache의 문제점 때문에 must-revalidate를 명시하여 Proxy Cache Server가 자체적으로 캐시 데이터를 반환하는 케이스를 사전에 차단하여야 한다.

image_38

  1. 클라이언트는 ETagmust-revalidate 설정을 하고 서버에게 요청한다.
  2. 중간에 프록시 캐시 서버에서 다시 본 서버에 요청하는데, 어떠한 이유로 인해 네트워크가 단절되어 접근이 안된다.
  3. 문제가 생겼으므로 일단 must-revalidate에서는 무조건 504 Gateway Timeout 오류를 응답한다.
  4. 클라이언트는 본 서버에 문제가 있음을 감지하고 별도의 재수정 로직을 거치게 된다.

다만, must-revalidate는 캐시 유효성 기간이 남아있으면 우선적으로 캐시 저장소를 조회하게 되어있다.

따라서 본 서버 네트워크 문제 해결과 항상 본 서버에 캐시 검증을 함께 사용하고 싶은 경우, no-cache와 함께 설정해주면 된다.

image_39

참고 사이트

Inpa Dev - 🌐 웹 브라우저의 Cache 전략 & 헤더 다루기

Semantics - HTTP (7) - 캐시와 조건부 요청 (Last-Modified / ETag)

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.