NESTJS를 배워보자(17) - Module reference

yoon·2023년 9월 25일
0

NESTJS를 배워보자

목록 보기
17/21

Module reference

nest의 공식문서를 토대로 작성합니다.

Nest는 내부 providers 목록을 탐색하고 해당 주입 토큰을 조회 키로 사용하여 모든 providers에 대한 참조를 얻을 수 있는 ModuleRef 클래스를 제공합니다. ModuleRef 클래스는 정적 및 범위가 지정된 providers를 모두 동적으로 인스턴스화하는 방법도 제공합니다. ModuleRef는 일반적인 방법으로 클래스에 주입할 수 있습니다.

# cats.service.ts

@Injectable()
export class CatsService {
  constructor(private moduleRef: ModuleRef) {}
}

Retrieving instances

ModuleRef 인스턴스에는 get() 메소드가 있습니다. 이 메소드는 인젝션 토큰/클래스 이름을 사용하여 현재 모듈에 존재하는(인스턴스화 된) provider, 컨트롤러 또는 injectable(가드, 인터셉터 등)을 검색합니다.

# cats.service.ts

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

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

Warning
get() 메소드에서는 범위가 지정된 provider를 검색할 수 없음. 대신 아래에 설명된 기술을 사용.

전역 컨텍스트 provider를 검색하려면 (예: provider가 다른 모듈에 삽입된 경우에) { strict: false } 옵션을 get()의 두 번째 인수로 전달합니다.

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

Resolving scoped providers

범위가 지정된 provider를 동적으로 확인하려면 provider의 인젝션 토큰을 인수로 전달하여 resolve() 메소드를 사용합니다.

# cats.service.ts

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

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

resolve() 메소드는 자체 DI 컨테이너 하위 트리에서 provider의 고유한 인스턴스를 반환합니다. 각 하위 트리에는 고유한 컨텍스트 식별자가 있습니다. 따라서 이 메소드를 두 번 이상 호출하고 인스턴스 참조를 비교하면 인스턴스 참조가 동일하지 않다는 것을 알 수 있습니다.

# cats.service.ts

@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() 메소드를 제공합니다.

# cats.service.ts

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

Registering REQUEST provider

수동으로 생성된 컨텍스트 식별자(ContextIdFactory.create()를 사용하여)는 Nest 종속성 주입 시스템에 의해 인스턴스화 및 관리되지 않기 때문에 REQUEST provider가 undefined입니다.

수동으로 생성된 DI 하위 트리에 대한 사용자 정의 REQUEST 객체를 등록하려면 다음과 같이 ModuleRef#registerRequestByContextId() 메소드를 사용합니다:

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

Getting current sub-tree

요청 컨텍스트 내에서 요청 범위가 지정된 provider의 인스턴스를 확인해야 하는 경우가 있습니다. CatsService가 request 범위로 설정되어 있고 request 범위가 설정된 provider로 표시된 CatsRepository 인스턴스를 확인하려 한다고 가정합니다. 동일한 DI 컨테이너 하위 트리를 공유하려면 새 컨텍스트 식별자를 생성하는 대신 현재 컨텍스트 식별자를 가져와야 합니다. 현재 컨텍스트 식별자를 얻으려면 @Inject() 데코레이터를 사용하여 reuqest 객체를 주입합니다.

# cats.service.ts

@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

이전에 provider로 등록되지 않은 클래스를 동적으로 인스턴스화하려면 module reference의 create() 메소드를 사용합니다.

# cats.service.ts

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

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

이 기술을 사용하면 프레임워크 컨테이너 외부에서 다양한 클래스를 조건부로 인스턴스화할 수 있습니다.

고생하셨습니다!
다음 글에서 만나요~~😀


저도 아직 배우는 단계입니다. 지적 감사히 받겠습니다. 함께 열심히 공부해요!!

profile
백엔드 개발자 지망생

0개의 댓글