docs.nestjs.com/techniques/configuration
먼저, 왜 dotenv만 쓰면 되지 @nestjs/config를 사용해야 하느냐는 의문이 있을 수 있습니다.
nest와 같은 프레임워크에서는 최소한 test/dev/prod 세 환경에서 실행하게 되는데 각 환경마다 다른 환경 변수를 사용할 필요가 있기 때문입니다. 예전 처럼 dev/prod 단 두 환경만 있었다면 어느 환경인지 간단하게 체크하기만 했어도 되었지만 이제는 점차 환경이 분화되고 있기에 @nestjs/config를 통해서 다양한 환경에 다양한 환경 변수를 사용하는 것이 좋습니다.
물론, nest 프레임워크를 사용하지 않고서도 다양한 환경에 적합한 환경 변수를 불러오는 방법이 있긴 합니다. 다음과 같이 별도의 환경 변수를 require하는 방식입니다.
* John Ahn님의 코드(github.com/jaewonhimnae/boilerplate-mern-stack/tree/master/server) 를 참고했습니다.
if (process.env.NODE_ENV === 'production') {
module.exports = require('./prod');
} else if (process.env.NODE_ENV === 'testing') {
module.exports = require('./test');
} else {
module.exports = require('./dev');
}
그러나 현재 nest 프레임워크를 이용해 프로젝트를 진행하고 있다면 굳이 이런 방식을 사용하실 필요는 없습니다. 편하게 쓰라고 만들어뒀으니 잘 쓰면 됩니다 ㅎ
Installation
yarn add @nestjs/config
The @nestjs/config package internally uses dotenv.
Getting started! (기본 사용)
ConfigModule을 불러와서 AppModule에 다음과 같이 장착합니다.
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [ConfigModule.forRoot()],
})
export class AppModule {}
forRoot에 옵션으로는 다음과 같은 것이 있습니다.
dynamic Module을 만들 때 사용되는 것보다 더 많은 옵션들이 존재하는 것을 확인하실 수 있습니다.
import { ConfigFactory } from './config-factory.interface';
export interface ConfigModuleOptions {
cache?: boolean;
isGlobal?: boolean;
ignoreEnvFile?: boolean;
ignoreEnvVars?: boolean;
envFilePath?: string | string[];
encoding?: string;
validate?: (config: Record<string, any>) => Record<string, any>;
validationSchema?: any;
validationOptions?: Record<string, any>;
load?: Array<ConfigFactory>;
expandVariables?: boolean;
}
envFilePath
By default, the package looks for a .env file in the root directory of the application.
To specify another path for the .env file, set the envFilePath property of an (optional) options object you pass to forRoot(), as follows:
ConfigModule.forRoot({
envFilePath: '.development.env',
});
여러 env 파일을 불러오고싶다면 아래처럼 arr 형태로 넘기면 됩니다. 그러나 모두 다 읽어오는 것이 아니라 처음 발견된 것 하나만 읽습니다 (If a variable is found in multiple files, the first one takes precedence.)
ConfigModule.forRoot({
envFilePath: ['.env.development.local', '.env.development'],
});
ignoreEnvFile
쉽게 말해 env 파일을 사용하고 싶지 않을 때 설정하는 겁니다.
If you don't want to load the .env file, but instead would like to simply access environment variables from the runtime environment (as with OS shell exports like export DATABASE_USER=test), set the options object's ignoreEnvFile property to true, as follows:
ConfigModule.forRoot({
ignoreEnvFile: true,
});
isGlobal
Dynamic Module을 공부하면서 Module을 직접 만들어보시면 알겠지만, 다른 곳에서도 환경 변수를 쉽게 불러와 사용하기 위해서 ConfigModule을 global module로 만들어줍시다.
ConfigModule.forRoot({
isGlobal: true,
});
자, 여기까지 정리한 내용을 반영하여 다음과 같이 작성할 수 있습니다.
우선 각 환경에 사용할 환경변수들을 담아 둘 파일을 생성합시다. 저는 dev, test, prod 환경에서 사용할 예정이라서 아래와 같이 만들었습니다.
ConfigModule.forRoot({
isGlobal: true,
envFilePath: process.env.NODE_ENV === 'dev' ? '.env.dev' : '.env.test',
ignoreEnvFile: process.env.NODE_ENV === 'prod', // prod할 때는 heroku에 따로 넣기로
}),
환경 변수 유효성 검사하기
docs.nestjs.com/techniques/configuration#schema-validation
환경 변수가 undefined이라던가 잘못된 값이 들어와서 왜 안되는지 고생해본 경험이 있으시다면 아주 유용할겁니다.
validationSchema
joi는 워낙 유명해서, 설명 방법을 npm 페이지 첨부로 설명을 갈음합니다.
yarn add joi
다음과 같이 사용하는 환경 변수를 validation할 수 있습니다.
import Joi from 'joi'; // export= 방시으로 되어 있어서 esModuleInterop를 true로 켜줌
ConfigModule.forRoot({
isGlobal: true,
envFilePath: process.env.NODE_ENV === 'dev' ? '.env.dev' : '.env.test',
ignoreEnvFile: process.env.NODE_ENV === 'prod', // prod할 때는 heroku에 따로 넣기로
validationSchema: Joi.object({
NODE_ENV: Joi.string().valid('dev', 'prod', 'test').required(),
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(),
}),
}),
다른 파일에서 환경 변수 로드하기
일반적으로 다음과 같이 process.env를 통해 환경 변수를 불러올 수 있습니다만 nest의 방식은 아닙니다.
// process.env.TOKEN_SECRET
const token = jwt.sign({ id: user.id }, process.env.TOKEN_SECRET, { algorithm: 'RS256' });
앞서 AppModule처럼 ConfigModule을 지정한 후 환경 변수를 사용하고자 하는 scope의 모듈에 ConfigService를 부착한다.
import { ConfigService } from '@nestjs/config';
@Module({
imports: [TypeOrmModule.forFeature([User]), ConfigService],
providers: [UserResolver, UsersService],
})
export class UsersModule {}
그리고 비즈니스 로직을 구현하는 곳에서(주로 Service) 다음과 같이 ConfigService를 불러온 후 this.config.get('환경 변수')꼴로 가져오면 된다.
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly users: Repository<User>,
private readonly config: ConfigService, // ConfigService 불러오기
) {}
... 중략
// make jwt token and git it to user
const token = jwt.sign({ id: user.id }, this.config.get('TOKEN_SECRET'), { algorithm: 'RS256' });
}
}
'Node, Nest, Deno > 🦁 Nest.js' 카테고리의 다른 글
Nest Middleware (0) | 2020.11.24 |
---|---|
dynamic-modules 만들기 (0) | 2020.11.24 |
DTO를 통한 검증을 위한 Pipe (Class Validator + mapped-types ...) (0) | 2020.11.19 |
Module에 대한 이해와 간단한 DI (0) | 2020.09.25 |
Provider 중 service에 대하여 (0) | 2020.09.25 |