nest의 공식문서를 토대로 작성합니다.
Nest는 내부 providers 목록을 탐색하고 해당 주입 토큰을 조회 키로 사용하여 모든 providers에 대한 참조를 얻을 수 있는 ModuleRef
클래스를 제공합니다. ModuleRef
클래스는 정적 및 범위가 지정된 providers를 모두 동적으로 인스턴스화하는 방법도 제공합니다. ModuleRef
는 일반적인 방법으로 클래스에 주입할 수 있습니다.
# cats.service.ts
@Injectable()
export class CatsService {
constructor(private moduleRef: ModuleRef) {}
}
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 });
범위가 지정된 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
}
}
REQUEST
provider수동으로 생성된 컨텍스트 식별자(ContextIdFactory.create()
를 사용하여)는 Nest 종속성 주입 시스템에 의해 인스턴스화 및 관리되지 않기 때문에 REQUEST
provider가 undefined
입니다.
수동으로 생성된 DI 하위 트리에 대한 사용자 정의 REQUEST
객체를 등록하려면 다음과 같이 ModuleRef#registerRequestByContextId()
메소드를 사용합니다:
const contextId = ContextIdFactory.create();
this.moduleRef.registerRequestByContextId(/* YOUR_REQUEST_OBJECT */, contextId);
요청 컨텍스트 내에서 요청 범위가 지정된 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);
이전에 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);
}
}
이 기술을 사용하면 프레임워크 컨테이너 외부에서 다양한 클래스를 조건부로 인스턴스화할 수 있습니다.
고생하셨습니다!
다음 글에서 만나요~~😀
저도 아직 배우는 단계입니다. 지적 감사히 받겠습니다. 함께 열심히 공부해요!!