본문 바로가기

javascript/이외

[nestjs] DI token

https://docs.nestjs.com/fundamentals/custom-providers#non-class-based-provider-tokens

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea

docs.nestjs.com


nestjs는 custom provider을 통해 4가지의 공급 타입을 제공한다.

  1. useValue: 상수 값이나 외부 라이브러리를 주입할 때 사용한다.
  2. useClass: 클래스를 주입할 때 사용한다.
  3. useFactory: 공급자를 동적으로 제공할 때 사용한다. 주입하는 내용은 팩토리에서 반환한 값을 기반으로 하며, inject 배열에 useFactory 파라미터에 넘길 값을 순서대로 제공해야 한다.
  4. useExisting: 기존 공급자에 대한 별칭을 만들어, 공급자에 접근하는 다른 방법을 제공한다.

  DI container는 각 공급자를 식별하고 의존성을 요청하는 위치에 대해 알맞은 의존성을 삽입해야 하는데, 위 제시된 프로퍼티만으로는 적절한 공급자를 적절한 자리에 전달해주기 어렵다. 따라서 각 공급자들을 구분하기 위한 속성인 provide를 두고 있는데, 이를 nestjs에서는 DI token이라고 부른다.

DI token은 각 공급자를 식별하는데 사용하는 토큰이다. 공식 문서에 따르면 토큰은 다음 자료형이 될 수 있다.

  1. 클래스
  2. 문자열
  3. 심볼(Symbol)
  4. typescript 열거형

표준 공급자를 사용하는 경우 의존성을 주입할 때 클래스만 넘기는데, 이는 provide 및 useClass 속성에 동일한 클래스를 지정한 방식과 동일하다. 요약하면 nestjs의 공급자는 DI token과 공급 방식을 정의해야 한다고 볼 수 있다.

providers: [
AppService,
{
  provide: MessagesService,
  useValue: mockupMessagesService
},
// MessagesService,
{
  provide: 'MSG_REPO',
  useClass: MessageRepository
}
]

위 제시된 MessageRepository의 경우 'MSG_REPO' 라는 문자열을 토큰으로 사용하고 있다. 이를 통해 AppModule에서 MessageRepository 클래스 인스턴스를 해당 토큰으로 가져올 수 있다.

@Injectable()
export class MessagesService {
    constructor(@Inject('MSG_REPO') private messageRepo: Repository) {
        console.log("Message Service Created")
    }

의존성을 주입할 필드에 @Inject(token) 데코레이터를 적용하여 내가 가져오고 싶은 공급자가 무엇인지 명시할 수 있다. 원래대로라면 타입스크립트가 컴파일 되는 과정에서 타입 정보 등이 모두 제거되어야 하지만, reflect-metadata 라이브러리 덕분에 데코레이터가 추가적인 정보를 자바스크립트 수준에 남길 수 있으므로 실제 코드가 실행되는 자바스크립트 환경에서 각 의존성을 구분하여 공급자를 주입할 수 있다.

DI 토큰이 식별의 역할을 수행한다는 점에서 내부 공급자를 외부로 공개하는 경우에도 DI 토큰을 이용한다. nestjs의 모듈의 providers에 등록된 공급자들은 기본적으로 해당 모듈 내부에서만 사용할 수 있다. 모듈 외부로 특정 공급자를 노출하고 싶다면 DI 토큰을 exports 배열에 등록한다. 

@Module({
  providers: [
    {
      provide: POWER_SERVICE,
      useClass: PowerService,
    },
  ],
  exports: [POWER_SERVICE], // 외부에서 접근하도록 명시적으로 exports에 등록
})
export class PowerModule {}

결론

nestjs는 공급자를 구분하기 위해 DI token을 이용한다. custom provider 기준 provide에 지정하는 값으로, @Inject 데코레이터에 전달하여 DI 컨테이너가 의존성을 주입할 위치를 식별할 수 있게 한다.

기본적으로 providers에 등록된 제공자들은 모듈 내에서만 이용 가능하므로, 타 모듈에 정의된 의존성을 주입하고 싶다면 imports 배열에 해당 제공자의 DI 토큰을 넘겨서 외부에서 사용할 수 있게 만들어야 한다.