Module Reference

이연중·2021년 10월 18일
0

NestJS

목록 보기
16/22
post-custom-banner

Nest는 ModuleRef 클래스를 제공해 내부 프로바이더의 목록을 탐색할 수 있게하고, 프로바이더를 정의할 때 지정된 주입 토큰을 키로 사용해 프로바이더에 대한 참조를 얻게한다.

ModuleRef 클래스를 이용해 정적 및 범위가 지정된 프로바이더를 모두 동적으로 인스턴스화 할수도 있다.

Retrieving Instances


ModuleRef에는 get()이라는 메서드가 있다. 이 메서드는 주입 토큰 또는 클래스 이름을 사용해 현재 모듈에 존재하는 프로바이더, 컨트롤러 Injectable(가드, 인터셉터 등)을 탐색한다.

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

  onModuleInit() {
    this.service = this.moduleRef.get(Service);
  }
}

단, 범위가 지정된 프로바이더는 검색이 불가능하다.

Global Context에서 프로바이더를 검색하려면(ex: 프로바이더가 다른 모듈에 삽입된 경우)\

{strict: false} 옵션은 get()에 두번째 인수로 전달해야 한다.

this.moduleRef.get(Service, { strict: false });

Resolving Scoped Provider


범위가 지정된 프로바이더를 동적으로 검색하기 위해서는 resolve() 메서드를 사용해 프로바이더의 주입 토큰을 인수로 전달해야 한다.

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

  async onModuleInit() {
    this.transientService = await this.moduleRef.resolve(TransientService);
  }
}

resolve() 메서드는 자체 DI 컨테이너 하위 트리에서 프로바이더의 고유한 인스턴스를 반환한다.

각 하위 트리에는 고유한 컨텍스트 식별자가 있다.

따라서, 이 메서드를 두번 이상 호출하고 인스턴스를 비교하면 같지 않음을 알 수 있다.

@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() 호출에서 단일 인스턴스를 생성하고 생성된 동일한 DI 컨테이너 하위 트리를 공유하도록 하려면 컨텍스트 식별자를 resolve() 메서드에 전달하면 된다.

컨텍스트 식별자를 생성하려면 ContextIdFactory 클래스의 create() 메서드를 사용하면 된다.

@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
  }
}

Register Request Provider


ContextIdFactory.create()를 사용하여 수동으로 생성한 컨텍스트 식별자는 Request-scope로 지정된 프로바이더가 Nest DI 시스템에 의해 관리되지 않으므로, undefined 상태이다.

이를 객체로 등록하기 위해서는 registerRequestByContextId() 메서드를 사용하면 된다.

const contextId = ContextIdFactory.create();
this.moduleRef.registerRequestByContextId(/* YOUR_REQUEST_OBJECT */, contextId);

Getting Current Sub-tree


경우에 따라 요청 컨텍스트내에서 요청 범위 프로바이더의 인스턴스를 확인하려 할 수 있다.

Request-scope가 지정된 CatsService가 Request-scope가 지정된 CatsRepository의 인스턴스를 확인한다고 해보자.

동일한 DI 컨테이너 하위 트리를 공유하기 위해서는 새 컨텍스트 식별자를 생성하는 대신 현재 컨텍스트 식별자를 가져와야 한다.

현재 컨텍스트 식별자를 얻으로면 먼저 @Inject() 데코레이터를 사용하여 요청 객체를 삽입하면 된다.

@Injectable()
export class CatsService {
  constructor(
    @Inject(REQUEST) private request: Record<string, unknown>,
  ) {}
}

이제 ContextIdFactory 클래스의 getByRequest() 메서드를 사용해 요청 객체를 기반으로 컨텍스트 ID를 만들고 이름 resolve()의 두번째 인자로 전달하면 된다.

const contextId = ContextIdFactory.getByRequest(this.request);
const catsRepository = await this.moduleRef.resolve(CatsRepository, contextId);

Instantiating Custom Classes Dynamically


등록되지 않은 클래스를 프로바이더로 동적으로 인스턴스화하려면 ModuleRefcreate() 메서드를 사용하면 된다.

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

  async onModuleInit() {
    this.catsFactory = await this.moduleRef.create(CatsFactory);
  }
}

이렇게 하면 프레임워크 컨테이너 외부에서 다른 클래스를 조건부로 인스턴스화할 수 있다.

그러면 Nest DI 시스템이 관리를 안하는건가?

참고

https://docs.nestjs.kr/fundamentals/module-ref

profile
Always's Archives
post-custom-banner

0개의 댓글