μν μμ‘΄μ±
μν μμ‘΄μ±μ λ ν΄λμ€κ° μλ‘λ₯Ό νμλ‘ ν λ λ°μν©λλ€. μλ₯Ό λ€μ΄, ν΄λμ€ Aκ° ν΄λμ€ Bλ₯Ό νμλ‘ νκ³ , ν΄λμ€ Bλ ν΄λμ€ Aλ₯Ό νμλ‘ νλ κ²½μ°μ λλ€. Nestμμλ λͺ¨λ κ° λλ νλ‘λ°μ΄λ κ°μ μ΄λ¬ν μν μμ‘΄μ±μ΄ λ°μν μ μμ΅λλ€.
μν μμ‘΄μ±μ κ°λ₯νλ©΄ νΌνλ κ²μ΄ μ’μ§λ§, νμ κ·Έλ κ² ν μλ μμ΅λλ€. μ΄λ¬ν κ²½μ°λ₯Ό μν΄ Nestλ λ κ°μ§ λ°©λ²μΌλ‘ νλ‘λ°μ΄λ κ°μ μν μμ‘΄μ±μ ν΄κ²°ν μ μλλ‘ μ§μν©λλ€. μ΄ μ₯μμλ forwardRefλ₯Ό μ¬μ©νλ λ°©λ²κ³Ό, DI 컨ν μ΄λμμ ModuleRef ν΄λμ€λ₯Ό ν΅ν΄ νλ‘λ°μ΄λ μΈμ€ν΄μ€λ₯Ό κ°μ Έμ€λ λ°©λ²μ μ€λͺ ν©λλ€.
λν, λͺ¨λ κ°μ μν μμ‘΄μ±μ ν΄κ²°νλ λ°©λ²λ μ€λͺ ν©λλ€.
κ²½κ³
μν μμ‘΄μ±μ index.tsμ κ°μ λ°°λ΄ νμΌ (barrel file)μ μ¬μ©νμ¬ importλ₯Ό κ·Έλ£Ήνν λλ λ°μν μ μμ΅λλ€. νΉν λͺ¨λμ΄λ νλ‘λ°μ΄λ ν΄λμ€μ κ΄λ ¨ν΄μλ λ°°λ΄ νμΌ μ¬μ©μ νΌν΄μΌ ν©λλ€. μλ₯Ό λ€μ΄, λμΌν λλ ν°λ¦¬ λ΄μ νμΌμ κ°μ Έμ¬ λλ λ°°λ΄ νμΌμ μ¬μ©νμ§ λ§μμΌ ν©λλ€. μ¦, cats/cats.controller μμ catsλ₯Ό ν΅ν΄ cats/cats.serviceλ₯Ό import νλ λ°©μμ μ§μν΄μΌ ν©λλ€. μμΈν λ΄μ©μ ν΄λΉ GitHub μ΄μλ₯Ό μ°Έκ³ νμΈμ.
μ λ°© μ μΈ (Forward reference)
μ λ°© μ μΈμ μμ§ μ μλμ§ μμ ν΄λμ€λ₯Ό forwardRef() μ νΈλ¦¬ν° ν¨μλ₯Ό μ¬μ©νμ¬ μ°Έμ‘°ν μ μλλ‘ ν©λλ€. μλ₯Ό λ€μ΄, CatsServiceμ CommonServiceκ° μλ‘λ₯Ό μμ‘΄νλ κ²½μ°, μμͺ½ λͺ¨λ @Inject()μ forwardRef()λ₯Ό ν¨κ» μ¬μ©νμ¬ μν μμ‘΄μ±μ ν΄κ²°ν μ μμ΅λλ€. κ·Έλ μ§ μμΌλ©΄ νμ λ©νλ°μ΄ν°κ° λͺ¨λ μ€λΉλμ§ μμ Nestκ° μ΄λ€μ μΈμ€ν΄μ€ννμ§ λͺ»ν©λλ€. λ€μμ κ·Έ μμμ λλ€:
@Injectable()
export class CatsService {
constructor(
@Inject(forwardRef(() => CommonService))
private commonService: CommonService,
) {}
}
ννΈ
forwardRef() ν¨μλ @nestjs/common ν¨ν€μ§μμ import λ©λλ€.
μ΄μ νμͺ½ κ΄κ³λ₯Ό μ²λ¦¬νμΌλ―λ‘, CommonServiceμμλ λμΌν λ°©μμΌλ‘ μ²λ¦¬ν΄ λ³΄κ² μ΅λλ€:
@Injectable()
export class CommonService {
constructor(
@Inject(forwardRef(() => CatsService))
private catsService: CatsService,
) {}
}
μ£Όμ
μΈμ€ν΄μ€ν μμλ κ²°μ λμ΄ μμ§ μμΌλ―λ‘, μ΄λ€ μμ±μκ° λ¨Όμ νΈμΆλλμ§μ μμ‘΄νλ μ½λλ μμ±νμ§ λ§μμΌ ν©λλ€. νΉν Scope.REQUEST λ²μλ₯Ό κ°μ§ νλ‘λ°μ΄λκ° μν μμ‘΄μ±μ ν¬ν¨λ κ²½μ°, μμ‘΄μ±μ΄ μ μλμ§ μμ μνκ° λ μ μμ΅λλ€. μμΈν λ΄μ©μ μ¬κΈ°λ₯Ό μ°Έκ³ νμΈμ.
ModuleRef λμ
forwardRef()λ₯Ό μ¬μ©νλ λμ , μ½λλ₯Ό 리ν©ν°λ§ νμ¬ ModuleRef ν΄λμ€λ₯Ό νμ©ν΄ μν κ΄κ³μ νμͺ½μμ νλ‘λ°μ΄λλ₯Ό μ§μ κ°μ Έμ€λ λ°©μλ μ¬μ©ν μ μμ΅λλ€. ModuleRef μ νΈλ¦¬ν° ν΄λμ€μ λν μμΈν λ΄μ©μ μ¬κΈ°λ₯Ό μ°Έκ³ νμΈμ.
λͺ¨λ μ λ°© μ μΈ
λͺ¨λ κ°μ μν μμ‘΄μ±μ ν΄κ²°νλ €λ©΄, μμͺ½ λͺ¨λμ μ°κ²°μμ λμΌνκ² forwardRef() μ νΈλ¦¬ν° ν¨μλ₯Ό μ¬μ©ν΄μΌ ν©λλ€. μλ₯Ό λ€μ΄:
@Module({
imports: [forwardRef(() => CatsModule)],
})
export class CommonModule {}
μ΄μ νμͺ½ λͺ¨λμμ μ λ°© μ μΈμ μ€μ νμΌλ―λ‘, CatsModuleμμλ λμΌν λ°©μμΌλ‘ μ€μ ν΄ λ³΄κ² μ΅λλ€:
@Module({
imports: [forwardRef(() => CommonModule)],
})
export class CatsModule {}
λͺ¨λ μ°Έμ‘°
Nestλ ModuleRef ν΄λμ€λ₯Ό μ 곡νμ¬ λ΄λΆ νλ‘λ°μ΄λ λͺ©λ‘μ νμνκ³ , μ£Όμ ν ν°μ ν€λ‘ μ¬μ©νμ¬ μνλ νλ‘λ°μ΄λμ μ°Έμ‘°λ₯Ό μ»μ μ μλλ‘ ν©λλ€. λν ModuleRefλ μ μ (static) νλ‘λ°μ΄λμ λ²μ (scope)κ° μ§μ λ νλ‘λ°μ΄λ λͺ¨λλ₯Ό λμ μΌλ‘ μΈμ€ν΄μ€ννλ κΈ°λ₯λ μ 곡ν©λλ€. ModuleRefλ μΌλ°μ μΈ λ°©μμΌλ‘ ν΄λμ€μ μ£Όμ ν μ μμ΅λλ€:
@Injectable()
export class CatsService {
constructor(private moduleRef: ModuleRef) {}
}
ννΈ
ModuleRef ν΄λμ€λ @nestjs/core ν¨ν€μ§μμ import λ©λλ€.
μΈμ€ν΄μ€ κ°μ Έμ€κΈ°
ModuleRef μΈμ€ν΄μ€ (μ΄ν λͺ¨λ μ°Έμ‘°)λ get() λ©μλλ₯Ό μ 곡ν©λλ€. κΈ°λ³Έμ μΌλ‘ μ΄ λ©μλλ νμ¬ λͺ¨λμ λ±λ‘λμ΄ μΈμ€ν΄μ€νλ νλ‘λ°μ΄λ, 컨νΈλ‘€λ¬ λλ μΈμ ν°λΈ (μ: κ°λ, μΈν°μ ν° λ±)μ μ£Όμ ν ν° λλ ν΄λμ€ μ΄λ¦μ μ¬μ©νμ¬ λ°νν©λλ€. λ§μ½ μΈμ€ν΄μ€λ₯Ό μ°Ύμ§ λͺ»νλ©΄ μμΈκ° λ°μν©λλ€.
@Injectable()
export class CatsService implements OnModuleInit {
private service: Service;
constructor(private moduleRef: ModuleRef) {}
onModuleInit() {
this.service = this.moduleRef.get(Service);
}
}
μ£Όμ
get() λ©μλλ λ²μκ° μ§μ λ νλ‘λ°μ΄λ (μ: transient λλ request λ²μ)λ₯Ό κ°μ Έμ¬ μ μμ΅λλ€. μ΄λ¬ν κ²½μ°μλ μλμ μ€λͺ λ κΈ°λ²μ μ¬μ©ν΄μΌ ν©λλ€. λ²μ μ μ΄μ λν μμΈν λ΄μ©μ μ¬κΈ°λ₯Ό μ°Έκ³ νμΈμ.
μ μ 컨ν μ€νΈμμ νλ‘λ°μ΄λλ₯Ό κ°μ Έμ€λ €λ©΄ (μ: ν΄λΉ νλ‘λ°μ΄λκ° λ€λ₯Έ λͺ¨λμμ μ£Όμ λ κ²½μ°), get() λ©μλμ λ λ²μ§Έ μΈμλ‘ { strict: false } μ΅μ μ μ λ¬νλ©΄ λ©λλ€.
this.moduleRef.get(Service, { strict: false });
λ²μκ° μ§μ λ νλ‘λ°μ΄λ ν΄κ²°νκΈ°
transient λλ request λ²μμ νλ‘λ°μ΄λλ₯Ό λμ μΌλ‘ ν΄κ²°νλ €λ©΄, ν΄λΉ νλ‘λ°μ΄λμ μ£Όμ ν ν°μ μΈμλ‘ μ λ¬νμ¬ 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 ν΄λμ€λ @nestjs/core ν¨ν€μ§μμ import λ©λλ€.
REQUEST νλ‘λ°μ΄λ λ±λ‘νκΈ°
ContextIdFactory.create()λ‘ μλ μμ±ν 컨ν μ€νΈ μλ³μλ Nestμ DI μμ€ν μ μν΄ μΈμ€ν΄μ€νλκ±°λ κ΄λ¦¬λμ§ μκΈ° λλ¬Έμ, ν΄λΉ DI νμ νΈλ¦¬ λ΄μμλ REQUEST νλ‘λ°μ΄λκ° μ μλμ΄ μμ§ μμ΅λλ€.
μ΄λ¬ν μλ μμ± DI νμ νΈλ¦¬μ μ¬μ©μ μ μ REQUEST κ°μ²΄λ₯Ό λ±λ‘νλ €λ©΄, ModuleRef#registerRequestByContextId() λ©μλλ₯Ό λ€μκ³Ό κ°μ΄ μ¬μ©ν΄μΌ ν©λλ€:
const contextId = ContextIdFactory.create();
this.moduleRef.registerRequestByContextId(/* λΉμ μ_REQUEST_κ°μ²΄ */, contextId);
νμ¬ νμ νΈλ¦¬ κ°μ Έμ€κΈ°
κ²½μ°μ λ°λΌ μμ² μ»¨ν μ€νΈ λ΄μμ request λ²μμ νλ‘λ°μ΄λ μΈμ€ν΄μ€λ₯Ό ν΄κ²°ν΄μΌ ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, CatsServiceκ° request λ²μλ‘ μ§μ λμ΄ μκ³ , λμΌνκ² request λ²μμΈ CatsRepository μΈμ€ν΄μ€λ₯Ό ν΄κ²°νκ³ μΆλ€κ³ κ°μ ν΄ λ΄ μλ€. λμΌν DI 컨ν μ΄λ νμ νΈλ¦¬λ₯Ό 곡μ νλ €λ©΄, ContextIdFactory.create()μ κ°μ΄ μλ‘μ΄ μ»¨ν μ€νΈ μλ³μλ₯Ό μμ±νλ λμ νμ¬ μ»¨ν μ€νΈ μλ³μλ₯Ό μ»μ΄μΌ ν©λλ€. μ΄λ₯Ό μν΄ λ¨Όμ @Inject() λ°μ½λ μ΄ν°λ₯Ό μ¬μ©νμ¬ request κ°μ²΄λ₯Ό μ£Όμ ν΄μΌ ν©λλ€.
@Injectable()
export class CatsService {
constructor(
@Inject(REQUEST) private request: Record<string, unknown>,
) {}
}
ννΈ
request νλ‘λ°μ΄λμ λν λ΄μ©μ μ¬κΈ°μμ νμΈν μ μμ΅λλ€.
μ΄μ 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);
}
}
μ΄ κΈ°λ²μ μ¬μ©νλ©΄ νλ μμν¬ μ»¨ν μ΄λ μΈλΆμμ 쑰건μ λ°λΌ μλ‘ λ€λ₯Έ ν΄λμ€λ₯Ό λμ μΌλ‘ μΈμ€ν΄μ€νν μ μμ΅λλ€.
Reference
'π 곡μ λ¬Έμ λ²μ > Nest.js' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[Nest.js] Fundamentals | Lifecycle events, Discovery service (1) | 2025.07.29 |
---|---|
[Nest.js] Fundamentals | Lazy-loading modules, Execution context (3) | 2025.07.29 |
[Nest.js] Fundamentals - Dynamic modules, Injection scopes (0) | 2025.07.16 |
[Nest.js] Fundamentals - Custom providers, Asynchronous providers (0) | 2025.07.15 |
[Nest.js] Overview - Interceptors, Custom decorators (0) | 2025.07.13 |