nestjs serverless 배포하기

개요
- 사이드 프로젝트 백엔드를 서버리스로 배포하는 방법에 대해 정리한다.
- 포트폴리오 시 링크를 달아야 할 때 서버리스로 배포하면 비용절감에 도움이 될 것이라 생각함
- 클라우드 서비스에 좀 더 익숙해지기 위해 사용
단점
nestjs + lambda를 적용하기 전에 단점부터 알아보자.
- 일단 nestjs 전체를 묶어서 올리는 것이기 때문에 무겁다.
- api gateway에서 routing을 한번, nestjs에서 routing을 한번 더 하기 때문에 속도가 느리다.
즉, 실제 고객한테 제공하는 서비스를 만들 예정이라면 쓰지 않는 것을 추천한다.
방법
순서
- serverless 환경설정
- serverless에 필요한 의존성 설치
- serverless yml 작성
- serverless offline으로 먼저 동작하는지 배포
- serverless deploy를 통해 배포
- script 실행
- api gateway, lambda, dynamodb가 모두 제대로 배포되는지 확인한다.
serverless 환경 설정하기
// serverless.yml (참고용)
service: backend-template
useDotenv: true
package:
individually: true
plugins:
- serverless-offline
- serverless-plugin-typescript
provider:
name: aws
runtime: nodejs18.x # 현재 serverless-offline이 18버전까지만 지원하기 떄문에 18버전으로 사용
region: ap-northeast-2
memorySize: 1024
timeout: 20
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:*
Resource: "*"
# lambda 리소스 설정
functions:
main:
handler: src/main.handler
events:
- http:
method: ANY
path: /
cors: true # cors 에러를 피하기 위해 설정
- http:
method: ANY
path: '{proxy+}'
cors: true # cors 에러를 피하기 위해 설정
resources:
Resources:
# DynamoDB 설정
usersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: Users
AttributeDefinitions:
- AttributeName: unique 속성
AttributeType: S
KeySchema:
- AttributeName: unique 속성
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
challengesTable:
Type: AWS::DynamoDB::Table
Properties:
TableName
AttributeDefinitions:
- AttributeName:
AttributeType: S
KeySchema:
- AttributeName:
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
custom:
serverless-offline:
httpPort: 4000
lambdaPort: 4001
// dependencies
{
"@aws-sdk/client-dynamodb": "^3.441.0",
"@aws-sdk/lib-dynamodb": "^3.441.0",
"@vendia/serverless-express": "^4.10.4",
"aws-lambda": "^1.0.7",
"aws-sdk": "^2.1484.0",
"aws-serverless-express": "^3.4.0",
}
//devdependencies
{
"@types/aws-lambda": "^8.10.125",
"@types/aws-serverless-express": "^3.3.7",
"serverless-offline": "^13.2.0",
"serverless-plugin-typescript": "^2.1.5",
}
기본적으로 위와 같이 설정 했다.
scripts 작성하기
# 서버리스 배포하기
"deploy": "serverless deploy --aws-profile <default일 경우 생략, 지정할 profile 있으면 설정할 것>
# 배포한 서버리스 삭제하기
"remove": "serverless remove --aws-profile <default일 경우 생략, 지정할 profile 있으면 설정할 것>
# 오프라인으로 실행하기
"dev:serverless": "serverless offline"
삽질
- 이번 serverless로 배포하면서 겪었던 삽질들을 모두 정리하려 한다.
그냥 실행이 안되요.
배포 전에 serverless-offline으로 먼저 제대로 실행이 되는지 알아본다.
nestjs의 경우 dev환경에서 로그가 뜨도록 설정해 놓았는데 로그가 뜨지 않는다.
이 경우엔 severless.yml의 memory size를 너무 작게 설정한건 아닌지 한번 확인 해봐야 한다.
cors 에러
아래의 링크를 통해 cors 에러를 해결했다
https://www.serverless.com/blog/cors-api-gateway-survival-guide
Your CORS and API Gateway survival guide
Get the basics on Cross-Origin Resource Sharing (CORS) and how to avoid problems with your Serverless web APIs on Lambda.
www.serverless.com
내가 사용한 방법은 두가지의 방법이다
- serverless yml에 cors 설정을 추가하는 방식 (위에 yml 파일 작성한거 확인)
- interceptors를 이용해 response시 header를 강제적으로 달아주는 방식이다.
interceptor.ts
// common-headers.interceptor.ts
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class CommonHeadersInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
// 원하는 헤더를 여기에 추가합니다.
const response = context.switchToHttp().getResponse();
response.header('Access-Control-Allow-Origin', '*');
response.header('Access-Control-Allow-Credentials', 'true');
// 다음 핸들러 실행
return next.handle();
}
}
// app.module.ts
import { APP_INTERCEPTOR } from '@nestjs/core';
providers: [
AppService,
{
provide: APP_INTERCEPTOR,
useClass: Interceptor, // 작성한 interceptor.ts
},
],
[개선해야 할 점]
- 일단 cors를 해결하는데 집중하기 위해 *표시를 사용했으나 그러면 모든 도메인에서 접근이 가능하기 떄문에 frontend 도메인에서만 사용할 수 있게 변경해야 한다.
- 물론 yml 파일도 마찬가지이다.
cloudwatch 확인하기 (bcrypt error)
lambda가 제대로 실행되는지를 확인하기 위해 cloudwatch 로그를 확인한다.
[에러]
"/var/task/node_modules/bcrypt/lib/binding/napi-v3/bcrypt_lib.node: invalid ELF header" in aws lambda
다음 링크를 확인해보자
I'm having a problem running bcrypt on AWS Lambda NodeJS, Can someone help me?
This is the error shown while running. On bcrypt's GitHub wiki, they say that it is a native module for node-js and that it requires a compiler and build dependencies in order to build. What am I d...
stackoverflow.com
bcrypt의 경우 node-gyp를 사용하기 때문에 lambda에서 사용할 때 에러가 난다고 나와있다
bcryptjs로 바꾸면 에러가 발생하지 않는다.
환경변수 관련 이슈
- env 파일에서 불러오지 못해 undefiend가 발생하는 현상
aws console > lambda로 들어가서 환경변수를 설정하는 부분이 있으니 거기에 추가해보자.
개선 사항
- cors 설정 다시하기
- build 해서 올리는데 너무 오래걸린다. yarn berry나 다른 것들을 도입해서 시간을 줄일 수 없는지 한번 확인해 봐야겠다.
- 환경변수 일일이 추가하기 귀찮다 build 단계에서 추가하는 법이 없나 찾아볼 예정이다.