둘 다 자식 요소/컴포넌트에 대한 참조를 조회하지만, 다른 곳을 봅니다. **@ViewChild**는 컴포넌트 자신의 템플릿을 조회하고, **@ContentChild**는 <ng-content>를 통해 컴포넌트에 프로젝션된 콘텐츠를 조회합니다.
@Component({
template: `
<input #nameInput /> <!-- 내 템플릿의 템플릿 참조 -->
<app-child></app-child>
`,
})
export class ParentComponent implements AfterViewInit {
@ViewChild("nameInput") input!: ElementRef; // 템플릿 참조로
@ViewChild(ChildComponent) child!: ChildComponent; // 컴포넌트 타입으로
ngAfterViewInit() {
this.input.nativeElement.focus(); // 뷰가 초기화된 후 사용 가능
this.child.doSomething(); // 자식 컴포넌트의 메서드 호출
}
}
@ViewChild는 컴포넌트 자신이 템플릿에 선언한 요소/컴포넌트에 접근합니다. **ngAfterViewInit**에서 사용 가능합니다.
@Component({
selector: "app-card",
template: `<div class="card"><ng-content></ng-content></div>`, // 여기로 콘텐츠 프로젝션
})
export class CardComponent implements AfterContentInit {
@ContentChild(CardTitleComponent) title!: CardTitleComponent;
ngAfterContentInit() {
// 프로젝션된 콘텐츠가 여기서 준비됨 (ngAfterViewInit보다 더 일찍)
console.log(this.title);
}
}
<!-- 부모가 콘텐츠를 app-card로 프로젝션 -->
<app-card>
<app-card-title>Hello</app-card-title> <!-- 이것이 ContentChild가 찾는 것 -->
</app-card>
@ContentChild는 부모가 전달한 콘텐츠(<ng-content>를 통해 프로젝션됨)에 접근합니다. **ngAfterContentInit**에서 사용 가능합니다.
@ViewChild → 이 컴포넌트 자신의 템플릿에 있는 요소 → ngAfterViewInit
@ContentChild → 부모로부터 프로젝션된 요소 → ngAfterContentInit
(복수형: @ViewChildren / @ContentChildren은 모든 매치의 QueryList를 반환)
@ViewChildren(ItemComponent) items!: QueryList<ItemComponent>; // 매칭되는 모든 항목
ngAfterViewInit() { this.items.forEach(i => ...); this.items.changes.subscribe(...); }
input = viewChild<ElementRef>("nameInput"); // signal 기반 쿼리 (최신 Angular)
@ViewChild(자신의 템플릿)와 @ContentChild(프로젝션된 콘텐츠)를 구분하는 것, 그리고 각각의 생명주기 타이밍(ngAfterViewInit 대 ngAfterContentInit)은, 자신의 템플릿 요소나 소비자가 프로젝션한 콘텐츠 중 어느 쪽과 상호작용해야 하는 재사용 가능한 컴포넌트를 만들 때 필수적입니다.
이는 흔한 혼동 지점이며, 자식 요소와 협력해야 하는 컴포넌트 라이브러리, 탭, 폼 컨트롤, 래퍼를 작성할 때 빈번히 필요합니다.