발생 원인

  1. 자식 Component에서 부모 Component의 값을 변경하는 경우
  2. ngAfterViewInit을 이용하여 값을 변경한 경우

에러 발생 과정

  1. Develop 모드에서 변화가 감지되었을 때, Change Detaction이 발생하고 부모 Component부터 체크한다.
  2. OldValue[0]에 값이 저장된다.
  3. OldValue에 저장했던 값과 현재 값을 비교하여 값이 다르면 ExpressionChangedAfterItHasBeenCheckedError를 발생한다.

에러가 발생하는 과정은 위의 3가지가 핵심이다.

 

Angular의 완결함으로 인해 lifecycle이 종료되지 않은 상황에서 lifecycle 시작 단계의 값과 중간 단계의 값이 다르기 때문에 에러가 발생한다.

해결 방법

  • 비동기 처리 핵(hack) : JVM 콜 스택 마지막에 값을 변경하는 방법으로 setTimeout에 시간을 지정하지 않으면 JVM의 콜 스택 마지막에 등록이 된다.
    ...
    
    public isOk: boolean = false;
    
    ngAfterViewInit() {
    	setTimeout(() => {
        	this.isOk = !this.isOk;
        }
    }
    
    ...​
  • ChangeDetectorRef 서비스 : 부모 Component의 ngAfterViewInit 단계에서 강제로 Change Detection를 발생시켜 check 과정을 취소하고 자식 Component에서 변경된 값으로 Check 한다.
    ...
    
    constructor(private changeDetector: ChangeDetectorRef) {}
    
    ngAfeterViewInit() {
    	this.changeDetector.detectChanges();
    }
    
    ...​

 

참고 자료

 

Slacking studio x BLOG

Slacking studio blog.

blog.eunsatio.io

 

ExpressionChangedAfterItHasBeenCheckedError 에러에 대하여

# ExpressionChangedAfterItHasBeenCheckedError 란? - 자식 component에서 부모 component의 값을 바꾸는 경우 - product mode에서는 굳이 에러를 던지진 않지만 developer 모드에서는 에러가 발생 - 값 자체는..

sddev.tistory.com

 

2019-09 Angular를 처음 접하게 되였다.

1년동안 Angular를 사용하면서 느낀 장, 단점을 공유 하고자 한다.
앞으로 사용하면서 느낀 장, 단점도 지속적으로 추가할 예정이다.

 

* 지극히 주관적인 의견입니다. 참고만 해주세요~

 

장점

  • 컴포넌트 단위로 모듈화를 할 수 있어 수정이 용이하다.
  • 화면 구성을 Angular에서 제공하는 기능들을 이용하여 구현하기 쉽다.
    • html에서 for문을 사용, if문으로 조건 걸어 사용, css, style을 데이터에 따라 변경 등을 이용하여 구현한다. 하지만 Angular와 같이 다른 프레임 워크도 충분히 가능하지만 Angular가 기능이 훨씬 많고 편리하다고 생각한다.
    • html 태그에서 사용할수 있는 attribute를 직접 만들어서 사용할 수 있다.
      • <div TestDirective></div> 라고 작성하면 TestDirective attribute에 더블클릭 이벤트를 넣어서 사용할 수 있다.

단점

  • 러닝 커브가 너무 심하다.
    • JavaScript와 jQuery를 접하고 시작했는데도 typescript와 reactive programming, funcational programing등의 파생되는 학습이 필요하여 익숙해지는데까지 시간이 오래 걸린다.
  • 버전 변경이 잦아 업그레이드를 자주 하기에는 리스크가 너무 크다.
    • 6개월에 한번씩 버전이 나온다. 최근 버전은 8.x에서 10.1.4로 업그레이드 하였는데 토이 프로젝트를 업그레이드 후 에러 잡는데 상당한 시간이 걸렸다.

 

많은 프레임 워크를 접한 것은 아니지만 무엇보다 Angular의 기능을 이용하면서 화면 구성하는 게 재미있다.

2020년 9월 29일 Angular 버전을 아래와 같이 변경 

이전 버전 : 8.2.x => 현재 버전 : 10.1.4

업그레이드 후 상황

변경 전 정상 작동 되던 프로젝트가 변경 후 컴파일 에러가 발생 한다.

에러는 아래의 두가지 에러가 발생했다.

 

  • ngStyle, ngFor, ngIf 등 CommonModule에서 제공하는 Directive를 인식하지 못하는 에러
* Error 1

error NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'.
  • Custom Elements를 인식하지 못하는 에러
* Error 2

error NG8001: 'app-xxx' is not a known element: 1. If 'app-xxx' is an Angular component, then verify that it is part of this module. 2. If 'app-xxx' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.

해결하는 과정

버전을 업그레이드한 후 발생한 문제였으니 package.json에 dependency에 있는 각 모듈들의 버전을 확인해보고 새로운 프로젝트를 만들어서 기존 프로젝트와 비교도 해보고 프로젝트를 하나 하나 뜯어보는 등 많은 방법으로 에러를 잡으려고 노력하였지만 원인 조차 찾지 못했다.

원인

프로젝트를 조금씩 지워나가면서 원인을 찾는 과정에서 app.module.ts에 사용하고자 하는 component를 import하면 에러가 사라지는 것을 발견하였다. 그것을 힌트로 얻어 app-routing.module.ts에서 각 component들을 호출하는 방식을 의심했다. 구체적인 원인은 찾지 못했지만 에러가 발생하는 원인은 lazy loading에 있었다.

 

기존에는 아래와 같이 app-routing.module.ts를 구성했다면

* app-routing.module.ts

const routes : Routes = [
 { path : '', component: TestComponent }
];

export const AppRoutingModule = RouterModule.forRoot(routes);

해결된 방법은 아래와 같다.

* app-routing.module.ts

const routes : Routes = [
 { path : '', loadChildren: () => import('./test/testmodule').then((m) => m.TestModule) }
];

export const AppRoutingModule = RouterModule.forRoot(routes);

---------------------------------------------------------------------------------------------------------------------------

* test.module.ts

const routes : Routes = [
 { path : '', component: TestComponent }
];

@NgModule({  
    declarations: [TestComponent],  
    imports: [RouterModule.forChild(routes)],
})
export class TestModule { }

차이점으로는 기존에는 app-routing.module.ts에서 TestComponent를 바로 호출했다면, 현재는 app-routing.module.ts에서 TestModule을 호출하고 TestModule이 TestComponent를 호출하는 방식으로 변경되어 일주일 만에 문제가 해결되었다.

* 아래의 참고 자료에서 Angular Lazy-loading 참고

 

Angular 내부적으로 Compile시 체크하는 로직이 변경된 것이 아닌가 하는 추측을 해보지만 정확하지는 않다.

추 후 이에 대한 원인을 알게 된다면 다시 재작성 하려고 한다.


Angular Issues에 보면 저와 비슷한 에러를 가지고 있는 사람들이 많은것 같습니다. 저와 마찬가지로 몇 주 동안 해결하지 못한 사람도 있는것 같아 조금이나마 도움이 되고자 이 글을 작성하였습니다. 동일한 방법으로 해결 될 지는 모르겠지만 이 글이 작은 실마리가 되었으면 합니다.

부족한 부분도 많고 잘못 이해한 부분도 있으리라 생각합니다. 언제든지 지적해주세요.

 

읽어주셔서 감사합니다.

 

참고 자료

+ Recent posts