기능 환경 생성하기

지난 몇 장에서 Lambda와 API Gateway를 로컬에서 작업하는 방법을 살펴봤습니다. 하지만 Lambda와 API Gateway 외에도 여러분의 프로젝트에는 다른 AWS 서비스들이 있을 것입니다. 코드를 로컬에서 실행하려면 모든 AWS 서비스를 시뮬레이트해야 합니다. serverless-offline과 유사하게, serverless-dynamodb-localserverless-offline-sns 같은 플러그인을 사용해 DynamoDB와 SNS를 시뮬레이트할 수 있습니다. 하지만 모킹은 IAM 권한을 시뮬레이트하지 않으며, 서비스의 최신 변경 사항을 항상 반영하지 못하기 때문에 한계가 있습니다. 여러분은 실제 리소스를 사용해 코드를 테스트하고 싶을 것입니다.

서버리스는 일시적인 환경을 생성하는 데 매우 뛰어납니다. 앱에 새로운 기능을 추가하려고 할 때의 워크플로우를 살펴보겠습니다.

예를 들어, 노트에 _좋아요_를 누를 수 있는 기능을 추가해 보겠습니다. 새로운 API 엔드포인트 /notes/{id}/like를 추가할 것입니다. 이 작업을 새로운 기능 브랜치에서 진행한 다음, Seed를 사용해 배포할 예정입니다.

기능 브랜치 생성

like라는 새로운 기능 브랜치를 생성합니다.

$ git checkout -b like

/notes/{id}/like를 엔드포인트로 사용할 예정이므로, 먼저 /notes/{id} API 경로를 내보내야 합니다. services/notes-api 서비스의 serverless.yml을 열고 리소스 출력에 다음 내용을 추가합니다.

ApiGatewayResourceNotesIdVarId:
  Value:
    Ref: ApiGatewayResourceNotesIdVar
  Export:
    Name: ${self:custom.stage}-ExtApiGatewayResourceNotesIdVarId

이제 리소스 출력은 다음과 같이 표시됩니다.

---
- Outputs:
    ApiGatewayRestApiId:
      Value:
        Ref: ApiGatewayRestApi
      Export:
        Name: ${self:custom.stage}-ExtApiGatewayRestApiId

    ApiGatewayRestApiRootResourceId:
      Value:
        Fn::GetAtt:
          - ApiGatewayRestApi
          - RootResourceId
      Export:
        Name: ${self:custom.stage}-ExtApiGatewayRestApiRootResourceId

    ApiGatewayResourceNotesIdVarId:
      Value:
        Ref: ApiGatewayResourceNotesIdVar
      Export:
        Name: ${self:custom.stage}-ExtApiGatewayResourceNotesIdVarId

이제 like-api 서비스를 생성합니다.

$ cd services
$ mkdir like-api
$ cd like-api

serverless.yml 파일을 추가합니다.

service: notes-app-ext-like-api

plugins:
  - serverless-bundle
  - serverless-offline

custom: ${file(../../serverless.common.yml):custom}

package:
  individually: true

provider:
  name: aws
  runtime: nodejs12.x
  stage: dev
  region: us-east-1
  tracing:
    lambda: true

  apiGateway:
    restApiId: !ImportValue ${self:custom.stage}-ExtApiGatewayRestApiId
    restApiRootResourceId: !ImportValue ${self:custom.stage}-ExtApiGatewayRestApiRootResourceId
    restApiResources:
      /notes/{id}: !ImportValue ${self:custom.stage}-ExtApiGatewayResourceNotesIdVarId

  environment:
    stage: ${self:custom.stage}

  iamRoleStatements:
    - ${file(../../serverless.common.yml):lambdaPolicyXRay}

functions:
  like:
    handler: like.main
    events:
      - http:
          path: /notes/{id}/like
          method: post
          cors: true
          authorizer: aws_iam

like-apinotes-api 서비스와 동일한 API 엔드포인트를 공유합니다.

핸들러 파일 like.js를 추가합니다.

import { success } from "../../libs/response-lib";

export async function main(event, context) {
  // 게시물 좋아요를 위한 비즈니스 로직 코드

  return success({ status: true });
}

이제 Git 브랜치를 푸시하기 전에 Seed에서 브랜치 워크플로우를 활성화합니다.

Seed에서 브랜치 워크플로우 활성화

Seed에서 여러분의 앱으로 이동한 후 Pipeline 탭으로 이동하여 Edit Pipeline을 클릭하세요.

Seed에서 파이프라인 편집 선택

Auto-deploy branches를 활성화하세요.

자동 배포 브랜치 활성화 선택

dev 스테이지를 선택하세요. 이 스테이지는 Development AWS 계정에 배포되길 원하기 때문입니다. Enable을 클릭하세요.

자동 배포 활성화 선택

Pipeline을 클릭하여 돌아가세요.

파이프라인으로 돌아가기

새로운 서비스를 Seed에 추가하기

서비스 추가를 클릭하세요.

서비스 추가 선택

서비스 경로 services/like-api를 입력하고 검색을 클릭하세요.

새 서비스 경로 검색 선택

코드가 아직 Git에 커밋되지 않았기 때문에 Seed는 서비스의 serverless.yml을 찾을 수 없습니다. 이는 정상적인 현상입니다. 서비스 이름을 like-api로 지정한 후 서비스 추가를 클릭하세요.

새 서비스 이름 설정

이렇게 하면 모든 스테이지에 새로운 서비스가 추가됩니다.

Seed에 새 서비스 추가됨

기본적으로 새 서비스는 마지막 배포 단계에 추가됩니다. 배포 단계 관리를 클릭하고, 이를 Phase 2로 이동시킵니다. 이는 notes-api에서 내보낸 API Gateway 리소스에 의존하기 때문입니다.

기본 배포 단계 표시

새로운 기능 배포를 위한 Git push

이제 새로운 기능 환경을 만들 준비가 되었습니다. 커맨드라인으로 돌아가서 코드를 like 브랜치에 푸시합니다.

$ git add .
$ git commit -m "Add like API"
$ git push --set-upstream origin like

Seed로 돌아가면 like라는 새로운 스테이지가 생성되고 자동으로 배포됩니다.

새로운 기능 스테이지 생성 표시

새로운 스테이지가 성공적으로 배포된 후, 스테이지의 리소스 페이지에서 API 엔드포인트를 확인할 수 있습니다. Resources 탭으로 이동합니다.

Seed에서 Resources 탭 선택

그리고 like 스테이지를 선택합니다.

기능 스테이지 선택

like 스테이지의 API Gateway 엔드포인트와 like 핸들러의 API 경로를 확인할 수 있습니다.

기능 스테이지에서 API Gateway 엔드포인트 표시

이제 프론트엔드에서 추가 테스트와 개발을 위해 이 엔드포인트를 사용할 수 있습니다.

새로운 기능 환경이 생성되었으니, 새로운 기능 작업을 위한 흐름을 간단히 살펴보겠습니다.

로컬에서 새로운 기능 환경 작업하기

환경을 생성한 후에는 기능 작업을 계속 진행하고 싶을 것입니다. 흔히 발생하는 문제는 serverless deploy 명령어 실행이 매우 오래 걸린다는 점입니다. 그리고 모든 변경 사항마다 serverless deploy를 실행하는 것은 실용적이지 않습니다.

‘serverless deploy’가 느린 이유

serverless deploy를 실행하면 Serverless Framework는 두 가지 작업을 수행합니다:

  1. Lambda 코드를 zip 파일로 패키징합니다.
  2. serverless.yml에 정의된 모든 리소스로 CloudFormation 템플릿을 빌드합니다.

코드는 S3에 업로드되고, 템플릿은 CloudFormation에 제출됩니다.

여기서 느려지는 주요 원인은 다음과 같습니다:

  • 기능 개발 중에는 대부분의 변경 사항이 코드 변경입니다. 모든 코드 변경마다 CloudFormation 템플릿을 다시 빌드하고 제출할 필요는 없습니다.
  • 코드를 변경할 때, 대부분의 경우 하나의 Lambda 함수만 변경합니다. 이 경우 서비스 내 모든 Lambda 함수의 코드를 다시 패키징할 필요가 없습니다.

개별 함수 배포하기

다행히도 serverless deploy -f 명령어를 사용해 개별 함수를 배포할 수 있습니다. 예제를 살펴보겠습니다.

새로 만든 like.js 코드를 다음과 같이 변경했다고 가정해 보겠습니다.

import { success } from "../../libs/response-lib";

export async function main(event, context) {
  // 게시물 좋아요를 위한 비즈니스 로직 코드

  console.log("테스트를 위해 디버그 코드 추가");

  return success({ status: true });
}

이 함수의 코드를 배포하려면 다음 명령어를 실행합니다.

$ cd services/like-api
$ serverless deploy -f like -s like

전체 스택을 배포하는 것보다 개별 함수를 배포하는 것이 훨씬 빠릅니다.

여러 함수 배포하기

때로는 코드 변경이 여러 함수에 동시에 영향을 미칠 수 있습니다. 예를 들어, 공유 라이브러리를 변경했다면 해당 라이브러리를 사용하는 모든 서비스를 다시 배포해야 합니다.

하지만 여러 Lambda 함수를 편리하게 배포할 수 있는 방법은 없습니다. 영향을 받는 Lambda 함수를 쉽게 파악할 수 있다면, 각각 개별적으로 배포하세요. 관련된 함수가 많다면 serverless deploy -s like를 실행하여 모두 배포합니다. 안전을 위해 이렇게 하는 것이 좋습니다.

이제 새로운 기능 작업을 마치고 프로덕션에 반영하기 전에 팀 리더가 코드를 검토할 수 있도록 하려고 합니다. 이를 위해 풀 리퀘스트 환경을 생성할 것입니다. 다음으로 이를 어떻게 하는지 살펴보겠습니다.