nest는 ConfigModule을 통해서 .env에 접근할 수 있는 기능을 제공한다. 그래서 보통 app.module.ts에 처음부터 import 해두는데 i/o를 불러오는 작업이 async해서 꼭 import한 순서대로 진행되지 않는다. 즉, 내가 process.env를 사용하려던 시점에 .env를 완전히 읽지 못해서 undefined가 오는 경우도 있을 수 있다는 것이다.
최근에 로그인 기능을 구현하면서 jwt 토큰을 발행할 일이 있었는데 JwtModule에 secret을 등록하기 위해 아래와 같이 process.env.JWT_SECRET을 입력해뒀다.
@Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
PassportModule.register({
defaultStrategy: 'jwt',
session: false,
}),
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1d' },
}),
],
controllers: [UsersController],
providers: [UsersService, UsersRepository],
})
export class UsersModule {}
앞서 설명했다시피 .env를 읽어오는 작업은 비동기적이다. 이로 인해 JwtModule.register에서 process.env.JWT_SECRET을 참조할 때까지 .env를 다 못 읽어오는 일이 발생했다. 그래서 Error: secretOrPrivateKey must have a value
라는 에러가 발생했지만 다행히도 이에 대한 해답을 stackoverflow에서 찾아냈다.
JwtModule에는 registerAsync라는 메소드가 있는데 여기에 ConfigService를 주입해두면 .env를 읽어올 때까지 secret을 등록하는 작업을 유예시킬 수 있다.
import { ConfigService } from '@nestjs/config';
@Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
PassportModule.register({
defaultStrategy: 'jwt',
session: false,
}),
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secret: config.get<string>('JWT_SECRET'),
signOptions: { expiresIn: '1d' },
}),
}),
],
controllers: [UsersController],
providers: [UsersService, UsersRepository],
})
export class UsersModule {}
추가로, ConfigModule.forRoot({ isGlobal: true })
처럼 isGlobal 옵션을 true로 바꿔주면 정상적으로 작동한다.
다른 모듈을 등록할 때도 비슷한 문제가 발생할 수 있으니 위의 사례를 참조해서 진행하자.
isGlobal을 true로 했는데 뭐가 문제지?
ConfigService 사용했는데 뭐가 문제지?
감사합니다 !!