AWS Lambda에 Nest Framework 기반 서버리스 백엔드 올리기

개요

Nest를 이용하여 토이 프로젝트 백엔드를 구현하고 이를 serverless를 이용하여 AWS Lambda에 배포해본다.

NestJS + Serverless + Lambda + AWS — In shortest steps. | by nish abe | Medium 글을 참조하여 진행하였다.

준비

먼저 본인의 환경에 Node.js, Nest Cli, Serverless Cli 가 설치되어 있어야 한다.

프로젝트 생성

다음 명령어를 통해 nest 기본 템플릿을 기반으로 초기화된 프로젝트 디렉토리를 생성할 수 있다.

1
nest new <project>

이후 생성된 프로젝트 디렉토리에 들어가 다음 명령어를 치면 Nest 서버를 켤 수 있다.

1
nest start

그리고 웹 브라우저를 통해 http://localhost:3000 로 접근하면 Hello World! 문자열이 출력된다.

AWS 계정 설정

AWS 콘솔에서 우상단 계정 이름 -> 내 보안 자격 증명에 들어가서 Access Key와 Secret Access Key를 발급할 수 있다.

이후 ~/.aws/credential 파일을 생성하여 다음과 같이 작성한다.

1
2
3
[default]
aws_access_key_id = YOUR_ACCESS_KEY
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY

Lambda 및 Serverless 코드 작성

src 디렉토리 내에 lambda.ts 파일을 생성하고 아래와 같이 코드를 작성한다.

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
import { Handler, Context } from "aws-lambda";
import { Server } from "http";
import { createServer, proxy } from "aws-serverless-express";
import { eventContext } from "aws-serverless-express/middleware";

import { NestFactory } from "@nestjs/core";
import { ExpressAdapter } from "@nestjs/platform-express";
import { AppModule } from "./app.module";

const express = require("express");

// NOTE: If you get ERR_CONTENT_DECODING_FAILED in your browser, this is likely
// due to a compressed response (e.g. gzip) which has not been handled correctly
// by aws-serverless-express and/or API Gateway. Add the necessary MIME types to
// binaryMimeTypes below
const binaryMimeTypes: string[] = [];

let cachedServer: Server;

async function bootstrapServer(): Promise<Server> {
if (!cachedServer) {
const expressApp = express();
const nestApp = await NestFactory.create(
AppModule,
new ExpressAdapter(expressApp)
);
nestApp.use(eventContext());
await nestApp.init();
cachedServer = createServer(expressApp, undefined, binaryMimeTypes);
}
return cachedServer;
}

export const handler: Handler = async (event: any, context: Context) => {
cachedServer = await bootstrapServer();
return proxy(cachedServer, event, context, "PROMISE").promise;
};

그리고 프로젝트 디렉토리 최상단에 serverless.yaml 파일을 생성하고 아래와 같이 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
service:
name: nest-serverless-lambda-demo

plugins:
- "serverless-plugin-typescript"
- serverless-plugin-optimize
- serverless-offline

provider:
name: aws
runtime: nodejs12.x

functions:
main: # The name of the lambda function
# The module 'handler' is exported in the file 'src/lambda'
handler: src/lambda.handler
events:
- http:
method: any
path: /{any+}

Serverless 및 AWS Lambda 관련 의존성 패키지들을 설치한다.

1
yarn add aws-serverless-express aws-lambda serverless-plugin-typescript serverless-plugin-optimize serverless-offline plugin

그리고 다음 명령어로 serverless를 통해 로컬에 서버를 실행시켜본다. (sls는 serverless의 alias다.)

1
sls offline start

그런데 아래와 같은 오류가 발생한다.

1
TypeError: Cannot read property 'getLineAndCharacterOfPosition' of undefined

뭔가 해서 찾아보니 TIL - 19/10/01 글을 보고 tsconfig.json 파일 내 incremental 옵션이 true라서 발생한 문제임을 알게 되었다.

incremental 옵션을 false로 변경하고 다시 sls offline start 명령을 실행하면 정상적으로 실행된다.

이제 보니 원문에도 아래와 같이 써있다. 스크립트만 집중해서 보다가 지나친 것 같다 ㅎㅎ;

1
If you see a compile error related to ‘incremental’, set that property value as ‘false’ in tsconfig.json file.

혹시 serverless로 잘 켜졌는데 http://localhost:3000/dev/ 로 접속하면 431 Request Header Fields Too Large 오류가 발생할 경우 크롬 개발자 도구 -> 응용 프로그램 탭 -> 저장소 -> 사이트 데이터 지우기 클릭하고 새로고침 하면 제대로 뜬다. json - 431 (Request Header Fields Too Large) - Stack Overflow 참고.

Lambda 배포

다음 명령어로 Nest Serverless Backend를 Lambda에 배포할 수 있다.

1
sls deploy -v

배포된 Lambda를 지우려면 아래 명령어를 사용한다.

1
sls remove