환경 변수 란?

실무에서 개발을 하다 보면 반드시 환경 설정을 하게 된다. 여기서 배포를 어떤 환경에 하느냐에 따라서 테스트용, 프로덕션용, QA용을 나눠주게 된다. 그리고 소스 코드에 들어가면 안되는 민감한 값이 있을 수 있는데 이런 값들은 최소 환경 변수로 설정하거나 valut 같은 소스 코드 이외의 외부 저장소에 두어야 한다.

NestJS에서 환경 변수 설정은 ConfigModule에서 할 수 있으며, 설정된 환경 변수를 다른 모듈에서 가져다 쓰려면 ConfigService를 주입받아서 사용해야 한다.

1. 프로젝트 생성 및 설정하기

프로젝트를 생성하고, 의존성 패키지를 설치한다.

npx @nestjs/cli new config-test
npm i @nestjs/config

2. NestJS 설정 및 테스트하기

@nestjs/config 설치 완료 후, 간단하게 환경 변수 설정 및 변수를 읽어보는 테스트를 아래 순서로 해보겠다.

(1) app.module.ts에 ConfigModule 설정 → (2) .env 파일 생성 → (3) app.controller.ts에 테스트 라우팅 함수 추가


순서 1. ConfigModule은 @nestjs/config 패키지에 포함되어 있는 클래스이다. 모듈(app.module.ts)에 ConfigModule을 설정한다

// app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config'; // 추가
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [ConfigModule.forRoot()], // 1. ConfigModule 설정
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

1번. ConfigModule.foorRoot() 함수는 많은 옵션을 설정할 수 있는데, 비어있는 이유는 최소한의 설정만한 상태를 의미한다. 아래 표에 환경 변수 설정 옵션을 참고하자. 여기서, 굵은 글씨로 표시한 isGlobal, cache, envFilePath는 자주 사용하므로 기억해두자.

옵션 설명
cache 메모리 환경 변수를 캐시할지 여부, 애플리케이션의 성능을 향상시킨다
isGlobal true이면 global module로 등록되어, 다른 모듈에서 임포트를 따로 해주지 않아도 된다
ignoreEnvFile true 이면 .env 파일이 무시된다
ignoreEnvVars true 이면 환경변수가 무효가 된다
envFilePath 환경 변수 파일(들)의 경로
encoding 환경 변수 파일의 인코딩
validate 환경 변수의 유효성 검증 함수
load 커스텀 환경 설정 파일을 로딩 시에 사용한다(ts 파일, YAML 파일 등)

순서 2. root 경로(config-test)에 .env 파일을 생성하고, dotenv 라이브러리로 .env 파일을 읽을 수 있다.

// config-test > .env

MESSAGE=hello nestjs // 1. 기본 환경 설정 파일 .env의 환경 변수

1번. MESSAGE가 키이고 hello nestjs가 값이다. 환경 변수의 키는 보통 대문자로 되어 있다.


순서 3. 컨트롤러(app.controller.ts)에 코드를 수정, 추가한다

import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; // 1. ConfigService 임포트

@Controller()
export class AppController {
  constructor(private configService: ConfigService) { } // 2. ConfigService 주입

  @Get()
  getHello(): string {
    const message = this.configService.get('MESSAGE'); // 3. configService.get() 호출
    return message;
  }
}

1번. @nestjs/config에 있는 ConfigService를 임포트

2번. 생성자에서 ConfigService를 주입

3번. getHello() 함수에서 configService.get(“환경 변수명”)을 호출해 값을 message에 할당


순서 4. 테스트하기

서버를 실행해, .env 파일에 저장했던 값(‘hello nestjs’)이 출력되면 성공

image-20230825164127169


3-1. 여러 환경 변수 파일 사용하기

실무에서는 dev(개발용), qa(QA용), beta(베타 서비스용), prod(실제 서비스용) 등 여러가지 환경 변수를 사용한다. Node.js에서는 일반적으로 NODE_ENV 환경 변수에 용도별 환경 변수를 정의해 사용한다. 서버 기동 시 사용하는 명령어를 수정해 NODE_ENV에 값을 할당해 환경별로 서버 설정을 다르게 적용할 수 있다

순서 1. 환경별로 서버가 기동되도록 스크립트 수정하기

local, dev, prod 환경에서 서버를 기동하려면 package.json의 script 항목에 스크립트를 아래와 같이 추가한다. 아래서 주의할 점은 NODE_ENV=local과 &&사이에 공백이 생기지 않도록 해야한다.

// package.json

... 생략 ...
"scripts": {
	... 생략 ...
	"start": "set NODE_ENV=local&&nest start", // 1. NODE_ENV에 local 할당
	"start:dev": "set NODE_ENV=dev&&nest start --watch", // 2. NODE_ENV에 dev 할당
	"start:debug": "nest start --debug --watch",
	"start:prod": "set NODE_ENV=prod&&node dist/main", // 3. NODE_ENV에 prod 할당
    ... 생략 ...

순서 2. 서버 기동 시 값이 올바르게 나오는지 모듈에 console.log()를 추가한 후, 테스트한다

... 생략 ...
console.log('env : ' + process.env.NODE_ENV); // 1. 기동 시 환경 변수 출력

@Module({
  imports: [ConfigModule.forRoot()],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

1번. 서버 기동 시 실행되며 NODE_ENV 환경 변수를 출력한다. Node.js에서 환경 변수는 process.env.{환경 변수명} 형식을 사용한다. process는 내장 객체이므로 따로 import문을 사용하지 않아도 된다.


순서 3. 터미널에서 npm run start:dev와 npm run start:prod를 실행하여 각각 환경 변수를 확인한다.

npm run start:dev
env : dev

npm run start:prod
env : prod

3-2. local, dev, prod 환경 변수 생성

각각 loca, dev, prod용 환경 변수를 만들고 서버 기동 시 각 환경에 맞는 환경 변수 파일을 사용한다. ConfigModule.forRoot()의 옵션 중 envFilePath 옵션을 사용해 구현하자

순서 1. root에 envs 디렉터리를 생성한다


순서 2. config-test 서비스 URL이 환경별로 각각 local : http://localhost:3000, dev : http://dev.config-test.com, prod : http://config-test.com 이라고 가정하고, envs 디렉터리 안에 local.env, dev.env, prod.env 파일을 생성해 환경 변수를 추가한다

// config-test > envs > local.env

SERVICE_URL=http://localhost:3000
// config-test > envs > dev.env

SERVICE_URL=http://dev.config-test.com
// config-test > envs > prod.env

SERVICE_URL=http://config-test.com

3-3. 환경 변수에 따라서 다른 환경 변수 파일을 사용하도록 설정 수정

환경 변수에 따라서 다른 환경 변수 파일을 사용하는 envFilePath를 적용할 수 있게 app.module.ts를 수정한다. envFilePath는 환경 변수 파일의 경로를 지정하는 옵션이다.

// app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';

console.log('env : ' + process.env.NODE_ENV);
console.log('current working directory : ' + process.cwd()); // 1. 현재 디렉터리 출력
console.log(`${process.cwd()}\\envs\\${process.env.NODE_ENV}.env`); // 2.환경 변수 파일 경로 출력

@Module({
  imports: [
    ConfigModule.forRoot({
      // 3. 환경 변수 파일 경로 지정
      isGlobal: true,
      envFilePath: `${process.cwd()}\\envs\\${process.env.NODE_ENV}.env`
    })
  ],
  // ... 3
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

1번. process.cwd()는 현재 디렉터리의 절대 경로를 출력한다

2번. 여기서 애를 먹었는데, 교재와 다르게 디렉터리 간 문자는 “\“를 사용하니 참고하자

3번. 1번 경로를 베이스로 envs > 환경변수값.env 를 참조한다


3-4. 테스트용 핸들러 함수로 테스트하기

환경 변수값을 확인하는 핸들러 함수를 만든다. http://localhost:3000/service-url에 접속해 각 환경별로 추가한 SERVICE_URL 환경 변수가 화면에 잘 표시되게 해보자

순서 1. 컨트롤러(app.controller.ts)에 환경 변수 테스트용 핸들러 함수를 추가한다

// app.controller.ts

... 생략 ...

@Controller()
export class AppController {
  ... 생략 ...
  @Get('service-url') // 1. http://localhost:3000/service-url의 경로 진입 시 실행
  getServiceUrl(): string {
    return this.configService.get('SERVICE_URL'); // 2. SERVICE_URL 환경 변수 반환
  }
}

1번. (설명 생략)

2번. 환경 변수에서 SERVICE_URL값을 읽어서 반환한다. 브라우저에서는 SERVICE_URL에 해당하는 문자열이 나오게 된다.


순서 2. npm run start, npm run start:dev, npm run start:prod를 실행하고 브라우저에서 접근해보자

image-20230826172646743

image-20230826172728558

image-20230826172804498


4-1. .env가 아닌, 커스텀 환경 설정 파일(config.ts/YAML) 사용하기

.env 파일 확장자를 가지는 환경 설정 파일 이외에 .ts를 확장자로 가지는 파일도 환경 설정 파일로 사용할 수 있다. 일반적으로 .env만으로도 충분하지만, 복잡한 설정이 필요할 때 .ts 환경 설정 파일을 사용한다.

순서 1. 실행 시 logLevel, apiVersion, MESSAGE 등 공통으로 사용할 환경 변수를 정의하는 파일을 정의한다

// src > configs > common.ts

export default { // 1. 공통 환경 변수를 반환
    logLevel: 'info',
    apiVersion: '1.0.0',
    MESSAGE: 'hello',
}

순서 2. 로컬 개발 환경의 환경 변수를 반환하는 파일을 만든다

// src > configs > local.ts

export default { // 로컬 개발 환경의 환경 변수 반환
    dbInfo: 'http://localhost:3306', // 1. DB 접속 정보
}

순서 3. 개발 환경의 변수를 반환하는 파일을 정의한다

// src > configs > dev.ts

export default { // 개발 환경의 환경 변수 반환
    logLevel: 'debug', // 1. 로그 레벨
    dbInfo: 'http://dev-mysql:3306',
}

순서 4. 프로덕션 환경의 환경 변수를 반환하는 파일을 정의한다

// src > configs > prod.ts
export default { // 프로덕션 환경의 환경 변수 반환
    logLevel: 'error',
    dbInfo: 'http://prod-mysql:3306',
}

순서 5. common.ts와 각 환경 변수를 합쳐주는 config.ts 파일을 만든다

// src > configs > config.ts

import common from './common';
import local from './local';
import dev from './dev';
import prod from './prod';

const phase = process.env.NODE_ENV; // 1. phase에 NODE_ENV값 저장

let conf = {}; // 2. phase의 값에 따라서 적절한 환경 변숫값을 conf에 저장
if (phase === 'local') {
    conf = local;
} else if (phase === 'dev') {
    conf = dev;
} else if (phase === 'prod') {
    conf = prod;
}

// 3. common과 conf에서 받은 값을 합쳐서 결괏값으로 주는 함수 반환
export default () => ({
    ...common,
    ...conf,
});

1번. process.env.NODE_ENV값을 여러번 사용해야 하므로 phase 변수에 저장

2번. (설명 생략)

3번. ()=>({}) 형태로 값을 주어야 하므로 ()=>()로 객체를 한번 감싸준다. Record는 객체를 결괏값으로 받는 함수 타입을 의미하고 <string, any>에서 string은 키의 타입이다.


순서 6. load 옵션을 추가하기 위해, app.module.ts의 ConfigModule 설정에 load 옵션을 추가한다

// app.module.ts

... 생략 ...
import config from './configs/config'; // 임포트 추가

... 생략 ...
@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `${process.cwd()}\\envs\\${process.env.NODE_ENV}.env`,
      load: [config], // 1. 커스텀 설정 파일 설정
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }


4-2. 커스텀 환경 변수 읽기 테스트하기

커스텀 환경 변수 읽기 테스트를 하기 위해, app.controller.ts에 테스트용 함수를 추가한다.

// app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Controller()
export class AppController {
... 생략 ...

  @Get('db-info') // 1. 라우팅 정보
  getTest(): string {
    console.log(this.configService.get('logLevel')); // 2. logLevel 터미널에 출력
    console.log(this.configService.get('apiVersion')); // 3. apiVersion 터미널에 출력
    return this.configService.get('dbInfo'); // 4. 웹브라우저에 dbInfo 표시
  }
}

image-20230826180143214

info
1.0.0

image-20230826180240120

debug
1.0.0

image-20230826180321126

error
1.0.0

참고

다음에 다루게 될 것

  • NestJS 환경변수(2) - 서버 기동과 환경 설정 파일 초기화 순서 및 YAML 파일을 사용한 환경 변수 설정

댓글남기기