본문 바로가기
TIL

TIL 240628 - Nest.js에서 Provider란? / useValue, useClass, useFactory 사용법

by lemonpie611 2024. 6. 28.

typeOrm 공부중 대체 useFactory가 뭔지 몰라서 헤매다가 이까지 왔음..

1. Providers

  • Service, Repository, Factory, Helper 등등 대부분의 Nest 클래스는 Provider로 취급
  • Provider는 Nest에서 의존성 주입이 가능
  • Nest에는 Provider간의 관계를 담당하는 내장 IoC(제어역전) 컨테이너 존재 > @Injectable 데코레이터를 사용하여 Nest IoC 컨테이너에서 관리할 수 있는 클래스임을 선언, IoC가 뭔지는 이전 글 참고...

2. 자주 사용되는 Provider

1) value provider - useValue : provide로 제공되는 key 값이 useValue에 제공되는 value에 매칭

  • 실제 구현을 모의 객체로 대체 > 테스트 목적
import { CatsService } from './cats.service';

const mockCatsService = {
  /* mock implementation
  ...
  */
};

@Module({
  imports: [CatsModule],
  providers: [
    {
      provide: CatsService,
      useValue: mockCatsService,
    },
  ],
})
export class AppModule {}
  • Nest 컨테이너에 외부 라이브러리를 삽입 : 프로바이더를 다음과 같이 정의 시 아래 방법들로 사용 가능
import { connection } from './connection';

@Module({
  providers: [{
      provide: 'CONNECTION',
      useValue: connection,
    }, {
      provide: 'MASTER_NAME',
      useValue: 'KGH'
    }],
})
export class AppModule {}
// DI 방식
@Injectable()
export class CatsRepository {
  constructor(@Inject('CONNECTION') connection: Connection) {	}
}

// 단순 삽입
@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];
  @Inject('MASTER_NAME') name: string;
}
  • 상수 삽입 > provide에 들어갈 값을 constant.js에서 미리 정해두어 사용
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: 'PORT',
      useValue: 3000
    }
  ],
})
export class AppModule {}
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const port = app.get('PORT')
  console.log(port) // -> 3000
  await app.listen(port);
}
bootstrap();

 

2) value provider - useClass : 객체(provider, guard 등)을 재정의할 인스턴스를 제공하기 위해 인스턴스화될 클래스 제공

  • 토큰이 확인하는 클래스를 동적으로 결정
  • 객체 재정의 
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})
export class AppModule {}
  • 변수나 개발환경에 따른 제공 인스턴스 변경
const configServiceProvider = {
  provide: ConfigService,
  useClass:
    process.env.NODE_ENV === 'development'
      ? DevelopmentConfigService
      : ProductionConfigService,
};

@Module({
  providers: [configServiceProvider],
})
export class AppModule {}

 

3) value porvider - useFactory : 객체를 재정의할 인스턴스를 제공하기 위해 인스턴스화 될 클래스 제공

  • 동적으로 provider 생성 가능 ( 그러니까, useClass는 사용함으로써 동적으로 주입하는건데, useFactory는 처음부터 동적인 객체를 넣을 수 있음)
TypeOrmModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: (configService: ConfigService) => ({
    type: 'mysql',
    host: configService.get('HOST'),
    port: configService.get<number>('PORT'),
    username: configService.get('USERNAME'),
    password: configService.get('PASSWORD'),
    database: configService.get('DATABASE'),
    entities: [__dirname + '/**/*.entity{.ts,.js}'],
    synchronize: true,
  }),
  inject: [ConfigService],
});
  • async/await을 사용하여 db 연결 등의 비동기 작업에 대한 DI 가능
{
  provide: 'ASYNC_CONNECTION',
  useFactory: async () => {
    const connection = await createConnection(options);
    return connection;
  },
}
  • 인수를 받고 함수로 값을 만드는 것도 가능