2026-05-19 학습 노트 — CloudFront, OAC, CDN

정적 파일의 관리

· 16 min read
study AWS CloudFront CDN 보안

정적 mp3 하나 호스팅하는데 왜 이렇게 길게 고민했나

ghworld 에 환경음 mp3 를 붙이는 단계에서, “이 파일 어디에 두지” 하나로 한참을 고민했다

처음에는 “그냥 S3 에 올리고 public 으로 풀면 끝 아닌가” 싶었다
근데 막상 결정을 하려고 하니 찝찝함이 남았다

S3 URL 을 그대로 프론트엔드에 박는 게 정말 괜찮은가?
누가 막 호출하면 비용은 어떻게 되나?
한국 밖에서 접속하면 느리지 않나?
CloudFront 를 끼면 뭐가 바뀌나? OAC 는 또 뭐고?

질문이 끝없이 나왔다
이번에는 그 질문에 답을 찾아가며 정리한 기록이다

···

출발점 — 운영 환경 환경음이 무음이었다

처음 발견한 건 사소한 증상이었다
로컬에서는 환경음이 잘 들리는데 운영 환경에서는 무음이었다

원인은 단순했다
mp3 파일이 .gitignore에 걸려 git 에서 추적되지 않았고, Docker 이미지에도 포함되지 않았던 것

해결을 위해서는 두 가지 방법이 있었다

  1. mp3 를 git 에 포함시켜 이미지 빌드에 포함시킨다
  2. 정적 파일 호스팅을 별도 인프라(S3 / CDN) 로 분리한다

정적 파일이 적다면 1 번도 나쁘지 않다
근데 정적 파일은 환경음뿐 아니라 캐릭터, 아이템 등의 에셋 파일도 필요해질 예정이다
”정적 파일은 정적 파일 인프라에 둔다” 라는 선을 지금 그어두는 게 맞다고 판단했다

그래서 자연스럽게 다음 질문으로 넘어갔다
”정적 파일을 어떻게 관리해야할까?“

1 차 후보 — S3 만 public 으로 노출

가장 빠른 방법이다
S3 버킷을 public read 로 풀고, 프론트엔드에서 https://***.amazonaws.com/sounds/village-night.mp3 같은 URL 을 직접 호출한다

장점은 명확하다

  • 설정이 단순하다
  • 추가 비용 없음

근데 단점이 있었다

단점 1. 한국 밖 사용자에게 느리다

S3 버킷은 하나의 리전에 존재한다 (ap-northeast-2 서울)
일본, 미국에서 접속하면 매번 서울까지 패킷이 다녀와야 한다
정적 파일이 로드하는데 오래 걸리면 사용자 사용성이 많이 나빠진다

단점 2. origin 부하 직격

매 요청이 S3 에 그대로 닿는다
유저 1 만 명이 마을에 들어와서 환경음을 동시에 받으면, S3 가 1 만 번 동일한 mp3 를 내보낸다
캐싱이 되지 않는 구조다

단점 3. 비용 폭주 위험 — 제일 심각한 문제이다

S3 egress 가 한국 리전 기준 대략 $0.126/GB
누가 악의적으로 mp3 URL 을 curl 루프로 박으면 (또는 그냥 봇이 긁어가면) 비용이 걷잡을 수 없이 커진다

“public 정적 파일이라 어차피 공개인데?”라고 넘기기엔 문제가 있었다
S3 는 “이 IP 가 좀 많이 가져가니까 잠깐 막을게” 같은 게 없다
오는 요청은 다 받고, 다 청구한다

2 차 후보 — CloudFront 를 앞단에 끼우기

여기서 CDN 이 왜 정적 파일 배포의 자연스러운 선택지처럼 여겨지는지 어느 정도 와닿았다

CDN — 정적 파일을 전 세계 edge 에 흩뿌린다

CloudFront 는 전 세계 edge location 에 정적 파일 사본을 둔다
사용자가 정적 파일을 요청하면, 가장 가까운 edge 가 응답한다

흐름은 이렇다

[ 사용자 ] → [ 가까운 edge ] ──(캐시 HIT)──> 200 응답 (origin 안 거침)

                          └─(캐시 MISS)──> [ S3 origin ] → edge 캐시에 저장 → 200 응답

첫 호출만 origin (S3) 까지 다녀오고, 이후로는 edge 에서 끝난다
캐시 TTL 을 24 시간으로 잡으면, 그 24 시간 동안 같은 edge 에서 발생하는 요청은 S3 를 거치지 않는다

결과적으로 origin 부하가 99.99% 가까이 줄어든다
edge 캐시 hit 비율이 높을수록 egress 비용이 낮아진다
CloudFront 응답 헤더에 X-Cache: Hit from cloudfront / Miss from cloudfront가 박힌다

캐시는 무조건 좋은가 — TTL 트레이드오프

CloudFront 의 CachingOptimized managed policy 를 쓰면 기본 TTL 이 하루다
mp3 같이 잘 안 바뀌는 정적 파일은 적절하다고 판단했다

근데 정적 파일명을 그대로 두고 내용만 갈아끼우면 24 시간 동안 edge 에는 옛날 mp3 가 남아있다
정적 파일을 수정할 때는 파일명을 바꾸거나 (village-night-v2.mp3), CloudFront invalidation 을 호출해야 한다

이걸 모르면 “왜 새 파일이 안 보이지?” 로 한참 헤맬 수 있다

3 차 — OAC 까지 더하면 뭐가 달라지나

여기까지만 봐도 CloudFront 의 가치는 충분해 보였는데
GPT 와 Gemini 한테 같이 검토를 시켜보니 Gemini 가 이런 말을 했다

S3 를 public 으로 두면 무단 데이터 접근 위험이 있습니다
OAC(Origin Access Control) 로 막아야 합니다

처음엔 “음 그렇겠지” 했는데, 다시 보니까 우리 컨텍스트에서는 좀 어긋난 말이었다
우리 정적 파일은 어차피 public 으로 모두에게 노출하려고 하는 파일이다
”무단 접근” 이라는 표현이 우리 케이스에는 안 맞는다

그럼 OAC 는 왜 끼우나?

OAC 가 실제로 하는 일

OAC 는 CloudFront 가 S3 origin 을 호출할 때 AWS SigV4 서명을 붙여준다
S3 Bucket Policy 는 “이 서명이 우리 CloudFront 에서 온 게 맞는지” 확인되면 통과시킨다

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "cloudfront.amazonaws.com" },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::***/*",
    "Condition": {
      "StringEquals": {
        "AWS:SourceArn": "arn:aws:cloudfront::***:distribution/E***"
      }
    }
  }]
}

결과적으로

  • https://***.cloudfront.net/sounds/village-night.mp3 ← 됨
  • https://***.amazonaws.com/sounds/village-night.mp3 ← 403

같은 mp3 가 같은 S3 에 있는데, 직접 S3 URL 로는 못 가져온다

그래서?

“URL 이 노출된다 = 접근할 수 있다” 가 아니다

S3 URL 이 어딘가에 새어나가도, 그 URL 로는 401/403 만 응답한다
누가 강제로 origin 부하를 일으키려고 해도 진입점이 CloudFront 하나로 좁혀진다

public_assets이라도 진입점을 좁혀두면 정책 적용이 쉽다

  • WAF 룰을 CloudFront 에만 붙이면 끝
  • 캐시 정책도 CloudFront 만 보면 끝
  • 비용 alert 도 CloudFront 메트릭 하나만 보면 끝

“우회로가 없다” 는 게 별 거 아닌 것 같아도 운영에 들어가면 차이가 크다

그래서 DDoS 도 막아주나?

여기서 한 가지 헷갈리기 쉬운 부분이 있다

CloudFront 는 DDoS 를 완전히 차단해주지 않는다
AWS Shield Standard 가 막는 건 Layer 3-4 공격이다
SYN flood, UDP reflection 같은 류

  • Layer 3-4 (네트워크 / 전송 계층) — Shield Standard 무료로 어느 정도 커버
  • Layer 7 (HTTP flood, 봇이 정상 형태로 무한 요청) — Shield Standard 로는 못 막는다
    • WAF rate-limit 룰을 별도로 붙이거나
    • Shield Advanced ($3,000/월~) 를 결합해야 한다

다만 Layer 7 공격이 와도 CloudFront 의 트래픽 분산 자체가 origin 보호에 효과가 있다
edge 가 1 차 흡수해주는 구조라 origin 까지 다 도달하지는 않는다

CloudFront 만으로는 “원천 차단” 까진 아니지만, 무방비 S3 보다는 압도적으로 낫다 정도이다

위협S3 만CloudFront + OAC
일반 트래픽 (정상 사용자)매번 S3 부하edge 캐시 흡수
Layer 3-4 DDoS (SYN flood 등)무방비Shield Standard 1 차 차단
Layer 7 DDoS (HTTP flood)무방비 + 비용 폭주edge 분산
비용 통제어렵다 (egress 직접)Budget alert + WAF rate-limit 으로 가드
URL 노출 시 직접 origin 접근가능차단 (403)

한국 사용자 latency 는 정말 빨라지나?

“CDN 을 중간에 끼우면 다 빨라진다” 라고 단순하게 외우고 있었는데, 실제로는 한 가지 의외의 포인트가 있다

케이스대략 latency
S3 직접 호출30~50ms
CloudFront 첫 호출 (cache MISS)40~60ms
CloudFront 2 번째 이후 (cache HIT)5~15ms

cache MISS 일 때는 S3 직접 호출보다 살짝 더 느릴 수 있다
edge → S3 한 단계가 더 끼기 때문이다

근데 한 번 캐시 처리가 되면 그 다음부터는 빠르다

Gemini 의 일반론을 그대로 받지 않은 이유

이번에 한 번 더 느꼈는데, AI 가 주는 보안 권고는 “일반적으로 맞는 말” 이긴 한데 내 컨텍스트가 그 일반론에 해당하는지 는 내가 판단해야 한다

Gemini 의 “무단 데이터 접근 위험” 은

  • 사내 문서, 개인정보, 결제 정보 같은 비공개 정적 파일을 다룰 때는 맞는 말이다
  • 근데 우리는 모두에게 들려주려고 만든 환경음 mp3
  • 우리 컨텍스트의 진짜 위협은 “무단 접근” 이 아니라 “비용 폭주” 다

같은 “S3 를 CloudFront 뒤로 숨기자” 라는 결론이라도, 왜 숨기는가 의 답이 다르다

  • Gemini - 파일이 새는 걸 막기 위해
  • 우리 케이스 - 정적 파일은 어차피 공개지만, 진입점을 좁혀 비용·운영 통제권을 확보하기 위해

AI 의 권고를 그대로 박아넣기보다 “우리한테 그 권고가 왜 유효한가” 를 한 번 더 따져보는 습관이 필요하다

마이그레이션 절차

S3 만 쓰던 상태에서 CloudFront + OAC 로 옮길 때, 순서가 꼬이면 갑자기 정적 파일이 안 보이는 사고가 난다
순서는 이렇다

  1. CloudFront distribution 을 먼저 생성한다
    • origin = S3 버킷
    • 이 시점에는 Bucket Policy 를 public 그대로 둔다
  2. 프론트엔드 코드의 정적 파일 base URL 을 CloudFront 도메인으로 교체한다
    • 예: NEXT_PUBLIC_ASSETS_BASE_URL 환경변수
  3. 운영 배포 후, 모든 정적 파일이 CloudFront 경유로 잘 뜨는지 확인한다
    • X-Cache: Hit from cloudfront 헤더 확인
  4. 마지막에 Bucket Policy 를 OAC 전용으로 교체한다
    • 이 시점부터 S3 URL 직접 호출은 403 이 된다

3 번을 건너뛰고 4 번을 먼저 하면, 만약 코드가 어딘가에서 아직 S3 URL 을 직접 부르고 있을 때 그 부분이 깨진다

이번 선택

  • 정적 파일은 S3 에 둔다
  • 앞단에 CloudFront 를 끼운다 (캐시 + 진입점 단일화)
  • OAC 로 S3 직접 접근을 막는다

mp3 파일을 어떻게 관리할 지 생각하다가, “정적 파일 인프라” 자체를 어떻게 관리할 지 고민할 수 있었다
앞으로 에셋들은 S3 에서 관리할 것이다

그런데 CloudFront 가 늘 정답은 아니다

이번 프로젝트는

  • 이유는 모르겠지만 CloudFlare 집계에서 해외 접속이 집계되었고(봇일 가능성이 높긴 하다)
  • 정적 파일 수가 점점 늘어날 예정이며
  • 비용 통제권을 일찍 확보하고 싶었다

위 조건이라 CloudFront 가 답이 되었다

만약

  • 정적 파일이 2~3 개로 끝나고 추가될 일이 없고
  • 모든 사용자가 한 리전 안에서만 쓰고
  • 트래픽 자체가 무시할 만한 규모면

S3 만 public 으로 노출하는 게 더 단순한 선택일 수 있다
운영 비용 (CloudFront 도메인, 인증서, invalidation 관리) 이 정적 파일 규모를 넘어선다

“CDN 은 무조건 좋다” 가 아니라 “CDN 을 활용할 때” 를 보는 시야가 필요하다

다음에 더 볼 것

  • CloudFront signed URL / signed cookie
  • Cache-Control 헤더와 CloudFront 캐시 정책의 우선순위
  • Lambda@Edge / CloudFront Functions
  • AWS WAF rate-limit 룰
  • HTTP/3 (QUIC)
  • Cloudflare 와의 비교

종합 회고

처음에는 “mp3 하나 어디 둘지” 의 고민이었는데, 한 번 파보니 CDN 의 본질 — 캐시, edge 분산, 진입점 단일화, 비용 통제 — 까지 줄줄이 연결됐다

그리고 한 가지 큰 교훈

AI 가 주는 보안 권고를 그대로 받지 말고
”우리 컨텍스트에서 그 권고가 왜 유효한가” 를 한 번 더 묻자

“S3 를 CloudFront 뒤로 숨기자” 라는 같은 결론이지만, 우리한테 진짜 위협은 무단 접근이 아니라 비용 폭주였다
이 차이를 알고 결정하는 것과 모르고 결정하는 것은 다르다고 생각한다