Huny's Dev Blog
huny.dev

HTTP ETag와 조건부 GET으로 304 응답 구현하기

HTTP ETag와 If-None-Match로 변경 없는 본문 전송을 줄인다. Python 예제로 200과 304를 재현하고, no-cache와 압축 응답의 적용 범위를 살펴본다.

Hun Jang
Hun Jang Nov 8, 2026

HTTP GET에서 데이터가 그대로인데 매번 본문을 내려보낸다면, 서버 응답에 ETag를 붙이고 다음 요청의 If-None-Match를 검사한다. 저장된 응답과 현재 응답이 일치하면 서버는 본문 없이 304 Not Modified를 보낸다. 공개 GET 엔드포인트에 적용할 수 있는 이 흐름을 Python 로컬 서버에서 재현하고, 본문이 바뀌었을 때 200 OK로 돌아오는지도 확인한다.

서버와 캐시 사이에서 응답의 지문을 비교하는 모습을 표현한 일러스트

같은 URL에서 200 응답이 반복되는 이유

캐시에 본문이 있어도 서버가 현재 버전과 비교할 검증자가 없으면, 이 방식으로 본문 전송을 생략할 수 없다. ETag는 URL 자체가 아니라 서버가 그 요청에 대해 선택한 표현에 붙이는 식별자다. 클라이언트는 받은 태그를 저장했다가 같은 리소스를 다시 확인할 때 If-None-Match로 돌려보낸다. GET에서 현재 표현의 태그가 이 조건에 일치하면 304를 반환한다. 캐시는 기존 본문을 재사용하고, 응답에 포함된 관련 메타데이터를 갱신할 수 있다. RFC 9110 §13.1.2 · RFC 9110 §15.4.5

Cache-Control: no-cache를 사용하면 응답을 저장할 수 있고, 캐시는 재사용 전에 원본 서버에서 검증을 받아야 한다. 저장 자체를 금지하려면 no-store를 사용한다. 본문 전송을 줄여도 재검증 요청의 네트워크 왕복은 남는다. RFC 9111 §5.2.2.4–5

응답 바이트에서 ETag 만들기

예제는 응답에 보낼 바이트를 한 번 읽어 변수에 담고, 같은 바이트에서 SHA-256을 계산해 큰따옴표로 감싼 강한 ETag를 만든다. 이렇게 하면 해시를 계산한 뒤 데이터가 바뀌어 다른 버전의 본문을 보내는 문제를 피할 수 있다. 배포 버전처럼 실제 표현의 변경을 놓칠 수 있는 값을 그대로 태그에 쓰면, 본문이 갱신되어도 클라이언트가 오래된 사본을 재사용할 수 있다.

아래 코드를 http_etag.py로 저장하고 python3 http_etag.py를 실행한다. 표준 라이브러리만 사용하며, 서버는 루프백 주소와 운영체제가 할당한 포트에 바인딩한다.

변경 전후의 200과 304 확인하기

2026년 11월 8일 Linux에서 Python 3.12.14로 실행한 결과다. 네 요청의 상태 코드·ETag·본문과 각 응답의 Cache-Control 값을 코드에서 검사한다.

두 번째 요청은 첫 번째 응답의 태그를 보낸다. 본문이 그대로여서 빈 304가 돌아온다. 세 번째 요청도 첫 번째 태그를 보내지만, 서버의 본문이 바뀌었으므로 새 태그와 version 2 본문을 담은 200이 돌아온다. 마지막 요청은 새 태그를 사용해 다시 빈 304를 받는다.

압축 응답과 운영 환경에서의 적용 범위

예제는 서버가 발급한 강한 태그 하나를 클라이언트가 그대로 보낸다는 조건에서 문자열 동등 비교를 사용한다. 운영 서버의 If-None-Match 처리는 약한 비교와 여러 태그, 와일드카드 *도 지원해야 한다. 검증자를 만드는 로직과 조건부 요청을 파싱하는 로직은 분리하고, 사용하는 프레임워크의 조건부 요청 기능이 이 규칙을 처리하는지 확인한다. RFC 9110 §13.1.2

Accept-Encoding에 따라 gzip과 비압축 표현을 선택한다면 Vary: Accept-Encoding으로 캐시가 표현을 구분하도록 하고, 실제 표현 데이터가 다른 두 응답에는 서로 다른 강한 ETag를 사용한다. 프록시나 CDN이 압축을 적용한다면 클라이언트가 최종으로 받는 헤더를 확인한다. 이 재현은 Python 클라이언트와 로컬 서버 사이의 HTTP 응답을 검증한 것이며 브라우저 캐시와 CDN 동작은 검증 범위에서 제외했다. RFC 9110 §8.8.3.3 · RFC 9111 §4.1

English version · How to Implement HTTP 304 Responses with ETag and Conditional GETHow to Implement HTTP 304 Responses with ETag and Conditional GET

추천 글