서버비 0원으로 이미지 리사이징 서버 구축하기
추가내용 (2022–07–30)
서버비 0원 관련
6월 2일부터 production환경에서 사용하고 2달간의 요청량과 비용을 확인해 보았어요.
Lambda 요청량
- 6월: 약 60 만건
- 7월: 약 90 만건
매달 많은 양의 요청을 처리 하고도 비용은 0원 나왔어요.
이유가 Lambda 요금이 “요청 1백만건당 $0.2” 인데요.
1백만건을 채우지 못해 0원이 청구 된 것으로 판단하고 있어요.
요청량이 2백만건으로 현재보다 2배 늘어도, 월 $0.41 청구될 것으로 나와 크게 비용걱정이 없어요.
AVIF 변환관련 (squoosh사용)
squoosh로 avif 변환을 시도를 해보았는데요. 해결하지 못한 버그가 있습니다. (T_T)
코드를 올리오니, 버그 원인 아시는분 알려주시면 감사하겠습니다.
오류 증상:
- avif로 변환 되나, 최종 response 할때 응답이 안가고 timeout error 발생.
index.js: https://gist.github.com/sungkwangkim/5e88b77ab48b0486c7455c52d11c2bf3#file-index-js-L175
🦛⚡전기 먹는 하마 인터넷
전 세계 데이터 센터는 연간 416.2 TWh(테라와트) 전기를 사용해요. 이 수치는 2021년 프랑스의 총 전력 소비량을 과 비슷해요.
뿐만 아니라 전송 네트워크, 우리가 손에 들고 있는 수십억 개의 장치에 이르기까지 모두 전기를 소비하고 있어요. 이는 전 세계 항공 산업과 같거나 더 많은 탄소를 배출한다고 해요.
출처: https://www.wholegraindigital.com/blog/website-energy-efficiency/
⚖️ 웹페이지 무게
HTTP Archive에 따르면, 지난 10년 동안 평균 웹페이지 무게는 484kb에서 약 2,205kb로 356% 증가했어요. 이 증가는 더 빠른 컴퓨터 프로세서, 데이터 전송/저장/수집/처리/모니터링 연결된 서비스 사용량 증가에 발맞추기 위해 모두 발전하고 있고, 이런 데이터 센터 장치에 전력을 공급하는데 많은 에너지가 사용되고 있어요.
🪶 가벼운 페이지 장점
웹페이지 무게를 가볍게 하면, 데이터 저장/전송을 더 빠르고 효율적으로 유지하여 전반적으로 에너지 수요를 줄이는 데 도움을 줄 수 있어요.
또한, 빠른 페이지 로딩으로 사용자 경험을 향상시킬 수 있고, Google 검색 순위에서 좋은 점수를 받을 수 있어요.
🖼️ 이미지의 무게
HTTP Archive에 따르면, 이미지는 전체 웹페이지 무게의 평균 21% 차지해요. 비율로 보았을 때, 스크립트와 글꼴보다 이미지 최적화는 더 중요하다고 생각해요.
🏋️♂️ 원티드 이미지 무게는?
5월 26일에 외부 콘텐츠를 큐레이션 하는 기능을 추가 하였는데, 외부 이미지를 사용하다보니 최적화 안된 이미지로 첫페이지 용량이 크게 늘어나게 되었어요.
⚡이로 인해 인터넷상에서 더 많은 에너지를 사용하게 되고
⏳페이지 로딩 속도가 느려지고,
🐢이미지 로드 되는 동안 상/하스크롤시 버벅이는 현상도 발생하고,
전반적인 사용자 경험이 안 좋아서 빠른 개선이 필요했어요.
🏃♂️ 개선시작!
이 글에서는 프론트엔드 개발자가 웹사이트를 보다 에너지 효율적으로 만들 수 있는 여러 방법 중, 이미지 최적화(image optimization)에 대한 부분을 다루어 보려고 해요.
📢 널리 알려진 Lambda@Edge 방식
실시간 이미지 리사이징 & webp 변환 방법은 Cloudfront + Lambda@Edge 조합으로 많이 이용해요.
이 방식은 이미지 원본이 S3에 있을 때 사용하는 방법이에요.
(S3에서 원본 응답 시 Lambda@Edge에서 리사이징 처리)
Lambda@Edge 이미지 리사이징 관련 블로그
🎯 Cloudfront + Lambda Function URL
원티드는 Lambda@Edge 방식을 사용할 수 없었어요.
왜냐면, 외부 컨텐츠 이미지는 원티드 S3에 없고, URL로만 존재했기 때문이에요.
하여, Lambda에서 이미지 URL을 호출하여 가지고 온 다음 리사이징을 해야 했어요.
위 그림을 설명하면 다음과 같아요.
- user1 사용자 이미지 요청 — request
GET https://{cloudfront URL}/optimize?src={이미지 url}&w=100&q=75
- cloudfront cache miss 되어, Lambda로 보냄.
- Lambda는 쿼리 스트링 src 값으로 이미지 원본 요청.
- 이미지 원본 응답이 오면
accept
header, 쿼리 스트링 w, q 값으로 리사이징 변환. - cloudfront cache 처리.
- user1 사용자 응답 — response
- user2 사용자 이미지 요청(user1 동일한 요청) — request
GET https://{cloudfront URL}/optimize?src={이미지 url}&w=100&q=75
- cloudfront cache hit
- user2 사용자 응답 — response
참고) Lambda Function URL
2022년 4월 Lambda를 HTTP 요청으로 실행이 가능하게 기능이 추가되었어요. 이 기능이 생김으로써 API Gateway 없이 Cloudfront에서 바로 연결이 가능해졌어요.
- https://aws.amazon.com/ko/blogs/korea/announcing-aws-lambda-function-urls-built-in-https-endpoints-for-single-function-microservices/
이제 구축하는 방법을 알아보도록 해요.
⚙️ Lambda Function URL 생성
aws console → lambda → 함수 생성
“고급 설정”에서 함수 URL 활성화
에 체크 해주세요.
📔 Cloud9 셋팅
Lambda 배포 편의를 위해 Cloud9을 사용했어요.
Cloud9 IDE 환경을 만들어요.
Terminal에서 package.json 파일을 만들고 sharp, axios를 설치해 주세요.
~/environment $ npm init -y
~/environment $ npm install sharp axios
root에 index.js 파일을 만들고, 아래 코드를 넣어주세요.
🍻 CloudFront → Lambda Function URL 연결
1. Cloudfront → 동작 → 동작생성
2. Lambda Function URL 입력
원본 및 원본그룹에 Lambda “함수 URL” 복사해 주세요.
- 맨앞
http://
제거 - 맨뒤
/
제거
🔮 세부 CloudFront 셋팅
1. 캐시 키 및 원본 요청
CloudFront가 캐시처리시, 키로 사용할 항목을 지정해줘야 해요.
캐시 키를 바르게 셋팅되어야, cache가 히트 되었을때, 문제없이 데이터가 내려갈수 있어요.
이렇게 하면, 모든 셋팅 완료 되었어요.
🥏 결과는?
테스트 환경
원티드에 처음 접속하는 유저로 가정해 보기로 했어요.
- 크롬 시크릿창
- Network → Disabled cache
🚀 이미지 무게 97% 절감!
이미지 무게가 97% 줄었고,
실제 사용 시 버벅거리는 증상도 없어져 사용자 경험도 좋아졌어요.
- before: 19.12MB
- afeter: 0.416MB
적용전
적용후
페이지 무게 비교
🌲 추가내용
항상 webp 포맷으로 변환해서 내려주지 않아요.
webp 포맷을 이해할 수 있는 client에게만 내려주고 있어요.
대부분의 브라우저에서 webp를 지원하지만, 자원 안 하는 브라우저로 접속 시 브라우저가 이해할 수 있는 이미지 포맷으로 내려주는 처리를 하였어요. 이런 처리를 “컨텐츠 협상(Content negotiation)” 이라고 해요.
🤝 콘텐츠 협상(Content negotiation)
이미지 HTTP Request를 살펴보아요.
1번줄picture.jpg
파일을 요청하고 있어요.
2번줄 accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Request header accept
값으로 Client가 이해할 수 있는 콘텐츠 타입들도 함께 올라가고 있어요.
accept
header는 개발자가 세팅을 하지 않아도, Client가(Browser, iOS, Android) 자동으로 세팅되요.
서버는 accept
정보를 가지고, Client가 이해 가능한 포맷으로 변환해서 내려주는 과정을 콘텐츠 협상(Content negotiation)이라고 해요.
대화식으로 표현해 보면 아래와 같아요.
🙆♂ ️accept-*
Client는 accept-*
header로, 이해가능한/선호하는 정보를 서버에게 알려줘요.
accept
: 클라이언트가 이해 가능한 콘텐츠 타입을 알려줌.accept-encoding
: 클라이언트가 이해 가능한 인코딩 타입을 알려줌.accept-language:
클라이언트가 이해 가능한 언어 및 어떤 언어를 더 선호하는지 알려줌.
accept-*
해더를 활용하면,
서버에서 이값을 활용하여 Client에게 더 최적화된 콘텐츠를 제공할수 있게 되요.
Content negotiation을 이용한 이미지 최적화란?
이미지 요청을 받은 서버는 accept
값을 확인하여, 클라이언트에게 최적화된 이미지 콘텐츠 포맷(webp)을 제공하는 방법을 말해요.
아래 그림은 lambda 코드 일부를 발췌했어요.
IE11에서 확인해 보면?
IE11은 webp 포맷을 지원하지 않아요.
webp를 지원하지 않는 client에게는 png, jpeg 포맷으로 응답이 되어야 해요.
IE11에서 jpeg로 응답이 되는 걸로 보아, Content negotiation이 정상으로 동작하는 것을 알수 있어요.
⁉️ 예상질문
질문: AVIF 포맷은 지원 안하나요?
답변:
- AVIF 테스트중에 lambda에서 timeout이 자주 발생 하였어요.
- 하지만, webp로 변환을 하였을때는 timeout이 발생하지 않았어요.
- 하여, 안정적인 서비스를 위해 webp로만 변환을 하게 되었습니다. (sharp 라이브러리 이슈로 파악되요)
- AVIF 지원을 위해서 sharpe 대신 squoosh 라이브러리로 테스트를 진행해볼 예정이에요.
마무리
“Makers(제품개발조직)의 작은 관심과 노력이 하나뿐인 지구를 살린다” 라는 생각으로 시한폭탄과 같은 탄소증가를 막고, 더 좋은 지구를 만드는데 동참해 주시면 좋을 것 같아요.
웹사이트 탄소 계산기 에요.
우리 웹사이트가 얼마만큼 탄소발생 시키는지 인지하고 줄여나가는데 도움이 되었으면 좋겠어요.
긴글 읽어주셔서 감사합니다.