공유일기 어플의 알파 테스트를 시작했다.
함께하고 싶은 사람들과 우리들만의 일기장을 만들어가는 어플이다.
일기장을 만들고 사람들을 초대하여 함께 일기를 작성해 나갈 수 있다.
1. 문제 발생
일기나 일기장에 사용되는 이미지들은 전부 S3 버킷에 저장된다.
그런데 일기장 하나에 딱 8명만 들어와서 사용하고 있는데 생각보다 S3 버킷에 들어오는 GET 요청수가 높았다.
피크가 328건이었고, 4시간 동안 약 600건 정도의 트래픽이 발생하였다.
→ 일기 목록 뷰에서 버그로 인해 추가적인 이미지 요청이 있는 것으로 확인되었다. (하지만 이를 감안하더라도 꽤 많은 요청)
일기장에 대략 이미지가 있는 게시글이 20개 가량이 있었다.
만약 이 서비스가 정식으로 출시되어 일기장 수가 늘어난다면 비용이 예상보다 훨씬 높게 책정될 우려가 생겼다.
이미지 하나당 용량: 1MB
일기장 하루 평균 GET 요청 수 : 600건
일기장 수: 30개
대략 이렇게 가정하여 S3 버킷에 대한 트래픽 양을 계산하면 다음과 같이 계산된다.
하루 트래픽: 1MB * 30 * 600 = 18000MB = 17.58GB
월간 트래픽: 17.58GB * 30 = 527.34GB
이를 aws 비용 계산기로 돌려보면 월 66.40 USD (한화 88,413원)이라는 비용이 발생한다.
서비스 규모에 비해 확실히 큰 비용이 청구된다.
서비스가 더 성장할수록 감당하기 어려운 비용이 발생될 것으로 예상된다.
2. 해결 방안
2-1. CloudFront를 함께 사용하기
CloudFront는 전세계에 엣지 로케이션을 둔 CDN이다.
엣지 서버를 통해 원본 컨텐츠(객체)를 캐싱해두고 엔드 유저가 위치한 곳과 가까운 서버에서 컨텐츠를 제공해준다. 따라서 전세계 어디에서나 빠른 속도로 컨텐츠에 접근할 수 있다는 장점을 가진다.
때문에 공유일기 어플 같이 국내 한정으로 제공되는 서비스의 경우 장점이 없다고 생각할 수 있다.
하지만 CloudFront는 비용 측면에서 S3 버킷을 직접 사용하는 것보다 효율적이다.
S3 버킷의 트래픽 요금 체계이다. 트래픽 양이 증가할수록 비용이 저렴하게 청구되는 구조이다.
CloudFront의 트래픽 요금 체계이다. 마찬가지로 트래픽 양이 증가할수록 비용이 저렴하게 청구되는 구조이다.
그런데 S3에 비해 모든 구간에서 비용이 상대적으로 저렴하다.
CloudFront를 적용했다고 가정하고 트래픽에 따른 요금을 다시 계산해보면
월 63.24 USD (한화 84,206원)의 비용이 발생한다.
현재 계산한 트래픽 양을 기준으로 했을 때는 절감 효과가 드라마틱하지는 않지만 트래픽 규모가 커질수록 비용 차이가 크게 발생할 것이다.
2-2. 중복 다운로드 줄이기 (캐싱)
일기 이미지 파일들은 모두 S3 버킷에 저장되고, DB에는 S3 버킷의 경로만 저장하고 있다.
클라이언트에서 일기 정보를 요청하면 서버는 DB에서 이미지 파일이 저장된 S3 경로를 포함하여 일기 정보를 제공하고 있다.
클라이언트에서 제공받은 이미지 파일과 주소를 캐싱하도록 하고, 캐싱된 값이 없거나 캐싱된 주소와 새로 응답 받은 주소가 다른 경우에 한해서만 S3 버킷으로부터 이미지 파일을 다운로드하도록 변경하면 트래픽 양을 크게 줄일 수 있을 것으로 기대한다.
2-3. 이미지 리사이징
클라이언트 측에서 리사이징
UIImage를 이용하여 이미지 크기를 조정하여 S3 버킷으로 업로드할 수 있다.
서버 측에서 리사이징
클라이언트 측에서 리사이징을 하는 것이 사용자 경험을 떨어뜨린다는 우려가 있다면 서버 측에서 리사이징을 하는 것을 고려할 수 있을 것이다.
현재 우리 서버는 애플리케이션 서버의 부담을 줄이기 위해 Presigned url이 적용되어 있다.
애플리케이션 서버에서는 S3 버킷에 이미지를 업로드할 수 있는 url만 클라이언트에게 제공하고, 클라이언트에서 직접 S3 버킷에 업로드를 하고 있다.
따라서 애플리케이션 서버에서 직접 이미지를 리사이징하는 것은 현 상황에서 적절하지 않다.
때문에 서버 측에서 리사이징을 한다고 하면 EventBridge와 Lambda를 활용하는 방안을 고려할 수 있다.
S3는 EventBridge와 연동하여 버킷에서 특정 이벤트가 발생하면 알림을 보낼 수 있다.
이미지를 리사이징하는 것을 담당하는 Lambda 함수를 만들어두고, PUT 요청이라는 이벤트가 발생했을 때, 알림의 트리거로 Lambda 함수를 실행하는 방안이다.
이렇게 한다면 애플리케이션 서버와 클라이언트 양측의 부담을 모두 덜면서 이미지를 리사이징할 수 있다.
3. 결론
정리하고 보니 1, 2, 3번 모두 같이 적용해볼만한 사안인 것 같았다.
CloudFront를 이용하여 조금 더 저렴하게 S3 버킷을 활용하고, 클라이언트 측에서는 다운로드 받은 이미지를 캐싱하여 트래픽을 줄이고, 클라이언트 측이든 서버 측이든 간에 이미지를 리사이징하여 용량을 줄이는 방식으로 유의미하게 비용을 절감할 수 있을 것 같다.
추후에 실제로 위 방안들을 적용해본 후, 전후로 발생하는 비용을 비교해보는 포스트를 작성해보겠다!