재검증
고친 사람 github-actions[bot]
재검증은 보관해 둔 사본이 아직 원본과 같은지 확인해 다시 쓸 수 있게 만듭니다. 사본을 버리고 처음부터 받아 오는 대신 물어보기만 합니다. 대개는 안 바뀌었다는 짧은 답이 돌아옵니다. 사본은 손대지 않은 채로 다시 쓰입니다.
쉽고 빠른 이해
재검증은 캐시가 들고 있는 사본을 원본에 대 보는 확인 절차입니다. 어제 받아 둔 이미지를 오늘도 써도 되는지 브라우저가 서버에 묻는 일이 그것입니다.
확인하는 수단이 없으면 곤란합니다. 보관 기한이 지난 사본은 그냥 내줄 수 없습니다. 그렇다고 버리고 다시 받으면 안 바뀐 내용까지 전부 내려받게 됩니다.
어떻게 도는가:
- 캐시가 사본의 버전 표식을 조건으로 적어 보냅니다
- 서버가 그 표식을 지금 버전과 맞춰 봅니다
- 같으면 본문 없는 짧은 답이 옵니다. 사본은 다시 쓸 수 있게 됩니다
대가가 있습니다. 확인만 하고 끝나도 서버까지 한 번은 다녀와야 해서 그만큼 답이 늦습니다. 그래서 기한이 남아 있는 사본은 묻지 않고 그냥 내줍니다. 기한이 다한 뒤에야 묻습니다.
상세
출력해서 벽에 붙여 둔 근무표를 떠올려 봅시다. 한 주가 지나면 이게 아직 맞는지 알 수 없습니다. 새 근무표를 통째로 받아 오는 대신 담당자에게 「제가 붙여 둔 것이 3월 2일판인데 아직 그게 최신입니까」라고 묻습니다. 맞다는 답만 들으면 벽에 붙은 종이를 떼지 않습니다.
재검증은 캐싱해 둔 응답이 아직 원본과 같은지 원본을 가진 서버에 확인받는 절차입니다. 페이지를 다시 열 때 브라우저가 저장해 둔 스타일시트를 그대로 써도 되는지 서버에 물어보는 것이 그 확인입니다. 확인이 끝나면 캐시는 저장해 둔 응답을 다시 쓰거나 새 응답으로 갈아 끼웁니다.
같은 절차를 검증이라고도 부릅니다. 한 번 받아 둔 것을 나중에 다시 확인한다는 쪽을 살릴 때 앞에 「재」를 붙입니다. 두 이름이 가리키는 절차는 하나입니다.
사본을 버리지 않는 까닭
캐시는 응답을 받아 두고 정해진 기한 동안 그 사본을 그대로 내줍니다. 이 기한이 남아 있는 상태를 신선도라고 부릅니다. 기한이 다한 사본은 낡은 데이터가 됩니다. 원본과 어긋났을 수 있어 캐시가 마음대로 내줄 수 없습니다.
가장 단순한 처리는 낡은 사본을 버리고 처음부터 다시 받는 것입니다. 그런데 기한이 다했다고 내용까지 바뀌는 것은 아닙니다. 로고 이미지나 스타일시트는 몇 달을 그대로 두기도 합니다.
재검증은 이 둘을 가릅니다. 바뀌었는지부터 묻습니다. 안 바뀌었으면 본문을 안 받습니다. 오가는 것이 헤더 몇 줄로 줄어들어 본문이 클수록 아끼는 양이 커집니다.
물음에 싣는 값
묻는 쪽과 답하는 쪽이 같은 버전을 가리킬 말이 있어야 합니다.
검증자는 서버가 지금 들고 있는 내용이 어느 버전인지 가리키는 짧은 값입니다. 앞에서 버전 표식이라 부른 것이 이것입니다. 내용을 고치면 이 값도 따라 바뀝니다.
이 값은 서버가 응답을 내려보낼 때 헤더에 실려 옵니다. 대표적인 헤더가 ETag입니다. 캐시는 그 값을 본문과 함께 보관해 둡니다.
캐시는 다음에 물을 때 그 값을 요청에 조건으로 적습니다. 조건을 붙인 요청을 조건부 요청이라고 부릅니다. 재검증은 이 요청을 타고 이뤄집니다. 조건은 「내가 든 버전과 다를 때만 본문을 보내라」는 한 줄입니다.
안 바뀌었다는 답에는 304 Not Modified라는 상태 코드가 붙습니다. 본문을 안 실었다는 표시이기도 합니다.
sequenceDiagram
participant 캐시
participant 서버
캐시->>서버: 첫 요청
서버-->>캐시: 응답 본문 · 검증자 "v7"
Note over 캐시: 본문과 검증자를 함께 보관한다
캐시->>서버: 기한이 다한 뒤 · 조건부 요청 "v7"
서버-->>캐시: 304 · 본문 없음 · 새 기한
Note over 캐시,서버: 버전이 다르면 여기서 새 본문이 통째로 온다
아래는 그림의 둘째 방문, 캐시가 조건을 실어 보내고 서버가 같다고만 답하는 한 왕복입니다. 응답 끝줄은 새 기한입니다. Cache-Control 헤더에 실려 온 max-age=600 이 「600초 동안은 다시 묻지 않아도 된다」는 뜻입니다.
GET /style.css
If-None-Match: "v7" // 캐시가 든 버전
→ 304 Not Modified // 본문이 없다
ETag: "v7" // 다음에 쓸 값
Cache-Control: max-age=600 // 새 기한
서버는 조건에 적힌 값을 지금 버전과 맞춰 보고 같다는 답만 돌려줬습니다. 본문은 오지 않았습니다. 다음 물음에 쓸 값만 헤더로 따라옵니다.
확인이 끝난 뒤의 사본
같다는 답을 받으면 캐시는 저장해 둔 본문을 손대지 않습니다. 함께 온 헤더로 기한을 새로 받습니다. 사본이 캐시에 머문 시간을 세는 나이도 다시 0부터 셉니다. 낡았던 사본이 이렇게 다시 신선해집니다.
버전이 달라졌으면 서버는 평소처럼 새 본문을 실어 보냅니다. 캐시는 사본을 새것으로 갈아 끼웁니다. 함께 온 새 검증자는 다음 물음에 쓸 값으로 보관합니다.
stateDiagram-v2
[*] --> 신선
신선 --> 낡음: 보관 기한이 다한다
낡음 --> 신선: 재검증 · 안 바뀌었다
낡음 --> 교체: 재검증 · 바뀌었다
교체 --> 신선: 새 기한을 받는다
그림의 「교체」는 새 본문을 받아 사본을 갈아 끼운 상태입니다. 어느 길로 가든 사본은 다시 신선한 상태에서 출발합니다. 기한이 다하면 같은 물음이 되풀이됩니다.
재검증을 부르는 계기
캐시가 언제나 묻는 것은 아닙니다. 기한이 남아 있으면 묻지 않고 바로 내줍니다. 묻게 만드는 계기는 대개 셋입니다.
| 계기 | 언제 묻나 |
|---|---|
| 보관 기한이 다함 | 그 뒤로 들어온 첫 요청에서 |
| no-cache 지시자가 붙은 응답 | 내주기 전에 매번 |
| 사용자가 새로 고침을 강제함 | 그 요청에서 바로 |
가운데 줄의 no-cache 는 Cache-Control 헤더에 실려 오는 지시자입니다. 이름과 달리 저장하지 말라는 뜻이 아니라, 저장은 하되 내줄 때마다 먼저 물으라는 뜻입니다.
must-revalidate는 성격이 조금 다릅니다. 묻는 시점을 정하는 것이 아니라, 못 물었을 때 낡은 사본을 내주지 못하게 막습니다. 캐시가 원본 서버에 닿지 못하면 낡은 사본 대신 오류를 내놓게 됩니다.
재검증이 지는 대가
첫째, 왕복 한 번은 듭니다. 본문을 안 받아도 서버까지 다녀오는 시간은 그대로라, 응답이 시작되기까지 사용자가 기다리는 시간은 줄지 않습니다. 이 기다림을 없애려고 낡은 사본을 먼저 내주고 뒤에서 확인하는 stale-while-revalidate 같은 수단이 따로 있습니다.
둘째, 검증자가 없으면 물을 수 없습니다. 서버가 버전을 가리키는 값을 안 주면 캐시는 조건으로 적을 말이 없어 본문을 통째로 다시 받게 됩니다.
셋째, 헛걸음이 생깁니다. 내용이 같은데 검증자만 달라지면 서버는 바뀐 것으로 보고 본문을 다시 보냅니다. 서버 여럿이 각자 다른 방식으로 검증자를 만들면 이런 일이 잦아집니다.
넷째, 재검증 자체가 실패할 수 있습니다. 원본 서버가 응답하지 않으면 캐시는 낡은 사본을 내줄지 오류를 낼지 골라야 합니다. 그 갈림을 미리 정해 두는 수단이 stale-if-error입니다.
flowchart TD
A["재검증을 보냈다"] --> B{"원본 서버가 답했나"}
B -->|예| C["돌아온 답대로 처리한다"]
B -->|아니오| D{"must-revalidate 가 걸렸나"}
D -->|예| E["오류를 내놓는다"]
D -->|아니오| F["stale-if-error 대로 낡은 사본을 내준다"]
못 물었을 때 무엇을 내줄지는 이 두 지시자가 가릅니다.
관련 항목
재검증이 조건으로 싣는 값
검증자 · ETag · Last-Modified · 엔티티 태그 · 강한 검증자 · 약한 검증자
재검증을 실어 나르는 요청 헤더 필드
If-None-Match · If-Modified-Since · If-Match · If-Unmodified-Since · 조건부 요청 · 헤더
물음에 돌아오는 상태 코드
304 Not Modified · 200 OK · 412 Precondition Failed · 상태 코드
재검증 시점을 정하는 캐시 지시자
no-cache · must-revalidate · max-age · stale-while-revalidate · stale-if-error · Cache-Control
사본이 낡는 정도를 재는 값
신선도 · 신선도 수명 · 나이 · 낡은 데이터 · 휴리스틱 신선도 · TTL
사본을 다루는 다른 절차
검증 · 무효화 · 퍼지 · 축출 · 캐시 적중 · 캐시 미스
재검증을 도는 캐시의 종류
브라우저 캐시 · HTTP 캐시 · 프록시 · CDN · 공유 캐시 · 캐싱
어느 사본을 물을지 고르는 기준
재검증이 오가는 프로토콜과 그 참여자
다른 이름: revalidation · 캐시 재검증