AWS

람다를 이용한 이미지 리사이징 - 1 :: 마이구미

mygumi 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개의 이미지를 저장하게 된다.

이미지가 많이 사용되는 서비스에서는 적합하지 않아보일 수 있다.

아무튼 많은 대안과 방식이 존재하기에, 여기서 다루는 내용은 너무 깊게 고민할 필요는 없고, "이런 방식이 있구나" 라고 바라보자. 

 

다음과 같은 기술 스택으로 진행한다.

 

  • AWS - S3, Lambda, IAM, CloudWatch
  • aws-cli (optional)
  • Node.js - sharp

 

원본 이미지에 대해 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 을 사용한다.

원하는 시나리오를 위한 코드의 순서는 다음과 같다.

 

  1. s3 에서 업로드된 원본 이미지를 가져온다. s3.getObject()
  2. 가져온 원본 이미지의 데이터를 통해 이미지 리사이징한다. sharp.resize()
  3. 리사이징된 이미지를 s3 에 업로드한다. s3.putObject()

각 메소드에 대한 자세한 사항은 문서를 참고하자.

전체 코드는 다음과 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const 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-resizing

 

aws 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.txt

 

3. 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

반응형