
문제 - CDN 비용이 너무 많이 나온다.
커뮤니티 서비스의 특성상, 대량의 이미지가 업로드되고 조회되고 있었다
현재 S3에 저장된 이미지는 CDN을 통해 제공되고 있으며,
일 평균 16만 건의 요청에, Data Transfer Out 바이트가 약 1,600GB가 사용되고 있었다.


이로 인해 한 달에 약 160만 원의 CDN 비용이 발생하고 있었다.
문제의 원인은 다음과 같았다.
1. 비효율적인 이미지 포맷 사용
- 대부분의 이미지는 JPG, PNG 형식으로 저장 및 조회됨
- 해당 포맷은 상대적으로 용량이 크고, 전송 시 데이터 비용이 높음
2. 이미지 크기 최적화 부족
- 사용자가 업로드한 원본 이미지를 리사이징 없이 그대로 사용
- 평균 이미지 크기가 약 2MB로, 불필요하게 큰 용량을 차지
3. 적합하지 않은 캐시 정책
- 관리형 캐시 정책 중 Caching Optimized를 사용 중이었다.
- 이 정책은 Cloud Front 가 캐시 키에 포함된 값을 최소화하여 캐시 효율성을 최적화하도록 설계되었다
- 캐시 키에 쿼리 문자열이나 쿠키를 포함하지 않고 캐싱을 한다
- 쿼리 문자열을 넣어서 CDN으로 요청을 하고 있었는데, 사실상 캐싱이 안된 거니깐 히트율이 매우 낮았다.
압축, 리사이징, 캐싱 진행 시켜.
사실 사용자는 이미지가 저장되고 잘 조회되기만 하면 된다.
그렇다면 파일 포맷을 바꾸고 리사이징을 적용하면 트래픽을 줄이고 비용을 절감할 수 있다.

CDN => Lambda => S3 구조로 Lambda 에서 어떤 이미지를 리턴할지 정의하여 사용했다.
1. 압축률이 좋은 avif, webp로 변환하여 사용하기
JPEG 대신 압축률이 뛰어난 avif, webp로 변환하여 트래픽을 최적화했다.
무조건 avif로 반환하면 안 돼?라는 질문이 있을 수 있다.
대부분의 브라우저가 AVIF를 지원하지만, 일부 환경에서는 지원되지 않을 수도 있다.
그래서 AVIF → WebP → JPG 순으로 포맷을 결정해 지원 가능한 형식으로 반환한다.
avif란?
- AV1 비디오 코덱을 활용한 차세대 이미지 포맷
- 높은 압축률과 우수한 화질 제공
- 빠른 로딩 속도 & 대역폭 절감
- 오픈 미디어 연합(Alliance for Open Media)에서 개발
webp란?
- JPEG 대비 25~30% 더 작은 크기로 무손실 압축 가능
- 구형 브라우저까지 폭넓게 지원
Avif는 변환하는데 레이턴시가 조금 있을 수 있다. 따라서 원본 파일 업로드 시점에 원본과 함께 Avif로 변환하여 S3에 저장했다.
webp는 응답 시점에 Lambda에서 즉시 변환 처리하였다.
실제로 jpeg 파일이 avif로 1.9MB => 652.3KB로 압축됨을 볼 수 있다.

2. Lambda 작성
CDN의 origin으로 설정할 lambda를 작성한다. lambda 서는 크게 다음과 같은 일을 해야 한다.
1. 클라이언트가 보내는 accept header를 확인한다.
- Accept Header?
- 클라이언트가 이해 가능한 콘텐츠 타입이 무엇인지 알려준다.
- 이 중 서버는 하나를 선택해 Content-Type 응답 헤더로 클라이언트에게 선택된 타입을 알려준다.
- 즉, 이 헤더의 역할은 서버야 나 이런 타입 수용 가능!이라고 알려주는 것이다.
2. webp, avif, png 중 가능한 타입이 있는지 확인한다.
- avif > webp > jpg 로 우선순위를 설정한다. (압축률이 높은 순)
3. 가능한 확장자로 파일을 만든다
- avif / png 라면, s3에서 확장자의 파일을 조회하여 반환한다.
- webp의 경우 lambda 내부에서 변환 후 반환한다.
4. 이미지 리사이징을 한다
- 클라이언트가 Query로 리사이징 할 이미지 height와 weight로 조정한다.
lambda는 function url을 설정하여, 외부에서 접근할 수 있는 URL을 달아준다.
3. CDN + 캐싱 정책 설정
1. origin이 s3였던 것을 2번에서 만든 lambda function url로 설정한다.
2. cache-policy를 설정해야 한다.
- 이 cache-policy는 custom 하여 사용하였는데 모든 query와 Accept 헤더를 허용하도록 했다.
결과

1. 이미지 크기 및 용량 감소
2. 적합한 캐싱 정책 사용
이 두 가지를 통해 결론적으로는 Data Transfer 용량 1500GB 에서 약 3GB로 줄어드는 99%의 효과를 얻었다.
비용도 약 160만 원이 다 줄었다고 해도 과언이 아니다.
사실 단순 파일 크기와 이미지 용량이 감소로는 약 60%의 절감 효과를 기대했다.
하지만 예상보다 너무 효율이 좋았고 분명 request 수는 줄지 않았는데 이상하다고 생각했다..?

생각해 보니 이유는 기존 캐싱 정책이 적합하지 않았어서 캐싱을 이용하지 못한 것과 다름없었던 것이다.
그래서 이렇게 엄청난 효과를 주었던 거다. 캐시 성공 요청도 배포 시점 이후로 100%에 근접해졌다.
사실 적용 이전에 너무 잘못 사용하고 있어서 개선 효과가 훨씬 컸던 것 같다
어떤 기술을 쓸 때, 제대로 써야지.. 잘못 쓰면 이렇게 낭비가 된다는 사실을 반성하며.... 개선 완료!

'회사에서 한 일' 카테고리의 다른 글
팬아웃 아키텍처를 활용한 320만개 쿠폰 안정적으로 배포하기 (9) | 2024.12.30 |
---|---|
DynamoDB에서 Redis로: 26억 건 데이터 마이그레이션과 비용 최적화 (5) | 2024.11.10 |
docker-compose로 테스트 환경 구축하기 (4) | 2024.09.08 |
DynamoDB 인덱스 개선으로 비용 절감하기 (3) | 2024.09.01 |
멀티태넌시 SaaS 모듈 설계 썰 푼다.txt (3) | 2024.06.29 |