환경 변수
응용 프로그램은 환경에 따라 다르게 실행될 수도 있기에 환경에 따라 다른 구성을 해야한다.
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'; // 이렇게 해줘야 인식이 된다.