개요
Angular에서 뷰에 데이터를 표시하는 데이터 바인딩에 이은 이번에는 데코레이터를 활용한 부모-자식 컴포넌트 간에 통신하는 방법에 대해 정리하려고 한다.
1. @Input()
@Input() 데코레이터는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용한다.
= 부모 -> 자식
// child.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template: `<p>안녕하세요, {{ name }}님!</p>`
})
export class ChildComponent {
@Input() name!: string;
}
-> @Input()은 name이라는 속성에 데코레이터를 붙여서, 외부에서 값을 주입할 수 있게 한다.
-> name! 에서 !는 TypeScript에서 '값이 나중에 들어올 거야'라는 의미로 사용하는 non-null assertio이다.
-> 템플릿에 전달받은 name을 바인딩 해 출력한다.
<!-- parent.component.html -->
<app-child [name]="'ho'"></app-child>
-> [name]="'ho'"는 자식 컴포넌트의 @Input() 속성인 name에 'ho'라는 값을 전달한다.
2. @Output()
자식 컴포넌트에서 이벤트를 발생시켜 부모 컴포넌트가 이를 처리하도록 만들 때 사용한다.
= 자식 -> 부모
// child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child',
template: `<button (click)="sendMessage()">부모에게 메시지 보내기</button>`
})
export class ChildComponent {
@Output() message = new EventEmitter<string>();
sendMessage() {
this.message.emit('안녕하세요! 자식 컴포넌트입니다.');
}
}
-> @output() 데코레이터를 사용해 message라는 이벤트를 부모로 보낸다.
-> EventEmitter<string>()는 문자열 타입의 이벤트를 발생시키겠다는 의미이다.
-> sendMessage() 함수는 버튼 클릭 시 호출되고, message.emit()으로 부모에게 메시지를 전달한다.
<!-- parent.component.html -->
<app-child (message)="onMessageReceived($event)"></app-child>
// parent.component.ts
onMessageReceived(msg: string) {
console.log('자식으로부터 받은 메시지:', msg);
}
-> (message)="onMessageReceived($event)"는 자식 컴포넌트에서 발생한 message 이벤트를 부모 컴포넌트의 onMessageReceived() 메서드로 연결한다.
-> $event는 자식이 emit 한 값이다.
* $event
-> Angular 템플릿 문법에서 제공하는 기본 예약어로 자식 컴포넌트에서 emit한 값을 받아오기 위한 표현 방식 중 하나이다.
3. @Input(), @Output() 혼합
이번에는 @Input()과 @Output() 모두 적용해보자.
1. 부모는 자식에게 초기 이름을 전달
2. 자식이 버튼 클릭 시 이름을 변경하고 부모에게 알린다.
3. 부모는 자식에게서 받은 이름으로 상태를 업데이트한다.
자식
// child.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child',
template: `
<p>현재 이름: {{ name }}</p>
<button (click)="changeName()">이름 바꾸기</button>
`
})
export class ChildComponent {
@Input() name!: string;
@Output() nameChange = new EventEmitter<string>();
changeName() {
const newName = 'TAEK';
this.nameChange.emit(newName);
}
}
부모
<!-- parent.component.html -->
<app-child [name]="parentName" (nameChange)="onNameChanged($event)"></app-child>
<p>자식이 바꾼 이름: {{ parentName }}</p>
// parent.component.ts
export class ParentComponent {
parentName = 'HO';
onNameChanged(newName: string) {
this.parentName = newName;
}
}
-> 초기 이름은 'HO'
-> 자식에서 'TAEK'이라는 이름을 보내면 parentName을 업데이트한다.
-> 이 값이 다시 자식에게 전달되기 때문에 이름이 화면에 즉시 반영된다.
처음 보면 헷갈리긴 한데 자주 쓰다보면 금방 익숙해질 것 같다!
4. @ViewChild란?
부모 컴포넌트에서 자식 컴포넌트, 디렉티브, DOM 요소를 직접 참조해서
자식들의 메서드 호출, 속성 접근, DOM 조작 등을 가능하도록 해준다!
위에 적었던 @Input, @Output의 경우에는 데이터 및 이벤트를 전달할 때 사용하는 거고, ViewChild와는 다르다!
각각의 상황에 대해 정리하면,
상황 | 해결 방법 |
자식 컴포넌트의 메서드를 부모에서 실행하고 싶을 때 | @ViewChild |
특정 DOM 요소에 직접 접근해야 할 때 | @ViewChild |
@Input 값 변경만으로 충분할 때 | (필요 없음) |
자식이 부모에게 이벤트를 알려야 할 때 | @Output 사용 |
기본 사용법
@ViewChild(selector, options) target: TargetType;
: selector
-> DOM의 템플릿 참조 변수(#변수) or 컴포넌트 클래스 이름
: options
-> { static: true}
-> 기본값 false = ngAfterViewInit에서 접근 가능
-> true = ngOnIt에서 접근 가능
: target
->접근할 대상 변수 (ElementRef, 자식 컴포넌트, 디렉티브 등)
예제
DOM 요소 접근
<input #inputRef type="text" placeholder="이름을 입력하세요" />
<button (click)="focusInput()">포커스 이동</button>
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
@Component({
selector: 'app-input-focus',
templateUrl: './input-focus.component.html'
})
export class InputFocusComponent implements AfterViewInit {
@ViewChild('inputRef') inputElement!: ElementRef<HTMLInputElement>;
ngAfterViewInit(): void {
this.inputElement.nativeElement.focus(); // 초기 포커스 이동
}
focusInput(): void {
this.inputElement.nativeElement.focus(); // 버튼 클릭 시 포커스 이동
}
}
: #inputRef
-> 템플릿에서 input 요소에 참조 변수를 지정
: @ViewChild('inputRef')
-> 지정한 참조 변수를 통해 DOM 요소를 가져옴
: ElementRef.nativeElement.focus()
-> 실제 DOM의 focus 메서드 호출.
자식 컴포넌트 메서드 호출
//자식 컴포넌트
@Component({
selector: 'app-child',
template: `<p>안녕하세요! 자식 컴포넌트입니다.</p>`
})
export class ChildComponent {
sayHello(): void {
console.log('자식 컴포넌트에서 sayHello 호출!');
}
}
//부모
<app-child #childComp></app-child>
<button (click)="callChild()">자식 메서드 실행</button>
import { Component, ViewChild } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html'
})
export class ParentComponent {
@ViewChild('childComp') child!: ChildComponent;
callChild(): void {
this.child.sayHello(); // 자식 컴포넌트의 메서드 호출
}
}
-> 부모가 자식의 sayHell() 메서드를 직접 호출한다.
'Web > Angular' 카테고리의 다른 글
[Angular] 컴포넌트 라이프싸이클 (0) | 2025.04.22 |
---|---|
[Angular] Service, 의존성 주입 (0) | 2025.04.21 |
[Angular] 데이터 바인딩(Data Binding)이란 (0) | 2025.03.24 |
[Angular] angular bootstrap 개요 (0) | 2025.03.23 |
[Angular] Component 개요 (0) | 2025.03.20 |