Serverless 환경에서 배포 전 환경변수 검증 자동화하기: TypeBox와 Bitbucket Pipeline 활용기

들어가며
배포 직후, 환경변수가 제대로 설정되지 않아 여러 API가 제대로 작동하지 않는 일이 있었습니다. 다행히 밤에 사용자가 없을 때 문제가 있었던 거라 영향도는 크지 않았지만 앞으로도 계속해서 발생할 수 있는 문제이기 때문에 해결해야 겠다고 생각했습니다. 개발 단계에서 문제가 발견되면 가장 좋겠지만, 현재 팀 상황에서는 백엔드 개발을 혼자 담당하고 있어 코드 리뷰나 검증 프로세스를 갖추기가 쉽지 않았습니다. 그래서 최소한 배포 전에 자동으로 환경변수를 검증하는 단계를 마련해야겠다고 판단했고, 이 글을 통해 어떤 방식으로 해결했는지 공유해보려 합니다.
문제 상황: 왜 환경변수 검증을 자동화해야 했나
장애의 원인
누락된 환경변수: 서버 실행 전에 환경변수를 제대로 확인하지 못해, Lambda를 Serverless Framework로 배포했지만 실제로 서버는 실행되지 않는 치명적인 상황이 발생했습니다.
복합적인 요인: 개발 단계에서의 검토 프로세스가 미흡했고, 배포 전 검증 프로세스도 없었으며, 혼자서 밤에 서둘러 배포하다 보니 실수를 놓칠 가능성이 컸습니다. 자동화된 테스트 역시 충분치 않아 문제를 조기에 발견하기 어려웠습니다.
왜 환경변수 자동화를 고려했나
백엔드를 혼자 운영: 팀 내에 백엔드 담당자가 저 한 명뿐이라, 코드 리뷰나 배포 프로세스를 고도화하기엔 무리가 있었습니다.
실수 전제: 사람이 하는 일이니 언제든 실수할 수 있다고 보고, 실수가 발생해도 장애가 최소화되도록 하는 안전장치가 필요했습니다.
빠른 해결책: 여러 프로세스를 동시에 개선하기는 어렵지만, 환경변수 검증만큼은 쉽고 빠르게 적용 가능하지만 영향도가 높다고 생각했습니다.
이러한 이유로, 배포 파이프라인에서 환경변수를 자동 검증하는 단계를 추가하게 되었습니다. 결과적으로, 배포 시점에 유효하지 않은 환경변수나 누락된 값을 미리 잡아낼 수 있어 "아예 API 사용이 불가능 한 상황"을 방지할 수 있었습니다.
해결책: TypeBox와 Bitbucket Pipeline을 활용한 환경변수 자동 검증
현재 제가 관리 중인 프로젝트에서는 다음과 같은 기술 스택을 사용하고 있습니다.
AWS Lambda
Serverless Framework
Fastify(서버 프레임워크)
TypeBox(Fastify 스키마 검증 도구)
이 프로젝트에서 환경 변수 스키마를 정의하고 검증하는 작업에도 TypeBox를 도입하게 된 이유는 다음과 같습니다.
Fastify와의 호환성: 이미 Fastify에서 TypeBox로 스키마 검증을 해왔기에, 이를 확장하여 환경 변수 검증에도 손쉽게 적용할 수 있었습니다.
팀 차원의 익숙함: 사내 다른 팀에서도 TypeBox를 적극적으로 활용하고 있어, 이미 팀원들이 사용법에 익숙하다는 장점이 있었습니다.
TypeScript 연계 가능성: TypeBox는 TypeScript 환경에서 객체 스키마를 정의하는 데 유용하며, 타입스크립트 컴파일 단계와 런타임 단계 모두에서 검증을 지원합니다.
간단한 typebox 스키마 정의 예시
typebox를 활용해 환경변수를 검증할 때는, Type.Object()
로 각 환경변수를 정의하고, Value.Check()
나 compiler 기능을 이용해 런타임 검증을 수행할 수 있습니다.
// env-schema.ts
import { Type } from '@sinclair/typebox';
import { Value } from '@sinclair/typebox/value';
// 기본적인 예시
const EnvSchema = Type.Object({
DYNAMO_TABLE_NAME: Type.String(),
API_KEY: Type.String(),
// 필요한 다른 변수들...
});
export function validateEnvValues(envObject) {
const isValid = Value.Check(EnvSchema, envObject);
if (!isValid) {
throw new Error('Invalid environment variables detected.');
}
return envObject;
}
Serverless Framework 문법으로 정의된 환경변수 검증
Serverless Framework에서는 serverless.yml
안에 environment
섹션을 정의해 함수별(또는 공통)로 환경변수를 주입할 수 있습니다. 예를 들어 다음과 같이 스테이지(stage)에 따라 테이블 이름을 동적으로 설정할 수도 있습니다:
service: my-service
provider:
name: aws
runtime: nodejs14.x
stage: ${opt:stage, 'dev'}
environment:
DYNAMODB_TABLE_NAME: dynamodb-table-${opt:stage}
API_KEY: ${env:API_KEY}
functions:
hello:
handler: handler.hello
어떤 문제가 있을까?
동적 레퍼런스 해석 문제
dynamodb-table-${opt:stage}
와 같은 문법은 Serverless Framework가 실제 배포 시점에${opt:stage}
를 해석해 최종 문자열로 치환합니다. 하지만.env
파일이나 등에서 불러온 환경변수만 조회할 경우 실제 값을 확인할 수 없거나 누락될 수 있습니다.CI/CD 상의 검증 누락
배포 전에 환경변수를 검증한다고 해도, 만약 CI 파이프라인이 동적으로 생성된 환경변수를 알 수 없다면 환경변수를 제대로 검증할 수 없습니다.
어떻게 해결했나?
이 문제를 해결하기 위해, Serverless Framework가 제공하는 serverless print
명령어를 활용합니다. 이 명령어는 ${opt:stage}
등의 문법을 실제로 해석해, 최종적으로 어떤 환경변수가 정의되는지 YAML로 출력해줍니다.
예를 들어, 다음과 같이 serverless print
결과물로 serverless-printed.yml
파일을 생성하면
serverless print --stage $STAGE > serverless-printed.yml
최종 해석된 문자열을 다음과 같이 얻을 수 있습니다.
provider:
encironment:
DYNAMODB_TABLE_NAME: dynamodb-table-dev
API_KEY: random-string
이를 이용하면 다음과 같은 식으로 스크립트 파일을 만들어 Bitbucket Pipeline에서 사용 가능합니다:
// scripts/validate-serverless-env.ts
import fs from 'fs';
import yaml from 'js-yaml';
import { validateEnvValues } from '../env-schema'; // typebox 스키마 함수
// serverless-printed.yml 파일을 읽고, YAML을 파싱한 뒤 검증을 수행하는 스크립트 예시
function main(): void {
const printedYaml = fs.readFileSync('serverless-printed.yml', 'utf8');
const printed = yaml.load(printedYaml) as Record<string, any>;
// 최종 해석된 환경변수 객체 추출
const environmentVars = printed?.service?.provider?.environment ?? {};
// 스키마 검증
validateEnvValues(environmentVars);
console.log('Environment variables validated successfully.');
}
// 스크립트를 실행하는 메인 함수 호출
main();
- step:
name: Validate environment variables
script:
- serverless print --stage $STAGE > serverless-printed.yml
- node scripts/validate-serverless-env.js
결론
이번 장애를 계기로, “배포 시점에 환경변수가 올바르게 설정되어 있는지 자동으로 점검한다”는 작업이 생각보다 중요하다는 사실을 다시금 깨달았습니다. 특히 Serverless Framework에서는 스테이지(stage)에 따라 환경변수를 동적으로 정의할 수 있는데, 이때 CI/CD 단계에서 최종 해석된 값을 검증하지 않으면, 배포 후 예상치 못한 문제가 발생할 수 있습니다.
하지만 이렇게 자동화된 검증 프로세스를 한 번 구축해두고 나니 사람이 검수하는 과정에서 놓치는 오류를 자동화된 단계에서 걸러내므로, 꼼꼼히 검토하기 어려운 상황에서 실수로 인한 장애를 크게 줄일 수 있습니다. 또한, Fastify와 연계해 이미 TypeBox를 사용 중이라면, 별다른 도구 학습 없이 환경변수 검증 로직도 손쉽게 만들 수 있다는 게 큰 장점이었습니다.
Subscribe to my newsletter
Read articles from Taejung Heo directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by