NestJS+Serverless 개발 환경 구축

NestJS, Serverless, ECR 활용한 개발 환경 구축

NestJS 개발 환경 구축

프로젝트 생성(NestJS CLI)

$ npx @nestjs/cli new {project name}
$ cd {project name}

환경 변수 세팅

라이브러리 설치

$ yarn add @nestjs/config cross-env

환경 변수 파일 생성

production .env는 lambda에서 설정 가능

.{process.env.NODE_ENV}.env 파일 생성 ex) .dev.env, .local.env

Script 수정

...
"scripts": {
    ...
    "start": "nest start",
    "start:dev": "cross-env NODE_ENV=dev nest start --watch",
    ...
}
...

NestJS 파일 수정

src/config.ts

export const PORT = process.env.PORT || 3000;

src/main.ts

import { NestFactory } from '@nestjs/core';
import { Logger, VersioningType } from '@nestjs/common';
import { AppModule } from './app.module';
import { PORT } from './config';

const globalPrefix = 'api';
const versionPrefix = 'v';
const defaultVersion = '1';

export const getApp = async () => {
  const app = await NestFactory.create(AppModule);

  app.setGlobalPrefix(globalPrefix);

  app.enableVersioning({
    type: VersioningType.URI,
    prefix: versionPrefix,
    defaultVersion: defaultVersion,
  });

  return app;
};

async function bootstrap() {
  const app = await getApp();
  await app.listen(PORT);
  Logger.log(
    `🚀 Application is running on: http://localhost:${PORT}/${globalPrefix}/${versionPrefix}${defaultVersion}`,
  );
}

if (process.env.NODE_ENV === 'dev') {
  bootstrap();
}

src/app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.${process.env.NODE_ENV}.env`,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Serverless 개발 환경 구축

라이브러리 설치

$ yarn add @vendia/serverless-express aws-lambda
$ yarn add -D @types/aws-lambda serverless-offline

컴파일 옵션 수정

...
"compilerOptions": {
    ...
    "esModuleInterop": true
    ...
}
...

Lambda Handler 생성

import serverlessExpress from '@vendia/serverless-express';
import { Callback, Context, Handler } from 'aws-lambda';
import { getApp } from './main';

let server: Handler;

const getExpressApp = async (): Promise<any> => {
  const app = await getApp();
  await app.init();

  const expressApp = app.getHttpAdapter().getInstance();
  return serverlessExpress({ app: expressApp });
};

export const handler: Handler = async (
  event: any,
  context: Context,
  callback: Callback,
) => {
  server = server ?? (await getExpressApp());
  return server(event, context, callback);
};

Dockerfile 생성

FROM public.ecr.aws/lambda/nodejs:18

COPY package*.json .
RUN npm install

ADD dist ./dist

ENV NODE_ENV='production'

CMD ["dist/lambda.handler"]

Serverless.yaml 생성

service: {project_name}

plugins:
  - serverless-offline

provider:
  name: aws
  runtime: nodejs18.x
  region: ap-northeast-2
  ecr:
    images:
      {project_name}:
        path: ./

functions:
  api:
    architecture: arm64
    image:
      name: {project_name}
      command:
        - dist/lambda.handler
      endpoint:
        - 'lambda-entrypoint.sh'
    events:
      - http:
          method: ANY
          path: '{any+}'

스크립트 수정

...
"scripts": {
    ...
    "deploy": "nest build && sls deploy",
    "deploy:dev": "nest build && sls offline",
    ...
}
...

출처

Last updated