Angular에서 변경 감지 전략을 사용하는 방법
소개
기본적으로 Angular 2+는 앱에서 변경 사항이 있을 때마다 모든 구성 요소(위에서 아래로)에서 변경 감지를 수행합니다. 사용자 이벤트 또는 네트워크 요청에서 받은 데이터에서 변경이 발생할 수 있습니다.
변경 감지는 성능이 매우 뛰어나지만 앱이 더 복잡해지고 구성 요소의 양이 증가함에 따라 변경 감지는 점점 더 많은 작업을 수행해야 합니다.
한 가지 솔루션은 특정 구성 요소에 대해 OnPush
변경 감지 전략을 사용하는 것입니다. 이렇게 하면 Angular가 새 참조가 전달될 때와 데이터가 변경될 때만 이러한 구성 요소와 해당 하위 트리에서 변경 감지를 실행하도록 지시합니다.
이 문서에서는 ChangeDetectionStrategy
및 ChangeDetectorRef
에 대해 알아봅니다.
전제 조건
이 기사를 따라 하려면 다음이 필요합니다.
- Angular 구성 요소에 대한 어느 정도의 지식이 도움이 될 수 있습니다.
- 또한 이 문서는 RxJS 라이브러리를 참조하며
BehaviorSubject
및Observable
에 익숙하면 도움이 될 수 있습니다.
ChangeDetectionStrategy 예제 살펴보기
수생 생물 목록을 표시하고 사용자가 목록에 새 생물을 추가할 수 있도록 하는 하위 구성 요소가 있는 샘플 구성 요소를 살펴보겠습니다.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
aquaticCreatures = ['shark', 'dolphin', 'octopus'];
addAquaticCreature(newAquaticCreature) {
this.aquaticCreatures.push(newAquaticCreature);
}
}
템플릿은 다음과 같습니다.
<input #inputAquaticCreature type="text" placeholder="Enter a new creature">
<button (click)="addAquaticCreature(inputAquaticCreature.value)">Add creature</button>
<app-child [data]="aquaticCreatures"></app-child>
app-child
구성요소는 다음과 유사합니다.
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html'
})
export class ChildComponent {
@Input() data: string[];
}
그리고 app-child
템플릿은 다음과 유사합니다.
<ul>
<li *ngFor="let item of data">{{ item }}</li>
</ul>
브라우저에서 애플리케이션을 컴파일하고 방문한 후 shark
, dolphin
및 octopus
가 포함된 정렬되지 않은 목록을 관찰해야 합니다.
입력 필드에 수생 생물을 입력하고 생물 추가 버튼을 클릭하면 새 생물이 목록에 추가됩니다.
Angular가 부모 구성 요소에서 데이터가 변경되었음을 감지하면 자식 구성 요소가 업데이트됩니다.
이제 하위 구성 요소의 변경 감지 전략을 OnPush
로 설정하겠습니다.
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
@Input() data: string[];
}
브라우저에서 애플리케이션을 다시 컴파일하고 방문한 후 shark
, dolphin
및 octopus
가 포함된 정렬되지 않은 목록을 관찰해야 합니다.
그러나 새로운 수생 생물을 추가해도 정렬되지 않은 목록에 추가되지 않는 것 같습니다. 새 데이터는 여전히 상위 구성 요소의 aquaticCreatures
배열로 푸시되지만 Angular는 데이터 입력에 대한 새 참조를 인식하지 못하므로 구성 요소에서 변경 감지를 실행하지 않습니다.
데이터 입력에 대한 새 참조를 전달하려면 Array.push
를 addAquaticCreature
에서 확산 구문(...)
으로 바꿀 수 있습니다.
// ...
addAquaticCreature(newAquaticCreature) {
this.aquaticCreatures = [...this.aquaticCreatures, newAquaticCreature];
}
// ...
이 변형을 사용하면 더 이상 aquaticCreatures
배열을 변경하지 않습니다. 완전히 새로운 배열을 반환하고 있습니다.
다시 컴파일한 후 응용 프로그램이 이전과 같이 작동하는지 관찰해야 합니다. Angular는 data
에 대한 새로운 참조를 감지하여 하위 구성 요소에서 변경 감지를 실행했습니다.
이것으로 OnPush
변경 감지 전략을 사용하도록 샘플 부모 및 자식 구성 요소 수정을 마칩니다.
ChangeDetectorRef 예제 살펴보기
OnPush
의 변경 감지 전략을 사용하는 경우 무언가가 변경되어야 할 때마다 새 참조를 전달하는 것 외에 ChangeDetectorRef
를 사용하여 완전한 제어를 할 수도 있습니다.
ChangeDetectorRef.detectChanges()
예를 들어 데이터를 계속 변경한 다음 새로 고침 버튼이 있는 하위 구성 요소에 버튼을 가질 수 있습니다.
Array.push
를 사용하려면 addAquaticCreature
를 되돌려야 합니다.
// ...
addAquaticCreature(newAquaticCreature) {
this.aquaticCreatures.push(newAquaticCreature);
}
// ...
그리고 refresh()
를 트리거하는 button
요소를 추가합니다.
<ul>
<li *ngFor="let item of data">{{ item }}</li>
</ul>
<button (click)="refresh()">Refresh</button>
그런 다음 ChangeDetectorRef
를 사용하도록 하위 구성 요소를 수정합니다.
import {
Component,
Input,
ChangeDetectionStrategy,
ChangeDetectorRef
} from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent {
@Input() data: string[];
constructor(private cd: ChangeDetectorRef) {}
refresh() {
this.cd.detectChanges();
}
}
브라우저에서 애플리케이션을 컴파일하고 방문한 후 shark
, dolphin
및 octopus
가 포함된 정렬되지 않은 목록을 관찰해야 합니다.
배열에 새 항목을 추가해도 정렬되지 않은 목록은 업데이트되지 않습니다. 그러나 새로 고침 버튼을 누르면 구성 요소에 대한 변경 감지가 실행되고 업데이트가 수행됩니다.
ChangeDetectorRef.markForCheck()
데이터 입력이 실제로 관찰 가능하다고 가정해 보겠습니다.
이 예제에서는 RxJS BehaviorSubject
를 사용합니다.
import { Component } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
aquaticCreatures = new BehaviorSubject(['shark', 'dolphin', 'octopus']);
addAcquaticCreature((newAquaticCreature) {
this.aquaticCreatures.next(newAquaticCreature);
}
}
그리고 자식 구성 요소의 OnInit
후크에서 구독합니다.
여기에서 aquaticCreatures
배열에 수생 생물을 추가합니다.
import {
Component,
Input,
ChangeDetectionStrategy,
ChangeDetectorRef,
OnInit
} from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
@Input() data: Observable<any>;
aquaticCreatures: string[] = [];
constructor(private cd: ChangeDetectorRef) {}
ngOnInit() {
<^>this.data.subscribe(newAquaticCreature => {
this.aquaticCreatures = [...this.aquaticCreatures, ...newAquaticCreature];
});
}
}
이 코드는 새 데이터가 관찰 가능한 data
를 변경하므로 Angular가 변경 감지를 실행하지 않기 때문에 완전하지 않습니다. 해결 방법은 observable을 구독할 때 ChangeDetectorRef
의 markForCheck
를 호출하는 것입니다.
// ...
ngOnInit() {
this.data.subscribe(newAquaticCreature => {
this.aquaticCreatures = [...this.aquaticCreatures, ...newAquaticCreature];
this.cd.markForCheck();
});
}
// ...
markForCheck
는 이 특정 입력이 변형될 때 변경 감지를 트리거하도록 Angular에 지시합니다.
ChangeDetectorRef.detach() 및 ChangeDetectorRef.reattach()
ChangeDetectorRef
로 할 수 있는 또 다른 강력한 기능은 detach
및 reattach
메서드를 사용하여 변경 감지를 수동으로 완전히 분리하고 다시 연결하는 것입니다.
결론
이 문서에서는 ChangeDetectionStrategy
및 ChangeDetectorRef
를 소개했습니다. 기본적으로 Angular는 모든 구성 요소에서 변경 감지를 수행합니다. ChangeDetectionStrategy
및 ChangeDetectorRef
는 구성 요소에 적용하여 데이터가 변경될 때와 비교하여 새 참조에 대한 변경 감지를 수행할 수 있습니다.
Angular에 대해 자세히 알아보려면 연습 및 프로그래밍 프로젝트에 대한 Angular 주제 페이지를 확인하세요.