먼저 환경변수 설정을 위한 패키지를 설치해줍니다.
$ npm i --save @nestjs/config
환경변수 설정을 위한 임시 파일을 생성해줍니다.
.env 파일을 활용할 수도 있지만 여기서는 .yaml형식을 사용합니다.
db:
host: 222.222.222.222
user: development.yaml
password: aaa
synchronize: true
db:
host: 111.111.111.111
user: test.yaml
password: test
synchronize: true
환경변수를 key, value형태의 객체로 변환하기 위한 로직을 작성해줍니다.
// 파일명 : configuration-yaml.ts
// 환경변수에 따라 설정 파일 이름을 결정
const YAML_CONFIG_FILENAME = `${process.env.NODE_ENV}.yaml`;
const YAML_CONFIG_PATH = join(__dirname, YAML_CONFIG_FILENAME);
console.log('경로 ', YAML_CONFIG_PATH);
export default () => {
return yaml.load(
readFileSync(YAML_CONFIG_PATH, 'utf-8')
) as Record<string, any>;
};
${process.env.NODE_ENV}.yaml
;
어떻게 보면 여기서 가장 눈여겨보아야 할 부분 중 하나입니다.
프로젝트를 시작할 때 지정된 환경변수를 가지고 와서 그에 따라 각자 다른 파일을 로드합니다.
이제 환경변수를 프로젝트에서 사용할 수 있도록 모듈에 등록해줍니다.
// app.module.ts
import configurationYaml from './config/configuration-yaml';
ConfigModule.forRoot({
// 배열을 통해 여러 경로를 지정할 수 있으며 앞에 있는 변수가 우선 적용
envFilePath: [`config/.env.${process.env.NODE_ENV}`],
ignoreEnvFile: true,
// 환경변수를 전역으로 사용할 시 설정
isGlobal: true,
load: [configurationYaml],
validate
}),
모듈에 정상적으로 등록까지 완료했다면 configService를 주입받아 환경변수를 활용할 수 있습니다.
먼저 development.yaml파일을 기준으로 설명을 해보겠습니다.
@Injectable()
export class MemberService {
constructor(
private jwtService: JwtService,
private configService: ConfigService
){
const dbConfig = this.configService.get("db", "default");
console.log('환경변수 테스트', dbConfig);
}
이런 방식으로 하다보면 단순 문자열이라 키 값이 일치하는지 확인하기 어렵습니다.
따라서 nest공식 문서에서 설명하는 타입 지정 방식을 조금 응용해 타입을 지정해보겠습니다.
export interface ConfigKey {
db: DbConfig
}
export interface DbConfig{
host: string;
user: string;
synchronize: boolean;
password: string;
}
yaml에 지정된 depth에 따라 인터페이스를 작성하였습니다.
이제 타입을 지정해서 설정값을 가져와보겠습니다.
@Injectable()
export class MemberService {
constructor(
private configService: ConfigService<ConfigKey>
){
const dbConfig: DbConfig = this.configService.get("dbfdfdf");
console.log('환경변수 테스트', dbConfig);
}
일부러 잘못된 키 값을 입력한 결과 컴파일 단계에서 에러가 발생시키는 것을 확인할 수 있습니다.
이번에는 db 밑에 있는 설정값들을 타입을 지정해 가져와보겠습니다.
@Injectable()
export class MemberService {
constructor(
private configService: ConfigService<ConfigKey>
){
const dbConfig: DbConfig = this.configService.get("db");
console.log('환경변수 테스트', dbConfig.host);
}
이런 방식으로 조금 더 안전하게 환경 변수를 활용할 수 있을 것 같습니다.
마찬가지로 db내에 존재하지 않는 키값을 입력했을 때 에러가 발생하는 것을 확인할 수 있습니다.
이런식으로 타입을 지정하면 키값이 수정되었을 때 interface만 수정해주면 되고, 컴파일 시점에 에러를 발견할 수 있으니 이런 방식으로 활용하는게 좋을 것 같습니다.
여기서 시간을 꽤 잡아먹었는데 공식 문서를 꼼꼼히 확인하지 않았네요.
기본적으로 nestjs에서는 ts파일 같은 동적 파일은 컴파일을 지원하지만 정적파일의 경우 별도로 dist파일로 컴파일을 하거나 하지 않습니다.
대신 nest-cli.json에서 아래와 같이 설정해줍니다.
"compilerOptions": {
"assets": [{"include": "../config/*.yaml", "outDir": "./dist/config"}],
"watchAssets": true,
"deleteOutDir": true
}
이제 실행 환경에 따라 환경변수를 별도로 설정해보겠습니다.
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:test": "cross-env NODE_ENV=test nest start --watch",
기존에 있던 start:dev 스크립트를 수정하고, 추가로 테스트를 위한 start:test 스크립트를 작성했습니다.
이제 각 실행환경을 테스트 해보면
start:dev
start:test
각 실행환경에 따라 다른 값이 지정된 것을 확인할 수 있습니다.
참고문서 : NestJS-Configuration