Jay's Cookbook
Menu
  • Tags
  • Categories
  • Projects
Computer Science
OS
Network
Data Structure
Algorithm
Language
Code Architecture
Python
Javascript
Typescript
Java
Backend
Backend Theory
TypeORM
Node.js
NestJS
FastAPI
Frontend
HTML/CSS
React
Next.js
Data Engineering
DE Theory
MySQL
MongoDB
Elastic
Redis
Kafka
Spark
Airflow
AI
Basic
Pytorch
NLP
Computer Vision
Data Analytics
Statistics
Pandas
Matplotlib
DevOps
Git
Docker
Kubernetes
AWS
[NestJS] Configuration
backend
NestJS

[NestJS] Configuration

Jay Kim
Jay Kim 15 Feb 2024
[NestJS] 구성요소(6) DTO [NestJS] 테스트 코드 (with JEST)

Table of Contents

  • Configuration
  • process.env.NODE_ENV
  • 설정 파일 만들기
  • 설정 파일의 값 로드하기
    • ConfigModuleOptions
      • envFilePath
      • isGlobal
      • load
    • 커스텀한 로드
      • process.env의 값들을 프로그래밍화해서 동적으로 사용하고 싶은 경우
      • json, yaml 같은 포맷의 설정 파일을 읽어서 사용하고 싶은 경우
  • 설정 파일의 값 사용하기
  • 실습: 데이터베이스 연결
  • 참고

Configuration

  • 애플리케이션 안에는 데이터가 저장되어 있는 데이터베이스 관련 설정, 시크릿 키와 같은 암호화 관련 설정값이 필요하게 된다
  • 이런 보안과 관련된 요소들은 절대 Github 저장소 같은 퍼블릭한 공간에 공개되어서는 안된다
  • 이러한 이유로 설정과 관련된 값들은 .env, toml, json, yaml 같은 포맷의 파일에 키-밸류 형태로 안전한 저장소에 따로 보관해야 한다


  • 또한 애플리케이션은 보통 개발(development) 단계, 상용(production) 단계처럼 다른 환경에서 실행된다
  • 그리고 이러한 환경들은 서로 다른 설정 값을 가질 수 있다
  • 이러한 이유로 .env.dev, .env.prod 처럼 여러 설정 파일을 가질 수 있다

process.env.NODE_ENV

  • node.js에서는 서버가 개발용 서버인지 상용화를 위한 서버인지 구분하기 위해 NODE_ENV 환경변수를 사용한다
  • (NODE_ENV는 사전에 정의된 환경변수는 아니기 때문에, 다르게 명명해도 상관없다)
  • node.js에서는 process.env 객체를 통해 환경변수에 접근할 수 있다
  • NODE_ENV 값을 환경변수로 지정하고 싶으면 쉘 터미널에 다음과 같이 명령어를 입력하면 된다
export NODE_ENV=development

설정 파일 만들기

  • node.js에서는 .env 포맷을 많이 사용한다
# .env.dev

DB_HOST = localhost
DB_PORT = 3316
  • nested한 구조를 위해 json, toml, yaml 같은 포맷을 사용해도 좋다
// dev.json

{
    "database": {
        "host": "localhost",
        "port": 3316
    },

    "credential": {
        "secret": "aAef3Jl14Viz2"
    }
}
# dev.toml

[database]
host = "localhost"
port = 3316

[credential]
secret = "aAef3Jl14Viz2"
# dev.yaml

database:
  host: "localhost"
  port: 3316

credential:
  secret: "aAef3Jl14Viz2"
  • 참고로 toml, yaml 포맷 파일을 파싱하기 위해서는 추가적인 라이브러리를 설치해야 한다
    • toml
      • npm i toml
    • yaml
      • npm i js-yaml
      • npm i -D @types/js-yaml

설정 파일의 값 로드하기

  • 설정 파일을 만드는 방법을 배웠으니 이제 설정 파일의 값들을 로드하는 방법을 배워보자
  • NestJS에서는 설정 값을 관리/접근하는데 있어 유용한 모듈인 ConfigModule을 제공한다
  • ConfigModule은 설정 값을 로드하며, ConfigModule에서 제공하는 ConfigService는 설정 값에 접근할 수 있는 메서드를 제공한다
  • (ConfigService로 설정 값에 접근하는 방법은 뒤에 나올 챕터에서 배운다)
  • ConfigModule은 @nestjs/config 패키지를 설치하면 사용할 수 있다
npm i --save @nestjs/config
  • ConfigModule 모듈을 루트 모듈인 AppModule에서 임포트한다
  • 아래 코드는 루트 디렉터리의 .env 파일에 설정된 값들을 process.env 객체에 머지한다
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

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

ConfigModuleOptions

  • forRoot() 메서드에는 ConfigModuleOptions 타입의 객체를 인자로 넘겨줌으로써 설정 값 로드와 관련된 여러가지 유용한 설정을 할 수 있다

envFilePath

  • 루트 디렉터리가 아닌 다른 곳에서 관리하고 싶은 경우에는 envFilePath 속성을 사용한다
  • -> 머지하고 싶은 환경 변수가 있고, 이를 포함한 .env 파일이 루트 디렉터리가 아닌 다른 경로에 있는 경우
  • 아래 코드는 <루트 디렉터리>/config 디렉터리에 있는 .env.global 파일에 설정된 값들을 process.env에 머지한다
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot({
        envFilePath: './config/.env.global'
    })
  ]
})
export class AppModule {}

isGlobal

  • isGlobal을 사용하면 다른 모듈에서 ConfigModule을 특별히 임포트 하지 않아도 사용할 수 있다
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, 
      envFilePath: './config/.env.global'
    }),
  ],
})
export class AppModule {}

load

  • 단순 .env 파일의 값을 process.env에 머지하는게 아니라, 프로그래밍화 하고 싶은 경우,
  • .env 파일이 아닌 json, yaml 같은 파일을 로드하고 싶은 경우
  • 이럴 때는 load 속성을 이용해 자바스크립트 객체를 리턴하는 팩토리 함수를 표기하면 된다
// configuration.ts
import { readFileSync } from "fs"
import * as path from "path"

const configuration = () => {
    const filename = (process.env.NODE_ENV === "prod" || process.env.NODE_ENV === "production") ? 'prod' : 'dev'
    const filePath = path.join(process.cwd(), "config", "envs", `${filename}.json`)
    const jsonFile = readFileSync(filePath, 'utf-8')
    return JSON.parse(jsonFile)
}

export default configuration
// app.module.ts

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

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

커스텀한 로드

  • 커스텀하게 설정 값을 로드하고 싶은 경우가 있다
  • 자바스크립트 객체를 리턴하는 팩토리 함수를 표기하면 된다

process.env의 값들을 프로그래밍화해서 동적으로 사용하고 싶은 경우

// config/configuration.ts

export default () => ({
  port: parseInt(process.env.PORT, 10) || 3000,
  database: {
    host: process.env.DATABASE_HOST,
    port: parseInt(process.env.DATABASE_PORT, 10) || 5432
  }
});

json, yaml 같은 포맷의 설정 파일을 읽어서 사용하고 싶은 경우

// config/configuration.ts

// 설정 파일이 json 포맷인 경우

import { readFileSync } from "fs"
import * as path from "path"

const configuration = () => {
    const filename = (process.env.NODE_ENV === "prod" || process.env.NODE_ENV === "production") ? 'prod' : 'dev'
    const filePath = path.join(process.cwd(), "config", "envs", `${filename}.json`)
    const jsonFile = readFileSync(filePath, 'utf-8')
    return JSON.parse(jsonFile)
}

export default configuration
// config/configuration.ts

// 설정 파일이 yaml 포맷인 경우

// 아래 명령어로 먼저 패키지 설치해야한다
// npm i js-yaml
// npm i -D @types/js-yaml

import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';

const YAML_CONFIG_FILENAME = 'config.yaml';

export default () => {
  return yaml.load(
    readFileSync(join(__dirname, YAML_CONFIG_FILENAME), 'utf8'),
  ) as Record<string, any>;
};
// app.module.ts

import configuration from './config/configuration';

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

설정 파일의 값 사용하기

  • .env 포맷을 사용한 경우에는 가장 간단하게는 process.env 객체로 값을 사용할 수 있다
const dbUser = process.env.DATABASE_USER
  • 또는 ConfigModule의 ConfigService 를 통해 설정 값에 접근할 수 있다
constructor(private configService: ConfigService) {}
// get an environment variable
const dbUser = this.configService.get<string>('DATABASE_USER');
  • ConfigModule.forRoot({load: [configuration]}) 이렇게 커스텀하게 한 경우도 똑같은 방법으로 접근하면 된다
// get a custom configuration value
const dbHost = this.configService.get<string>('database.host');

실습: 데이터베이스 연결

  • 데이터베이스를 연결할 때 필요한 설정 값(ex. 호스트, 포트, DB명 등)은 보통 설정 파일로 따로 관리된다

  • ConfigModule 모듈 사용하기 위해 @nestjs/config 라이브러리 설치

npm i --save @nestjs/config
  • 설정 파일 포맷은 json을 사용했다
// 루트디렉터리/config/envs/dev.json
{
    "database": {
        "type": "mysql",
        "db": "dev_portfolio_ecommerce",
        "host": "localhost",
        "port": 3316,
        "username": "client",
        "password": "client"
    }
}
  • NODE_ENV 환경변수는 .env.global 파일로 등록
// 루트디렉터리/config/.env.global
NODE_ENV=dev
  • JSON파일의 설정 값을 로드하기 위해 configuration.ts 파일 작성
// 루트디렉터리/config/configuration.ts
import { readFileSync } from "fs"
import * as path from "path"

const configuration = () => {
    const filename = (process.env.NODE_ENV === "prod" || process.env.NODE_ENV === "production") ? 'prod' : 'dev'
    const filePath = path.join(process.cwd(), "config", "envs", `${filename}.json`)
    const jsonFile = readFileSync(filePath, 'utf-8')
    return JSON.parse(jsonFile)
}

export default configuration
  • ConfigModule 등록
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import configuration from 'config/configuration';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [configuration],
      envFilePath: './config/.env.global'
    })
  ]
})
export class AppModule {}

  • 여기까지 하면 이제 설정 값이 로드되었다
  • 이제 데이터베이스를 연결할 차례다
  • 먼저 MySQL 데이터베이스를 실행하고, 데이터베이스(database)를 만든다
cd /usr/local/mysql

sudo ./support-files/mysql.server start
CREATE DATABASE dev_portfolio_ecommerce;
  • 연결을 위해 TypeORM을 사용할 것이다. 관련 패키지를 설치하자
npm install --save @nestjs/typeorm typeorm mysql2
  • 만약 설정값을 따로 분리하지 않고 하드코딩 한다면 아래와 같이 작성해도 된다
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3316,
      username: 'client',
      password: 'client',
      database: 'dev_portfolio_ecommerce',
      entities: [],
      synchronize: true,
    }),
  ],
})
export class AppModule {}
  • 설정 값을 사용하기 위해서는 ConfigService 인스턴스를 주입한 후 TypeOrmModule 모듈이 생성되어야 한다
  • 이를 위해 TypeOrmModule 모듈이 ConfigModule 모듈이 생성된 이후 생성되도록 해야 한다
  • 최종 코드는 아래와 같다
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import configuration from 'config/configuration';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [configuration],
      envFilePath: './config/.env.global'
    }),
    TypeOrmModule.forRootAsync({
      inject: [ConfigService],
      useFactory: async (configService: ConfigService): Promise<TypeOrmModuleOptions> => {
        return {
          type: configService.get<"mysql" | "sqlite" | "mongodb" | "postgres">('database.type'),
          database: configService.get<string>('database.db'),
          host: configService.get<string>('database.host'),
          port: configService.get<number>('database.port'),
          username: configService.get<string>('database.username'),
          password: configService.get<string>('database.password'),
          autoLoadEntities: true, // true면 entities: [] 에 일일이 엔티티 클래스 추가하지 않아도 됨
          synchronize: true // Entity에 맞춰 DB 테이블의 스키마가 변경됨 -> 프로덕션 환경에서는 사용하면 안됨
        }
      }
    })
  ],
})
export class AppModule {}

참고

  • NestJS 공식문서
  • Nest.js TypeORM과 Entity, 개발하는 기획자
[NestJS] 구성요소(6) DTO [NestJS] 테스트 코드 (with JEST)

You may also like

See all NestJS
14 Oct 2024 [NestJS] API 문서화
backend
NestJS

[NestJS] API 문서화

09 Oct 2024 [NestJS] SSE
backend
NestJS

[NestJS] SSE

08 Oct 2024 [NestJS] 웹 소켓
backend
NestJS

[NestJS] 웹 소켓

Jay Kim

Jay Kim

Web development, data engineering for human for the Earth. I share posts, free resources and inspiration.

Rest
Lifestyle
Hobby
Hobby
Hobby
Hobby
2025 © Jay's Cookbook. Crafted & Designed by Artem Sheludko.