에러 백과

DOM 구조를 다시 생성하게 만드는 추적 표현식

@for 반복문에 지정한 객체 동일성 기반 추적 표현식 때문에 모든 항목에 해당하는 DOM이 다시 생성되었습니다. 이는 불변 자료 구조를 다룰 때 흔히 발생하는 매우 비용이 큰 작업입니다. 예를 들면 다음과 같습니다.

@Component({
  template: `
    <button (click)="toggleAllDone()">All done!</button>
    <ul>
      @for (todo of todos; track todo) {
        <li>{{ todo.task }}</li>
      }
    </ul>
  `,
})
export class App {
  todos = [
    {id: 0, task: 'understand trackBy', done: false},
    {id: 1, task: 'use proper tracking expression', done: false},
  ];

  toggleAllDone() {
    this.todos = this.todos.map((todo) => ({...todo, done: true}));
  }
}

이 예제에서는 항목의 “done” 상태를 전환한 뒤 모든 뷰(DOM 노드, Angular 디렉티브, 컴포넌트, 쿼리 등)를 포함한 전체 목록이 다시 생성됩니다! 이 경우에는 비용이 비교적 적은 done 프로퍼티의 바인딩 갱신만으로 충분합니다.

DOM 트리를 다시 생성하면 성능이 크게 저하될 뿐 아니라 포커스, 텍스트 선택, iframe에 로드한 사이트 같은 DOM 엘리먼트의 상태도 사라집니다.

오류 해결

객체의 동일성과 관계없이 컬렉션의 항목을 고유하게 식별하도록 추적 표현식을 변경하세요. 위 예제에서는 고유한 id 프로퍼티(item.id)를 사용해야 합니다.

@Component({
  template: `
    <button (click)="toggleAllDone()">All done!</button>
    <ul>
      @for (todo of todos; track todo.id) {
        <li>{{ todo.task }}</li>
      }
    </ul>
  `,
})
export class App {
  todos = [
    {id: 0, task: 'understand trackBy', done: false},
    {id: 1, task: 'use proper tracking expression', done: false},
  ];

  toggleAllDone() {
    this.todos = this.todos.map((todo) => ({...todo, done: true}));
  }
}