-
람다를 이용한 이미지 리사이징 - 1 :: 마이구미AWS 2019. 5. 16. 23:00반응형
이 글은 AWS 람다 서비스를 이용해 이미지 리사이징에 대한 다룬다.
우선 제목에서도 1편이라고 한 이유는 다음과 같다.
람다를 이용한 이미지 리사이징은 여러 방식이 있고, 계속해서 새로운 방식이 나오고 있기 때문이다.
여기서는 가장 기본적이고, 간단한 예제를 통해 다룬다.
Lambda@Edge 를 활용한 이미지 리사이징 2편 - https://mygumi.tistory.com/377
AWS 문서 기반 - https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/with-s3-example.html크게 S3 와 Lambda 를 이용한 예제를 다루려고 한다.
S3 에 이미지를 업로드하면, 자동적으로 원하는 크기로 리사이징되어 서버에 저장되길 원한다.
즉, 여러 디바이스 및 환경에 맞게 사용할 수 있는 썸네일을 원하는 시나리오라고 생각하면 된다.
하나의 원본 이미지가 있으면, 그에 따른 리사이징된 썸네일들도 존재하는 것이다.
여기서는 이것을 Lambda 와 S3 를 통해 만들어본다.
참고할 사항은 하나의 이미지를 기준으로 필요한 리사이징 이미지가 4개라면, 결과적으로 1000개의 이미지는 5000개의 이미지를 저장하게 된다.
이미지가 많이 사용되는 서비스에서는 적합하지 않아보일 수 있다.
아무튼 많은 대안과 방식이 존재하기에, 여기서 다루는 내용은 너무 깊게 고민할 필요는 없고, "이런 방식이 있구나" 라고 바라보자.
다음과 같은 기술 스택으로 진행한다.
원본 이미지에 대해 width 가 200px, 400px, 600px 을 가지는 리사이징된 이미지들이 존재하길 원한다.
예를 들어, images 폴더 안에는 origin, w_200, w_400, w_600 이라는 이름을 가진 이름이 존재할 것이다.
우리가 원하는 것은 images/origin 폴더에 원본 이미지를 저장하면, 자동적으로 리사이징된 이미지를 목적에 맞는 폴더에 저장하는 것이다.
origin, w_200, w_400, w_600 이라는 폴더에는 똑같은 이미지들이 존재하지만, 크기는 서로 다를 것이다.
다음과 같이 그림으로 표현할 수 있다.
위처럼 images/origin 폴더에 이미지를 업로드 했을 경우, 자동적으로 w_200, w_400, w_600 폴더에도 이미지를 생성한다.
이 과정을 본격적으로 알아보자.
1. S3 버킷 생성
우선 이미지 리소스를 저장하는 S3 버킷을 만든다.
'mygumi-resource' 라는 버킷을 만들고, 위 그림처럼 images/origin, w_200, w_400, w_600 구조를 만든다.
2. Lambda 함수 생성
콘솔 페이지에서 단순히 create 버튼 클릭만으로 생성한 후, 코드를 작성하면 된다.
이 부분은 편의를 위해 aws-cli 을 사용하도록 하겠다.
aws-cli 을 설치하고, aws configure 명령어를 통해 관련 변수를 셋팅해주면 끝이다. (https://github.com/aws/aws-cli README.md 참고)
우선 index.js 파일을 생성한 후, 이미지 리사이징 코드를 작성한다.
리사이징 관련 모듈은 sharp 을 사용한다.
원하는 시나리오를 위한 코드의 순서는 다음과 같다.
- s3 에서 업로드된 원본 이미지를 가져온다. s3.getObject()
- 가져온 원본 이미지의 데이터를 통해 이미지 리사이징한다. sharp.resize()
- 리사이징된 이미지를 s3 에 업로드한다. s3.putObject()
각 메소드에 대한 자세한 사항은 문서를 참고하자.
전체 코드는 다음과 같다.
12345678910111213141516171819202122232425262728293031323334353637383940const sharp = require("sharp");const aws = require("aws-sdk");const s3 = new aws.S3();const Bucket = "mygumi-resource";const transforms = [{ name: "w_200", width: 200 },{ name: "w_400", width: 400 },{ name: "w_600", width: 600 }];exports.handler = async (event, context, callback) => {const key = event.Records[0].s3.object.key;const sanitizedKey = key.replace(/\+/g, " ");const parts = sanitizedKey.split("/");const filename = parts[parts.length - 1];try {const image = await s3.getObject({ Bucket, Key: sanitizedKey }).promise();await Promise.all(transforms.map(async item => {const resizedImg = await sharp(image.Body).resize({ width: item.width }).toBuffer();return await s3.putObject({Bucket,Body: resizedImg,Key: `images/${item.name}/${filename}`}).promise();}));callback(null, `Success: ${filename}`);} catch (err) {callback(`Error resizing files: ${err}`);}};cs 참고로 sharp.resize().toBuffer() 를 설명하면 다음과 같다.
sharp.resize() 파라미터 값으로 width 만 지정한 이유는 이미지의 비율을 깨고 싶지 않아서이다.
결과적으로 width 를 기준으로 height 는 자동적으로 비율에 맞게 조정된다.
s3 업로드를 위한 메소드 putObject() 에서의 파라미터 Body 를 위해 이미지의 버퍼 형태로 가져오기 위해 toBuffer() 를 사용한다.
이제 작성된 코드를 aws-cli 를 통해 람다를 생성해본다.
작성된 index.js 파일을 가지는 폴더에 sharp 와 같은 모듈은 설치해주어야한다.
$ npm install sharp
결과적으로 다음과 같은 디렉토리 구조를 가질 것이다.
lambda-image-resize
|- index.js
|- node_modules/sharp함수 코드(index.js) 및 종속 모듈들의 대한 배포 패키지를 zip 명령어를 통해 생성한다.
$ zip -r function.zip .
폴더에 function.zip 파일이 존재할 것이다.
생성된 zip 파일을 기반으로 관련 aws-cli 명령어를 통해 람다 함수를 생성한다.
$ aws lambda create-function --function-name imageResizing \
--zip-file fileb://function.zip --handler index.handler --runtime nodejs8.10 \
--role arn:aws:iam::123456789012:role/lambda-s3-resizingaws lambda create-function 명령어는 요약하면 다음과 같다.
- imageResizing 이라는 이름을 갖는 람다 함수.
- node.js 8.10 버전을 기반.
- arn:aws:iam::123456789012:role/lambda-s3-resizing IAM 역할을 기반.
성공적으로 완료했다면, 콘솔 페이지를 통해 확인하면 람다 함수가 생성된 모습을 볼 수 있다.
그렇다면, 생성된 람다 함수가 잘 동작하는지 확인해보자.
콘솔 페이지에서 아래 입력값을 통해 Test 버튼을 통해 확인할 수도 있고, aws lambda invoke 명령어를 통해 확인할 수 있다.
* 테스트는 S3에 접근해야하기 때문에, imageResizing 람다 함수가 가지는 Role(arn:aws:iam::123456789012:role/lambda-s3-resizing) 에 S3 접근 정책을 추가해야한다.
input.txt
{"Records":[{"eventVersion":"2.0","eventSource":"aws:s3","awsRegion":"us-west-2","eventTime":"1970-01-01T00:00:00.000Z","eventName":"ObjectCreated:Put","userIdentity":{"principalId":"AIDAJDPLRKLG7UEXAMPLE"},"requestParameters":{"sourceIPAddress":"127.0.0.1"},"responseElements":{"x-amz-request-id":"C3D13FE58DE4C810","x-amz-id-2":"FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"},"s3":{"s3SchemaVersion":"1.0","configurationId":"testConfigRule","bucket":{"name":"mygumi-resource","ownerIdentity":{"principalId":"A3NL1KOZZKExample"},"arn":"arn:aws:s3:::mygumi-resource"},"object":{"key":"images/origin/1.jpg","size":1024,"eTag":"d41d8cd98f00b204e9800998ecf8427e","versionId":"096fKKXTRTtl3on89fVO.nfljtsv6qko"}}}]}cs $ aws lambda invoke --function-name imageResizing --invocation-type Event --payload file://input.txt outputfile.txt
이 테스트를 위해서는 images/origin/ 경로에 1.jpg 파일이 존재해야한다.
그렇지 않으면, "NoSuchKey: The specified key does not exist" 관련 에러가 발생할 것이다.
정상적으로 동작했다면, 각 폴더에는 너비가 200px, 400px, 600px 인 이미지가 저장되어있을 것이다.
만약, 원하는 결과를 얻지 못했다면, 람다 함수 실행에 의한 연동되어있는 CloudWatch Logs 를 보고 확인하면 된다.
추가적으로, 이 과정에서 코드를 계속해서 수정해야할 때는 간단한 예로 aws-cli 명령어를 통해 쉽게 업데이트 할 수 있다.
(배포 패키지 생성 => 람다 함수 업데이트 => 람다 함수 실행)
$ zip -r function.zip .
$ aws lambda update-function-code --function-name imageResizing --zip-file fileb://function.zip --publish
$ aws lambda invoke --function-name imageResizing --invocation-type Event --payload file://input.txt outputfile.txt3. S3 - Lambda 연동
현재까지는 람다 함수를 직접 실행해서 동작을 확인했다.
결과적으로 우리가 원하는 건 S3 에 이미지가 업로드 되었을 경우, 람다 함수가 실행되어 이미지 리사이징이 일어나는 것이다.
이를 위해 S3 는 관련 이벤트가 발생했다는 것을 람다 함수에게 알림을 줘야한다.
이 기능은 간단하게 S3 버킷에서 Properties->Advanced settings->Events 에 존재한다.
우리가 원하는 조건을 위와 같이 줄 수 있다.
Prefix 를 통해 images/origin 내부에서의 이벤트 발생을 발생하게 한다.
Suffix 를 통해 jpg 파일만을 감지하게 한다.
Send to, Lambda 를 통해 람다 함수 imageResizing 를 실행한다.
이벤트 알림을 생성하면, 람다 콘솔 페이지에서도 연동된 것을 확인할 수 있다.
결과적으로, 간단하게 람다 함수를 이용해서 이미지 리사이징을 구현할 수 있다.
처음에도 언급했지만, 더 효율적이고 좋은 방법은 존재한다.
더 효율적이고 좋은 방식은 이러한 방식이 존재해서 대안으로 나온 것이기에, 참고하면 좋을 것이다.
다음에는 다른 방식을 다루려고 한다.
Lambda@Edge 를 활용한 이미지 리사이징 2편 - https://mygumi.tistory.com/377
반응형'AWS' 카테고리의 다른 글
람다를 이용한 이미지 리사이징 - 2 :: 마이구미 (0) 2020.08.17 [Cognito] 사용자별 파일 접근하기 :: 마이구미 (2) 2019.11.04 [Slack]Slash Command-Lambda 연동 :: 마이구미 (0) 2018.06.30 Slack-Codecommit 연동 :: 마이구미 (0) 2018.06.29 Amazon S3: 파일 관리를 위한 aws-sdk 활용법 :: 마이구미 (6) 2018.06.08