AWS 중급 #7 CloudFront로 정적 사이트 배포
#3 S3의 정적 호스팅 방식에는 한계가 있습니다. HTTPS가 안 되고, 사용자 가까이에서 캐시도 못 하고, 커스텀 도메인 + SSL도 바로 붙지 않습니다. 이 세 가지를 한 번에 푸는 서비스가 CloudFront입니다.
CloudFront는 AWS의 **CDN (Content Delivery Network)**입니다. 전 세계 600+ 곳의 Edge Location에 캐시를 두고, 사용자에게 가장 가까운 Edge가 응답합니다. 정적 사이트뿐 아니라 동적 API도 통과시킬 수 있습니다.
이 글에선 CloudFront의 구조 → S3 + CloudFront 패턴 → OAC → 캐시 정책 → 무효화까지 한 줄로 정리합니다.
CDN이 푸는 문제 #
S3 정적 사이트를 그대로 쓰면 다음이 좋지 않습니다.
| 문제 | CloudFront가 푸는 기능 |
|---|---|
| 미국 사용자가 서울 S3에서 받으면 200ms+ | Edge 캐시로 ms 응답 |
| HTTPS 직접 안 됨 | ACM 인증서 + 자동 HTTPS |
| Egress 비용이 비싸 ($0.09/GB) | CloudFront → 인터넷 ($0.085/GB) + 캐시 hit는 무료 |
| 캐시 헤더 / 압축 / HTTP/2 / HTTP/3 직접 | 자동 |
| WAF / Shield 직접 | CloudFront와 통합 |
| DDoS | AWS Shield Standard 자동 |
S3 + CloudFront는 모든 정적 사이트의 표준 패턴입니다.
CloudFront의 구조 #
사용자
│
│ HTTPS request: example.com/path/to/asset.js
▼
┌────────────────────────────────────┐
│ CloudFront Edge Location │
│ (사용자에 가장 가까운 지점) │
│ │
│ Cache 확인 │
│ ├── HIT → 캐시에서 응답 │
│ └── MISS │
└────────────────────────────────────┘
│ MISS 시
▼
┌────────────────────┐
│ Cache Behavior │
│ (path 매칭 / 정책) │
└────────┬───────────┘
▼
┌────────┐
│ Origin │ ← S3, ALB, EC2, Lambda Function URL ...
└────────┘핵심 구성 요소:
- Distribution. 한 묶음의 설정 (도메인 + 인증서 + 동작)
- Origin. 캐시 미스 시 받을 곳 (S3, ALB, 사용자 도메인 등)
- Cache Behavior. path 매칭 + 캐시 정책 + Origin 매칭
Distribution 만들기 #
aws cloudfront create-distribution \
--origin-domain-name my-bucket.s3.ap-northeast-2.amazonaws.com \
--default-root-object index.html설정 항목:
| 옵션 | 의미 |
|---|---|
| Origin domain | S3 버킷 또는 ALB DNS 또는 직접 도메인 |
| Default root object | / 요청 시 보낼 파일 (index.html) |
| Alternate domain (CNAME) | example.com, www.example.com |
| SSL certificate | ACM (반드시 us-east-1) |
| Price class | 어느 대륙 Edge까지 쓸지 |
| Logging | S3에 access log 저장 |
| WAF | Web ACL 연결 |
Price Class #
| Price Class | 의미 |
|---|---|
| All | 전 세계 모든 Edge. 비용 가장 높음 |
| 200 | 미국 / 유럽 / 아시아 / 호주 |
| 100 | 미국 / 유럽 / 이스라엘 / 일부 아시아. 가장 저렴 |
한국 / 일본 사용자만이면 200이상이 권장. 글로벌 서비스는 All.
Origin: 어디서 받을까 #
S3 Origin #
가장 흔한 용도입니다. 정적 사이트 / 이미지 / 비디오.
Origin: my-bucket.s3.ap-northeast-2.amazonaws.com
(REST API endpoint, NOT the s3-website-... URL)
s3-website-*URL이 아니라 REST API URL을 사용. 그래야 OAC (아래)로 안전하게 보호 가능.
Custom Origin (ALB / 임의 HTTP 서버) #
ALB / EC2 / 외부 서버 도 Origin가능. 이 패턴은 동적 API가속 용도.
Origin: my-alb-1234567890.elb.ap-northeast-2.amazonaws.com
HTTPS Port: 443
SSL Protocols: TLSv1.2, TLSv1.3
Origin Path: (없음 또는 /api)ALB → CloudFront → 사용자 패턴은 WAF + Edge SSL termination + 캐시를 한 번에 얻습니다.
Lambda Function URL Origin #
Lambda가 직접 Origin이 되는 방식입니다. 서버리스 정적 + 동적 사이트에 씁니다.
Origin Group: 페일오버 #
Primary Origin이 죽으면 Secondary로. 멀티 리전 DR 구성.
Cache Behavior: 라우팅 + 캐시 정책 #
같은 Distribution 안에서 path마다 다른 동작을 줄 수 있습니다.
Path Pattern | Origin | Cache Policy | TTL
---------------+--------+--------------+--------
/api/* | ALB | NoCaching | 0
/static/* | S3 | Optimized | 1 day
default (*) | S3 | Optimized | 1 hour처리 순서: path pattern 매칭 (구체적인 게 먼저) → 매칭 없으면 default.
Cache Policy #
캐시 동작을 정의합니다. AWS 제공 기본 정책 + 커스텀이 있습니다.
CachingOptimized ← 정적 자산 (이미지 / JS / CSS)
CachingOptimizedForUncompressedObjects ← 압축 안 된 자산
CachingDisabled ← API 캐시 안 함
Elemental-MediaPackage ← 미디어 스트리밍캐시 키 구성 (= 무엇이 다르면 별도 캐시 항목):
- Query string. 모두 / 일부 / 없음
- Header. 어떤 헤더로 구분할지
- Cookie. 어떤 쿠키로 구분할지
Query: 무시
Header: 무시 (또는 Accept, Accept-Encoding)
Cookie: 무시Query: 모두
Header: Accept-Language, CloudFront-Is-Mobile-Viewer
Cookie: session-idOrigin Request Policy #
Origin으로 무엇을 보낼지 정합니다. 캐시 키와는 다른 개념입니다.
| Cache Policy | Origin Request Policy | |
|---|---|---|
| 무엇을 결정 | 캐시 키 + TTL | Origin으로 보낼 헤더 / 쿼리 / 쿠키 |
| 효과 | 캐시 히트율 | 동적 응답에 무엇이 영향을 주는가 |
Response Headers Policy #
응답 헤더를 자동으로 추가 / 수정합니다. Strict-Transport-Security, X-Content-Type-Options 같은 보안 헤더에 씁니다.
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; ...TTL: 얼마나 캐시할까 #
캐시 TTL의 기준:
| TTL | 용도 |
|---|---|
| 0 | 캐시 안 함. 모든 요청이 Origin에 |
| 60 ~ 300초 | 자주 바뀌는 콘텐츠 (뉴스 헤드라인) |
| 1시간 ~ 1일 | 일반 정적 자산 |
1년 (max-age=31536000) | hashed filename (app.abc123.js) |
Cache-Control 헤더 #
Origin 응답의 Cache-Control 헤더가 우선 (Min/Default/Max TTL 안에 들어가면).
Cache-Control: public, max-age=31536000, immutable ← hashed asset (1년)
Cache-Control: public, max-age=300 ← HTML 페이지 (5분)
Cache-Control: no-store ← 캐시 절대 안 함운영 기준:
- HTML = 짧게 (60s ~ 5min). 새 빌드를 빠르게 보이게
- JS / CSS / 이미지 with hash = 1년 + immutable. 콘텐츠가 바뀌면 새 파일명
- API = 보통 캐시 안 함 (또는 short)
S3 + CloudFront 패턴 #
정적 사이트의 표준 셋업.
사용자
│
▼
example.com (Route 53 Alias) ──▶ CloudFront
│
▼ (OAC)
S3 my-bucket ← Public Access Block 모두 켜짐
│
▼ Bucket Policy
CloudFront만 GetObject 허용OAC: Origin Access Control #
옛 방식은 **OAI (Origin Access Identity)**입니다. 새 권장 방식은 OAC입니다.
- S3의 PAB 모두 켬 (public 절대 안 됨)
- Bucket Policy가 CloudFront의 OAC만 허용:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "cloudfront.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EXXXXXX"
}
}
}]
}이러면:
- S3 직접 접근은 모두 차단.
s3.amazonaws.com/my-bucket/x로 들어오면 403 - CloudFront만 통과. Edge 캐시를 거쳐야만 응답
- Egress 비용 절감 + 캐시 hit가속 + WAF 적용
OAI vs OAC #
| OAI (옛 방식) | OAC (새 방식) | |
|---|---|---|
| 인증 방식 | CloudFront의 가상 IAM Identity | SigV4 서명 |
| 새 리전 / 기능 | 부분 지원 | 모두 |
| KMS-encrypted S3 | 추가 설정 필요 | 매끄러움 |
| 권장 | 마이그레이션 검토 | 새 프로젝트 기본 |
새 셋업은 OAC를 쓰고, 옛 방식은 OAI 그대로 쓰다 마이그레이션합니다.
무효화 (Invalidation) #
캐시된 객체를 만료 전에 강제로 비우는 동작. 배포 직후 새 버전을 즉시 보이게.
aws cloudfront create-invalidation \
--distribution-id EXXXXXX \
--paths "/*"구성:
- 정적 사이트 배포 후
/*한 줄 - 특정 페이지만 갱신:
/index.html /about.html - 와일드카드:
/blog/2026/*
비용 함정 #
CloudFront의 invalidation은 첫 1000 path / 월 무료, 그 후 path당 $0.005. /* 한 번 = 1 path 라 자주 호출 OK.
Cache Versioning 패턴: 무효화 안 쓰기 #
진짜 운영에서는 무효화를 안 씁니다. 대신:
빌드 결과:
/static/app.abc123.js ← 콘텐츠 해시
/static/app.def456.js ← 다음 빌드는 다른 해시
HTML은 짧게 캐시 (1분), JS/CSS는 1년:
Cache-Control: public, max-age=31536000, immutable
새 배포 시:
/index.html이 새 해시의 JS를 가리킴
HTML 캐시 만료 (1분) → 새 JS가 자동 로드이 패턴이면 invalidation 없이 즉시 반영 + 캐시 효율 최대.
Lambda@Edge / CloudFront Functions #
Edge에서 요청 / 응답을 가로채 코드를 실행하는 기능입니다.
CloudFront Functions #
- JavaScript ES5만
- 요청 / 응답 viewer 단계만
- 5ms 이내, 1MB 이내 메모리
- 매우 저렴 ($0.10 / 백만)
- 용도: redirect, header 추가, A/B 테스트, 인증 토큰 검사
function handler(event) {
var request = event.request;
if (request.uri === '/old-page') {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: { 'location': { value: '/new-page' } }
};
}
return request;
}Lambda@Edge #
- Node.js / Python
- 4 단계 (viewer request / origin request / origin response / viewer response)
- 5초 (viewer) / 30초 (origin)
- 비싸고 느리지만 강력
- 용도: 동적 컨텐츠 변환, 멀티 리전 라우팅, 복잡 인증
요즘은 가능한 한 CloudFront Functions로 시작하고, 부족할 때만 Lambda@Edge.
Signed URL / Signed Cookie: 비공개 콘텐츠 #
유료 비디오 / 전용 다운로드 같은 데 씁니다. CloudFront의 서명으로 시간 / IP를 제한합니다.
https://d111111abcdef8.cloudfront.net/video.mp4?
Expires=1714992000&
Signature=...
Key-Pair-Id=APKAEX...- Signed URL. 한 자원에 대한 한 URL
- Signed Cookie. 한 사용자가 여러 자원 접근 (사이트 단위 정기 구독)
S3의 presigned URL과는 다릅니다. CloudFront Edge에서 검증하고, 더 글로벌하며 캐시도 가능합니다.
CloudFront와 ACM #
#6에서 다룬 ACM의 리전 규칙:
ACM (us-east-1)
├── *.example.com ← CloudFront가 사용
└── example.com
ACM (ap-northeast-2)
├── api.example.com ← ALB가 사용콘솔에서 인증서 받을 때 **반드시 N. Virginia (us-east-1)**로 리전 전환.
압축과 HTTP/2 / HTTP/3 #
CloudFront가 자동으로:
- gzip / Brotli 압축. 옵션 켜기
- HTTP/2. 기본
- HTTP/3 (QUIC). 옵션
- TLS 1.3. Security Policy에서
이런 처리는 ALB 직접 노출보다 CloudFront가 더 매끄럽습니다.
자주 만나는 함정 #
1) S3가 여전히 Public #
OAC 셋업했는데 PAB를 안 켜서 S3가 직접 접근 가능. PAB 4개 모두 켜기 + Bucket Policy가 CloudFront만 허용.
2) CNAME mismatch #
ACM 인증서가 *.example.com인데 Distribution의 Alternate domain이 app.example.com (와일드카드 자식이라 OK). 잘못 적으면 SSL 인증서 mismatch 오류. 인증서 SAN과 도메인을 정확히 맞추기.
3) 캐시가 너무 강해 새 배포가 안 보임 #
max-age=31536000 짜리 HTML을 그대로 캐시 → 새 배포 후에도 사용자가 옛 HTML 받음. HTML은 짧게 / hashed asset만 길게.
4) 쿼리 캐시 키 잘못 #
쿼리 모두 캐시 키에 → 같은 파일도 ?v=1, ?v=2마다 별도 캐시 → 히트율 0%. 진짜 영향이 있는 쿼리만 캐시 키에.
5) Default root object 누락 #
/로 요청하면 403/404. Default root object = index.html 설정.
6) Origin으로 보내는 Host 헤더 #
S3 Origin의 경우 CloudFront가 자동으로 Host 헤더 처리. 하지만 ALB / 임의 서버는 Origin Request Policy에서 Host 헤더 전달 옵션를 명시.
7) Origin의 SSL 인증서 신뢰 안 됨 #
Custom Origin (자체 서버)이 self-signed → CloudFront가 거부. ACM 발급 인증서 또는 신뢰된 CA.
8) index.html redirect
#
S3 정적 사이트의 /about/ → /about/index.html 자동 변환이 OAC 구성에선 안 됨. CloudFront Function으로 path rewrite 직접:
function handler(event) {
var request = event.request;
var uri = request.uri;
if (uri.endsWith('/')) {
request.uri = uri + 'index.html';
} else if (!uri.includes('.')) {
request.uri = uri + '/index.html';
}
return request;
}정리 #
이번 글에서 잡은 것:
- CloudFront = 글로벌 CDN. Edge 600+ 곳에서 캐시 응답
- Distribution + Origin + Cache Behavior의 셋
- Origin = S3 / ALB / Custom HTTP / Lambda URL / Origin Group
- S3 + CloudFront + OAC가 정적 사이트 표준 패턴
- OAC가 새 권장. PAB 4개 모두 켜고 Bucket Policy로 CloudFront만 허용
- Cache Policy로 캐시 키 (쿼리/헤더/쿠키) + TTL. Origin Request Policy는 Origin으로 보낼 대상
- TTL 전략. HTML 짧게, hashed asset 1년 + immutable
- 무효화는 가끔. Hashed filename 패턴이 무효화 안 쓰는 길
- CloudFront Functions = 빠르고 저렴. Lambda@Edge = 강력하지만 무거움
- Signed URL / Cookie로 비공개 콘텐츠
- ACM 인증서는 반드시
us-east-1 - 압축 / HTTP/2 / HTTP/3 자동
- 함정. S3 public, CNAME mismatch, 캐시 너무 강함, 쿼리 키 잘못, default root, Host 헤더, Origin SSL, path rewrite
다음: AWS 고급 시작 #
이로써 AWS 중급 7편이 마무리됐습니다. EC2 / VPC / S3 / RDS / Route 53 / ALB / CloudFront라는 기본 도구상자가 하나로 모였습니다.
이제 그 위에 컨테이너 / 서버리스 / 이벤트 기반 영역이 더해집니다. AWS 고급 #1 ECS와 Fargate에서는 EC2 위에 직접 앱을 띄우던 방식을 컨테이너로 옮기고, ASG의 컨테이너 버전인 ECS / Fargate의 운영 방식을 정리하겠습니다.