Xcode에서 Codeium 사용하기
NestJS + Serverless + RDS 배포하기 for AWS
2023-05-21
Explanation
오늘은 아주아주 간단하게 NestJS를 Serverless를 통해 배포하고, RDS까지 연결해보려 합니다.
이 글은 최소한의 모듈이나 플러그인을 사용해서 배포까지의 과정으로 대략적인 흐름을 위한 글로, 좋은 사례나 효율적인 구성의 이야기하는 글은 아닙니다!
예를 들면.. 이 글에서는 환경변수를 람다에서 직접 작성하였는데, ‘serverless-dotenv-plugin’과 같은 플러그인을 사용하면 조금 더 효율적으로 구성할 수 있는 방법도 있답니다.
https://www.serverless.com/plugins/serverless-dotenv-plugin
작성된 코드는 아래의 Github에 리포지토리에서 확인하실 수 있습니다.
https://github.com/falsy/blog-post-example/tree/master/nest-serverless-rds
공홈에 따라 CLI를 통해 간편하게 설치해줍니다.
1 2 |
$ npm i -g @nestjs/cli $ nest new project-name |
그리고 간단하게 test modules과 test controller를 만들어주고 app module에 등록해 줄게요.
1 2 3 4 5 6 7 8 9 10 |
// /src/test.controller.ts import { Controller, Get } from '@nestjs/common'; @Controller('/test') export class TestController { @Get() async test() { return 'test'; } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
// /src/test.module.ts import { Module } from '@nestjs/common'; import { TestController } from './test.controller'; @Module({ imports: [], controllers: [TestController], providers: [], exports: [], }) export class TestModule {} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// /src/app.module.ts import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { TestModule } from './test.module'; @Module({ imports: [ TestModule, ], controllers: [AppController], providers: [AppService], }) export class AppModule {} |
일단 필요한 것들 먼저 설치해 줄까요?
1 2 3 |
$ npm i -g serverless $ npm i @vendia/serverless-express aws-lambda $ npm i -D @types/aws-lambda |
다음으로 람다 핸들러 함수를 만들어 줄게요.
https://docs.nestjs.com/faq/serverless#example-integration
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 |
// /src/lambda.ts import { NestFactory } from '@nestjs/core'; import serverlessExpress from '@vendia/serverless-express'; import { Callback, Context, Handler } from 'aws-lambda'; import { AppModule } from './app.module'; let server: Handler; async function bootstrap(): Promise<Handler> { const app = await NestFactory.create(AppModule); 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 bootstrap()); return server(event, context, callback); }; |
그리고 serverless 설정 파일을 만들어 줄게요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# /serverless.yaml service: nest-serverless-example frameworkVersion: '3' plugins: provider: name: aws region: ap-northeast-2 runtime: nodejs16.x functions: api: handler: dist/lambda.handler events: - http: method: any path: /{proxy+} |
마지막으로 타입스크립트 설정을 수정해 줄게요.
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 |
// /serverless.yaml { "compilerOptions": { "module": "commonjs", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "target": "es2017", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", // "incremental": true, // "tsBuildInfoFile": ".tsbuildinfo", "skipLibCheck": true, "strictNullChecks": false, "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, "noFallthroughCasesInSwitch": false, "esModuleInterop": true, "allowJs": true } } |
음.. 기본 설정에서 많이 바꾸진 않았는데요.
우선 ‘incremental’를 사용하지 않았어요. ‘incremental’은 마지막 컴파일에 프로젝트에 대한 정보를 저장해서 프로젝트 변경을 효과적으로 동작하게 하는 기능인 거 같은데요.
기억이 잘 안 나는데 뭔가 오류가 떠서… 당장 필수 옵션은 아니라 일단 빼놓고 했어요.
그리고 ‘esModuleInterop’와 ‘allowJs’를 true로 선언해 주었습니다.
‘esModuleInterop’ – CommonJS 모듈을 가져올 수 있게 해줍니다.
https://www.typescriptlang.org/tsconfig#esModuleInterop
‘allowJs’ = 자바스크립트 파일을 가져올 수 있게 해줍니다.
https://www.typescriptlang.org/tsconfig#allowJs
1 2 |
$ npm run build $ sls deploy |
짜잔~ 정상적으로 배포가 된 것을 확인 할 수 있답니다.
간단하게 DB 설정을 추가해 볼게요. 우선 역시 필요한 모듈 먼저 설치할게요.
1 |
$ npm i @nestjs/typeorm |
App module을 수정해 줄게요. 일단은 DB 정보는 환경변수로 설정해 주었어요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// /src/app.module.ts import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { TestModule } from './test.module'; @Module({ imports: [ TestModule, TypeOrmModule.forRoot({ type: 'mysql', host: process.env.DB_HOST, port: Number(process.env.DB_PORT), username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, entities: [], synchronize: true, }), ], controllers: [AppController], providers: [AppService], }) export class AppModule {} |
1 2 |
$ npm run build $ sls deploy |
짜잔~ 성공적?으로 오류가? 떴습니다.?!
음… RDS를 만들고 하는 것 까지는 조금.. 약간 맥락에 벗어나는 느낌이라 생략할게요!
AWS 콘솔에 접속해서 조금전에 배포했던 Lambda 설정에서 ‘구성’ -> ‘환경 변수’ 탭으로 이동합니다.
그리고 아까 위에서 환경 변수로 선언한 내용들을 환경 변수로 선언해 줄게요.
그리고 아까 위에서 환경 변수로 선언한 내용들을 환경 변수로 선언해 줄게요.
다음으로 Role을 수정할건데요, Role을 수정하는 이유는 RDS에 해당 Lambda를 허용해주려면 보안 그룹을 설정해줘야 하는데, 그럴려면 기본적으로 설정되어 있는 Role에 추가 권한이 필요하답니다.
Lambda 설정에서 ‘구성’ -> ‘권한’ -> ‘실행 역할’ 에서 역할 이름을 눌러 IAM으로 이동합니다.
‘권한’ -> ‘권한 정책’에서 ‘정책 이름’을 선택해서 ‘편집’ 버튼을 눌러서 아래 내용을 추가해 주세요.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
... { "Effect": "Allow", "Action": [ "ec2:DescribeNetworkInterfaces", "ec2:CreateNetworkInterface", "ec2:DeleteNetworkInterface", "ec2:DescribeInstances", "ec2:AttachNetworkInterface" ], "Resource": "*" } ] } |
람다에 적용해 줄 보안그룹을 만들어 줄게요. 인바운드는 HTTP, HTTPS를 열어줬어요.
만들어진 보안그룹은 Lambda 설정의 ‘구성’ -> ‘VPC’로 이동해서 VPC와 서브넷 그리고 앞서 만든 보안그룹을 설정해 줍니다.
이제 마지막으로 RDS의 데이터베이스의 보안그룹에 Lambda에 선언한 보안그룹을 인바운드에 허용해주면 끝이랍니다!