Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

59장. CloudFront + S3로 정적 콘텐츠 서비스하기

이 장에서 말하고자 하는 것

CloudFront와 S3를 묶어
정적 자원 서비스의 표준 구성 을 만든다.

  • SPA (React/Vue/Next) 빌드 결과
  • 이미지 · CSS · JS
  • 다운로드 파일
  • 공개 미디어

부품은 38·39·56장에서 다 봤다.


1. 표준 구성도

[사용자]
   ↓ HTTPS, DNS (Route 53 ALIAS)
[CloudFront] (us-east-1 ACM, WAF)
   ↓ OAC
[S3 버킷] (비공개)
  • 사용자 응답 빠름 (엣지 캐시)
  • S3 노출 없음 (OAC)
  • HTTPS 자동 (CloudFront + ACM)

2. SPA 호스팅

SPA의 특징:

  • / 만 진짜 파일 — index.html
  • /login, /orders/123 같은 경로도 같은 index.html 을 받아야 함 (브라우저 라우터)

CloudFront에서 한 줄로 해결한다.

오류 응답 매핑:
  S3에서 404 NoSuchKey → CloudFront가 / 의 index.html 200 응답

3. 캐시 무효화 없이 새 버전 배포

invalidation에 의존하지 않는다. 대신 파일명 해시 패턴.

빌드 결과:
  index.html
  static/app.a3f2c1.js
  static/style.b91e8.css
  • index.html 은 캐시 짧게 (1분)
  • static/*.js, *.css 는 캐시 길게 (1년) — 새 빌드는 새 파일명

4. 도메인 한 개에 정적 + API

example.com
 ├─ /api/*    → API Gateway → ALB → ECS
 └─ /*        → S3 (SPA)

CloudFront 한 배포 안에서 두 origin을 가진다.

  • CORS 문제 없음
  • 사용자가 보기에 한 도메인

5. 우리 서비스에서

[Route 53] example.com → CloudFront
[CloudFront]
 ├─ default → S3 "static"
 │           - Cache 1년
 │           - 404 → / 의 index.html (SPA)
 ├─ /api/*  → API Gateway → ALB → ECS
 └─ /img/*  → S3 "img"
             - Cache 7일

6. 배포 흐름 — CI/CD에서

# 1. 빌드
npm run build

# 2. 정적 자원 (긴 캐시)
aws s3 sync ./dist s3://msa-static/ \
  --cache-control "public,max-age=31536000,immutable" \
  --exclude index.html

# 3. index.html (짧은 캐시)
aws s3 cp ./dist/index.html s3://msa-static/index.html \
  --cache-control "public,max-age=60"

# 4. (필요 시) invalidation
aws cloudfront create-invalidation \
  --distribution-id E123ABCD \
  --paths "/index.html"

invalidation은 index.html 한 줄로 끝. 정적 자원은 파일명이 바뀌어 자동 새 캐시.


7. 직접 확인해보기 — CLI

curl -I https://example.com/static/app.a3f2c1.js
# Cache-Control: public,max-age=31536000,immutable
# x-cache: Hit from cloudfront

curl -I https://example.com/
# Cache-Control: public,max-age=60

8. 코드로는 이렇게 생겼다 — Terraform (요약)

resource "aws_s3_bucket" "static" {
  bucket = "msa-prod-static"
}

resource "aws_cloudfront_origin_access_control" "s3" {
  name                              = "s3-oac"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

resource "aws_cloudfront_distribution" "main" {
  enabled = true
  aliases = ["example.com"]

  origin {
    domain_name              = aws_s3_bucket.static.bucket_regional_domain_name
    origin_id                = "s3"
    origin_access_control_id = aws_cloudfront_origin_access_control.s3.id
  }

  default_cache_behavior {
    target_origin_id       = "s3"
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    cache_policy_id        = "658327ea-f89d-4fab-a63d-7e88639e58f6"  # CachingOptimized
  }

  custom_error_response {
    error_code         = 404
    response_code      = 200
    response_page_path = "/index.html"
  }

  custom_error_response {
    error_code         = 403
    response_code      = 200
    response_page_path = "/index.html"
  }

  viewer_certificate {
    acm_certificate_arn = aws_acm_certificate.us_east.arn
    ssl_support_method  = "sni-only"
  }

  restrictions {
    geo_restriction { restriction_type = "none" }
  }
}

이게 정적 SPA 호스팅의 최소 운영 구성이다.


9. 이렇게 쓰면 망한다 — 안티패턴

안티패턴 1. S3 정적 호스팅 엔드포인트를 그대로 쓴다

HTTPS · DDoS · 캐시 다 약하다.

항상 CloudFront 앞에

안티패턴 2. SPA 404 매핑을 안 한다

/orders/123 으로 직접 들어오면 흰 화면.

안티패턴 3. 모든 파일을 같은 캐시로 둔다

index.html 도 1년 캐시 → 새 배포가 안 보임.

index.html은 짧게, 해시 파일은 길게

안티패턴 4. 새 빌드마다 /* 전체 invalidation

비용 + 지연. 파일명 해시로 대체.


10. 한 줄로 정리

CloudFront + S3 (OAC) + 파일명 해시 + SPA 404 매핑이
정적 자원 서비스의 사실상 표준이다


11. 이 장의 핵심 정리

  1. CloudFront + S3 (OAC) 가 정적 콘텐츠의 표준 모양이다.
  2. SPA는 404 → index.html 200 매핑이 필요하다.
  3. 파일명 해시 + 차등 캐시로 invalidation 부담을 없앤다.
  4. 정적 + API를 한 도메인에 두면 CORS 문제도 사라진다.
  5. S3 정적 웹사이트 엔드포인트는 사실상 쓸 일이 없다.