업데이트됨 1개월 전
브라우저가 서버에 리소스를 요청할 때, 이미 그 리소스를 캐시에 가지고 있는 경우가 많습니다. 이때 핵심 질문은 "이 파일을 주세요"가 아니라 "마지막으로 받은 이후로 이 파일이 변경되었나요?"입니다.
304 Not Modified 상태 코드는 서버의 답변입니다: 변경된 것 없습니다. 갖고 있던 것 그대로 쓰세요.
이 단 하나의 응답은 — 본문 없이 헤더만 포함된 — 전체 다운로드를 절약합니다. 2MB짜리 이미지가 200바이트짜리 확인 응답으로 바뀌는 것입니다. 이것이 모든 페이지 로드의 모든 리소스에 걸쳐 누적되면, 304가 HTTP에서 가장 중요한 성능 최적화 중 하나인 이유를 알 수 있습니다.
조건부 요청 패턴
304는 대화의 일부로만 의미가 있습니다. 작동 방식은 다음과 같습니다.
첫 번째 방문: 리소스 가져오기
브라우저는 CSS를 캐시에 저장하면서 두 가지 정보를 함께 보관합니다: 마지막으로 수정된 시각과 ETag(콘텐츠의 지문).
재방문: 변경 여부 확인
캐시 유효성 검사가 필요할 때, 브라우저는 조건부 요청을 보냅니다:
브라우저가 이렇게 묻는 것입니다: "이 날짜에 이 지문을 가진 버전이 있습니다. 아직 유효한가요?"
서버의 결정
리소스가 변경되지 않은 경우:
본문 없음. 내용 없음. 확인만. 브라우저는 캐시된 복사본을 사용합니다.
리소스가 변경된 경우:
새 콘텐츠, 새 지문, 캐시 업데이트.
두 가지 유효성 검사 방법: 시간 vs. 지문
HTTP는 두 가지 유효성 검사 메커니즘을 제공합니다. 각각 장단점이 있습니다.
Last-Modified (타임스탬프)
클라이언트가 보내는 것:
서버가 확인하는 것: 이 날짜 이후에 파일이 수정되었는가?
장점: 단순합니다. 파일 시스템 타임스탬프를 사용합니다. 사람이 읽기 쉽습니다.
한계: 1초 단위 정밀도. 같은 초 안에 파일이 두 번 변경되거나, 콘텐츠 변경 없이 타임스탬프만 업데이트되면 유효성 검사가 올바르게 작동하지 않습니다.
ETag (지문)
클라이언트가 보내는 것:
서버가 확인하는 것: 이 지문이 현재 콘텐츠와 일치하는가?
장점: 정밀합니다. 콘텐츠 변경을 즉시 감지합니다. 시계에 의존하지 않습니다.
필요 사항: 지문을 계산하거나 별도로 관리해야 합니다.
둘 다 사용하기
클라이언트는 두 헤더를 모두 보낼 수 있습니다. 둘 중 하나라도 리소스가 변경되지 않았음을 나타내면 서버는 304를 반환할 수 있습니다. 둘 다 있을 때는 ETag가 우선합니다. 더 정밀하기 때문입니다.
권장 사항: 둘 다 구현하세요. ETag는 정밀도를 위해, Last-Modified는 구형 클라이언트와의 호환성을 위해.
ETag 생성
ETag는 여러 방법으로 계산할 수 있습니다:
콘텐츠 해시 (가장 정밀):
파일 메타데이터 (더 빠르지만 덜 정밀):
버전 번호 (버전이 있는 리소스의 경우):
강한 ETag vs. 약한 ETag
강한 ETag는 바이트 단위로 동일함을 의미합니다:
약한 ETag는 의미상 동등함을 나타냅니다(W/ 접두사):
약한 ETag는 압축 방식이나 공백 차이 같은 사소한 변경을 허용하면서도 콘텐츠가 기능적으로 동일하다는 것을 나타냅니다.
304 응답: 헤더만
304 응답에는 본문이 포함되어서는 안 됩니다. 헤더만 있습니다:
포함해야 할 것: Date, ETag, Cache-Control, Expires(사용하는 경우)
포함하지 말아야 할 것: Content-Type, Content-Length(또는 0으로 설정), 모든 본문 내용
304 응답에 ETag를 포함하는 것이 중요합니다 — 클라이언트가 이후 요청에서 If-None-Match 헤더에 무엇을 보내야 할지 알 수 있기 때문입니다.
서버 구현
로드 밸런싱: ETag 일관성 문제
로드 밸런싱 환경에서는 모든 서버가 동일한 콘텐츠에 대해 동일한 ETag를 생성해야 합니다. 서버 A가 같은 파일에 대해 "abc123"을 계산하고 서버 B가 "xyz789"를 계산하면, 유효성 검사가 무작위로 실패합니다 — 클라이언트가 보낸 ETag가 일치하지 않아 304 대신 전체 다운로드가 발생합니다.
해결 방법:
- 콘텐츠 기반 해시 사용 (같은 콘텐츠 = 서버와 무관하게 동일한 해시)
- 서버 간 ETag 계산 공유
- ETag를 일관되게 처리하는 CDN 사용
304 직접 확인해보기
- DevTools 열기 (F12)
- Network 탭으로 이동
- 페이지 로드
- 새로 고침
- 304 상태 코드 찾기
- Size 열에서 아주 작은 전송량 또는 "from cache" 확인
브라우저가 이 모든 것을 자동으로 처리합니다. 304를 발견했다면, HTTP가 제 역할을 하는 것을 보고 있는 겁니다 — 변경 없음을 확인하고, 다운로드를 절약하고, 웹을 더 빠르게 만드는 것을.
304 Not Modified 자주 묻는 질문
304 Not Modified와 캐시에서 리소스를 가져오는 것의 차이점은 무엇인가요?
캐시에서 리소스를 가져온다는 것은 브라우저가 서버에 아무 요청도 보내지 않았다는 뜻입니다 — Cache-Control에 따라 캐시된 복사본이 아직 만료되지 않은 상태였던 것입니다. 304는 브라우저가 캐시된 복사본을 검증하기 위해 서버에 요청을 보냈고, 서버가 여전히 유효하다고 확인한 것입니다. 304는 서버와 한 번 통신해야 하지만, 캐시 히트는 그렇지 않습니다.
304 응답에 본문이 포함될 수 있나요?
아니요. HTTP 명세가 이를 명시적으로 금지합니다. 304 응답에는 헤더만 포함되어야 합니다. 본문을 포함하면 대역폭을 낭비하고 프로토콜을 위반합니다 — 304의 핵심 목적은 리소스 전송 자체를 피하는 것입니다.
캐시가 유효한 것 같은데 서버가 304 대신 200을 반환하는 이유는 무엇인가요?
여러 가능성이 있습니다: 로드 밸런싱 환경에서 ETag 생성이 서버마다 일관되지 않을 수 있고, 서버가 조건부 요청을 구현하지 않았을 수 있으며, If-Modified-Since 타임스탬프에 1초 정밀도 문제가 있을 수 있고, 서버 시계가 동기화되지 않았을 수도 있습니다. 조건부 요청 헤더가 올바르게 전송되는지, 서버가 ETag/Last-Modified 유효성 검사를 지원하는지 확인하세요.
ETag와 Last-Modified 중 무엇을 사용해야 하나요?
둘 다 사용하세요. ETag는 더 정밀하며 모든 콘텐츠 변경을 감지할 수 있습니다. Last-Modified는 더 단순하고 구형 시스템과의 호환성을 제공합니다. 둘 다 있을 때는 ETag가 우선합니다. 둘 다 구현하는 데 단점이 없습니다.
이 페이지가 도움이 되었나요?