[NestJS 공식문서 정독하기] Fundamentals - Module reference

2022. 8. 14. 23:13Web/NestJS

반응형
🔗  https://docs.nestjs.com/fundamentals/module-ref
 

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 Progamming), FP (Functional Programming), and FRP (Functional Reac

docs.nestjs.com

Module reference

@Injectable()
export class CatsService {
  constructor(private moduleRef: ModuleRef) {}
}
  • ModuleRed 클래스로 내부 provider 리스트를 탐색하고 injection token을 조회 키로 사용하여 provider에 대한 참조를 얻을 수 있음
  • static/scoped provider를 동적으로 인스턴스화하는 방법도 제공함
  • 위와 같이 일반적인 방법으로 클래스에 주입할 수 있음

Retrieving instances

@Injectable()
export class CatsService implements OnModuleInit {
  private service: Service;
  constructor(private moduleRef: ModuleRef) {}

  onModuleInit() {
    this.service = this.moduleRef.get(Service);
  }
}
this.moduleRef.get(Service, { strict: false });
  • module reference(ModuleRef 인스턴스)의 get() 메소드로 현재 모듈에 존재하는 provider, controller, injectable(guard, interceptor 등등...)을 얻을 수 있음
  • global context의 provider를 얻으려면 get() 메소드에 { strict: false } 옵션을 부여하면 됨
  • 단, scoped provider는 get() 메소드로 얻을 수 없음 (아래에 설명할 기능을 사용해야 함)

Resolving scoped providers

@Injectable()
export class CatsService implements OnModuleInit {
  private transientService: TransientService;
  constructor(private moduleRef: ModuleRef) {}

  async onModuleInit() {
    this.transientService = await this.moduleRef.resolve(TransientService);
  }
}
@Injectable()
export class CatsService implements OnModuleInit {
  constructor(private moduleRef: ModuleRef) {}

  async onModuleInit() {
    const transientServices = await Promise.all([
      this.moduleRef.resolve(TransientService),
      this.moduleRef.resolve(TransientService),
    ]);
    console.log(transientServices[0] === transientServices[1]); // false
  }
}
  • resolve() 메소드를 injection token와 함께 사용해서 scoped provider를 동적으로 처리할 수 있음
  • resolve() 메소드는 자신의 DI container sub-tree에서 유니크한 privider 인스턴스를 반환함
  • 따라서, 해당 메소드를 여러번 호출하여 인스턴스 참조를 비교하면 서로 다른 인스턴스임을 확인할 수 있음
@Injectable()
export class CatsService implements OnModuleInit {
  constructor(private moduleRef: ModuleRef) {}

  async onModuleInit() {
    const contextId = ContextIdFactory.create();
    const transientServices = await Promise.all([
      this.moduleRef.resolve(TransientService, contextId),
      this.moduleRef.resolve(TransientService, contextId),
    ]);
    console.log(transientServices[0] === transientServices[1]); // true
  }
}
  • 하나의 인스턴스를 생성해서 여러 resolve() 간에 공유하고 싶다면 ContextIdFactory.create() 를 사용해서 context identifier를 생성하고 resolve() 메소드를 사용할 때 같이 전달하면 됨

Registering REQUEST provider

const contextId = ContextIdFactory.create();
this.moduleRef.registerRequestByContextId(/* YOUR_REQUEST_OBJECT */, contextId);
  • 수동으로 생성된 context identifier는 REQUEST provider가 undefined인 DI sub-tree임 (NestJS의 DI 시스템이 인스턴스화하지 않았기 때문)
  • ModuleRefregisterRequestByContextId() 메소드로 커스텀 REQUEST 객체를 등록할 수 있음

Getting current sub-tree

@Injectable()
export class CatsService {
  constructor(
    @Inject(REQUEST) private request: Record<string, unknown>,
  ) {}
}
const contextId = ContextIdFactory.getByRequest(this.request);
const catsRepository = await this.moduleRef.resolve(CatsRepository, contextId);
  • request context내의 request-scoped provider를 처리하고 싶을 수 있음
  • 이럴땐 새로운 context identifier를 생성하는게 아니라 @Inject(REQUEST) 로 요청 객체를 주입받고 ContextIdFactory.getByRequest() 메소드로 요청 객체 기반의 context id를 생성해서 사용

Instantiating custom classes dynamically

@Injectable()
export class CatsService implements OnModuleInit {
  private catsFactory: CatsFactory;
  constructor(private moduleRef: ModuleRef) {}

  async onModuleInit() {
    this.catsFactory = await this.moduleRef.create(CatsFactory);
  }
}
  • provider로 등록되지 않은 클래스를 동적으로 인스턴스화 할 수 있음
  • 이렇게 생성된 인스턴스는 NestJS의 컨테이너 밖에 존재
반응형