Angular의 컴포넌트 개발에서 템플릿 내의 특정 DOM 요소나 컴포넌트에 접근해야 하는 경우는 매우 흔합니다. 과거에는 @ViewChild
데코레이터를 사용했지만, Angular v17부터 시그널(Signal) 기반의 새로운 viewChild
함수가 도입되어 더 직관적이고 반응적인 개발이 가능해졌습니다.
이 글에서는 모던 Angular의 viewChild
함수와 강력한 기능인 read
옵션에 대해 자세히 알아봅니다.
viewChild
시그널 함수viewChild
는 템플릿 내에서 특정 요소나 컴포넌트를 찾아 그 참조를 시그널로 반환하는 함수입니다. 이 시그널은 초기에 undefined
값을 가지며, Angular가 뷰 렌더링을 완료하고 해당 요소를 찾으면 그 참조로 값이 업데이트됩니다.
기본적인 사용법은 다음과 같습니다.
import { Component, viewChild, ElementRef, Signal } from '@angular/core';
@Component({
standalone: true,
template: `<div #myDiv>Hello, Modern Angular!</div>`,
})
export class MyComponent {
// #myDiv 템플릿 참조 변수를 찾아 ElementRef 시그널을 반환합니다.
myDivRef: Signal<ElementRef | undefined> = viewChild('myDiv');
}
read
옵션으로 원하는 타입 읽기viewChild
함수의 두 번째 인자로 { read: TokenType }
옵션을 전달하여, 찾은 요소에서 특정 타입의 인스턴스를 가져올 수 있습니다. 이는 매우 유연한 기능을 제공합니다.
read: ElementRef
- DOM 요소 직접 접근가장 기본적인 옵션으로, 해당 DOM 요소의 ElementRef
를 가져옵니다. 네이티브 DOM API에 접근해야 할 때 유용합니다.
// #main 템플릿 참조에서 ElementRef를 읽어옵니다.
mainEl = viewChild<ElementRef>('main', { read: ElementRef });
// mainEl()?.nativeElement 접근 가능
read: ViewContainerRef
- 동적 컴포넌트 생성ViewContainerRef
는 뷰 컨테이너를 나타내며, 동적으로 컴포넌트를 생성하거나 템플릿을 삽입할 수 있는 위치를 제공합니다.
// #main 템플릿 참조에서 ViewContainerRef를 읽어옵니다.
mainVcr = viewChild<ViewContainerRef>('main', { read: ViewContainerRef });
read: Component
- 자식 컴포넌트 인스턴스 접근템플릿 참조가 있는 요소에 특정 컴포넌트가 적용되어 있다면, 해당 컴포넌트의 인스턴스를 직접 가져올 수 있습니다.
// #main 템플릿 참조가 있는 요소의 MainComponent 인스턴스를 읽어옵니다.
mainComponent = viewChild<MainComponent>('main', { read: MainComponent });
viewChild
와 read
옵션 활용하기아래는 viewChild
와 read
옵션을 활용하는 모던 Angular 컴포넌트 예제입니다.
import { Component, viewChild, ElementRef, Signal, ViewContainerRef, effect } from '@angular/core';
// 예제용 자식 컴포넌트
@Component({
standalone: true,
selector: 'app-child',
template: '<p>I am a child component</p>',
})
export class ChildComponent {
name = 'ChildComponent';
}
// 부모 컴포넌트
@Component({
standalone: true,
imports: [ChildComponent],
template: `
<app-child #main></app-child>
`,
})
export class ParentComponent {
// 1. read 옵션 없이: ChildComponent 인스턴스를 가져옴
childComp = viewChild<ChildComponent>('main');
// 2. read: ElementRef: DOM 요소의 ElementRef를 가져옴
childEl = viewChild<ElementRef>('main', { read: ElementRef });
// 3. read: ViewContainerRef: 동적 생성을 위한 ViewContainerRef를 가져옴
childVcr = viewChild<ViewContainerRef>('main', { read: ViewContainerRef });
constructor() {
effect(() => {
// viewChild 시그널은 effect 내에서 반응적으로 사용될 수 있습니다.
// 시그널의 값이 설정되면(undefined가 아니면) 이 effect가 실행됩니다.
if (this.childComp()) {
console.log('Component Instance:', this.childComp()); // ChildComponent {}
console.log('ElementRef:', this.childEl()); // ElementRef { nativeElement: <app-child> }
console.log('ViewContainerRef:', this.childVcr()); // ViewContainerRef {}
}
});
}
}
시그널 기반의 viewChild
함수와 read
옵션을 사용하면, 기존 @ViewChild
데코레이터보다 더 명확하고 타입 안전하며, Angular의 반응형 패러다임에 잘 맞는 코드를 작성할 수 있습니다.
effect
나 computed
와 함께 사용하여 참조가 가능해지는 시점에 반응적으로 코드를 실행할 수 있습니다.read
옵션을 통해 가져오는 값의 타입을 명확하게 지정할 수 있습니다.모던 Angular 개발에서는 viewChild
시그널 함수를 적극적으로 활용하여 더 나은 컴포넌트를 설계해 보세요.