Nest는 ModuleRef
클래스를 제공해 내부 프로바이더의 목록을 탐색할 수 있게하고, 프로바이더를 정의할 때 지정된 주입 토큰을 키로 사용해 프로바이더에 대한 참조를 얻게한다.
ModuleRef
클래스를 이용해 정적 및 범위가 지정된 프로바이더를 모두 동적으로 인스턴스화 할수도 있다.
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 });
범위가 지정된 프로바이더를 동적으로 검색하기 위해서는 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
}
}
ContextIdFactory.create()
를 사용하여 수동으로 생성한 컨텍스트 식별자는 Request-scope로 지정된 프로바이더가 Nest DI 시스템에 의해 관리되지 않으므로, undefined
상태이다.
이를 객체로 등록하기 위해서는 registerRequestByContextId()
메서드를 사용하면 된다.
const contextId = ContextIdFactory.create();
this.moduleRef.registerRequestByContextId(/* YOUR_REQUEST_OBJECT */, contextId);
경우에 따라 요청 컨텍스트내에서 요청 범위 프로바이더의 인스턴스를 확인하려 할 수 있다.
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);
등록되지 않은 클래스를 프로바이더로 동적으로 인스턴스화하려면 ModuleRef
의 create()
메서드를 사용하면 된다.
@Injectable()
export class CatsService implements OnModuleInit {
private catsFactory: CatsFactory;
constructor(private moduleRef: ModuleRef) {}
async onModuleInit() {
this.catsFactory = await this.moduleRef.create(CatsFactory);
}
}
이렇게 하면 프레임워크 컨테이너 외부에서 다른 클래스를 조건부로 인스턴스화할 수 있다.
그러면 Nest DI 시스템이 관리를 안하는건가?