- 목차
- 공부 배경
- 이 글을 읽고 답할 수 있는 질문
- CDN이란?
- 캐시 수명 주기는 누가 정할까?
- 캐시 레이어와 304 Not Modified의 정체
- Cache-Control 지시어(디렉티브)
- CDN 캐시에서 헷갈리는 점 정리
- 실습
- 결론
- 참고자료
공부 배경
CDN 설정을 다룰 일이 많이 없었는데, 최근에 CDN 설정에 대해 고민할 것이 있었습니다. 이 기회를 통해 CDN 설정에 대해 공부하게 되었고, 이 글에서는 Cache-Control에 대해 공부한 것을 정리합니다. 메인 주제는 Cache-Control입니다.
이 글을 읽고 답할 수 있는 질문
- Cache-Control은 무엇이고 어떤 곳에 영향을 받고, Cache-Control 설정은 누가 정할까요?
- HTTP응답 중 304 Not Modified는 언제, 왜 발생할까요?
- HTTP request헤더의 If-None-Match의 의미를 이해합니다.
CDN이란?
CDN(Content Delivery Network)은 전 세계에 분산된 엣지 서버들을 통해 이미지, 영상, HTML 등을 제공하는 서비스입니다. 사용자에게 지리적으로 가까운 서버가 빠르게 응답을 줍니다. 사용자는 DNS 서버 응답을 통해 지리적으로 가까운 엣지 서버를 알 수 있습니다.
엣지 서버는 사용자가 요청한 콘텐츠가 캐시 목록에 있으면 가지고 있는 캐시를 그대로 사용자에게 응답합니다. 캐시가 없다면 오리진 서버에서 콘텐츠를 가져와 캐시를 저장하고 사용자에게 응답을 줍니다.

캐시 수명 주기는 누가 정할까?
캐시의 수명 주기는 max-age라는 HTTP 필드가 정합니다. 그렇다면 max-age 필드는 누가 결정할까요?
바로 오리진 서버가 max-age를 설정합니다. 오리진 서버는 캐시 주기를 HTTP 응답에서 Cache-Control 필드에 담아서 전달합니다.

엣지 서버는 오리진 서버가 전달한 Cache-Control 헤더를 사용하지 않고, 엣지 서버 본인만의 Cache-Control을 사용할 수 있습니다. 엣지 서버가 오리진 서버의 Cache-Control 헤더를 사용하면 존중한다는 의미로 respect라는 단어를 사용합니다.
아래 그림은 AWS CloudFront가 AWS S3 설정의 Cache-Control을 존중한다는 예제입니다.

정리하면, 캐시 수명 주기는 오리진 서버가 정하지만 엣지 서버의 설정에 따라 바뀔 수 있습니다. 이 부분이 실무에서 중요합니다.
이상적인 CDN 또는 캐시 서버는 중간에 한 개만 있으면 좋지만, 캐시 서버가 여러 대 있을 수 있습니다. 의도한 대로 캐시가 동작하지 않으면 중간에 캐시 서버가 Cache-Control을 변경하지 않았나 의심해야 합니다.

캐시 레이어와 304 Not Modified의 정체
지금까지 캐시 수명 주기를 설명할 때 CDN 또는 엣지 서버만 이야기했습니다. 실제 CDN 캐시를 말할 때는 웹 브라우저 캐시도 같이 생각해야 합니다. 웹 브라우저에서 오리진 서버까지 도달하는데 마주치는 캐시들을 캐시 레이어라고 합니다.

웹 브라우저는 사용자 PC의 메모리 또는 디스크를 사용해서 캐시를 합니다. 처음 요청 후 받은 max-age 동안 웹 브라우저는 엣지 서버에 요청을 전송하지 않고 웹 브라우저 내부에서 요청을 처리합니다. max-age가 끝나면 엣지 서버에게 요청합니다.
이 때 HTTP request 헤더에 ETag를 같이 포함해서 전송합니다. ETag는 내가 요청한 콘텐츠의 해시값이라고 생각하면 됩니다.
엣지 서버는 받은 ETag를 보고 요청의 콘텐츠와 엣지 서버의 캐시 데이터가 일치하는지 비교합니다. 만약 같다면 엣지 서버는 콘텐츠를 주지 않고 304 Not Modified 응답을 줍니다. 반대로 ETag와 엣지 서버 캐시 데이터가 다르다면 200 응답을 주고 웹 브라우저가 요청한 콘텐츠를 줍니다. 이 과정을 조건부 요청이라고 합니다.
조건부 요청을 할 때 ETag는 HTTP 헤더의 If-None-Match 필드에 있습니다.

Cache-Control 지시어(디렉티브)
Cache-Control은 캐시 수명 주기 말고도 여러 지시어(디렉티브)가 있습니다.
| max-age=N | O | O | 모든 캐시에서 N초간 유효 |
| s-maxage=N | X | O | shared cache(공유 캐시, CDN/프록시)에만 적용 |
| public | O | O | 모든 캐시가 저장 가능 |
| private | O | X | 브라우저만 저장 가능, CDN은 저장 불가 |
| no-cache | O | O | 저장하되 사용 전 반드시 재검증 |
| no-store | O | O | 어디에도 저장 금지 |
no-cache vs no-store
많이 사용하는 지시어 중 하나는 no-cache, no-store입니다.
store”는 브라우저 캐시만 의미하는 게 아니라 모든 캐시 레이어에 적용됩니다. 어디에도 저장하지 말라는 뜻입니다. no-cache는 저장해둔 캐시가 있으므로 ETag(리소스의 고유 식별자) 기반 재검증(304 응답)이 가능합니다. no-store는 저장된 게 없으므로 매번 전체 응답을 받아야 합니다.
- no-cache: 캐시를 저장은 하지만, 사용할 때마다 오리진 서버에 “이거 아직 유효해?”라고 물어봅니다
- no-store: 캐시를 아예 저장하지 않습니다. 매번 오리진 서버에서 새로 받아옵니다
public vs private
다음에 많이 사용하는 Cache-Control 지시어는 public, private입니다. 사용자별 응답(프로필, 장바구니 등)에는 private을 사용해야 합니다. public으로 설정하면 CDN이 사용자 A의 응답을 캐시해서 사용자 B에게 전달하는 보안사고가 발생합니다.
- public: 모든 캐시(브라우저, CDN, 프록시)가 저장 가능
- private: 브라우저만 저장 가능. CDN이나 프록시 같은 shared cache는 저장하면 안 됨
CDN 캐시에서 헷갈리는 점 정리
- CDN Invalidation은 CDN 캐시만 삭제합니다. 브라우저 캐시는 별개의 장소에 있으므로 영향을 받지 않습니다
- no-cache는 “캐시하지 마”가 아닙니다. 캐시는 저장하되 매번 재검증하라는 뜻입니다. 캐시를 아예 하지 말라는 것은 no-store입니다
- 웹 브라우저가 아닌 애플리케이션도 CDN을 사용합니다. 모바일 앱, 서버 간 통신도 CDN을 경유해 요청할 수 있습니다. 차이점은 브라우저 캐시 레이어가 없다는 것입니다. HTTP 클라이언트(requests, axios 등)는 대부분 기본적으로 캐시하지 않으므로, CDN 캐시만 동작합니다
실습
실습자료는 저의 GitHub에 있습니다:
결론
Cache-Control은 “오리진 서버 → CDN” 정책이 아니라, “오리진 서버 → 모든 캐시 레이어(CDN + 브라우저)” 정책입니다. max-age는 브라우저 캐시에도 적용되고, s-maxage로 CDN만 별도 제어할 수 있습니다. 캐시 정책을 설정할 때는 CDN뿐 아니라 브라우저 캐시까지 고려해야 합니다.
참고자료
'전공영역 공부 기록' 카테고리의 다른 글
| KMS 봉투암호화 쉽게 이해하기 - data key와 root key의 관계(실습, AWS KMS) (0) | 2026.03.02 |
|---|---|
| 리눅스에서 tmp 디렉터리가 메모리 사용률을 올리는 이유 (0) | 2026.03.01 |
| Claude Code 첫 프로젝트 후기 - SLO 계산기와 배포방법 고민 (0) | 2026.02.22 |
| Claude Code 동작 원리 정리 - Harness, Context, Memory까지 (0) | 2026.02.18 |
| 단순한 온프레미스 리다이렉트 서버를, AWS로 옮기는 방법들은 뭐가 있을까?(ALB vs CloudFront) (0) | 2026.02.17 |