서론
안녕하세요 팀 원코의 백엔드 개발을 맡고 있는 박준선입니다. 🙇♂️

원코 프로젝트는 S3 이미지 저장 방식을 사용하고 있었는데요.
S3 객체 URL을 그대로 DB 저장하고, 클라이언트 서버에게 S3 객체 URL를 서빙하는 구조였습니다.
이번 포스팅은 기존 S3 방식에서
"왜 CloudFront를 도입하게 되었고, 그 과정에서 발생한 문제와 그 해결 과정"을 정리해보고자 합니다.
측정은 Chrome DevTools → Network에서 이미지 요청의 Duration과 Timing(Content Download)을 기준으로 했습니다.
"정확한 비교를 위해 Disable cache 옵션도 켜서 측정했습니다."
본론
기존 구조: S3 URL을 DB에 저장하던 방식
원코 프로젝트 초반에는 구조가 단순했습니다.
이미지는 AWS S3에 올리고, S3 객체 URL을 그대로 DB에 저장한 뒤 클라이언트에 그대로 내려주는 방식이었어요.
S3 버킷에 이미지를 업로드하면 바로 URL이 생기고 DB에는 그 URL만 넣으면 되고 프론트는 그 URL로 이미지를 띄우면 끝이었습니다.
CloudFront를 도입한 이유 (성능 / 편의성)
CloudFront를 도입한 이유는 성능과 편의성 이렇게 두가지였어요.
1. 캐싱으로 성능 개선
이미지는 특성상 한 번 업로드되면 여러번, 반복해서 조회되는 정적 리소스입니다.
즉, 캐싱으로 큰 효과를 볼 수 있는 구조였습니다.
기존 방식(S3 객체 URL을 그대로 내려주는 구조)에서는 사용자가 이미지를 조회할 때마다, 매번 S3로 요청이 들어갑니다.
S3 직접 요청 방식만으로 현재 약 842ms로 운영 환경에서 당장 체감하지는 못했었습니다.
그러나 트래픽이 늘어났을 때도 지금의 응답성을 유지할 수 있을까? 라는 고민이 생겼습니다.
무엇보다 이미지처럼 캐싱 효과가 큰 리소스는 CloudFront만 붙여도 성능 개선 여지가 명확했고, 적용하지 않을 이유가 없었습니다.
CloudFront를 붙이면 사용자와 가까운 엣지 로케이션에서 캐싱된 이미지가 응답되고,
동일한 이미지 요청은 원본(S3)까지 가지 않아도 처리가 됩니다.
2. CDN 도메인으로 이미지 관리 편의성
사실 두 번째 이유가 CloudFront 도입을 생각하게 된 이유입니다.
S3 객체 URL을 그대로 저장하면 URL이 버킷/리전 구조에 종속되고,
동시에 URL 자체가 길고 복잡해 가독성과 관리성이 떨어지는 문제가 있었습니다.
그래서 CloudFront를 앞단에 두고, cdn.oneco.site 같은 고정 진입 도메인을 만들었습니다.
결과적으로 DB에는 항상 https://cdn.oneco.site/... 형태로 저장할 수 있었고,
원본이 S3든 추후 다른 스토리지든 뒷단 변경이 훨씬 자유로워졌습니다.
특히 초기에는 S3 폴더/파일 네이밍 정책이 계속 바뀌기 쉬운데, CDN 도메인으로 한 번 감싸두니 URL 변경 비용 부담이 줄었습니다.
도입 직후 발생한 문제: 브라우저에서만 10초
그리고 문제는 여기서부터 시작됐습니다.
도메인을 붙이고, 인증서를 연결하고, 캐시 정책과 권한(OAC)을 설정하는 과정에서 예상치 못한 성능 이슈가 발생했습니다.
CloudFront로 전환한 뒤, 일부 이미지의 브라우저 렌더링 시간이 10초 이상 걸리기 시작한 것입니다.
S3로 직접 서빙하던 시점에는 약 842ms 수준이었기 때문에,
"CDN을 붙였는데 왜 더 느려졌지?" 라는 의문이 들었고 원인 분석을 시작했습니다.

원인 분석: 요청은 빠른데 다운로드가 느리다
먼저 비교를 위해 S3 버킷을 임시로 Public으로 열어둔 뒤,
파일의 크기가 비슷한 이미지들을 S3 URL(예:51,52)과 CloudFront URL(예:49,50)로 각각 조회해 렌더링 시간을 비교했습니다.

그 결과 문제는 "이미지 자체"가 아니라 CloudFront 경로에서만 발생한다는 점을 확인할 수 있었습니다.
처음에는 캐싱이 적용되지 않는 것이 원인이라고 의심했습니다.
그래서 로컬 환경에서 동일한 이미지 URL을 직접 요청해 응답 시간을 비교해봤는데,
의외로 CloudFront가 S3보다 TTFB(첫 바이트 응답)는 더 빠른 경우도 있었습니다.

즉, 브라우저에서 발생한 12초는 "서버 응답이 늦어서"가 아니라, 개발자 도구(Network) 기준으로 Content Download(다운로드 구간)이 비정상적으로 길어지는 문제였습니다.

또한 반복 요청 시 X-Cache: Miss from CloudFront → Hit from CloudFront로 바뀌며 캐시가 생기는 것도 확인했습니다.

그런데 여기서 의문이 다시 생겼습니다.
"첫 요청(Miss)는 느릴 수 있어도, 두 번째, 세 번째(Hit)는 빨라져야 정상인데, Hit인데도 다운로드 시간이 계속 길다"
게다가 S3는 왜 비슷한 조건에서 오래 걸리지 않을까? 라는 질문이 이어졌습니다.
조금 더 찾아보니 S3도, 브라우저/OS 레벨에서 디스크 캐시의 영향을 받을 수 있었고,
"단순 비교만으로 CloudFront만 느리다"를 결론 내리기 어려운 상태였습니다.
CDN이 느렸던 이유는 무엇이었을까?
처음에는 CloudFront가 "가까운 엣지"를 타지 못하고, 먼 리전에 붙어서 느린 게 아닐까 의심했습니다.
그래서 응답 헤더에서 x-amz-cf-pop을 확인했는데, 결과는 예상과 달랐습니다.
응답 POP는 ICN(아시아/한국 인근)으로 정상이었고,
X-Cache: Miss from cloudfront → 반복 요청 시 Hit from cloudfront로 바뀌는 것도 정상적으로 확인했습니다.
즉, 라우팅(엣지 위치) 문제도 아니고, 캐시가 아예 안 붙는 문제도 아니었습니다.
그런데도 브라우저에서는 유독 Content Download 구간이 길어져서, 이미지 렌더링이 10초 이상 걸리는 현상이 발생했습니다.
이 상태에서 제가 할 수 있었던 건 "하나씩 가설을 지워나가는 것"이었습니다.
- 응답이 느린가? → 로컬에서 curl로 보면 응답은 빠른 편
- 엣지가 먼가? → POP는 ICN으로 정상
- 캐시가 안 붙나? → Hit 확인됨
- 그런데 브라우저에서만 다운로드가 길다 → 네트워크 단에서 뭔가 이상하다..
다음날이 되자 문제가 사라졌다 🤷♂️(CloudFront 전파/워밍업 이슈로 추정)
재밌게도(그리고 당황스럽게도) 다음날 아침 다시 테스트 했더니 문제가 재현되지 않았습니다.
- 동일 이미지
- 동일 브라우저
- 동일 CloudFront URL
- 동일한 테스트 방식(DevTools Timing 확인)
그런데도 이미지 로딩이 정상적으로 내려오고, 이전처럼 10초 이상 걸리던 Content Download 구간이 사라졌습니다.
이 시점에서 "내 설정이 뭔가 잘못됐다" 라기보다는, CloudFront 배포 직후에 발생할 수 있는 전파/워밍업(캐시 계층 준비) 과정에서 일시적으로 비정상적인 체감 지연이 발생했을 가능성이 가장 높다고 판단했습니다.
Distribution 설정이 전 세계의 엣지로 전파되고, 캐시 계층(Edge/Regional)이 요청 패턴에 맞게 워밍업이 되면서, 안정화 되는 과정이 존재합니다.
결론
CloudFront 도입은 결과적으로 원코 서비스에 분명한 이점을 가져다줬습니다.
특히 캐시가 안정화된 이우에는 이미지 응답 성능이 눈에 띄게 개선되었습니다.
- S3 직서빙: 약 843ms
- CloudFront(Cache Miss): 약 621ms
- CloudFront(Cache Hit): 약 298ms
캐시가 한번 잡히고 나면(S3까지 가지 않고) 체감 속도가 크게 좋아지는 걸 수치로 확인할 수 있었습니다.


배운점
"이번 이슈는 CloudFront가 본질적으로 느렸다"라기 보다는,
배포 직후 캐시 계층이 완전히 안정화되기 전까지 체감 성능이 흔들릴 수 있다는 점을 보여준 사례입니다.
실제로 저는 X-Cache(Hit/Miss), x-amz-cf-pop(응답 POP), DevTools Timing(특히 Content Download 구간)을 확인하면서
CloudFront의 캐싱 동작과 브라우저가 이미지를 내려받는 과정을 처음부터 끝까지 다시 뜯어보게 됐습니다.
이번 경험은 단순히 문제 해결을 넘어서, CDN을 운영 관점에서 어떻게 바라봐야 하는지를 배우는 계기가 되었습니다.

