이번 글은 짧은 내용을 다룬다. 아마 dotenv를 사용하다가 Nestjs와 ConfigModule을 사용하려던 사람들은 시행착오를 많이 겪을 것이다. 사실 TypeORM을 써서 발생하는 문제는 아닌데, 이런 상황을 주로 만나는 게 아무래도 ORM일 것 같아서 제목에도 넣었다.
Nestjs에는 ConfigModule이 있다. ConfigModule을 사용해서 미리 .env를 삽입해두면 그 하위 모듈에서는 모두 process.env를 통해서 값에 접근할 수 있게 된다. 하지만 아래 같은 경우를 보자.
// nestjs recipe의 typeORM 문서를 보면 아래의 예제 코드가 있다.
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: configService.get('DB_HOST'),
port: +configService.get<number>('DB_PORT'),
username: configService.get('DB_USERNAME'),
database: configService.get('DB_DATABASE'),
password: configService.get('DB_PASSWORD'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
}),
UsersModule,
],
})
export class ApiModule {}
이렇게 만들어진 Providers를 DatabaseModule이라는 이름에 넣어주자. 아래처럼.
import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';
@Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}
만약 상위 모듈 어디에선가 global하게 ConfigModule을 넣었다면 당연히 되어야 할 것 같은데, 이건 동작하지 않는다. databaseProviders를 import 하지 않고 Module에 직접 작성하여도 바뀌는 건 없다. 이 코드는 여전히 동작하지 않는다. 왜냐하면, process.env에 DB와 관련된 환경 변수들이 없기 때문이다.
모듈은 정적으로 만들어진다. 그리고 dotenv와 달리 Nest에서의 configModule은 정적이다. 그래서 안 된다. 무슨 말이냐면, controller나 service처럼, Module들로 인해 모든 application이 만들어진 다음에야 configModule을 사용할 수 있다는 것이다. module의 생성 시점에는 configModule을 사용할 수 없다.
당연하지만 이에 대한 해결책이 있다.
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
@Module({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => {
return {
type: 'mysql',
host: configService.get('DB_HOST'),
port: +configService.get<number>('DB_PORT'),
username: configService.get('DB_USERNAME'),
database: configService.get('DB_DATABASE'),
password: configService.get('DB_PASSWORD'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
};
},
}),
UsersModule,
],
})
export class ApiModule {}
위는 실제로 내가 사용하는 코드이다. 나는 imports에 TypeOrmModule을 넣어주었다. 다시 이 모듈은 imports와 inject를 사용하는데, 말인 즉슨, ConfigModule을 직접 사용할 것임을 뜻한다. 그리고 useFactory라는 키워드를 사용해 설정을 반환하는데, 이 내부에서 configService를 사용한다. useFactory라는 키워드가 바로, 동적으로 모듈을 만드는 방법이다.
이렇게 하고 나면 모듈에서도 ConfigModule에 저장된 값을 사용할 수 있게 된다.
현재 부딪친 문제를 해결하는데 도움이 되었습니다 감사합니다 :)