CDN 캐시 TTL 전략, 우리는 이렇게 하고 있다

호기성세균·2025년 4월 11일
0

트러블슈팅

목록 보기
23/24
post-thumbnail

✅ CDN과 TTL 간단 정리

  • CDN은 CloudFront처럼, S3의 파일을 전 세계에 뿌려주는 엣지 네트워크
  • TTL(Time To Live)은 캐시를 얼마 동안 유지할지 정하는 시간
    • 예: TTL 86400 → 1일 동안 다시 요청 안 하고 그대로 사용

왜 이걸 신경 써야 하냐?

❗ 잘못된 TTL 설정 = CloudFront가 자주 S3에 요청 = S3 요금↑, 응답 속도↓

예시:

  • TTL 기본값 1일 → 하루 지나면 매번 S3 요청
  • 캐시 무의미 → S3 트래픽 줄줄 새고 비용만 낭비됨

→ 해당 프로젝트에서는 이미지 변경이 없으므로, CloudFront에서 TTL을 1년으로 명시적으로 고정


프로젝트의 개선 후 구조

항목설정
CloudFront 캐시TTL 1년 (CachePolicy-Thumbnail-1y)
업로드 시 Cache-Control설정하지 않음
파일명UUID 기반 랜덤 → 경로 중복 없음
iOS 앱 로컬 캐시TTL 짧게 가져가서 디스크 부담 최소화
이미지 변경없음 (기존 파일 덮어쓰기 없음)

이 전략이 효과적인 이유

  1. S3 접근 거의 없음 → CloudFront 캐시로만 처리
  2. 업로드 실수 방지 → 헤더 안 붙이니 CloudFront 정책만 따름
  3. invalidate 필요 없음 → 랜덤 파일명 + TTL 고정으로 충돌 X
  4. 디스크 부담 없는 앱 구조 → 로컬 캐시는 짧고 빠름

그럼 immutable 전략은 아닌 거네?

정확히는 "immutable 헤더는 없음", 하지만 동작은 비슷하게 흘러감

비교 항목현재 구조immutable 전략
TTL 설정CloudFront 정책 기반Cache-Control: max-age, immutable
클라이언트 헤더없음명시적 설정 필요
invalidate 필요없음없음
효과사실상 동일동일

📌 정리하자면: 기술적으로는 immutable은 아니지만, 실질적인 캐시 효율은 같다


iOS 앱의 로컬 캐시, 왜 짧게 가져가는가?

  • iOS의 URLCache는 TTL이 길수록 디스크에 오래 저장함
  • 이미지가 많으면 디스크 부담이 커짐

→ 그래서 로컬 캐시는 짧게 유지

→ 필요 시 CloudFront에서 다시 받아옴 (속도 문제 없음)


CDN 캐시 요금 관련

캐시가 많다고 요금이 올라가는 게 아님 캐시를 못 쓰면 요금이 올라감

구분요금 발생 여부
캐시 저장 공간❌ 없음
데이터 전송✅ 있음 (캐시든 S3든)
요청 수✅ 있음 (10,000건당 과금)
CloudFront Functions 등✅ 있음 (별도 과금)

실수 주의: Cache-Control 헤더

업로드할 때 잘못된 Cache-Control 붙이면, CloudFront는 그걸 응답 헤더로 인식함

PUT /image.png HTTP/1.1
Cache-Control: no-store

→ 이게 객체 메타데이터로 저장됨

→ CloudFront는 응답 시 이 헤더 따라감

캐시 안 됨, 매번 S3로 요청 감

업로드 시 Cache-Control 아예 안 붙이는 게 전략적으로 안전함


최종 정리

  • CloudFront TTL은 정책으로 1년
  • Cache-Control은 안 붙임
  • 앱 로컬 캐시는 짧게 → CloudFront 캐시만 믿고 간다
  • immutable은 안 쓰지만 동작은 거의 동일
  • 파일명은 중복없는 랜덤 → invalidate 필요 없음
  • 요금 걱정은 캐시 적중률만 잘 챙기면 끝

🧰 CloudFront + S3에서 immutable 전략 적용 단계별 가이드

1단계. CloudFront Behavior 설정 수정 (TTL 명시적으로 지정)

CloudFront > Behaviors(동작) > 편집

캐시 키 및 원본 요청 > Create Cache Policy

CloudFront의 새로운 캐시 설정은 총 3가지 정책 기반 구성으로 나뉜다.

설명
Cache Policy어떤 요청을 캐시 키로 사용할지, 그리고 TTL을 얼마나 설정할지
Origin Request Policy캐시에 없어서 원본(S3)에 요청할 때, 어떤 헤더, 쿼리, 쿠키를 전달할지
Response Headers PolicyCDN이 응답을 보낼 때 어떤 HTTP 헤더를 강제로 추가할지 (옵션)

Cache policy > “Create cache policy” 클릭

  • 이름: CachePolicy-Thumbnail-1y
  • TTL: 31536000 (default/max), 0 (min)
  • Headers, Cookies, Query Strings: 전부 None
항목의미
Min TTL캐시가 최소한 무조건 유지돼야 하는 시간
Default TTLCache-Control 없을 때, CloudFront가 기본으로 유지할 시간
Max TTLCache-Control에서 아무리 길게 줘도 이 시간을 넘기지는 못함

Min TTL = 0 으로 설정하는 이유는?

TTL이 남아 있어도 강제 invalidate 또는 변경 요청 시 바로 반영되게 하려는 안전장치

  • 99.9%는 1년 동안 캐시되게 놔두되,
  • 0.1%라도 강제로 바꾸고 싶을 때 (ex. 실수로 파일 잘못 올림),
  • Min TTL이 0이면 → 바로 CloudFront가 새로 요청 가능함

Min TTL = 캐시 강제 최소 유지 시간인데, 만약 이걸 1초 이상으로 설정해버리면?

❗ CloudFront는 그 시간 동안 "무조건 캐시된 것만 사용"하고,

ETag/If-Modified-Since 등 변경 여부 확인도 안 함.

문제 되는 시나리오:

  • 원래는 TTL 1년 줬지만,
  • 나중에 뭐가 잘못돼서 invalidate 시도하거나, 파일 바꾸고 다시 업로드했는데도,
  • Min TTL이 1 이상이면 캐시가 무조건 남아있음.

즉, CloudFront가 원본에 다시 확인도 안 해버림

위와 같이 캐시 키 및 원본 요청 설정 > 변경 사항 저장

항목설정 값
Cache policyCachePolicy-Thumbnail-1y (직접 생성한 TTL 1년짜리 정책)
Origin request policyNone (정적 파일이라 전달할 헤더 없음)
Response headers policyNone (보안/헤더 커스터마이징 안 한다면 불필요)

2단계. S3에 이미지 업로드시 Cache-Control 헤더 주의

CloudFront는 S3 객체의 Cache-Control 응답 헤더를 그대로 따른다.

즉, 이미지 업로드할 때 붙이는 헤더 하나가 CloudFront 캐시 정책 전체에 영향을 줄 수 있다.


✔️ 현재 우리가 설정하는 방식

  • 업로드 시 Cache-Control 헤더는 따로 지정하지 않는다
  • CloudFront는 Behavior 설정에 있는 정책(TTL 1년) 을 따름

이렇게 하면 업로드 실수로 캐시 꼬일 일이 없음.

Cache-Control 헤더는 뭐길래 이렇게 중요할까?

브라우저뿐 아니라, CloudFront 같은 CDN도 이 헤더를 보고 캐시 전략을 정한다.

공식 스펙(RFC 9111)에서도 다음과 같이 정의돼 있음: “Cache-Control은 요청/응답 체인에 있는 모든 캐시들에 적용된다.”

즉, 다음 대상이 모두 이 헤더를 따라 움직인다는 얘기:

  • 브라우저
  • 중간 프록시
  • CDN (CloudFront 포함)
  • 모바일 앱의 URLCache 등

결론

구분영향 받음?설명
CloudFront 캐시S3 응답 헤더의 Cache-Control을 기반으로 TTL 계산
iOS 앱 캐시URLSession이 응답 헤더를 기준으로 로컬 저장 판단
클라이언트 요청 헤더서버 응답에는 영향 없음 (단, S3 업로드 시 예외)

핵심 요약

  • Cache-Control은 CDN, 브라우저, 앱까지 모두 영향을 주는 헤더
  • presigned URL로 이미지 업로드할 때 이 헤더가 S3 객체의 응답 헤더가 되어버린다
  • 이상한 값 붙이면 CloudFront 캐시 무력화됨
  • 그래서 우리는 업로드 시 Cache-Control 헤더를 아예 안 붙이고, CloudFront 정책(TTL 1년)만 믿고 가는 전략을 택했다

3단계. 프론트엔드에서 파일명 랜덤화 유지

  • 이미지 변경 시에도 같은 경로 재사용 금지
  • 항상 UUID나 랜덤값이 포함된 경로로 업로드
https://cdn.example.com/images/user/thumbs/9d0a3f90e8.png

이 방식을 유지하면:

  • 캐시 충돌 없음
  • TTL이 길어도 이미지 덮어쓰기 문제 없음
  • invalidation도 필요 없음

즉, 파일명이 바뀌면 이미지도 완전히 새로운 리소스로 인식되기 때문에, 캐시 전략을 강하게 가져가도 전혀 문제 없다.

4단계. CloudFront Invalidation 걱정은 내려놔도 됨

현재 구조는 다음 조건을 모두 만족하고 있다

  • 경로 충돌 없음 (랜덤 파일명)
  • 이미지 교체 없음
  • TTL은 최대치로 유지
  • invalidate API 호출 필요 없음

이 말인즉슨, 캐시 무효화 정책을 아예 운영하지 않아도 된다.

만약 같은 URL에 다른 이미지를 덮어쓰는 구조였다면 CloudFront invalidation API를 사용해야 했을 거야.

하지만 지금은 그럴 일이 없으니, 운영 부담 자체가 사라진다.


profile
공부...열심히...

0개의 댓글