dev/nestjs

환경 변수

wlrn566 2023. 2. 12. 00:07

응용 프로그램은 환경에 따라 다르게 실행될 수도 있기에 환경에 따라 다른 구성을 해야한다.

nodejs 에서는 .env 키-값쌍을 포함하는 파일을 이용해 각 환경을 나타내는 것이 일반적이다.

nestjs 에서는 적절한 파일을 로드하는 ConfigureModule을 만드는 것이 좋은 방법이다. 

 

ConfigureModule

 

npm i --save @nestjs/config

 

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

 

패키지 설치후 ConfigModule을 루트에서 import해준다.

 

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { TestModule } from './test/test.module';
import { AuthModule } from './auth/auth.module';
import { ConfigModule } from '@nestjs/config';

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

 

기본적으로 .env 파일을 읽지만 사용자가 정의한 파일을 읽으려면 forRoot() 에 옵션으로 사용할 env파일을 설정해준다.

envFilePath: [~, ~] 이렇게 여러파일을 지정할 수도 있다.

 

// app.module.ts
@Module({
  imports: [
    UserModule,
    TestModule,
    AuthModule,
    ConfigModule.forRoot({
      envFilePath: '.development.env',
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

여러가지 옵션이 있다.

 

import { ConfigFactory } from './config-factory.interface';
import { DotenvExpandOptions } from 'dotenv-expand';
export interface ConfigModuleOptions {
    cache?: boolean;
    isGlobal?: boolean; // 전역 사용
    ignoreEnvFile?: boolean; // env 파일 비활성화
    ignoreEnvVars?: boolean; 
    envFilePath?: string | string[]; // env 파일 경로
    encoding?: string;
    validate?: (config: Record<string, any>) => Record<string, any>;
    validationSchema?: any;
    validationOptions?: Record<string, any>;
    load?: Array<ConfigFactory>;
    expandVariables?: boolean | DotenvExpandOptions;
}

 

isGlobal 옵션을 true 로 설정하면 다른 모듈에서 import 할 필요가 없다.

 

// app.,module.ts
@Module({
  imports: [
    UserModule,
    TestModule,
    AuthModule,
    ConfigModule.forRoot({
      envFilePath: '.development.env',
      isGlobal: true,
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

환경 변수를 사용하려면 ConfigService의 get()을 이용하면 된다.

 

import { UserDto } from './user.dto';
import { UserService } from './user.service';
import { Body, Controller, Post } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Controller('user')
export class UserController {
  constructor(
    private readonly userService: UserService,
    private readonly configService: ConfigService,
  ) {}

  @Post()
  getHelloUser(@Body() user: UserDto): string {
    const dbUser = this.configService.get<string>('DB_USERNAME');
    console.log(dbUser);

    return this.userService.getHelloUser(user.name);
  }
}

 

main.ts 에서 환경 변수 사용

 

import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  const configService = app.get(ConfigService);
  const host = configService.get<string>('DB_HOST');
  console.log(host);  // 127.0.0.1
  
  await app.listen(3000);
}
bootstrap();

 

ConfigModule.forRoot()의 옵션이 길어져서 AppModule이 보기 힘들어진다면 따로 모듈을 만들어서 import 해줘도 된다.

 

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

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: '.development.env',
    }),
],
})
export class ConfigurationModule {}

 

ConfigurationModule을 하나 만들고 AppModule에 import 해준다.

 

// app.module.ts
@Module({
  imports: [ConfigurationModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

 cross-env 

 

운영체제별로 환경변수(NODE_ENV)를 설정하는 방법이 다른데, cross-env 를 이용하여 모든 운영체제에서 동일한 방법으로 환경 변수를 변경 할 수 있다.

 

npm install --save cross-env

 

cross-env 를 이용해 환경변수를 할당한 뒤 명령을 실행하게끔 package.json 파일을 수정해준다.

 

// 기존
"start:dev": "nest start --watch",
"start:prod": "node dist/main",

// 변경
"start:dev": "cross-env NODE_ENV=dev nest start --watch",
"start:prod": "cross-env NODE_ENV=prod node dist/main",

 

유효성 검사

 

Joi 패키지를 이용해 유효성 검사를 한다. 

 

npm install --save joi

 

ConfigurationModule의 forRoot() 옵션에 validationSchema 를 추가한다.

 

// config.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.${process.env.NODE_ENV}.env`,
      validationSchema: Joi.object({
        DB_HOST: Joi.string().required(),
        DB_PORT: Joi.string().required(),
        DB_USERNAME: Joi.string().required(),
        DB_PASSWORD: Joi.string().required(),
        DB_DATABASE: Joi.string().required(),
      }),
    }),
  ],
})
export class ConfigurationModule {}

 

 

Joi를 import 할때 주의해야한다.

 

import Joi from 'joi'; // 인식이 안됨
import * as Joi from 'joi'; // 이렇게 해줘야 인식이 된다.