
캐시는 자주 쓰이는 문서 사본을 자동 보관하는 HTTP 장치다. 웹 요청이 캐시에 도착했을 때. 캐시된 로컬 사본이 있다면, 그 문서는 원 서버가 아니라 그 캐시로부터 제공된다.
프론트 라이브러리들은 각자 사용하기 편한 인터페이스를 제공하지만, 근간에는 HTTP 캐시 디렉티브가 연관되어 기본적인 이해가 필요하다. Next.js의 최신 버전은 서버 측 fetch
함수의 오버라이드와 라우트 캐싱 설정 등 HTTP 캐싱 헤더와 관련된 네임스페이스를 사용한다.
캐시가 필요한 문제상황과 이점들
캐시의 이점
불필요한 데이터 전송을 줄여 비용(요금)을 줄여줌.
네트워크 병목을 줄여줌. 대역폭을 늘리지 않고 페이지를 빨리 불러올 수 있게함.
원 서버로의 요청을 줄여줌. 서버 부하를 줄이고 더 빨리 응답할 수 있음.
거리로 인한 지연을 줄여줌.
불필요한 데이터 전송
여러 클라이언트가 자주 쓰이는 원 서버 페이지에 접근할 때, 복수의 사용자에게 같은 문서를 보냄.
똑같은 바이트가 네트워크를 통해 반복하여 이동함 -> 불필요한 데이터 전송
캐시를 사용하여 첫번째 서버 응답은 캐시에 보관 -> 뒤이은 요청에 캐시된 사본이 응답으로 사용됨
결과적으로 중복된 트래픽을 주고 받는 낭비를 줄일 수 있음
대역폭 병목
캐시는 앞서 살핀대로 네트워크 병목 또한 줄여준다.
많은 네트워크가 원격 서버보다 로컬 네트워크 클라이언트에 더 넓은 대역폭을 제공
클라이언트들이 원서버에 접근하는 속도는 그 경로의 가장 느린 네트워크 속도와 같음
빠른 LAN에서 캐시된 사본을 가져온다면 성능 개선이 가능
갑작스런 요청 쇄도
캐싱은 갑작스런 요청 쇄도에 대처하는데 중요하다.
요청쇄도: 갑작스런 사건(뉴스 속보, 스팸 등)으로 많은 사람이 거의 동시에 웹문서에 접근할 때 발생
요청쇄도로 불필요한 트래픽이 급증하여 네트워크, 웹서버에 장애가 발생할 수 있게됨
요청 쇄도에 대응할 수 있도록 캐시를 사용할 필요가 있음
거리로 인한 지연
클라이언트와 서버의 물리적인 위치 또한 응답 지연에 영향을 줄 수 있다
빛의 속도로 인한 지연이 발생할 수 있다. (실제 신호는 이보다 더 느려서 더 크다)
당연히 거리가 더 멀 수록, 이 지연은 더 커진다. 물리적인 위치 근처에 캐시를 사용해 이 지연을 낮출 수 있다.
적중과 부적중
캐시는 세상 모든 문서의 사본을 저장하지 않는다.
캐시에 요청이 도착했을 때, 이에 대응하는 사본이 있다면, 그 캐시를 사용해 요청이 처리된다.
이를 캐시 적중(cache hit)이라고 한다.
반대로 사본이 없다면 요청은 그대로 원서버에 전달된다. 이를 캐시 부적중(cache missed)라고 한다.
재검사 Revalidation
원 서버의 컨텐츠가 변경될 수 있다. 캐시된 사본과 서버의 컨턴츠가 동일한지 점검해야한다.
이러한 '신선도 검사'를 HTTP 재검사revalidation이라고 한다.
서버에서 해당하는 컨텐츠 전체를 가져오지 않고 신선도를 검사할 수 있다.
효율적인 재검사를 위해 특별한 요청 정의가 필요하다.
캐시는 캐시된 사본의 재검사를 위헤 원 서버에 아주 작은 재검사 요청을 보낸다.
HTTP는 캐시된 객체를 재검사하기 위한 도구를 제공한다.
If-Modified-Since 요청 헤더가 일반적으로 사용된다. GET 요청에 사용하며, 캐시된 시간 이후 변경된 경우에만 사본을 보낸다. [참조]([[Chapter 3 HTTP 메시지]])
재검사 이후 다음 3가지의 응답이 올 수 있다.
재검사 적중(느린 적중)
재검사 이후, 캐시가 변경되지 않을 시 304 Not modified 응답을 보낸다.
304를 받은 캐시는 여전히 유효한 캐시 사본임을 확인하고 이를 클라이언트에 제공한다.
원 서버와 검사를 하기 때문에 순수 캐시 적중보다 느리다.
캐시 부적중 보다 빠르다. 원 서버에서 데이터를 받을 필요가 없기 때문이다.
재검사 부적중
캐시된 사본과 서버 데이터가 다르다면, 서버는 컨텐츠 전체와 함께 평범한 HTTP 200 응답을 클라이언트에 보낸다
객체 삭제
서버 객체가 서버에서 삭제되었다면, 서버는 404 not found 응답을 보내고 캐시를 삭제한다.
적중률 (HIT/MISS)
캐시 적중률(캐시 적중비): 캐시가 요청을 처리하는 비율
얼마나 많은 웹 트랜잭션을 외부로 내보내지 않았는지 보여준다.
트랜잭션은 고정된 소요 시간을 포함하고, 지연될 수 있기 때문에 문서 적중률을 개선하면 전체 대기시간이 줄어든다.
적중률은 0~1까지의 값으로 되어있고 보통 %로 표현
0% 모든 요청이 캐시 부적중, 100%는 모든 요청이 캐시 적중
실제 적중률은 캐시가 얼마나 큰지, 캐시 사용자들의 관심사가 얼마나 비슷한지, 캐시된 데이터가 얼마나 자주 변경되거나 개인화되는지, 어떻게 캐시가 설정 되었는지에 따라 다르다.
예측은 어렵지만, 보통 40%면 준수한 적중률이다.
보통 크기의 캐시라도 자주 사용되는 문서를 충분히 캐시한다면, 트래픽을 상당히 줄이고 성능을 개선할 수 있다.
캐시는 유용한 컨텐츠가 캐시 안에 머무르도록 보장하기 위해 노력한다.
문서 적중률이라고도 불린다.
바이트 적중률
바이트 적중률: 바이트 단위의 캐시 적중률. 캐시를 통해 제공된 모든 바이트의 비율을 표현
얼마나 많은 바이트가 인터넷으로 나가지 않았는지 보여준다.
바이트 단위 적중률 개선은 대역폭 절약을 최적화한다.
문서가 모두 같은 크기는 아니기 때문에 적중률만으로 캐시가 효율적으로 사용되는지 알 수 없다.
바이트 적중률 100% = 모든 바이트가 캐시에서 왔으며, 어떤 트래픽도 인터넷으로 나가지 않았음
문서 적중률과 함께 유용한 캐시 지표이다.
적중과 부적중의 구별
HTTP는 클라이언트에게 응답이 캐시 적중인지 부적중인지 알려주지 않는다. 두 경우 클라이언트는 200 ok응답만 볼 것.
종종 프락시 캐시는 Via 헤더를 제공해 캐시 적중 여부를 알려주는 서비스도 있다.
Date 헤더를 이용해 캐시 여부를 알 수 있다.응답의 Date와 현재 시각을 비교하여, 응답 생성일이 더 오래되었다면, 캐시임을 알 수 있다.
또다른 방법으로 응답의 Age 헤더를 사용하는 것이다.
캐시 토폴로지
캐시는 한 명에게만 할당될 수도 있고, 수천 명의 사용자들간에 공유될 수도 있다. 한 명에게만 할당된 캐시를 개인 전용(private) 캐시라 하고, 공유된 캐시는 공용(public) 캐시라고 한다.
개인 전용 캐시
개인만을 위한 캐시. 한 명의 사용자가 자주 찾는 페이지를 담는다
많은 저장 공간을 필요로 하지 않아 작고 저렴할 수 있다.
브라우저는 개인 전용 캐시를 내장한다.
대부분의 브라우저는 자주 쓰이는 문서를 pc의 메모리에 캐시하고, 사용자가 캐시 사이즈와 설정을 할 수 있게 한다. 또한 브라우저가 캐시한 파일을 열어볼 수 있다
공용 프록시 캐시
사용자 집단에게 자주 쓰이는 페이지를 담는다.
캐시 프록시 서버 혹은 프록시 캐시라 불리는 특별한 종류의 공유된 프록시 서버다.
프록시 캐시는 로컬 캐시에서 문서를 제공하거나, 사용자 입장에서 서버에 접근한다.
여러 사용자가 접근하기 때문에 트래픽을 줄일 수 있는 기회가 많다.
개인 전용 캐시는 캐시되지 않은 문서에 제각각 접근한다. 각 개인 전용 캐시는 같은 문서를 여러번 가져온다.
공용 캐시는 자주 찾는 객체를 단 한 번만 가져와 모든 요청에 공유된 사본을 제공해 트래픽을 줄인다.
프록시 캐시는 [앞서 살핀 프록시 규칙]([[Chapter 6 프록시]])을 따른다. 수동 프록시를 지정하거나, 자동설정 파일을 설정하여 브라우저가 프록시 캐시를 사용하도록 설정할 수 있다. 혹은 인터셉트 프록시를 사용해 브라우저 설정없이 HTTP 요청이 캐시를 통하도록 강제할 수 있다.
프록시 캐시 계층
작은 캐시에서 캐시 부적중이 발생 시, 더 큰 부모 캐시가 '걸러 남겨진' 트래픽을 처리하도록 계층을 만들 수 있다.
클라이언트 주변에는 작고 저렴한 캐시를 사용하고, 계층 상단에는 많은 사용자가 공유하는 문서를 유지하는 것이다.
캐시 계층이 깊다면 요청은 캐시의 긴 연쇄를 따라가게 된다. 프록시 연쇄가 길어질 수록 각 중간 프록시는 성능 저하가 나타날 수 있다.
캐시망, 컨텐츠 라우팅, 피어링
단순한 캐시 계층 대신 복잡한 캐시망을 만드는 방법
캐시망의 프록시 캐시는 서로 대화하여 캐시 커뮤니케이션 결정을 동적으로 내린다.
어떤 부모 캐시와 대화할 것인지, 요청이 캐시를 완전히 우회해서 바로 원서버로 가게 할 것인지 결정할 수 있다.
캐시망 안에서 컨텐츠 라우팅을 위해 설계된 캐시로 할 수 있는 일들
URL에 근거하여 부모 캐시와 원 서버 중 하나를 동적으로 선택
URL에 근거하여 특정 부모 캐시를 동적으로 선택
부모 캐시에 가기 전에 캐시된 사본을 로컬에서 찾아봄
다른 캐시들이 캐시된 컨텐츠에 부분적으로 접근을 허용하되, 캐시를 통한 인터넷 트랜짓을 허용하지 않기
캐시 사이의 관계는 서로 다른 조직들이 상호 이득을 위해 캐시를 연결하여 서로 찾아볼 수 있게 한다.
선택적인 피어링을 지원하는 캐시를 형제 캐시라고 부른다.
HTTP는 형제 캐시를 지원하지 않기 떄문에, 인터넷 캐시 프로토콜(ICP)나 하이퍼텍스트 캐시 프로토콜(HTCP)을 이용해 HTTP를 확장할 수 있다.
캐시 처리 단계
오늘날 상용 프록시 캐시는 복잡하다. 고성능이면서도 HTTP와 다른 기술의 고급기능을 지원하도록 만들어진다. 하지만 기본적인 웹 캐시의 동작은 단순하다. 다음 7 단계를 따른다.
요청받기 -> 파싱 ->검색 -> 신선도 검사 -> 응답 생성 -> 전송 -> 로깅
1. 요청 받기
캐시는 네트워크로부터 도착한 요청 메시지를 읽는다.
캐시는 네트워크 커넥션에서의 활동을 감지하고, 들어오는 데이터를 읽어들인다.
고성능 캐시는 여러 개의 들어오는 커넥션으로부터 데이터를 동시에 읽고, 메시지 전체가 도착하기 전에 트랜잭션 처리를 시작한다.
2. 파싱
캐시는 메시지를 파싱하여 URL과 헤더들을 추출한다.
요청 메시지를 여러 부분으로 파싱하고, 헤더 부분을 조작하기 쉬운 자료구조에 담는다.
캐싱 소프트워에는 헤더 필드를 처리하고 조작하기 쉽게 만들어준다.
3. 검색
캐시는 로컬 복사본이 있는 지 검사하고, 사본이 없다면 사본을 받아온다 (그리고 로컬에 저장)
URL을 알아내고, 그에 해당하는 로컬 사본이 있는지 검사한다.
로컬 복사본은 메모리, 디스크 혹은 근처 다른 컴퓨터에도 저장될 수 있다.
사본을 로컬에서 가져올 수 없다면, 캐시는 상황과 설정에 따라 원 서버나 부모 프록시에서 가져오거나 실패를 반환한다.
캐시된 객체는 서버 응답 본문과 원서버 응답 헤더를 포함하기 때문에 캐시 적중 동안 올바른 서버 헤더가 반환될 수 있다.
캐시된 객체는 객체가 얼마나 오랫동안 캐시에 머무르고 있었는지 알려주는 기록이나 얼마나 자주 사용되었는 지 등등 메타데이터를 포함한다.
4. 신선도 검사
캐시된 사본이 충분히 신선한지 검사하고, 신선하지 않다면 변경사항이 있는지 서버에 물어본다.
HTTP는 일정 기간동안 서버 문서의 사본을 보유할 수 있게 해준다.
이 기간 동안 문서를 '신선'하다고 간주하고, 캐시는 서버와의 접촉 없이 이를 제공한다.
캐시된 사본이 신선도 한계를 넘을 정도로 오래 보관되었다면, 문서에 변경이 있었는지 검사하기 위해 서버와 재검사revalidation을 해야한다.
5. 응답 생성
캐시는 새로운 헤더와 캐시된 본문으로 응답 메시지를 만든다.
캐시된 응답을 원 서버에서 온 것처럼 보이게 하기 위해, 캐시는 캐시된 서버 응답 헤더를 토대로 응답 헤더를 생성한다.
이 base 헤더들은 캐시에 의해 수정되고 늘어난다.
캐시 (서버)는 클라이언트에 맞게 이 헤더를 조정할 책임이 있다.
ex) 클라이언트가 HTTP/1.1 응답을 기대하는데 서버가 1.0 응답을 반환했다면, 캐시는 반드시 헤더를 적절하게 번역해야한다.
캐시는 캐시 신선도 정보(Cache-Control, Age, Expires 헤더), 요청이 프록시 캐시를 거쳐갔으믈 알리기 위해 Via 헤더를 포함시켜야한다.
캐시는 Date 헤더를 조정해서는 안된다. Date 헤더는 원 서버와 최초로 응답 객체를 생성한 날짜를 가리키기 때문이다.
6. 전송
네트워크를 통해 응답을 클라이언트에 돌려준다.
응답 헤더까지 준비되면, 캐시는 응답을 클라이언트에 돌려준다.
프록시 캐시는 모든 프록시 서버와 마찬가지로 클라이언트와 커넥션을 유지할 필요가 있다.
7. 로깅
선택적: 캐시는 로그파일에 트랜잭션에 대해 서술한 로그를 남길 수 있다.
대부분의 캐시는 로그 파일과 캐시 사용에 대한 통계를 유지한다.
각 캐시 트랜잭션 완료 이후, 캐시는 통계 캐시 적중과 부적중 횟수 등 관련 지표의 통계를 갱신하고, 로그 파일에 요청 종류 URL 등 어떤 일이 일어났는지 알리는 항목을 추가한다.
Squid fog, Netscape extended common log 포맷이 일반적이지만 많은 캐시 제품은 커스텀 로그를 허용한다.
사본을 신선하게 유지하기
캐시된 사본이 항상 서버 데이터와 일치하는 것은 아니다. 데이터에 변경이 있을 때 재검사를 진행하는 것을 알아봤다. 캐시가 오래된 데이터를 제공한다면 문제가 될 수 있다. 캐시 데이터는 서버 데이터와 일치하도록 관리해야한다.
HTTP는 어떤 캐시가 사본을 갖고 있는지 서버가 기억하지 않더라도, 캐시된 사본이 서버와 일치하도록 유지해주는 단순한 메커니즘을 갖고 있다. 이를 문서 만료 Expires와 서버 재검사라고 한다.
문서 만료 Expires
HTTP는 Cache-Control과 Expires라는 헤더를 이용해 원 서버가 각 문서에 유효기간을 붙일 수 있게 해준다.
유효기간과 마찬가지로, 이 헤더들은 컨텐츠가 얼마나 신선한 상태로 보일 수 있는지 결정
캐시된 사본이 만료되면 반드시 서버와 문서에 변경이 있는지 검사해야하며, 유효기간이 지났다면, 새로운 캐시(와 유효기간)를 얻어야한다. 이
유효 기간과 나이
서버는 max-age 응답 헤더를 이용해 유효기간을 명시한다. Expires와 Cache-Control:max-age 헤더는 기본적으로 같은 일을 하지만, 차이가 있다.
Cache-Control: max-age
문서가 처음 생성된 이후부터, 유효기간이 만료될 때까지 경과한 시간의 최댓값. 초S 단위이다.
Cache-Control: max-age=36000
Expires
절대 유효기간을 명시한다. 유효기간이 경과했다면 그 문서는 더이상 신선하지 않다.
Expires: Fri, 05 Jul 2024, 0%:00:00 UTC
서버 재검사
캐시된 문서가 만료되었다는 뜻은 서버 데이터가 실제로 다르다는 뜻은 아니다. 검사할 시간이 되었음을 뜻한다. 캐시가 원서버에 변경되었는지 여부를 물어봐야 한다는 의미에서 '서버 재검사'라고 한다.
재검사 결과 컨텐츠가 변경되었다면, 캐시는 문서의 새로운 사본을 가져와 오래된 데이터 대신 저장한 뒤 클라이언트에게도 보낸다
재검사 결과 변경되지 않았다면. 캐시는 새 만료일을 포함한 새 헤더들만 가져와서 캐시 안의 헤더들을 갱신한다.
매번 검증을 요청하는 대신 만료 시간 이후에만 검증하는 점에서 효율적이다.
HTTP 프로토콜에서 캐시는 다음 중 하나를 반환해야한다.
충분히 신선한 캐시 사본 (만료 기간이 넉넉한)
원 서버와 재검사되어 충분히 신선한 캐시 사본
에러 메시지 (재검사 해야하는 원 서버가 다운될 시)
경고 메시지가 부착된 캐시 사본(부정확 할 시)
조건부 메서드와 재검사
HTTP의 조건부 메서드는 재검사를 효율적으로 만든다. HTTP는 캐시가 서버에 조건부 GET 요청을 보낼 수 있게 해준다. 이 경우, 재검사에서 서버 데이터와 캐시된 데이터가 다를 경우에만 응답 객체 본문을 보내달라고 하는 것이다. 신선도 검사와 객체를 받아오는 것은 하나의 조건부 GET으로 결합된다.
조건부 GET은 요청 메시지에 특별한 조건부 헤더를 추가하여 시작된다. 웹 서버는 조건이 참인 경우에만 응답 객체를 반환한다.
HTTP에는 다섯가지 조건부 요청 헤더가 있다. 그 중 둘은 캐시 재검사를 할 때 유용한 If-Modified-Since와 If-None-Match이다. (이전 글 참조)
If-Modified-Since: 주어진 날짜 이후로 수정되었다면 요청 메서드를 처리한다. 캐시된 버전으로부터 컨텐츠가 변경된 경우에만 가져오기 위해 Last-Modified 서버 응답 헤더와 함께 사용된다
If-None-Match: 마지막 변경된 날짜를 맞춰보는 대신, 서버는 문서에 대한 일렬번호와 같이 동작하는 특별한 태그를 제공할 수 있다.(ex:Etag) If-None-Match 헤더는 캐시된 태그가 서버에 있는 문서의 태그와 다를 때만 요청을 처리한다.
If-Modified-Since: 날짜 재검사
If-Modified-Since: <캐시된 마지막 수정일>
가장 흔히 사용된다. If-Modified-Since의 재검사 요청은 IMS 요청으로 불린다.
서버에게 리소스가 특정 날짜 이후로 변경된 경우에만 요청한 본문을 보내달라고 한다.
변경된 경우: 정상적인 GET 요청처럼 성공하고 새 문서와 만료 날짜들이 담겨 캐시에 반환된다.
변경되지 않은 경우: 본문을 보내지 않고 304 응답을 캐시에 보낸다. 이때 업데이트할 필요가 있는(헤더 만료 날짜)만 보낸다.
If-Modified-Since 요청 헤더는 Last-Modified 응답 헤더와 함께 사용된다. 원서버는 제공하는 문서에 최근 변경 일시를 붙인다. 이 두 값을 비교하여 판단한다
종종 실제 날짜 비교 대신 IMS 요청의 날짜와 Last-Modified의 날짜를 문자열 비교할 때도 있다. 문서가 업데이트될 때마다 Last-Modified가 바뀐다면, 실제 날짜가 아닌 다른 문자열이어도 동작은 할 것이다.
If-None-Match: 엔터티 태그 검사
최근 변경 일시 재검사가 어려운 상황이 있다.
어떤 데이터는 일정 시간 간격으로 업데이트 되지만, 실제로는 같은 데이터를 포함한다.내용엔 아무런 변화가 없고 Last-Modified만 바뀐다.
어떤 변경은 모든 캐시가 다시 사본을 받기엔 사소한 것일 수 있다. (문서의 철자, 주석 변경)
어떤 서버는 페이지에 대한 Last-Modified를 정확히 판별할 수 없다.
1초보다 작은 간격으로 갱신되는 문서를 제공하는 서버들에게는 1초의 정밀도는 충분하지 않을 수 있다.
이를 해결하기 위해 문서의 엔터티 태그를 새로운 버전으로 표현할 수 있다.
엔터티 태그가 변경되었다면, 캐시는 새 문서의 사본을 GET 하기 위해 If-None-Match 조건부 헤더를 사용할 수 있다.
ex) 클라이언트:If-None-Match: 'v2.6' 요청
Etag 변경되지 않았을 시: 서버는 304 Not Modified 반환
Etag 변경 시: 서버는 새 문서와 새 Etag를 반환
캐시가 객체에 대한 여러 개의 사본을 갖고 있는 경우, 이를 서버에 알리기 위해 If-None-Match 헤더에 여러 개의 엔터티 태그를 포함시킬 수 있다. ex) If-None-Match: "v2.4", "v2.5", "v2.6"
약한 검사기와 강한 검사기
최근 변경 일시, 엔터티 태그 둘다 캐시 검사기다.
서버는 때로 모든 캐시 사본을 무효화 시키지 않고 문서를 살짝 고칠 수 있도록 허용하고 싶은 경우가 있다.
HTTP/1.1은 컨텐츠가 조금 변경되어도 서버가 '그정도면 같은 것'이라고 주장할 수 있게 해주는 '약한 검사기'를 지원한다.
약한 검사기
약한 검사기는 어느 정도 컨텐츠 변경은 허용하지만, 중요한 의미가 변경되면 함께 변경된다.
조건부 특정범위 가져오기 같은 몇몇 동작은 약한 검사기로 불가능하기 때문에 서버는 'W/' 접두사로 약한 검사기를 구분한다.
약한 엔터티 태그는 대응하는 엔터티에 유의미한 변경이 있을 때마다 같이 변경되어야한다.
원 서버는 약한 엔터티 태그 값이라도 서로 다른 두 엔터티에 대해 재사용해서는 안된다. 유효기간에 상관없이 캐시 항목은 임의의 긴 시간 동안 계속될 수 있다.
Etag: W/"v2.6"
If-None-Match: W/"v2.6"
강한 검사기
강한 검사기는 컨텐츠가 바뀔 때마다 바뀐다.
강한 엔터티 태그는 대응하는 값이 어떻게 바뀌든 매번 반드시 같이 바뀌어야 한다.
원 서버는 다른 두 엔터티 태그를 강한 엔터티 태그 값으로 재사용해서는 안된다.
엔터티 태그와 최근 변경일Last-Modified 사용
서버가 엔터티 태그를 반환했다면, 반드시 엔터티 태그 검사기를 사용
서버가 Last-Modified만 반환했다면, 클라이언트는 If-Modified-Since를 사용할 수 있음
만약 엔터티 태그와 최근 변경일 모두 사용 가능하다면, 둘 모두 재검사하는 것이 좋다.
서버가 가능하다면 엔터티 태그 검사기를 보내야 하며, 이점이 있다면 강한 엔터티 태그 대신 약한 엔터티 태그를 보낸다. 이때 최근 변경일 또한 있다면 함께 검사하는 것이 좋다.
캐시나 서버가 If-Modified-Since와 엔터티 태그 조건부 헤더 모두 받았다면, 요청의 모든 조건부 헤더 필드의 조건에 부합되지 않는 한 304를 반환해선 안됨
캐시 제어
HTTP는 문서가 만료되기 전까지 얼마나 오랫동안 캐시될 수 있게 할 것인지 서버가 설정할 수 있는 방법을 정의한다.
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: must-revalidate
Cache-Control: max-age
Expires
아무 만료 정보를 주지 않고 캐시가 스스로 체험적인(휴리스틱) 방법으로 결정하게 할 수 있다
캐시 제어 응답 헤더들
Cache-Control: no-store & no-cache
HTTP는 신선도를 관리하기 위해, 객체를 캐시하는 것을 제한하거나 캐시된 객체를 제공하는 여러가지 방법을 제공. no-store와 no-cache 헤더는 캐시가 검증되지 않은 캐시된 객체로 응답하는 것을 막는다.
no-store: 캐시가 그 응답의 사본을 만드는 것을 금지한다. 이 경우 캐시는 클라이언트에게 no-store 응답을 전달하고 객체를 삭제할 것이다.
no-cache: 로컬 캐시 저장소에 저장될 수 있다. 다만 먼저 서버와 재검사를 하지 않고 캐시에서 클라이언트로 제공될 수 없다.
max-age
신선하다고 간주되었던 문서가 서버로부터 온 시간 이후 흐른 시간. 초로 나타냄. max-age를 0으로 설정하여 캐시가 매 접근마다 문서를 캐시하지 않거나 리프레시 하도록 요청할 수 있다.
s-maxage: 'max-age'와 동일하지만 공유된(공용) 캐시에만 적용된다.
Expires
deprecated. 초단위 대신 실제 만료 날짜를 명시한다. 많은 서버가 동기화 되어있지 않거나 부정확한 시계를 갖고 있기 때문에 만료를 절대 시각 대신 경과된 시간으로 표현하는 것이 낫다고 HTTP 설계자들은 판단했다.
Must-Revalidate
경우에 따라 캐시는 성능 상의 이유로 신선하지 않은(만료된) 객체를 제공하도록 할 수도 있다. 만약 캐시가 만료 정보를 엄격하게 따르길 원한다면 must-revalidate를 사용한다. 이 응답 헤더는 신선하지 않은 사본을 원 서버와의 최초 재검사 없이 제공해서는 안됨을 의미한다.
휴리스틱 만료
만약 응답이 Cache-Control:max-age, Expires 헤더 중 어느 것도 포함하지 않는다면, 캐시는 경험적인 방법으로 최대 나이를 계산할 것이다.
이때 여러 알고리즘이 사용될 수 있다. 하지만 이를 위해선 Last-Modified이 필요하다. 이조차 없다면 캐시는 판단 근거가 될 정보를 갖지 못한다.
이 때 보통 기본 신선도 유지기간(1시간~하루)를 유지하거나, 보수적으로 0의 신선도 수명을 설정하여 매번 재검사를 강제하기도 한다.
클라이언트 신선도 제약
웹 브라우저는 브라우저나 프록시 캐시의 신선하지 않은 컨텐츠를 강제로 갱신시켜주는 리프레시(새로고침), 리로드 버튼을 갖고 있다. 이 리프레시 버튼은 Cache-Control 요청 헤더가 추가된 GET 요청을 발생시켜 강제로 재검사하거나 서버로부터 컨텐츠를 무조건 가져온다. 동작은 브라우저, 문서, 중간 캐시 설정에 따라 다를 수 있다.
클라이언트는 Cache-Control 요청 헤더를 사용하여 만료 제약을 설정할 수 있다.
문서를 최신으로 유지할 필요가 있는 애플리케이션은 Cache-Control 헤더를 사용해 만료를 더 엄격히 할 수 있다.
혹은 성능, 신뢰성, 비용 개선을 위해 절충안으로 신선도 요구사항을 느슨하게 하고자 할 수도 있다.
클라이언트 Cache-Control 요청 지시어
Cache-Control: max-stale
Cache-Control: max-stale=s
캐시는 신선하지 않은 문서도 자유롭게 제공할 수 있다. 만약 s 매개변수가 지정되면 만료시간이 s만큼 지난 문서도 받아들인다. 이는 캐싱 규칙을 느슨하게 한다.
Cache-Control: min-fresh=s
클라이언트는 지금르오부터 s초 후까지 신선한 문서만 받아들인다. 이는 캐싱 규칙을 엄격하게 한다.
Cache-Control: max-age=s
캐시는 s초보다 오래된 캐시 문서를 반환할 수 없다. 캐싱 규칙을 더 엄격하게 만든다.
Cache-Control: no-cache
이 클라이언트는 캐시된 리소스를 재검사하기 전까지 받아들이지 않는다
Cache-Control: no-store
이 캐시는 저장소에서 문서의 흔적을 최대한 빨리 삭제해야한다. 민감한 정보를 포함하기 때문이다.
Cache-Control: only-if-cached
클라이언트는 캐시에 들어있는 사본만을 원한다
주의할 점
문서 만료는 완벽한 시스템이 아니다. 유효기간을 실수로 까마득한 미래로 설정한다면, 만료되기 전까지 그 문서에 대한 어떤 변경도 캐시에 반영되지 않을 것이다. 이런 이유로 보통 만료 기간을 길게 잡지 않는다.
혹은 유효기간을 사용조차 하지 않아서 문서가 얼마나 오랫동안 신선하게 할 것인지 캐시가 알기 어려운 경우도 많다.