공유 캐시
고친 사람 github-actions[bot]
공유 캐시는 한 번 받아 둔 응답을 여러 사용자가 함께 꺼내 쓰게 하는 캐시입니다. 앞사람이 받아 온 것을 뒷사람에게도 내주므로 원본 서버까지 다녀오는 횟수가 사람 수만큼 줄어듭니다. 대신 한 사람에게만 보여야 할 내용까지 다음 사람에게 내줄 수 있습니다. 그래서 무엇을 담아도 되는지 정하는 규칙이 따라붙습니다.
쉽고 빠른 이해
공유 캐시는 사용자와 원본 서버 사이에 서서, 한 번 받아 온 응답을 여러 사용자에게 나눠 주는 저장소입니다. 어느 회사 홈페이지의 첫 화면 이미지는 누가 요청해도 같은 것이 나가므로 한 번만 받아 두면 됩니다.
사용자마다 자기 캐시만 들고 있으면 방문자가 늘어도 원본 서버가 받는 요청은 안 줄어듭니다. 누구든 첫 방문에는 원본까지 다녀와야 합니다. 방문자가 많을수록 그 왕복도 늡니다. 캐시 하나를 여럿이 나눠 쓰면 그 왕복이 한 번으로 줄어듭니다.
어떻게 도는가:
- 사용자가 보낸 요청이 공유 캐시를 먼저 지납니다
- 들고 있는 응답이 아직 쓸 만하면 원본에 안 가고 그것을 내줍니다
- 없거나 낡았으면 원본 서버에서 받아 옵니다. 담아도 되는 응답이면 다음 사람 몫으로 남깁니다
대가는 남의 것을 내줄 위험입니다. 로그인한 사람의 화면처럼 사람마다 달라야 하는 응답을 담아 두면 다음 사람이 그 화면을 보게 됩니다. 담아도 되는 응답인지는 원본 서버가 표시해 보내야 합니다. 그 표시가 틀리면 사고가 납니다.
상세
회사 복도에 붙은 공용 공지판을 떠올려 봅니다. 한 장을 붙여 두면 지나가는 사람이 저마다 그것을 읽고 갑니다. 같은 종이를 사람 수만큼 인쇄해 돌릴 필요가 없습니다.
거기에 한 사람 앞으로 온 편지를 붙이면 어떻게 될지도 같이 보입니다. 지나가는 모두가 그 편지를 읽습니다.
캐싱은 한 번 얻은 것의 복사본을 가까운 곳에 두고 다음번에 다시 얻지 않는 일입니다. 그 복사본을 담아 두는 저장소를 캐시라고 부릅니다. 공유 캐시는 그 저장소 하나를 여러 사용자가 함께 쓰는 것입니다.
이 말은 주로 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜) 캐시에서 씁니다. 브라우저가 보낸 요청은 응답을 처음 만들어 내는 서버까지 곧장 가지 않고 중간 지점을 여럿 지납니다. 그 응답을 처음 만드는 서버를 오리진 서버라고 부릅니다. 앞에서 원본 서버라고 부른 그것입니다.
중간에서 응답을 들고 있다가 다음 요청에 내주는 것이 공유 캐시입니다. 두 사람이 같은 것을 차례로 요청하면 오리진 서버까지 가는 것은 앞사람의 요청뿐입니다.
sequenceDiagram
participant U1 as 사용자 가
participant SC as 공유 캐시
participant OS as 오리진 서버
participant U2 as 사용자 나
U1->>SC: 공지 화면을 달라
SC->>OS: 들고 있는 것이 없다 · 받아 온다
OS-->>SC: 응답
SC-->>U1: 응답 · 다음 사람 몫으로 담아 둔다
U2->>SC: 같은 공지 화면을 달라
SC-->>U2: 담아 둔 응답
Note over SC,U2: 여기서 끝난다 · 오리진 서버까지 안 간다
한 번 다녀온 뒤로는 같은 것을 찾는 사람이 몇이든 공유 캐시 앞에서 끝납니다.
사설 캐시와 나누는 기준
캐시를 한 사람만 쓰면 사설 캐시입니다. 브라우저가 자기 디스크에 담아 두는 브라우저 캐시가 그렇습니다. 담긴 것을 그 사람만 꺼내 가므로 무엇을 담든 남에게 안 샙니다.
여럿이 나눠 쓰면 공유 캐시입니다. 회사 바깥으로 나가는 길목의 프록시, 서버 앞에 선 리버스 프록시, 세계 곳곳에 놓인 CDN(Content Delivery Network, 콘텐츠 전송 네트워크)이 여기에 해당합니다.
아래 그림은 두 캐시가 요청 경로의 어디에 서는지 보인 것입니다.
flowchart TD
A["사용자 가"] --> A1["사설 캐시 · 가의 브라우저"]
B["사용자 나"] --> B1["사설 캐시 · 나의 브라우저"]
A1 --> S["공유 캐시 · 프록시나 CDN"]
B1 --> S
S --> O["오리진 서버"]
사설 캐시는 사람마다 하나씩이라 그림에 둘이 있습니다. 공유 캐시는 둘이 함께 지나므로 하나입니다. 이 차이가 이득과 위험을 한꺼번에 만듭니다. 사설 캐시와 공유 캐시는 네 축에서 달라집니다.
| 사설 캐시 | 공유 캐시 | |
|---|---|---|
| 누가 쓰나 | 한 사람 | 여러 사람 |
| 어디에 있나 | 그 사람의 브라우저·기기 | 프록시 · 리버스 프록시 · CDN |
| 남의 것이 샐 위험 | 없다 | 있다 |
| 오리진 서버가 받는 요청 | 사람 수만큼 남는다 | 한 번으로 줄어든다 |
여럿이 나눠 쓰기에 못 담는 응답
공유 캐시에 담긴 응답은 다음 사람에게 나갑니다. 그래서 사람마다 달라야 하는 응답은 담으면 안 됩니다. 장바구니 화면을 담아 두면 다음 손님이 앞사람의 장바구니를 보게 됩니다.
무엇을 담아도 되는지는 오리진 서버가 응답에 실어 알려 줍니다. 그 지시를 싣는 응답 헤더가 Cache-Control입니다. 헤더는 본문과 따로 붙어 오는 이름과 값의 쌍입니다. 캐시는 본문을 읽지 않고 이 헤더만 보고 판단합니다.
이 헤더의 값에 적는 낱말 하나하나를 지시어라고 합니다. Cache-Control: private 이라고 적혀
오면 그 private 이 지시어입니다. 쉼표로 여러 개를 나란히 적을 수도 있습니다.
private 이 붙은 응답은 공유 캐시가 담지 않습니다. 이름 그대로 한 사람 것이라는 뜻입니다.
사설 캐시는 이 응답을 담아도 됩니다.
로그인이 필요한 요청도 같은 뿌리에서 막힙니다. 자격 증명을 실어 보내는 Authorization 요청
헤더가 붙은 요청의 응답은 공유 캐시가 담지 않는 것이 기본입니다. 응답이 따로 허락할 때만
담습니다. 이 규칙이 없으면 한 사람의 로그인 뒤 화면이 다음 사람에게 나갑니다.
public 은 그 허락을 적는 지시어입니다. 방금 본 Authorization 붙은 요청의 응답처럼
원래대로면 못 담을 응답까지 담아도 된다고 알립니다.
담아도 되는지를 공유 캐시가 어떤 순서로 따지는지 아래 그림이 보입니다.
flowchart TD
A["요청에 Authorization 이 붙었나"] -->|예| B["응답이 담아도 된다고 따로 허락했나"]
A -->|아니오| C["응답에 private 이 붙었나"]
B -->|예| D["담는다"]
B -->|아니오| E["안 담는다"]
C -->|예| E
C -->|아니오| D
Authorization 이 붙은 요청만 허락을 따로 받습니다. 나머지는 private 이 붙었느냐만
봅니다.
응답을 어느 요청에 내줄지 정하는 기준
공유 캐시는 담아 둔 응답을 어떤 요청에 되돌려 줄지 정해야 합니다. 그 기준을 캐시 키라고 합니다. 대개 요청의 메서드와 대상 주소로 만듭니다.
주소가 같아도 응답이 달라지는 경우가 있습니다. 같은 주소인데 요청한 언어에 따라 다른 번역을 내주는 화면이 그렇습니다. 이때 오리진 서버는 Vary 응답 헤더로 어느 요청 헤더가 응답을 나누는지 알려 줍니다. 공유 캐시는 그 헤더까지 캐시 키에 넣습니다.
아래 그림은 Vary 가 있을 때와 없을 때 캐시 키가 무엇으로 이뤄지는지 보인 것입니다.
flowchart TD
subgraph V0["Vary 없음"]
A["메서드 GET"]
B["주소 /page"]
end
subgraph V1["Vary: Accept-Language"]
C["메서드 GET"]
D["주소 /page"]
E["Accept-Language: ko"]
end
캐시 키가 같으면 같은 응답이 나갑니다.
Vary 를 빠뜨리면 한국어 화면을 받아 간 사람 뒤에 영어를 요청한 사람이 한국어 화면을 받습니다. 사설 캐시라면 자기 화면이라 안 드러나지만, 공유 캐시에서는 다른 사람에게 드러납니다.
공유 캐시에만 걸리는 수명
담아 둔 응답을 얼마 동안 오리진 서버에 다시 묻지 않고 써도 되는지를 신선도라고 합니다. 그 길이도 응답에 실려 옵니다.
max-age 지시어는 모든 캐시에 걸립니다. s-maxage 는 공유 캐시에만 걸립니다.
둘이 함께 오면 공유 캐시는 s-maxage 를 보고 max-age 를 무시합니다.
Cache-Control: public, max-age=60, s-maxage=600 이라고 적힌 응답이라면 브라우저는 60초,
공유 캐시는 600초를 씁니다. 브라우저에는 짧게, 중간의 CDN 에는 길게 들고 있게 하는 식으로
나눌 수 있습니다.
자주 쓰는 지시어는 걸리는 상대가 다릅니다.
| 지시어 | 걸리는 상대 | 무엇을 정하나 |
|---|---|---|
max-age |
모든 캐시 | 신선하게 쓸 수 있는 길이 |
s-maxage |
공유 캐시만 | 신선하게 쓸 수 있는 길이. 공유 캐시는 max-age 대신 이 값을 쓴다 |
private |
공유 캐시만 | 담지 마라 |
public |
공유 캐시만 | Authorization 이 붙은 요청의 응답처럼 원래대로면 못 담을 응답도 담아도 된다 |
여러 사람에게 함께 가는 낡은 응답
공유 캐시가 들고 있는 응답이 낡으면 그 낡음이 한 사람이 아니라 그 캐시를 지나는 모두에게 갑니다. 오리진 서버에서 글을 고쳐도 담긴 응답이 수명을 다 채울 때까지 옛 글이 나갑니다.
그래서 공유 캐시를 쓰는 서비스는 고친 즉시 담긴 응답을 버리게 하는 무효화 수단을 함께
둡니다. 파일 이름에 판 구분을 섞어 주소 자체를 바꿔 버리는 방법도 흔히 씁니다. app.js 를
고칠 때마다 app.3f9c1.js 처럼 다른 이름으로 내보내는 식입니다. 주소가 달라지면 캐시 키도
달라져서 낡은 응답이 안 잡힙니다.
같은 이름을 쓰는 다른 계층의 캐시
여럿이 나눠 쓰는 캐시라는 뜻으로 이 말을 다른 계층에서도 씁니다. 프로세서 안에서는 코어들이 함께 쓰는 바깥쪽 층을 공유 캐시라고 부릅니다. 여기서 프로세서는 CPU(Central Processing Unit, 중앙처리장치)를 말합니다. 그 안에 든 캐시가 CPU 캐시입니다.
애플리케이션에서도 같은 말을 씁니다. 서버 여러 대가 같은 캐시 서버 하나를 바라보면 그 캐시 서버가 공유 캐시입니다(Redis·memcached).
셋을 꿰는 성질은 같습니다. 복사본 하나를 여럿이 나눠 쓰므로 절약이 큽니다. 그만큼 누가 무엇을 담았는지가 남에게 영향을 줍니다. 다만 웹 이야기에서는 대개 HTTP 캐시 쪽을 뜻합니다.
절약이 큰 응답과 못 쓰는 응답
절약이 큰 것은 누가 요청해도 같은 것이 나가는 응답입니다. 이미지, 스타일 파일, 공개된 글이 그렇습니다. 같은 응답을 찾는 사람이 많을수록 절약도 커집니다.
사람마다 달라야 하는 응답에는 쓸 것이 없습니다. 장바구니나 알림함은 담아도 다음 사람이 못 씁니다. 잘못 담으면 남의 것을 보여 줍니다. 이런 응답은 개인용이라고 표시해 사설 캐시에만 남게 합니다.
관련 항목
공유 캐시가 서는 중간 지점
프록시 · 리버스 프록시 · CDN · 로드 밸런서 · API 게이트웨이 · 오리진 서버
공유 캐시와 맞세워지는 반대편 캐시
사설 캐시 · 브라우저 캐시 · 로컬 캐시 · 디스크 캐시
공유 캐시가 속하는 상위 분류
공유 캐시의 저장 여부와 수명을 정하는 헤더·지시어
Cache-Control · s-maxage · max-age · Expires · Age · no-store
공유 캐시가 요청과 응답을 맞춰 보는 기준
공유 캐시가 담은 응답을 언제까지 쓸지 정하는 개념
신선도 · 재검증 · 조건부 요청 · ETag · Last-Modified · 낡은 응답
공유 캐시에 담긴 응답을 지우거나 밀어내는 수단
공유 캐시에서 터지는 문제
캐시 스탬피드 · 캐시 미스 · 캐시 포이즈닝 · 캐시 오염 · 낡은 데이터
같은 이름을 쓰는 다른 계층의 캐시
CPU 캐시 · 분산 캐시 · Redis · memcached · 페이지 캐시
공유 캐시를 정의하는 표준·문서
다른 이름: shared cache · 공용 캐시