본문으로 건너뛰기
개발 머꼬
개발 노트NestJS
hohyeon.dev17

순환 의존도 아닌데 can't resolve dependencies가 난 이유

  • #Common Pitfall
  • #Engineering Note
  • #NestJS

문제 발생

UserService를 다른 모듈의 컨트롤러에 주입했더니 부팅이 실패했습니다. 순환 의존은 없었습니다.

Nest can't resolve dependencies of the OrderController (?).
Please make sure that the argument UserService at index [0]
is available in the OrderModule context.

OrderModuleprovidersUserService를 추가하니 뜨긴 했는데, 그때부터 캐시가 비어 있는 것처럼 동작했습니다.

원인 분석

두 가지가 겹쳐 있습니다.

첫째, provider는 모듈에 갇혀 있습니다. 공식 문서가 명시합니다 — 모듈은 기본적으로 provider를 캡슐화하므로, 현재 모듈에 속하거나 가져온(imported) 모듈이 명시적으로 내보낸(exported) provider만 주입할 수 있습니다.

즉 공유하려면 두 가지가 모두 필요합니다 — 소유한 모듈이 exports에 넣고, 쓰려는 모듈이 그 모듈을 imports에 넣는 것입니다. 둘 중 하나만으로는 안 됩니다.

둘째, providers에 다시 넣으면 별개 인스턴스가 생깁니다. 오류는 사라지지만 그 모듈만의 새 인스턴스를 갖게 됩니다. 문서가 강조하는 것이 이 지점입니다 — Nest에서 모듈은 기본적으로 싱글턴이므로 여러 모듈이 같은 provider 인스턴스를 손쉽게 공유할 수 있습니다. 그 이점을 스스로 버린 셈입니다. 캐시가 비어 보였던 이유가 이것입니다.

해결 방안

  1. 소유 모듈에서 내보냅니다.
@Module({
  providers: [UserService],
  exports: [UserService],   // 이게 없으면 밖에서 주입할 수 없다
})
export class UserModule {}
  1. 쓰는 모듈에서 가져옵니다.
@Module({
  imports: [UserModule],    // providers에 UserService를 넣지 않는다
  controllers: [OrderController],
})
export class OrderModule {}
  1. 네 속성의 역할을 구분합니다.
속성의미
providers이 모듈이 소유해 인스턴스를 만드는 provider
controllers이 모듈의 컨트롤러
imports필요한 provider를 내보내는 다른 모듈 목록
exportsproviders 중 다른 모듈에 공개할 것
  1. providers에 남의 서비스를 넣지 않습니다. 오류를 없애는 가장 빠른 방법이지만, 인스턴스가 갈라지면서 상태·캐시·커넥션이 두 벌이 됩니다. 오류 메시지가 사라진다고 해결된 것이 아닙니다.

  2. 모듈도 다시 내보낼 수 있습니다. 여러 곳에서 같은 조합을 쓸 때 유용합니다.

@Module({
  imports: [UserModule, AuthModule],
  exports: [UserModule, AuthModule],   // 이 모듈을 가져오면 둘 다 따라온다
})
export class CoreModule {}
  1. 전역 모듈은 아껴 씁니다. @Global()을 붙이면 imports 없이 쓸 수 있지만, 의존 관계가 코드에 드러나지 않습니다. 설정(ConfigModule)처럼 정말 어디서나 필요한 것에만 씁니다.

  2. 오류 메시지를 그대로 읽습니다. available in the XxxModule context가 핵심입니다 — "이 모듈의 문맥에서 보이지 않는다"는 말이고, 답은 거의 항상 exportsimports 둘 중 하나입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.