Lambda를 이용해서 S3와 연동시켜보자!

윤학·2023년 4월 30일
0

Aws

목록 보기
5/6
post-thumbnail

S3 작업에 대한 로직을 개선하기 위해 Lambda Function을 이용했었다.

이번 글에서는 어떻게 Lambda Function을 S3와 연동하는지 해당 방법과 마주쳤던 오류들만 빠르게 살펴보자!

1. 함수 생성

일단 Lambda 서비스에 가서 함수를 생성해보자.

이 부분은 개인의 환경에 맞게 설정하면 될 것 같다.

필자는 Node 18.12.1버전을 사용하고 있었다.

그리고 역할을 지정해주면 되는데 이 부분 역시 각자 다를 것이다.

다만, 기존에 역할을 생성해 놓아서 해당 역할에 연결시키고 싶은데 리스트에 나타나지 않는다면 해당 역할 페이지에 가서 신뢰 관계에 Lambda 서비스를 신뢰할 수 있는 서비스로 작성해 주어야 한다.

{
    "Effect": "Allow",
    "Principal": {
        "Service": "lambda.amazonaws.com"
     },
    "Action": "sts:AssumeRole"
}

2. 트리거 추가

트리거 추가를 누른 이후 트리거를 구성해주자.

만약 객체 삭제 시 Trigger가 되길 원한다면 삭제에 관한 이벤트 유형을 설정하면 된다.

또한, 해당 버킷에 여러 폴더가 존재하면 특정 폴더에서의 이벤트만 Trigger될 수 있도록 접두사를 지정할 수 있고, 같은 의미로 .mp4와 같은 접미사를 지정할 수 있다.

3. 코드 작성

트리거를 추가하고 난 후 밑으로 내린다면 함수를 작성할 수 있는 페이지가 아래와 같이 나온다.

만약 index.js로 파일이 생성되었다면 모듈을 require방식으로 불러와야 하는데 import로 불러오고 싶다면 index.mjs로 변경해주면 된다.

만약 설정한 유형의 동작이 발생해 handler함수가 호출되었다면 event는 어떠한 형태를 띄는지 보자.

{
  Records: [
    {
      eventVersion: '2.1',
      eventSource: 'aws:s3',
      awsRegion: 'ap-northeast-2',
      eventTime: '2023-04-30T16:12:31.753Z',
      eventName: 'ObjectCreated:Put',
      userIdentity: [Object],
      requestParameters: [Object],
      responseElements: [Object],
      s3: [Object]
    }
  ]
}

Records[0]으로 객체들의 상세 정보를 볼 수 있다.

{
  eventVersion: '2.1',
  eventSource: 'aws:s3',
  awsRegion: 'ap-northeast-2',
  eventTime: '2023-04-30T16:20:23.883Z',
  eventName: 'ObjectCreated:Put',
  userIdentity: { principalId: 'ALYQ45OA7KY7I' },
  requestParameters: { sourceIPAddress: '175.207.155.132' },
  responseElements: {
    'x-amz-request-id': 'S8D7EVE6PW99WJB0',
    'x-amz-id-2': 'zsM99EX/W++5m0NZcGXOOsjWIX6UAYMqe2kzlnk/Cx4VDTz1kwATjkDASq11PZduE69gWATCIHsW16CKWcsJlcxPRskEevyy0lipYWJGC5k='
  },
  s3: {
    s3SchemaVersion: '1.0',
    configurationId: 'a2df3b65-54d1-4a45-8230-5ba50363e857',
    bucket: {
      name: 'videodot-project-storage',
      ownerIdentity: [Object],
      arn: 'arn:aws:s3:::videodot-project-storage'
    },
    object: {
      key: 'images/Screenshot_20230407-231725_storeProject-1681401825829.jpg',
      size: 306293,
      eTag: '66fb668d4ffda88e0cbe6a2fbf9c17a2',
      sequencer: '00644E9547BC4A2CDC'
    }
  }
}

이 결과를 참고하여 각자의 로직에 맞게 Lambda Function 코드를 작성할 수 있을 것이다.

근데 왜 로그를 보면 같은 이벤트가 여러번 찍혀있나요?

Lambda Function이 호출된 기록은 CloudWatch 서비스에 가면 볼 수 있는데 하나의 이벤트에 여러번 호출되어 실행된 기록을 볼 수 있다.

시간대를 보면 한번 실행된 이후 3초 정도 있다가 실행되었는데 이는 오류나 시간초과가 났을 때 자동으로 Lambda함수가 재시도 하도록 구성이 되어있기 때문이다.

S3에서 발생시킨 이벤트는 Lambda함수가 비동기로 처리하기 때문에 비동기 처리 시 오류가 났을 때는 Lambda 함수가 재시도 하지만, 만약 로그에 오류가 없는데 여러번 실행되었다면 이는 클라이언트 측 문제라고 한다.

Lambda 함수가 처리하는 시간을 늘리고 싶다면 일반 구성 탭을 편집해주면 된다.

재시도 횟수를 변경하고 싶다면 비동기식 호출 탭에서 편집하자.

재시도 횟수를 0번으로 변경하면 오류가 나더라도 재시도를 하지 않는다.

4. 환경 변수

코드 작성 시 노출되면 안되는 코드들을 환경 변수로 관리하고 싶다면 환경 변수 탭에 가서 편집해주면 된다.

사용시에는 Nodejs기준으로 process.env.{환경 변수 이름} 으로 코드에서 사용하면 된다.

마주친 오류

1) aws-sdk를 찾을 수 없어요

AWS 공식 문서에 있는 Nodejs 예제를 보면 함수 코드가 아래와 같이 되어 있다.

console.log('Loading function');
        
const aws = require('aws-sdk');

const s3 = new aws.S3({ apiVersion: '2006-03-01' });

exports.handler = async (event, context) => {
    //console.log('Received event:', JSON.stringify(event, null, 2));

    // Get the object from the event and show its content type
    const bucket = event.Records[0].s3.bucket.name;
    const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    const params = {
        Bucket: bucket,
        Key: key,
    }; 
    try {
        const { ContentType } = await s3.getObject(params).promise();
        console.log('CONTENT TYPE:', ContentType);
        return ContentType;
    } catch (err) {
        console.log(err);
        const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
        console.log(message);
        throw new Error(message);
    }
};

근데 실행해보면 Error: Cannot find module 'aws-sdk'라고 뜬다.

Lambda의 Nodejs 런타임에는 javascript용 aws sdk가 포함이 되는데 Nodejs 16까지 js용 aws sdk v2가 포함이 되어 위와 같이 사용할 수 있었고,

필자는 Nodejs 18 런타임을 선택하였는데 해당 런타임에는 v3가 포함이 되어찾을 수 없다고 나온 것이다.

import { S3Client } from "@aws-sdk/client-s3";

이런식으로 해결할 수 있다.

2) Cannot use import statement outside a module

require문 말고 import문으로 모듈을 불러올 때 위와 같은 오류가 발생할 수 있다.

콘솔에서 기본적으로 함수를 생성하면 핸들러가 담긴 파일을 생성해주는데 파일명이 index.js일수도 있고 index.mjs일수도 있다.

기본적으로 Lambda는 .js파일을 CommonJS 모듈로 취급하기에 만약 위와 같은 오류가 뜬다면 파일명이 index.js인지 확인하자.

해당 오류를 해결하는 방법은 2가지가 있다.

Lambda가 .mjs파일은 ES 모듈로 취급하기 때문에 index.mjs로 파일명을 변경하면 해결할 수 있다.

다른 방법은 index.js파일명을 그대로 사용하고 package.json에 type을 module로 변경해주자.

package.json

{
  "type": "module"
}

3) 외부 라이브러리를 포함해야 해요

AWS SDK는 기본적으로 런타임에 포함되어 있기 때문에 추가 다운없이 사용할 수 있지만 코드 내에서 외부 api를 호출하기 위해 axios를 사용한다고 하면 다운받아야 할 것이다.

AWS Lambda에서는 zip파일로 묶어 함수를 실행시키는데 필요한 함수 코드와 종속성을 포함시킬 수 있는데 과정을 살펴보자.

(1) 폴더 생성 및 핸들러 파일 추가

먼저 자신의 컴퓨터에 폴더를 하나 생성하고 핸들러 함수가 포함된 index.js파일을 생성하자.

index.js파일은 AWS 콘솔상에서 작성해 두었던 코드를 전부 포함시킨 것이라고 생각하면 된다.(개인의 구성에 맞게 index.mjs도 상관없다)

(2) 생성한 폴더에서 필요한 모듈 다운로드

axios를 다운받는다 했으니 npm install axios를 해주자.

그럼 위와 같이 이루어져 있을 것이고 현재 폴더에 있는 모든 파일을 묶어 zip파일로 생성해주자.

zip -r put-s3-trigger-function . //zip 파일 이름은 자유

생성된 zip파일을 만들어 놓은 Lambda 함수에 올려주자.

여기서는 CLI를 사용했지만 콘솔에서도 올릴 수 있다.

aws lambda update-function-code --function-name <만들어 놓은 Lambda 함수 이름> --zip-file fileb://<생성한 zip파일 .zip 포함>

그러고 다시 콘솔에 가보면 생성되어 있는 것을 볼 수 있다.

CLI로 올리는데 오류가 납니다

An error occurred (AccessDeniedException) when calling the UpdateFunctionCode operation

만약 위와 같은 오류가 난다면 CLI로 작업하기 위해 처음 등록했던 사용자에게 UpdateFunctionCode동작에 대한 권한이 있는지 살펴보자.

위와 같은 오류가 나온다면 이후에 CLI로 환경 변수에 대해 작업할 때 UpdateFunctionConfiguration동작에 대한 권한 오류가 난다.

CLI로 작업한다면 Lambda에 대한 권한을 전부 추가해준 뒤에 사용하거나 콘솔로 작업하는게 나은 것 같다.

마치면서...

연동되는 AWS 서비스도 많고 URL로도 함수를 호출할 수 있으니 잘 활용한다면 애플리케이션에 있던 코드를 많이 분리할 수 있을 것 같다.

이번에는 하나의 함수에 대해서 작업했기에 코드와 모듈들을 같이 포함했지만 함수가 여러개가 된다면 Lambda 계층을 이용하는 방법을 공부해서 사용해야 할 것 같다.

참고

Tutorial: Using an Amazon S3 trigger to invoke a Lambda function
AWS Lambda function handler in Node.js
How do I prevent duplicate Lambda function invocations?
Node.js 18.x runtime now available in AWS Lambda
Asynchronous invocation
Deploy Node.js Lambda functions with .zip file archives

profile
해결한 문제는 그때 기록하자

0개의 댓글