[HTTP] HTTP Header(캐시 시간 초과, 검증 헤더, 조건부 요청)

Yujeong·2024년 1월 7일
0
post-thumbnail

모든 개발자를 위한 HTTP 웹 기본 지식 강의 수강 후, 정리한 글입니다.

목차

  1. 캐시 시간 초과
  2. 검증 헤더와 조건부 요청

1. 캐시 시간 초과

캐시 유효 시간이 너무 길면, 실제 DB에 있는 데이터와 차이가 발생할 수 있다는 문제가 있다. 그래서 캐시 유효 시간을 적당히 설정하는 게 중요한데, 캐시 유효 시간을 초과해서 서버에 다시 요청할 때에는 다음의 2가지 상황이 있다.

1) 서버에서 기존 데이터를 변경함
2) 서버에서 기존 데이터를 변경하지 않음

캐시 유효 시간 만료 후에도 서버에서 데이터를 변경하지 않았다면, 데이터를 전송하는 대신 저장해두었던 캐시를 재사용할 수 있을 것이다. 이때, 클라이언트의 데이터와 서버의 데이터가 같다는 사실을 확인할 수 있는 방법이 필요하다. 이것을 확인할 수 있는 방법으로 검증 헤더조건부 요청 헤더가 있다.

2. 검증 헤더와 조건부 요청

검증 헤더는 캐시 데이터와 서버 데이터가 같은지 검증한다.

  • Last-Modified, ETag

조건부 요청 헤더는 검증 헤더로 조건에 따라 나누어 검증한다.

  • If-Modified-Since: Last-Modified 사용
  • If-None-Match: ETag 사용
  • 조건을 만족하면, 200 OK
    조건 만족하지 않으면, 304 Not Modified

1) Last-Modified

  • If-Modified-Since, If-Unmodified

동작

Last-Modified 헤더는 데이터가 마지막에 수정된 시간을 의미하며, UTC 표기법을 사용해야 한다. 클라이언트는 서버가 보내준 데이터 최종 수정일을 캐시 유효 시간과 함께 캐시에 저장한다.

② 다시 star.jpg파일이 필요하여 캐시에 먼저 확인해보니 캐시 유효 시간이 초과되었다면, 조건부 요청 헤더 if-modified-since를 가지고 캐시가 가지고 있는 데이터 최종 수정일이 DB와 동일한지 확인하는 요청을 보낸다.

③ 요청을 받은 서버는 데이터 최종 수정일을 비교한다.

④ 데이터가 수정되지 않았다면, HTTP Body를 빼고 cache-controlLast-Modified 헤더를 포함하여 304 Not Modified 응답한다. Body를 생략하고 불필요한 데이터 전송을 하지 않기 때문에 용량이 적은 헤더 정보만 다운로드하여 네트워크 사용량을 줄일 수 있다.

chrome google에서 "cache 검색 → F12 → tia.png"를 보면, Status Code 항목에 캐시에서 불러온 것이라는 것을 알 수 있고, Cache-Control, Last-Modified 헤더를 사용하는 것을 볼 수 있다. 새로고침 후에 status code가 304라면, RequestHeader에 if-modified-since가 있을 것이다.

정리

If-Modified-Since: 데이터가 수정되었다면?

A. 데이터 미변경시

  • 캐시: 2024년 1월 1일 10:00:00 vs 서버: 2024년 1월 1일 10:00:00
  • 304 Not Modified, 헤더 데이터만 전송(Body 미포함)
  • 전송 용량: 0.1M(헤더 0.1M)

B. 데이터 변경시

  • 캐시: 2024년 1월 1일 10:00:00 vs 서버: 2024년 1월 1일 11:00:00
  • 200 OK, 모든 데이터 전송(Body 포함)
  • 전송 용량: 1.1M(헤더 0.1M + 바디 1.0M)

단점

  • 1초 미만(0.x초) 단위로 캐시 조정이 불가능
  • 날짜 기반의 로직 사용
  • 데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 데이터 결과가 똑같은 경우(복붙)
  • 서버에서 별도의 캐시 로직을 관리하고 싶은 경우
    ex) 스페이스나 주석처럼 크게 영향이 없는 변경에서 캐시를 유지하고 싶은 경우

2) Etag

  • If-Match, If-None-Match

Etag(Entity Tag)는 캐시용 데이터에 임의의 고유한 버전 이름을 달아둔 것이다.
ex) Etag: "v1.0", Etag: "qwer1234"

데이터가 변경되면, 이 이름을 바꾸어서 변경한다. (Hast를 다시 생성)
ex) Etag: "qwer1234" → Etag:"asdf5678"

즉, Etag를 보내서 같으면 데이터를 유지하고, 다르면 다시 받는다.

동작

① 클라이언트는 서버가 보내준 데이터 Etag를 캐시 유효 시간과 함께 캐시에 저장한다.

② 다시 star.jpg파일이 필요하여 캐시에 먼저 확인해보니 캐시 유효 시간이 초과되었다면, 조건부 요청 헤더 If-None-Match를 가지고 캐시가 가지고 있는 데이터 최종 수정일이 서버와 동일한지 확인하는 요청을 보낸다. 요청을 받은 서버는 Etag를 비교한다.

③ 데이터가 수정되지 않았다면, HTTP Body를 빼고 Cache-ControlEtag 헤더를 포함하여 304 Not Modified 응답한다. Body를 생략하고 불필요한 데이터 전송을 하지 않기 때문에 용량이 적은 헤더 정보만 다운로드하여 네트워크 사용량을 줄일 수 있다.

Etag 어떻게 생성? 어디에 저장?

Etag 헤더가 어떻게 사용되는 지는 알겠는데, 어떻게 생성하고 서버는 Etag를 어디에 저장할까?
Etag는 해시함수를 사용하여 생성하고, 서버 메모리나 데이터베이스에 저장한다.

일반적으로 MD5 해시함수를 사용하여 생성된 값 message digest를 사용한다고 한다.

A. 문자열

import hashlib

def generate_etag_from_string(data):
	hash_object = hashlib.md5(data.encode())
    etag = hash_object.hexdigest()
    return etag

B.파일

import hashlib

def generate_etag_from_file(file_path):
	with open(file_path, 'rb') as f:
    file_content = f.read()
    hash_object = hashlib.md5(file_content)
    etag = hash_object.hexdigest()
    return etag

fastapi: fastapi-etag
spring boot: Shallow ETag

정리

  • 단순하게 Etag만 서버에 보내서 같으면 데이터 유지, 다르면 다시 받기
  • 캐시 제어 로직을 서버에서 완전히 관리
  • 클라이언트는 단순히 이 값을 서버에 제공(클라이언트는 캐시 메커니즘을 모름)
    ex) 서버는 베타 오픈 기간인 3일 동안 파일이 변경되어도 Etag를 동일하게 유지하고,
    애플리케이션 배포 주기에 맞춰 Etag 모두 갱신한다.
profile
공부 기록

0개의 댓글