본문 바로가기

Infrastructure/Network

[HTTP] 헤더 - 6 (조건부 요청)

효율적인 캐싱을 위한 조건부 요청에 대해서 알아본다.

Last-Modified (데이터가 마지막에 수정된 시간)을 이용

요청

GET /car.jpg

응답

HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 40000
cache-control: max-age=60
Last-Modified: 2021-06-25 10:00:00

eyJzdWIiOiJzYWxseUBpbWFzbGFiLmN...

첫 번째 요청에서 클라이언트는 응답 결과, 캐시 유효 시간, 마지막 수정 일시를 저장한다.

61초 후에 재요청

GET /car.jpg
if-modified-since: 2021-06-25 10:00:00

이때, 로컬에 저장되어 있는 클라이언트의 캐시 유효 시간은 60초이므로 서버에 마지막 수정 일시를 전달한다.
서버는 클라이언트에서 전달한 마지막 수정일시와 요청 리소스에 대한 마지막 수정일시를 비교한다.
이때 수정된 경우와 수정되지 않은 경우 두 경우가 있다.

응답 (리소스의 마지막 수정 일시가 클라이언트에서 전달한 마지막 수정일시를 초과하지 않는 경우)

HTTP/1.1 304 Not Modified
Content-Type: image/jpeg
Content-Length: 40000
cache-control: max-age=60
Last-Modified: 2021-06-25 10:00:00

응답 (리소스의 마지막 수정 일시가 클라이언트에서 잔달한 마지막 수정일시를 초과한 경우)

HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 40000
cache-control: max-age=60
Last-Modified: 2021-06-25 11:00:00

eyJzdWIiOiJzYWxseUBpbWFzbGFiLmN...

정리를 해보면

  • 캐시 유효 시간이 초과하더라도 서버의 데이터가 수정되지 않으면 304 Not Modified 응답으로 바디에 들어가는 데이터의 양을 절약할 수 있다.
  • 클라이언트는 서버가 보낸 응답 헤더 정보로 캐시의 메타 정보를 갱신할 수 있다.

Last-Modified와 if-modified-since 를 통한 효율적인 캐싱에 대해서 알아보았다.
아래에서는 ETag와 If-None-Match 를 사용한 조금 더 효율적인 캐싱에 대해서 알아본다.

ETag (Entity Tag)

  • 캐시용 데이터에 임의의 고유한 버전 이름을 달아둔다.
    • 예) ETag: "v1.0", ETag: "rand1111"
  • 데이터가 변경되면 고유한 버전 이름을 바꾸어서 다시 변경한다.(Hash를 다시 생성)
    • 예 ETag: "rand1111" -> "rand2222"
  • 데이터가 변경되면 데이터를 기반으로 생성한 해시값이 변경된다. 이러한 해시의 특징을 이용한 방식이다.

ETag와 If-None-Match를 사용한 요청과 응답의 흐름을 살펴본다.

1) 요청

GET /car.jpg

2) 응답

HTTP/1.1 200 OK
Content-Type: image/jpeg
Content-Length: 40000
cache-control:max-age=60
ETag: "rand1111"

eyJzdWIiOiJzYWxseUBpbWFzbGFiLmN...

# 브라우저의 캐시 시간을 초과하지 않은 경우 서버로 요청을 보내지 않는다. 아래는 캐시 시간을 초과 하였다고 가정한다.

3) 요청

GET /car.jpg
If-None-Match: "rand1111"

4) 응답 (서버의 데이터가 변경되지 않았다고 가정)

HTTP/1.1 304 Not Modified
Content-Type: image/jpeg
Content-Length: 40000
cache-control: max-age=60
ETag: "rand1111"

4)의 응답 메시지의 바디를 살펴보면 데이터가 없는 것을 확인할 수 있다.

캐시 제어 헤더

  • Cache-Control: 캐시 제어

    • Cache-Control: max-age (캐시 유효 시간, 초 단위)
    • Cache-Control: no-cache (캐시가 가능하지만 항상 ORIGIN 서버에 검증하고 사용해야한다.)
    • Cache-Control: no-store (데이터에 민감한 정보가 있으므로 저장하면 안된다.)
    • Cache-Control: public (응답이 public 캐시에 저장 가능하다.)
    • Cache-Control: private (응답이 해당 사용자만을 위한 것이므로 private 캐시에 저장해야한다.)
    • Cache-Control: s-maxage (프록시 캐시에만 적용되는 max-age)
    • Age: 60 (ORIGIN 서버에서 응답 후 프록시 캐시 내에 머문 시간을 나타낸다.)
  • Pragma: 캐시 제어

  • Expires: 캐시 유효 기간

캐시 무효화

Cache-Control: no-cache, no-store, must-revalidate

  • Cache-Control: no-cache
    • 데이터는 캐시해도 되지만, 항상 ORIGIN 서버에 검증하고 사용해야한다.
  • Cache-Control: no-store
    • 데이터에 민감한 정보가 있으므로 저장하면 안된다.
  • Cache-Control: must-revalidate
    • 캐시 만료후 최초 조회시 ORIGIN 서버에 검증해야한다.
    • ORIGIN 서버 접근 실패시 반드시 504(Gateway Timeout) 오류가 발생해야한다.
    • 캐시 유효 시간이라면 캐시를 사용해야한다.

must-revalidate를 사용하지 않은 상태에서 프록시 캐시에 데이터가 캐싱되어 있다고 가정해보자.
만약 ORIGIN 서버에 문제가 생겨서 통신이 불가능한 상황이 되더라도 프록시 캐시는 클라이언트에게 200 OK 와 함께 캐싱된 데이터를 전달할 것이다.

절대로 캐싱되지 말아야하는 상황이라면 위의 캐시 지시어를 사용하여 캐싱을 방지하도록 하자.

'Infrastructure > Network' 카테고리의 다른 글

[HTTP] 헤더 - 3 (일반 정보)  (0) 2021.06.28
[HTTP] 헤더 - 2 (전송 방식)  (0) 2021.06.28
[HTTP] 헤더 - 5 (캐시)  (0) 2021.06.25
[HTTP] 헤더 - 1 (일반 헤더)  (0) 2021.06.25
[HTTP] 상태 코드 (3xx)  (0) 2021.06.25