온디멘드 이미지 리사이징을 위한 인프라 구성

사용자 경험 품질 향상을 위한 이미지 최적화에서 온디멘드 이미지 리사이징 에 대한 이야기를 공유해드렸습니다. 이번 글에서는 CloudFront에서 lambda@edge 를 통해 직접 구현해본 과정을 정리해보려 합니다.

TL;DR

  1. S3 버킷을 생성하고 이미지를 업로드합니다.
  2. CloudFront를 통해 S3 컨텐츠를 배포합니다.
  3. 라시아징, webp 파일 형식 변환을 위한 노드 환경 람다 함수를 구현합니다.
  4. 버킷 접근 권한 및 역할을 담은 IAM을 정의하고 람다 함수에 부여합니다.
  5. CloudFront의 오리진 응답 엣지에 람다 함수를 연동합니다.

S3, 샘플 이미지 업로드

일반적으로 사용되는 S3 버킷을 생성하는 과정과 동일합니다. 모든 퍼블릭 엑세스 차단이 비활성화 가 선택된 버킷을 생성하고 샘플 이미지 객체를 업로드해주세요.

create s3 bucket

CloudFront, S3 컨텐츠 배포

CloudFront 배포를 생성합니다. 기본적으로 CloudFront는 쿼리 문자열을 무시하게 되는데, 쿼리 문자열 파라미터 기반의 컨텐츠 캐싱이 가능하도록 캐시 키 및 원본 요청 에서 Lagacy cache settings 를 선택하여 컨텐츠 캐싱에 사용할 쿼리 문자열을 설정 해줍니다.

쿼리 문자열은 이미지 변환에 대한 설정을 전달하기 위한 역할로서 너비, 높이, 이미지 핏, webp 변환 에 대한 설정값을 담을 수 있습니다.

또한, 쿼리 문자열 파라미터 기반의 컨텐츠 캐싱 이라는 특징에 따라, 동일한 쿼리 조합의 요청에 대한 리사이징된 컨텐츠가 CloudFront에 캐싱되어 있다면, S3까지 오리진 요청이 전달되지 않고, CloudFront에서 뷰어 응답으로 빠르게 컨텐츠를 전달합니다.

https://...cloudfront/sample-image.jpg?w=500&h=300

cloudFront

람다 함수에 부여할 IAM 역할 생성

람다 함수에서 우리를 대신해 S3 객체에 접근할 수 있도록 s3:GetObject 와 같은 권한을 부여해주어야 합니다. IAM 콘솔 로 접근하여 역할을 생성, AWS 서비스 / Lambda 를 선택하고 단계를 넘어갑니다.

create-iam

다음 단계에서는 정책 생성 을 통해 JSON 편집을 통해 권한을 편집합니다. 아래와 동일하게 작성하셔도 무방합니다.

{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "iam:CreateServiceLinkedRole", "lambda:GetFunction", "lambda:EnableReplication", "cloudfront:UpdateDistribution", "s3:GetObject", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams" ], "Resource": "*" } ] }

정책 이름 을 설정하고, 정책 생성 을 완료했다면, IAM 탭으로 돌아가 람다 함수에게 실행 역할 즉, AWS 서비스 및 리소스에 접근할 수 있는 엑세스 권한을 정책 연결을 통해 제공합니다.

connect-policy

역할 / 신뢰 관계 / 신례 관계 편집 을 선택하여 마찬가지로 JSON 편집을 통해 역할에 신뢰 관계를 추가합니다.

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": ["edgelambda.amazonaws.com", "lambda.amazonaws.com"] }, "Action": "sts:AssumeRole" } ] }

온디멘드 이미지, WebP 파일 형식 변환을 위한 Lambda 함수 생성 (w/ sharp.js)

람다 함수를 생성합니다. 이때, 람다 트리거를 CloudFront로 설정할 수 있는 지역은 버지니아 북부 만 가능하기 때문에 지역을 변경하고 람다 함수를 생성해주세요.

create-lambda

권한 / 구성 탭에서 생성한 IAM 역할을 설정합니다.

set-policy

이제, 람다 함수 대시보드로 이동하여 이미지 리사이징, webp 포맷팅 적용을 위한 코드를 업로드합니다. 코드는 AWS Lambda@Edge에서 실시간 이미지 리사이즈 & WebP 형식으로 변환를 참고하고 약간의 수정을 더하여 작성하였는데, 위 글에서도 잘 정리되어 있지만, 구현하면서 짚고 넘어가야 될 부분들이 몇 가지 있었는데요.

노드 환경에서 동작하는 람다 머신이 별도로 가지고 있지 않은 모듈은 직접 node_modules 폴더를 함께 압축하여 제공해주어야 했습니다. 의존성 해결을 위한 별도의 설치 과정이 포함되어 있지 않습니다. querystring, aws-sdk 를 포함한 몇 가지 모듈들은 이미 의존성이 해결되어 있습니다.

// Dependencies resolved const querystring = require('querystring') const AWS = require('aws-sdk')

람다 함수에서 바디를 조작했다면, 그 결과가 1MB 이하여야 했습니다. 만약 1MB 보다 크다면, 이미지의 퀄리티를 단계적으로 낮추는 방법으로 접근했습니다.

const MEGABYTE = 1046528 const byteLength = Buffer.byteLength(resizedImage, 'base64') if (byteLength > MEGABYTE) { resizedImage.toFormat(requiredFormat, { quality: 90 }) }

퀄리티 저하의 한계점을 넘어가거나, 타임 아웃이 되면 원본을 반환했습니다. 1MB 제약을 지키기 위해서 변환 과정이 너무 오래 걸리거나, 이미지 퀄리티가 너무 저하되는 경우 원본을 반환하도록 했습니다.

이제 비즈니스에 맞게 코드를 수정하고, node_modules 와 함께 압축된 코드 업로드를 마치셨다면, 람다 함수 상단 메뉴 작업 / Lambda@edge 배포 를 선택합니다.

upload-code

CloudFront 이벤트는 사용자 경험 품질 향상을 위한 이미지 최적화에 기반하여 오리진 응답 을 설정합니다. 이제, 람다 함수를 배포 합니다.

distribution-lambda

마치면서

단순히 코드 레벨과 물리적인 대응 방안을 고민하는 단계에서 더 넓은 접근 방식을 배울 수 있었던 좋은 경험이었습니다. 이미지 리사이징, webp 포맷팅이 잘 적용되는 지 확인해보실 수 있는 간단한 프로젝트를 구성해두었으니 경험해보실 수 있습니다.

Reference

https://medium.com/daangn/lambda-edge로-구현하는-on-the-fly-이미지-리사이징-f4e5052d49f3