본문 바로가기

Back-End/Nest.js

동적 모듈을 활용한 환경 변수 구성

동적 모듈

- 모듈이 생성될때 동적으로 어떠한 변수들이 정해지는 것.

- 모듈 인스턴스마다 다르게 결정되어야 하는 것들을 소비 모듈에서 지정할 수 있기 때문에 코드가 간결해지는 장점.

- 동적 모듈의 대표적인 예로 Config 모듈이 있음.

 

Config

- 실행 환경에 따라 서버에 설정되는 환경 변수(environmental variable)를 관리하는 모듈.

- 일반적으로 서비스를 개발할때 실행 환경이 3가지로 나뉘는데 다음과 같다.

1. Development Server : 개발자 PC 환경에서 개발및 테스트를 진행하게 되는 곳.
2. Stage Server : 개발한 코드를 테스트 환경에 배포하여 통합 테스트를 진행하는 곳.
3. Production Server : 실제 서비스 운영을 위한 환경

- 이렇게 실행 환경이 달라지게 되는데, 실행 환경에 따라 달라지는 변수들이 있다.

- 환경마다 다른 호스트 이름을 가지는 데이터베이스에 연결하기 위해 코드를 따로 작성하는 것은 매우 비효율적이다.

- 그렇기 때문에 우선 각 환경 변수를 .env 확장자를 가진 파일에 저장해두고 서버가 구동될 때 이 파일을 읽어 해당 값을 환경 변수로 설정해준다.

환경 변수 파일은 시크릿 키와 같이 민감한 정보가 저장되는 경우가 많다.
따라서 github에 배포되지 않도록 해야한다.  그러므로 환경 변수 파일을 .gitignore파일에 추가해야 한다.

- Node.js는 NODE_ENV라는 환경 변수를 활용하여 서버의 환경을 구분힌다.

- 다음 코드는 package.json 파일을 수정하여 yarn run start:dev 명령어가 수행될때 NODE_ENV가 development로 설정되도록 한다.

"scripts": {
    "prebuild": "rimraf dist",
	...
    "start:dev": "yarn run prebuild && NODE_ENV=development nest start --watch",
	...
}

- 또한 깨끗한 상태에서 다시 컴파일하도록 prebuild 명령을 먼저 수행한다.

 

- Node에서는 dotenv패키지를 직접 사용할 수 있지만, Nest는 dotenv를 내부적으로 활용하는 @nestjs/config 패키지를 제공한다.

- 이 패키지에는 ConfigModule 이름을 가진 모듈이 이미 존재한다.

...
import { ConfigModule } from '@nestjs/config';

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

- 정적 모듈을 가져올때와는 달리 .forRoot() 메서드를 호출하는 것을 볼 수 있다.

forRoot()

- 동적 모듈을 리턴하는 정적 메서드이다.

- 인수로는 ConfigModuleOptions를 받는다. 

- 즉, 소비 모듈이 원하는 옵션값을 전달하여 원하는대로 동적으로 ConfigModule을 생성한다.

ConfigModuleOptions에는 envFilePath 외에도 여러 가지 옵션이 있다.

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;
}

1) envFilePath

- 기본적으로 패키지는 응용 프로그램의 루트 디렉토리에서 .env 파일을 찾는다.

- .env파일의 다른 경로를 지정할때 envFilePath 옵션을 지정해 주면 된다. 

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

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath:
        process.env.NODE_ENV === 'production'
          ? '.production.env'
          : process.env.NODE_ENV === 'stage'
          ? '.stage.env'
          : '.development.env',
    }),
    ...
    ],

2) isGlobal

- true로 지정해 준다면 다른 곳에서도 환경 변수를 불러와 사용할 수 있게 된다. 

 

 

커스텀 Config 파일 작성

- 모든 환경 변수가 .env파일에 선언되어 있지만, 가져다 쓸 때는 DatabaseConfig, EmailConfig와 같이 의미 있는 단위로 묶어서 처리하고 싶을 때가 있다.

 

- 우선 이메일 관련 환경 변수를 관리하는 emailConfig.ts를 작성해보자.

- 이 코드에는 앞서 이메일을 발송하기 위해 사용했던 계정 정보와 패스워드를 작성한다.

// /src/config/emailConfig.ts

import { registerAs } from '@nestjs/config';

export default registerAs('email', () => ({
  service: process.env.EMAIL_SERVICE,
  auth: {
    user: process.env.AUTHEMAIL,
    pass: process.env.AUTHEMAILPASSWORD,
  },
  baseUrl: process.env.EMAIL_BASE_URL,
}));

- emailConfig.ts의 코드를 쉽게 설명하면 'email'이라는 토큰으로 ConfigFactory를 등록할 수 있는 함수라고 이해하면 된다.

 

동적 ConfigModule 등록

- 일반적으로 실행환경에 따른 .env파일은 루트 경로가 아니라 src/config/env 디렉토리에 모아서 관리한다.

- Nest의 기본 빌드 옵션은 .ts 파일 외에 asset은 제외하도록 되어 있다.

- 따라서 .env파일을 Out 디렉터리에 복사할 수 있도록 nest-cli.json에서 옵션을 바꿔줘야 한다.

Out Directory : 컴파일한 파일들을 저장하는 폴더.
{
	...
	"compilerOptions": {
        "assets": [
          {
            "include": "./config/env/*.env",
            "outDir": "./dist"
          }
        ],
   	...
}

- 이제 AppModule.ts에 ConfigModule을 동적 모듈로 등록해보자.

...
import { validationSchema } from './config/validationSchema';
...
import emailConfig from './config/emailConfig';

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath: [`${__dirname}/config/env/.${process.env.NODE_ENV}.env`],
      load: [emailConfig], // load 속성을 통해 앞에서 구성해둔 ConfigFactory를 지정함.
      isGlobal: true,
      validationSchema, // 환경변수 값에 대해 유효성 검사를 수행하도록 joi를 이용하여 유효성 검사 객체를 작성함.
    }),
  ...
  ],
...

- 환경 변수의 값에 대해 유효성 검사를 수행하도록 joi를 이용하여 유효성 검사 객체를 작성한다.

import * as Joi from 'joi';

export const validationSchema = Joi.object({
  EMAIL_SERVICE: Joi.string().required(),
  EMAIL_BASE_URL: Joi.string().required().uri(),
  AUTHEMAIL: Joi.string().required(),
  AUTHEMAILPASSWORD: Joi.string().required(),
});

 

- 이제 emailConfig를 우리가 사용하려고 하는 곳에 주입받아 사용할 수 있다.

 

'Back-End > Nest.js' 카테고리의 다른 글

내장 로거  (0) 2023.02.13
파이프와 유효성 검사  (0) 2023.02.01
멋사 2주차 JS decorator, MVC 패턴, Nest.js 찍먹  (0) 2023.01.11
멋사 스터디 2주차 HTTP, RESTFUL,웹 프레임워크  (0) 2023.01.10
TypeScript  (0) 2023.01.02