에러 백과

import 순환 감지

이 컴포넌트가 참조하는 컴포넌트, 디렉티브, 파이프를 위해 컴파일러가 import를 추가하면 import 순환이 발생하게 됩니다. 예를 들어 Parent가 템플릿에서 Child를 참조하는 상황을 살펴보겠습니다.

parent.ts

import {Component} from '@angular/core';
import {Child} from './child';

@Component({
  selector: 'app-parent',
  imports: [Child],
  template: '<app-child/>',
})
export class Parent {}

child.ts

import {Component, inject} from '@angular/core';
import {Parent} from './parent';

@Component({
  selector: 'app-child',
  template: 'The child!',
})
export class Child {
  private parent = inject(Parent);
}

Child가 생성자에서 Parent를 참조하므로 child.ts에는 이미 parent.ts를 불러오는 import가 있습니다.

HELPFUL: 부모 컴포넌트의 템플릿에는 <child></child>가 있습니다. 따라서 이 템플릿에서 생성한 코드에는 Child 클래스 참조가 포함되어야 합니다. 이 참조를 만들려면 컴파일러가 parent.ts에 child.ts를 불러오는 import를 추가해야 하며, 그러면 다음과 같은 import 순환이 발생합니다.

parent.ts -> child.ts -> parent.ts

원격 스코핑

NgModule을 사용한다면 순환을 만드는 import를 추가하지 않도록 해당 컴포넌트를 선언한 NgModule 클래스에 의존성을 연결하는 코드를 추가합니다.

이를 “원격 스코핑(remote scoping)”이라고 합니다.

라이브러리

하지만 “원격 스코핑” 코드는 부수 효과가 있어 트리 셰이킹을 방해하므로 라이브러리에서는 사용할 수 없습니다. 따라서 "compilationMode": "partial" 설정으로 라이브러리를 빌드할 때 순환 import가 필요한 컴포넌트가 있으면 NG3003 컴파일러 오류가 발생합니다.

오류 디버깅

오류 메시지에는 생성될 순환 관계가 표시됩니다. 예를 들면 다음과 같습니다.

The component Child is used in the template but importing it would create a cycle:
/parent.ts -> /child.ts -> /parent.ts

이 정보를 이용해 참조한 컴포넌트, 파이프, 디렉티브가 컴파일 중인 컴포넌트에 어떻게 다시 의존하는지 확인하세요. 다음과 같은 방법으로 문제를 해결할 수 있습니다.

  • 순환이 생기지 않도록 의존 관계를 재구성해 보세요. 예를 들어 독립적인 파일에 중간 인터페이스를 정의하면 서로 의존하는 두 파일에서 import 순환 없이 이 인터페이스를 불러올 수 있습니다.
  • 서로 참조하는 클래스를 같은 파일로 옮겨 클래스 사이의 import를 없애세요.
  • 불러온 선언을 타입으로만 사용한다면 import type 문법으로 타입 전용 import로 전환하세요. 타입 전용 import는 순환을 만들지 않습니다.