들어가기전에…

웹을 배포했을 때, YAML 파일을 직접 수정했던 기억이 있는데, 원래 교재에서 다루는 이 부분을 넘어가려다가 YAML과 관련된 내용이 나와서 보는 편이 좋을 것 같아 공부해보려고 한다

1. 서버 기동과 환경 설정 파일 초기화 순서 알아보기

서버 기동 시에 환경 설정 파일을 읽어서 환경 변수를 사용할 수 있다는 것을 앞에서 알아보았다. 그렇다면 언제 환경 설정 파일을 읽는 것일까? config-test 프로젝트를 기준으로 서버 기동 시 초기화된 순서를 알아보자.

image-20230826190725502

1번. npm run start 명령어로 먼저 실행되는 파일은 main.ts다. main.ts에는 bootstrap() 함수가 있으며 해당 함수를 실행하는 것으로 시작된다

2번. bootstrap() 함수에서는 NestFactory.create()를 실행한다. NestFactory.create()는 설정되어 있는 모듈을 초기화하는 작업을 진행한다

3번. 각 모듈이 초기화될 때 의존성 주입을 해야 하는 부분들을 초기화하고 주입하도록 인스턴스를 생성하는 일을 한다. ConfigModule을 먼저 초기화해 환경 변수를 어떤 모듈에서든지 읽을 수 있는 준비를 한다

4번. AppModule에 설정되어 있는 ConfigModule.forRoot()를 실행해 설정 파일을 읽는다. 아무런 옵션이 없으면, 즉 괄호 안에 어떠한 설정도 없다면, .env 파일에서 설정을 읽어온다

5번. 모듈에서 envFilePath 설정이 있다면 리스트에 담겨 있는 순서대로 설정을 읽어서 저장한다

6번. process.env에 있는 환경 변수를 병합한다

7번. load 옵션이 있다면 load에 있는 환경 변수를 6에서 만든 환경 변수와 합치게 된다

8번. 모듈이 모두 초기화되었다면, 컨트롤러의 인스턴스를 생성하고 컨트롤러에 있는 핸들러 함수를 URL과 매핑하는 작업을 진행한다. 이 작업이 끝나면 서버는 성공적으로 시작된다는 메시지를 보여준다


2. YAML 파일을 사용해 환경 변수 설정하기

최근에는 환경 변수를 설정할 때, YAML을 사용하는 경우가 많다. 예를 들어 쿠버네티스, 스프링, 앤서블 등에서는 지원하고 있으니 알아두는 것이 좋다.

YAML은 문법이 간결하며 JSON에서 표현하는 모든 데이터를 표현할 수 있다. 또한 JSON에서 불가능한 주석도 지원한다. ConfigModule이 YAML 파일을 읽으려면 앞에서 다룬 ‘커스텀 환경 설정 파일‘에 YAML 파일을 읽어오도록 코드를 작성해야 한다.

Nest.js에서 YAML로 환경 변수를 설정한다. js-yaml 패키지를 설치하고 config.yaml 파일을 생성한 후 config.ts 파일을 수정한다.

순서 1. js-yaml 설치하기

타입 정보가 있으면 IDE에서 타입 정보를 확인해 자동 완성과 컴파일 타임의 타입 체크 등의 기능을 지원해주는 모듈을 설치한다

// chapter9 > config-test (root 경로)

npm i js-yaml
npm i -D @types/js-yaml

순서 2. config.yaml 파일 생성하기

config-test/envs 디렉터리 아래에 config.ts에서 읽어올 config.yaml 파일을 만들고 다음과 같이 작성한다

// src > envs > config.yaml

http:
  port: 3000

redis:
  host: 'localhost'
  port: 6379

순서 3. config.ts 수정하기

YAML 파일은 커스텀 설정 파일로 취급하므로 이전에 만들었던 config.ts에 설정을 추가한다.

// src > configs > config.ts

... 생략 ...
// 임포트 추가
import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
// ...

... 생략 ...
const yamlConfig: Record<string, any> = yaml.load( // 1. YAML 파일 로딩
    readFileSync(`${process.cwd()}\\envs\\config.yaml`, 'utf8'),
);

export default () => ({
    ...common,
    ...conf,
    ...yamlConfig, // 2. 기존 설정의 마지막에 덧붙임
});

1번. yaml.load를 사용해 config.yaml 파일을 읽어온다. 이때 반환되는 타입은 Record<string, any>이다.

2번. 기존의 환경 설정에 yaml.load로 읽어온 설정을 덧붙인다.


순서 4. 테스트용 핸들러 함수로 테스트하기

테스트용 핸들러 함수를 추가하고, npm run start:dev로 서버를 실행하여 브라우저에서 테스트를 해본다

// app.controller.ts

... 생략 ...
@Controller()
export class AppController {
  
  ... 생략 ...
  @Get('redis-info')
  getRedisInfo(): string {
    return `${this.configService.get('redis.host')}:${this.configService.get('redis.port')}`;
  }
}

image-20230826193832458


3. 캐시 옵션 사용하기

설정 파일을 읽고, 내용을 파싱해 메모리에 키와 값으로 저장해야 사용할 수 있다. 설정 파일은 서버가 한번 기동된 뒤에 변경되지 않으므로 캐시를 사용하면 성능에서 이득이다. 캐시를 사용할 수 있게 옵션을 수정한다

// app.module.ts

ConfigModule.forRoot({
	// ... 생략 ...
	cache: true	// 1. 캐시
})

cache: true 옵션을 사용하면 ConfigService의 get() 함수를 사용할 때 캐시에서 먼저 불러오게 되므로 성능상의 이점이 있다.


4. 확장 변수 사용하기

확장 변수란?

이미 선언된 변수를 다른 변수에 ${ 변수명}으로 할당하는 기능이다

순서 1. 확장 변수의 설정 추가

// envs > local.env

SERVER_DOMAIN=localhost	// 1. 환경 변수 SERVER_DOMAIN 선언
SERVER_PORT=3000 // 2. 환경 변수 SERVER_PORT 선언

// 3. 확장 변수 기능을 사용한 변수 선언
SERVER_URL=http://${SERVER_DOMAIN}:${SERVER_PORT}

순서 2. 확장 변수를 사용할 수 있게 추가 설정하기

확장 변수를 사용하려면 ConfigModule에 한가지 더 설정을 해줘야 한다. expandVariables 옵션을 추가한다

// app.module.ts

ConfigModule.forRoot({
	... 생략 ...	
	expandVariables: true // 확장 변수 옵션 추가
})

순서 3. 테스트용 핸들러 함수로 테스트하기

constroller.ts의 service-url 핸들러 함수의 내용을 추가하여, 확장 변수를 잘 읽어오는 확인한다

// app.controller.ts

... 생략 ...
@Get('service-url')
getServiceUrl(): string {
	return this.configService.get('SERVER_URL'); // 1. 확장 변숫값 읽기
}

image-20230828094629707


5. main.ts 에서 환경 변수 사용하기

main.ts에서 NestFactory.create()를 호출해주기 전에 ConfigModule이 활성화되지 않는다. 그리고 클래스가 아니라 bootstrap() 함수만 있으므로 기존처럼 클래스의 생성자로 의존성 주입을 받을 수 없어, 다른 방법으로 ConfigService를 사용해야 한다.

main.ts에서 환경 변수를 읽어오기 위해서는 app.get() 메서드에 ConfigService 클래스를 인수로 주고, 반환값을 받는 방식을 사용한다.

// main.ts

... 생략 ...
import { ConfigService } from '@nestjs/config';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const configService = app.get(ConfigService); // 1. ConfigService를 app.get()에 추가
  await app.listen(configService.get("SERVER_PORT")); // 2. configService 사용, app.listen(3000)과 동일
}
bootstrap();

1번. app.get() 메서드에 ConfigService를 넣으면 앱의 환경 변수를 사용할 수 있는 configService 인스턴스를 반환해준다

2번. configService 인스턴스를 사용해 서버 포트 정보를 환경 변수로 변경하였다

정리

  • 개발, 배포 등에 상황에 따라 환경 변수를 달리 설정해야 한다. 한 번 잘 설정해두면 개발중이던 앱을 플래그만 변경해 배포할 수도 있다
  • ConfigModule에는 다양한 옵션이 있는데 그 중 전역 모듈로 만들어주는 isGlobal 옵션, 환경 변수 파일의 경로를 지정하는 envFilePath 옵션, 커스텀한 환경 변수 파일을 사용할 수 있게 하는 load 옵션, 캐싱을 지원하는 cache 옵션, 확장 변수를 지원하는 expandVariables 옵션이 있다
  • 환경 설정 파일은 애플리케이션에서 사용하는 각종 정적인 값을 저장한 파일
  • ConfigModule은 NestJS에서 환경 설정에 사용하는 모듈
  • ConfigService는 앱의 환경 변수를 읽어들이는 서비스
  • NODE_ENV는 Node.js 애플리케이션에서 주로 사용하는 환경 변수 이름
  • YAML은 환경 설정용 파일 모샣.
  • 확장 변수 환경 변수를 다른 환경 변수의 값에 변수로 포함시키는 기능
  • .env 파일은 NestJS의 기본 환경 변수 파일이다. NestJS에서는 내부적으로 dotenv 라이브러리를 사용한다

참고

다음에 다루게 될 것

  • 회원 가입과 인증하기(1) - SQLite로 회원 가입 구현하기

댓글남기기