심화 가이드
템플릿

양방향 바인딩

양방향 바인딩(two way binding) 은 엘리먼트와 어떤 값을 바인딩하면서, 동시에 바인딩한 값이 변경되는 것을 감지하는 방식입니다.

문법

양방향 바인딩 문법은 대괄호를 사용하는 프로퍼티 바인딩과 소괄호를 사용하는 이벤트 바인딩이 조합된 [()] 입니다. Angular 커뮤니티에서는 비공식적으로 "상자 안에 든 바나나" 라고 하기도 합니다.

폼 컨트롤 양방향 바인딩

양방향 바인딩은 컴포넌트 데이터와 폼 컨트롤을 같은 값으로 동기화하는 용도로 자주 사용됩니다. 사용자가 문자열을 입력하면 컴포넌트의 상태가 갱신되는 과정도 이 경우에 해당됩니다.

아래 코드는 firstName 값을 동적으로 갱신하는 예제 코드입니다:

import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';

@Component({
  imports: [FormsModule],
  template: `
    <main>
      <h2>Hello {{ firstName }}!</h2>
      <input type="text" [(ngModel)]="firstName" />
    </main>
  `,
})
export class App {
  firstName = 'Ada';
}

기본 폼 컨트롤에 양방향 바인딩을 사용하려면 이렇게 하면 됩니다:

  1. @angular/forms 패키지에서 FormsModule을 로드합니다.
  2. ngModel 디렉티브와 양방향 바인딩 문법을 활용해서 [(ngModel)]과 같이 구현합니다.
  3. 갱신할 컴포넌트 프로퍼티를 지정합니다. 위 예제 코드에서는 firstName과 연결했습니다.

이렇게 한 번 연결하고 나면 <input> 엘리먼트 값이 변경되면 Angular가 변경된 값으로 컴포넌트 상태를 갱신합니다!

NgModel 도 자세하게 알아보세요.

컴포넌트 간 양방향 바인딩

폼 엘리먼트가 아니라 부모-자식 컴포넌트에 양방향 바인딩을 연결하려면 설정해야 하는 것이 조금 더 있습니다.

아래 코드는 App에서 초기값을 설정하지만 화면을 렌더링하거나 갱신하는 로직은 자식 컴포넌트인 Counter 안에 있는 예제 코드입니다.

app.ts

import {Component} from '@angular/core';
import {Counter} from './counter';

@Component({
  selector: 'app-root',
  imports: [Counter],
  template: `
    <main>
      <h1>Counter: {{ initialCount }}</h1>
      <app-counter [(count)]="initialCount"></app-counter>
    </main>
  `,
})
export class App {
  initialCount = 18;
}

counter.ts

import {Component, model} from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="updateCount(-1)">-</button>
    <span>{{ count() }}</span>
    <button (click)="updateCount(+1)">+</button>
  `,
})
export class Counter {
  count = model<number>(0);

  updateCount(amount: number): void {
    this.count.update((currentCount) => currentCount + amount);
  }
}

양방향 바인딩 연결하기

위 예제에서 보듯이, 컴포넌트끼리 양방향 바인딩하려면 이런 조건이 필요합니다:

자식 컴포넌트에는 model 프로퍼티가 존재해야 합니다.

간단하게 보면 이렇습니다:

counter.ts

import {Component, model} from '@angular/core';

@Component({
  /* 생략 */
})
export class Counter {
  count = model<number>(0);

  updateCount(amount: number): void {
    this.count.update((currentCount) => currentCount + amount);
  }
}

그리고 부모 컴포넌트에는:

  1. 템플릿에서 model 프로퍼티에 양방향 바인딩 문법으로 바인딩합니다.
  2. 부모의 프로퍼티나 시그널을 자식의 model 프로퍼티에 바인딩합니다.

간단하게 보면 이렇습니다:

app.ts

import {Component} from '@angular/core';
import {Counter} from './counter';

@Component({
  selector: 'app-root',
  imports: [Counter],
  template: `
    <main>
      <app-counter [(count)]="initialCount"></app-counter>
    </main>
  `,
})
export class App {
  initialCount = 18;
}