[NestJS] Configuration

cdwde·2022년 11월 16일
post-thumbnail

✅ Configuration

환경에 따라 다양한 구성 변수 세트를 사용해야 한다. 예를 들어, 로컬 환경은 로컬 DB 인스턴스에만 유효한 특정 데이터베이스 자격 증명에 의존한다. 프로덕션 환경은 별도의 DB 자격 증명 집합을 사용한다. 구성 변수가 변경되므로 모범 사례는 구성 변수를 환경에 저장하는 것이다.

외부에서 정의된 환경 변수는 process.env을 통해 내부에서 볼 수 있다. 각 환경에서 개별적으로 환경 변수를 설정해 문제를 해결할 수 있다.

Node.js 애플리케이션에서는 .env 파일을 사용해 환경을 나타내는 것이 일반적이다. 다른 환경에서 앱을 실행시킬 때 다른 .env 파일을 사용하면 된다.

Nest에서는 .env 파일을 로드하는 ConfigService를 expose 하는 ConfigModule을 만드는 방식으로 위의 기술을 사용한다.

✅ Installation

$ npm i --save @nestjs/config

@nestjs/config는 내부적으로 dotenv를 사용한다.

✅ Getting started

ConfigModule을 import 해주자. AppModule에서 임포트해주고, ..forRoot() 메소드를 사용해서 관리할 것이다. 이 단계에서 환경변수 키/값 쌍이 분석되고 해결된다. 이제 다른 기능 모듈에서 ConfigModuleConfigService 클래스에 엑세스 하는 몇가지 옵션들을 볼 수 있다.

  • app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

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

위의 코드는 기본 위치(프로젝트 루트 디렉터리)에서 .env 파일을 로드하고 분석을 한다. .env 파일의 키/값 쌍을 process.env에 할당된 환경 변수와 병합하고, ConfigService를 통해 접근할 수 있는 private structure에 저장한다.

forRoot()메서드는 get() 메서드로 환경 변수를 읽을 수 있는ConfigService provider를 등록한다. @nestjs/config는 dotenv에 의존하므로, 환경 변수 이름의 충돌을 해결하기 위해 패키지의 규칙을 사용한다. 키가 런타임 환경에 환경변수로 존재하는 경우, .env 파일에 키가 있는 경우 런타임 환경 변수가 우선 시 된다.

✅ Custom env file path

기본적으로 루트 디렉터리에서 .env 파일을 찾는다.

ConfigModule.forRoot({
  envFilePath: ['.env.development.local', '.env.development']
});

✅ Use module globally

ConfigModule을 다른 모듈에서도 사용하고 싶다면, import하면 된다. 아니면 isGlobal 속성을 true로 설정해 global module로 선언해도 된다.

ConfigModule.forRoot({
  isGlobal: true,
});

✅ Custom configuration files

더 복잡한 프로젝트의 경우, 사용자 지정 파일을 사용해 중첩된 구성 개체를 반환할 수 있다. 이를 통해 관련 구성 설정을 기능별로 그룹화하고, 개별 파일에 관련 설정을 저장하여 개별적으로 관리할 수 있습니다.

  • config/configuration.ts
export default () => ({
  port: parseInt(process.env.PORT, 10) || 3000,
  database: {
    host: process.env.DATABASE_HOST,
    port: parseInt(process.env.DATABASE_PORT, 10) || 5432
  }
});

이 파일은 ConfigModule.forRoot() 메서드에 전달하는 load 속성을 사용해 로드한다.

import configuration from './config/configuration';

@Module({
  imports: [
    ConfigModule.forRoot({
      load: [configuration],
    }),
  ],
})
export class AppModule {}

아니면 YAML을 사용할 수도 있다.

http:
  host: 'localhost'
  port: 8080

db:
  postgres:
    url: 'localhost'
    port: 5432
    database: 'yaml-db'

  sqlite:
    database: 'sqlite.db'

YAML을 사용하기 위해서는 js-yaml 패키지가 필요하다.

$ npm i js-yaml
$ npm i -D @types/js-yaml

작성한 YAML 파일을 로드하기 위해 yaml#load함수를 사용한다.

import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';

const YAML_CONFIG_FILENAME = 'config.yaml';

export default () => {
  return yaml.load(
    readFileSync(join(__dirname), YAML_CONFIG_FILENAME), 'utf8'),) as Record<string, any>;
};

✅ Using the ConfigService

ConfigService에서 구성 값에 접근하려면 먼저 ConfigService를 주입해야 한다.

  • feature.module.ts
@Module({
  imports: [ConfigModule],
  // ...
})

그리고 주입하자.

constructor(private configService: ConfigService) {}

다른 곳에서 이런 식으로 사용한다.

const dbUser = this.configService.get<string>('DATABASE_USER');
const dbHost = this.configService.get<string>('database.host');

configService.get() 메서드는 변수 이름으로 환경 변수를 가져온다.
해당 메서드는 두 번째 예시처럼 중첩된 사용자 지정 구성 개체에서 뽑아올 수도 있다.

또한 인터페이스를 타입 힌트로 사용해 전체 중첩 사용자 지정 구성 개체를 가져올 수도 있다.

interface DatabaseConfig {
  host: string;
  port: number;
}

const dbConfig = this.configService.get<DatabaseConfig>('database');
const port = dbConfig.port;

키가 존재하지 않을 때 두번째 값을 가져오게 할 수도 있다.

// use "localhost" when "database.host" is not defined
const dbHost = this.configService.get<string>('database.host', 'localhost');

0개의 댓글