[NestJS] Sequelize 옵션을 별도 파일에서 관리하기

cabbage·2023년 8월 12일
0

NestJS

목록 보기
14/17
post-thumbnail

문제

Nest 앱모듈에서 @Module 데코레이터의 imports 부분에 SequelizeModule을 등록하는 부분에서 옵션을 별도의 파일로 관리하고 싶었다. 앱모듈에 모듈을 많이 등록할수록 앱모듈 코드가 계속 복잡해졌던 경험이 있기 때문이다.

그래서 SequlizeModuleOptions 타입의 옵션을 별도의 파일에 작성하고 export해주었다.

// ./src/config/sequelize.config.ts
import { SequelizeModuleOptions } from '@nestjs/sequelize';

export const sequelizeConfig: SequelizeModuleOptions = {
  dialect: process.env.DATABASE_DIALECT as 'postgres',
  host: process.env.DATABASE_HOST,
  port: Number(process.env.DATABASE_PORT),
  username: process.env.DATABASE_USERNAME,
  password: process.env.DATABASE_PASSWORD,
  database: process.env.DATABASE_DATABASE,
  models: [__dirname + '/../**/*.model.*'],
  autoLoadModels: Boolean(process.env.DATABASE_AUTOLOADMODELS),
  synchronize: Boolean(process.env.DATABASE_SYNCHRONIZE),
};

앱모듈은 다음과 같이 작성하였다.

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

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `${__dirname}/config/env/.${process.env.NODE_ENV}.env`,
    }),
    SequelizeModule.forRoot(sequelizeConfig),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

하지만 이 방식으로는 환경변수 값들을 제대로 읽어올 수 없었다.

import 구문에서 sequelize.config 파일을 읽는 과정에서 아직 환경변수 값들이 설정되지 않는 것을 확인했다. process.env.DATABASE_DIALECT를 콘솔에 찍어보니 계속 undefined가 출력되었다.

해결 과정

문제를 해결하기 위해 구글링한 결과 외부 파일에서 옵션 값들을 읽어서 사용하려면 forRoot 메서드가 아닌 forRootAsync 메서드를 사용한다는 것을 알게 되었다.

forRootAsync 메서드를 사용하는 경우는 다음과 같다고 한다.

  • 외부 모듈이나 외부 서비스에 의존하는 경우 사용한다. 모듈 설정이 완료되기 전에 의존하는 데이터를 받아야 하기 때문이다.

결국 SequelizeModule 옵션 설정이 외부 모듈과 외부 서비스인 ConfigModule과 ConfigService에 의존하므로 forRootAsync 메서드를 사용해야 한다고 이해하였다.

먼저 앱모듈을 수정하였다.

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

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `${__dirname}/config/env/.${process.env.NODE_ENV}.env`,
    }),
    SequelizeModule.forRootAsync(sequelizeConfig),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}
  • forRoot 메서드가 아닌 forRootAsync 메서드를 사용한다.

그리고 sequelize.config 파일을 다음과 같이 수정하였다.

import { ConfigModule, ConfigService } from '@nestjs/config';
import { SequelizeModuleAsyncOptions } from '@nestjs/sequelize';

export const sequelizeConfig: SequelizeModuleAsyncOptions = {
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (configService: ConfigService) => {
    const options = {
      dialect: configService.get('DATABASE_DIALECT'),
      host: configService.get('DATABASE_HOST'),
      port: configService.get('DATABASE_PORT'),
      username: configService.get('DATABASE_USERNAME'),
      password: configService.get('DATABASE_PASSWORD'),
      database: configService.get('DATABASE_DATABASE'),
      models: [__dirname + '/../**/*.model.*'],
      autoLoadModels: configService.get('DATABASE_AUTOLOADMODELS'),
      synchronize: configService.get('DATABASE_SYNCHRONIZE'),
    };
    console.log(options);
    return options;
  },
};
  • SequelizeModuleOptions 타입이 아닌 SequelizeModuleAsyncOptions 타입을 사용한다.
  • ConfigModule을 통해 ConfigService를 주입 받는다.
  • useFactory에서 configService의 get 메서드를 사용해 process.env에 등록된 환경변수를 읽어온다.
  • useFactory에서 options를 리턴하면 forRootAsync 메서드에 모듈 옵션이 전달된다.

결과적으로 이 과정을 통해 문제를 해결할 수 있었는데, 도커 컴포즈로 실행하면 문제 없이 실행되지만 로컬에서 실행하면 문제가 해결되지 않았다. 로컬 문제는 좀 더 고민해봐야 할 것 같다.

참고

profile
캐비지 개발 블로그입니다. :)

1개의 댓글

comment-user-thumbnail
2023년 8월 12일

즐겁게 읽었습니다. 유용한 정보 감사합니다.

답글 달기