환경 관련 설정 관리

이번 장에서는 여러 환경에 배포된 서비스들이 서로 어떻게 연결되는지 살펴보겠습니다.

먼저 서비스 구성 장에서 만든 설정을 간단히 복습해 보겠습니다.

  1. 두 개의 저장소가 있습니다. serverless-stack-demo-ext-resourcesserverless-stack-demo-ext-api입니다. 하나는 인프라 관련 리소스를 담고 있고, 다른 하나는 모든 Lambda 함수를 포함합니다.
  2. serverless-stack-demo-ext-resources 저장소는 devprod 같은 장기간 유지되는 환경에 배포됩니다.
  3. 반면 serverless-stack-demo-ext-api는 위의 장기간 환경 외에도 dev 환경에 연결된 featureX 같은 일시적인 환경에 배포됩니다.

하지만 featureX 같은 일시적인 환경에 배포하기 전에, 서비스들이 어떤 인프라 환경과 통신해야 하는지 알 수 있는 방법을 찾아야 합니다.

인프라 리소스와 API 서비스 환경이 다음 표와 같이 매핑되도록 해야 합니다.

API Resources
prod prod
dev dev
featureX dev
pr#12 dev
etc… dev

즉, API 서비스의 모든 일시적인 단계가 리소스의 dev 버전을 공유하도록 하고 싶습니다.

이를 어떻게 구현할지 살펴보겠습니다.

앱 간 환경 연결하기

serverless.common.yml(모든 Serverless 서비스에서 공유되는 설정 파일)에서 리소스 앱에 연결할 것입니다. 리소스는 CDK를 사용해 구성되고 SST로 배포된다는 것을 기억하세요.

먼저 Serverless 서비스가 배포될 스테이지를 정의합니다.

custom:
  # Serverless 커맨드를 실행할 때 전달된 스테이지를 사용합니다.
  # 또는 provider 섹션에 설정된 값을 기본값으로 사용합니다.
  stage: ${opt:stage, self:provider.stage}

다음으로 Serverless 서비스와 SST 앱 간의 devprod 스테이지 이름을 매핑합니다.

sstAppMapping:
  prod: prod
  dev: dev

두 저장소에서 사용하는 스테이지 이름이 동일하기 때문에 약간 중복될 수 있습니다. 하지만 Serverless 서비스에서는 dev로, SST 앱에서는 development로 부를 수도 있습니다.

다음으로 SST 앱에 대한 참조를 생성합니다.

sstApp: ${self:custom.sstAppMapping.${self:custom.stage}, self:custom.sstAppMapping.dev}-notes-ext-infra

이를 자세히 살펴보겠습니다.

  • 기본 형식은 ${VARIABLE}-notes-ext-infra입니다.
  • notes-ext-infra는 SST 앱의 이름으로 하드코딩되어 있습니다. 리소스 저장소sst.json에 나열된 대로입니다.
  • ${VARIABLE} 형식은 대체 값을 지정할 수 있게 합니다. ${VARIABLE_1, VARIABLE_2}의 경우, 먼저 VARIABLE_1을 시도하고, 해결되지 않으면 VARIABLE_2를 시도합니다.
  • Serverless Framework는 먼저 self:custom.sstAppMapping.${self:custom.stage}를 해결하려고 시도합니다. 현재 배포 중인 스테이지(self:custom.stage)가 self:custom.sstAppMapping에 매핑되어 있는지 확인합니다. 매핑되어 있다면 해당 값을 사용합니다. 즉, 현재 dev 또는 prod로 배포 중이라면 SST 앱의 해당 스테이지를 사용합니다.
  • 현재 배포 중인 스테이지가 SST 앱의 스테이지(dev 또는 prod가 아님)와 일치하지 않으면 self:custom.sstAppMapping.dev로 대체됩니다. 즉, SST 앱의 dev 스테이지를 사용합니다.

이를 통해 Serverless Framework 서비스와 SST 앱 간의 환경을 올바르게 매핑할 수 있습니다.

참고로, serverless.common.yml의 상단은 다음과 같습니다.

custom:
  # Serverless 커맨드를 실행할 때 전달된 스테이지를 사용합니다.
  # 또는 provider 섹션에 설정된 값을 기본값으로 사용합니다.
  stage: ${opt:stage, self:provider.stage}
  sstAppMapping:
    prod: prod
    dev: dev
  sstApp: ${self:custom.sstAppMapping.${self:custom.stage}, self:custom.sstAppMapping.dev}-notes-ext-infra

이제 sstApp을 기반으로 리소스를 사용할 것입니다. notes-api 서비스의 serverless.yml 파일을 엽니다.

...

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

provider:
  environment:
    stage: ${self:custom.stage}
    tableName: !ImportValue ${self:custom.sstApp}-ExtTableName
...

!ImportValue ${self:custom.sstApp}-ExtTableName 라인은 SST 앱의 적절한 스테이지에서 CloudFormation 내보내기를 가져올 수 있게 합니다. 이 경우 생성된 DynamoDB 테이블의 이름을 가져옵니다.

provider:environment: 옵션은 Lambda 함수에 환경 변수를 추가할 수 있게 합니다. 런타임에 process.env.tableName 변수를 통해 이에 접근할 수 있습니다.

따라서 list.js에서 process.env.tableName 환경 변수에서 tableName을 읽습니다.

const params = {
  TableName: process.env.tableName,
  KeyConditionExpression: "userId = :userId",
  ExpressionAttributeValues: {
    ":userId": event.requestContext.identity.cognitoIdentityId
  }
};

위 설정은 API 서비스에 대해 수많은 임시 환경을 생성하더라도 항상 리소스의 dev 환경에 연결되도록 보장합니다.

다음으로, 환경 간에 비밀을 저장하는 방법을 살펴보겠습니다.