개발/front-end

Angular의 ExpressionChangedAfterItHasBeenCheckedError

희운1205 2023. 4. 1. 13:52
반응형

Angular ExpressionChangedAfterItHasBeenCheckedError 오류

Angular는 변경 감지 사이클을 통해 표현식을 바인딩하는데 때때로 ExpressionChangedAfterItHasBeenCheckedError와 같은 바인딩 오류가 발생합니다. 

일반적으로 다음과 같은 경우에 많이 발생합니다.

1. Angular의 뷰 라이프사이클 후킹 메소드 안에서 모델 데이터를 변경한 경우
2. Angular의 Change Detection이 실행된 후에 모델 데이터를 변경한 경우
3. 자식 컴포넌트에서 이벤트를 트리거하여 부모 컴포넌트의 속성을 변경한 경우

반응형

이 오류를 해결하기 위해 Angular는 몇 가지 방법을 제공합니다. 

1. ChangeDetectionStrategy 설정
컴포넌트의 ChangeDetectionStrategy를 OnPush로 설정하여 변경 감지 전략을 변경할 수 있습니다. 이렇게 하면 컴포넌트의 변경 감지가 자동으로 실행되지 않고, 수동으로 변경 감지를 실행해야 합니다. 이 방법은 컴포넌트가 불필요하게 자주 업데이트되는 것을 방지할 수 있습니다.

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

@Component({
  selector: 'app-my-component',
  template: `
    <div>{{ myData }}</div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
  myData: string;

  constructor(private myService: MyService) {}

  ngAfterViewInit() {
    this.myService.getData().subscribe(data => {
      this.myData = data;
    });
  }
}

2. setTimeout 사용
변경 감지가 완료된 후에 DOM 변경을 실행하려면, setTimeout 함수를 사용하여 변경 감지를 마무리한 후에 변경을 실행하도록 지연시킬 수 있습니다.

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

@Component({
  selector: 'app-my-component',
  template: `
    <div>{{ myData }}</div>
  `
})
export class MyComponent implements AfterViewInit {
  myData: string;

  constructor(private myService: MyService) {}

  ngAfterViewInit() {
    this.myService.getData().subscribe(data => {
      setTimeout(() => {
        this.myData = data;
      });
    });
  }
}

3. ChangeDetectorRef 사용
ChangeDetectorRef를 사용하여 수동으로 변경 감지를 실행하고, 변경 감지 이후에 변경을 실행할 수 있습니다.

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

@Component({
  selector: 'app-my-component',
  template: `
    <div>{{ myData }}</div>
  `
})
export class MyComponent implements AfterViewInit {
  myData: string;

  constructor(private myService: MyService, private cdr: ChangeDetectorRef) {}

  ngAfterViewInit() {
    this.myService.getData().subscribe(data => {
      this.myData = data;
      this.cdr.detectChanges(); // 변경 감지 수동 실행
    });
  }
}

4. ngOnChanges 사용

ngOnChanges 라이프사이클 후킹 메소드를 사용하여 변경 감지 이후에 변경을 실행할 수 있습니다.

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

@Component({
  selector: 'app-my-component',
  template: `
    <div>{{ myData }}</div>
  `
})
export class MyComponent implements OnChanges {
  myData: string;

  ngOnChanges(changes: SimpleChanges) {
    if (changes.myData) {
      this.myData = changes.myData.currentValue;
    }
  }

  ngAfterViewInit() {
    this.myService.getData().subscribe(data => {
      this.myData = data;
    });
  }
}

5. ViewChild 사용

ViewChild 데코레이터를 사용하여 컴포넌트 또는 디렉티브의 참조를 가져와서, ngAfterViewInit 메서드에서 변경을 실행할 수 있습니다.

import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { MyDirective } from './my.directive';

@Component({
  selector: 'app-my-component',
  template: `
    <div myDirective></div>
  `
})
export class MyComponent implements AfterViewInit {
  @ViewChild(MyDirective) myDirective: MyDirective;

  ngAfterViewInit() {
    this.myService.getData().subscribe(data => {
      setTimeout(() => {
        this.myDirective.doSomething(data);
      });
    });
  }
}

 

이러한 방법 중 하나를 사용하여 ExpressionChangedAfterItHasBeenCheckedError 오류를 방지할 수 있습니다. 그러나 모든 경우에 이 오류를 완전히 예방하기는 어려울 수 있으며, 이 오류가 발생하는 경우 대부분은 Angular의 변경 감지 시스템의 작동 방식에 대한 이해가 필요합니다.

반응형