[NestJS] TypeORM 설정

2024. 4. 21. 13:47NestJS

NestJS를 사용하며 TypeORM을 사용하는 경우 설정할 때 이전에 했던 방식과 더 나은 방식을 기술할 예정입니다.
 
일단 TypeORM 설정을 파일로 따로 관리하여 AppModule에 Module decorator에 길게 관리하는 것을 싫어했습니다. 이에 아래와 같이 따로 파일로 관리하였습니다.

/// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
import { typeORMConfig } from 'src/config/typeorm.config';
import { QuestionModule } from 'src/question/question.module';
import { AnswerModule } from 'src/answer/answer.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    TypeOrmModule.forRoot(typeORMConfig),
    QuestionModule,
    AnswerModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}


/// src/config/typeorm.config.ts
import { ConfigService } from '@nestjs/config';
import 'dotenv/config';
import { TypeOrmModuleOptions } from "@nestjs/typeorm";
import { Answer } from 'src/entities/answer.entity';
import { Question } from 'src/entities/question.entity';

const config : ConfigService = new ConfigService();

export const typeORMConfig : TypeOrmModuleOptions = {
  type: 'postgres',
  host: config.get<string>('DATABASE_HOST'),
  port: config.get<number>('DATABASE_PORT'),
  username: config.get<string>('DATABASE_USERNAME'),
  password: config.get<string>('DATABASE_PASSWORD'),
  database: config.get<string>('DATABASE_DATABASE'),
  entities: [Question, Answer],
  synchronize : false,
  keepConnectionAlive: true,
}

export const testTypeORMConfig : TypeOrmModuleOptions = {
  type: 'postgres',
  host: config.get<string>('TEST_DATABASE_HOST'),
  port: config.get<number>('TEST_DATABASE_PORT'),
  username: config.get<string>('TEST_DATABASE_USERNAME'),
  password: config.get<string>('TEST_DATABASE_PASSWORD'),
  database: config.get<string>('TEST_DATABASE_DATABASE'),
  entities: [Question, Answer],
  logging: true,
  synchronize : true
}

 
NestJS는 내부적으로 IoC container를 이용하여 DI(의존성 주입)을 관리합니다. 위와 같은 방식을 사용한다면 외부에서 ConfigService를 생성하고 dotenv/config도 이용하기에 여러 번 환경변수가 load되는 것이 매우 불편하였습니다.
이러한 방식이 매우 불편하였기에 좀더 NestJS사용에 맞게 사용하고자 하였습니다.
TypeOrmModule.forRoot 대신 TypeOrmModule.forRootAsync로 교체하였으며 AppModule의 ConfigModule에서 config를 load하고 이후에 ConfigService를 주입하는 방식을 채택하였습니다.
 

/// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { typeORMConfig } from 'src/config/typeorm.config';
import { QuestionModule } from 'src/question/question.module';
import { AnswerModule } from 'src/answer/answer.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    TypeOrmModule.forRootAsync({
      inject: [ConfigService],
      useFactory: async (config: ConfigService) => await typeORMConfig(config)
    }),
    QuestionModule,
    AnswerModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}


/// src/config/typeorm.config.ts
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions } from "@nestjs/typeorm";
import { Answer } from 'src/entities/answer.entity';
import { Question } from 'src/entities/question.entity';

export const typeORMConfig = async (config: ConfigService): Promise<TypeOrmModuleOptions> => {
  return {
    type: 'postgres',
    host: config.get<string>('DATABASE_HOST'),
    port: config.get<number>('DATABASE_PORT'),
    username: config.get<string>('DATABASE_USERNAME'),
    password: config.get<string>('DATABASE_PASSWORD'),
    database: config.get<string>('DATABASE_DATABASE'),
    entities: [Question, Answer],
    synchronize : false,
    keepConnectionAlive: true,
  }
};

export const testTypeORMConfig = async (config: ConfigService): Promise<TypeOrmModuleOptions> => {
  return {
    type: 'postgres',
    host: config.get<string>('TEST_DATABASE_HOST'),
    port: config.get<number>('TEST_DATABASE_PORT'),
    username: config.get<string>('TEST_DATABASE_USERNAME'),
    password: config.get<string>('TEST_DATABASE_PASSWORD'),
    database: config.get<string>('TEST_DATABASE_DATABASE'),
    entities: [Question, Answer],
    logging: true,
    synchronize : true
  }
};

 
하지만 아직 불편한 점은 Entity를 정의할 때마다 TypeOrmModuleOptions 중 entities에 매번 추가 해줘야 한다는 것이 불편하여 그냥 알아서 해주길 바라기 때문에 경로를 통하여 entities를 사용할 수 있게 설정하였습니다. 경로는 build후의 경로에 js를 로드하는 방식으로 하였습니다. (만약 ts 경로를 하는 경우 코드상에 있는 경로들을 외부 경로로 인식하여 import하지 못하는 에러가 발생하는 이슈가 있었습니다... build후 실행되기에 생각해보면 당연한 이슈..)

/// src/config/typeorm.config.ts
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions } from "@nestjs/typeorm";

export const typeORMConfig = async (config: ConfigService): Promise<TypeOrmModuleOptions> => {
  return {
    type: 'postgres',
    host: config.get<string>('DATABASE_HOST'),
    port: config.get<number>('DATABASE_PORT'),
    username: config.get<string>('DATABASE_USERNAME'),
    password: config.get<string>('DATABASE_PASSWORD'),
    database: config.get<string>('DATABASE_DATABASE'),
    entities: ['dist/entities/*.js'],
    synchronize : false,
    keepConnectionAlive: true,
  }
};

export const testTypeORMConfig = async (config: ConfigService): Promise<TypeOrmModuleOptions> => {
  return {
    type: 'postgres',
    host: config.get<string>('TEST_DATABASE_HOST'),
    port: config.get<number>('TEST_DATABASE_PORT'),
    username: config.get<string>('TEST_DATABASE_USERNAME'),
    password: config.get<string>('TEST_DATABASE_PASSWORD'),
    database: config.get<string>('TEST_DATABASE_DATABASE'),
    entities: ['dist/entities/*.js'],
    logging: true,
    synchronize : true
  }
};

 
옵션들을 좀더 NestJS과 TypeORM에게 할 일들을 맡기는 구조로 바뀌어 NestJS과 TypeORM을 동시 사용에 좀더 적합(?)하다는 견해입니다.
 
틀린 부분이 있을 수 있으니 알려주시면 감사하겠습니다 :)