기본 콘텐츠로 건너뛰기

Angular Change Detection & Zone.js

Angular Change Detection & Zone.js

Change Detection 란 무엇인가?

Change Detection(이하 CD)는 뷰와 모델을 동기화 할 수 있도록 컴포넌트의 변경을 감지하고 값을 동기화 하는 프로세스다. Angular JS (1.x) 뷰단의 변경을 감지하는 것 뿐만 아니라, 비동기로 돌아가는 자바스크립트(ajax, setTimeout 등..)가 데이터 변경하는 시점을 감지하여 모델의 변경의 뷰단에 반영해야 하는데 Angular 1에서는 이런 로직을 Digest Loop로 구현 했었다. Digest Loop로 감시할 대상을 지정하기 위해 scope마다 watcher을 등록하여 감지할 목록들을 만들어야 했는데 이런 상황 때문에, 데이터의 변경을 감지하기 위해서는 Angular 도구만을 사용해야 했다. ajax call 또는 click 같은 이벤트도 $http와 ng-click으로 구현해야만 모델 변경이 감지되었다. 이를 보완하기 위해 명시적 호출($diest, $apply, $timeout)을 지원했지만 개발자가 직접호출해야한다는 이질감이 있었다. 또한 Angular 1은 양방향 데이터 바인딩으로 구현했기 때문에 watcher들이 늘어날 수록 Digest Loop은 걷잡을 수 없이 혼란스러워졌다.

Angular Angular2에서는 $scope기능을 없애고 Zone.js를 이용하여 CD를 구현하였는데, Zone.js 덕분에 Angular 도구 뿐만 아니라, 순수 자바스크립트로 컴포넌트를 구성할 수 있게 되었다. 또한 Angular 2는 단방향 바인딩을 구현하였기 때문에 (ngModel을 사용해서 양방향 바인딩을 구현할때도 데이터의 흐름은 단방향) CD는 어플리케이션 전체를 변경할 필요 없이 데이터가 바인딩 되어있는 곳만 변경을 감지하고 Angular2는 변경된 부분만 다시 그려주면 되었다. 이를 통해 Angular는 성능을 높일 수 있었고 강제되던 Angular 도구들을 제거할 수 있었다.

Figure: File Structure 출처 - https://angular-2-training-book.rangle.io

어떻게 변화를 감지하는가? (Zone.js - 동영상)

Angular는 Angular도구가 아닌 순수 자바스크립트로 변경한 데이터 변경 또한 감지 할 수 있다고 했다. 우리가 웹에서 ajax로 데이터로 가져오거나, Timer로 함수를 실행하거나, click 이벤트를 발생시키는것은 모두 비동기로 이루어지는데 사용자가 명시적으로 호출하지 않고도 어떻게 Angular는 어플리케이션 상태 변화를 감지할 수 있을까? 답은 Zone.js에 있었다. (Angular는 자체적인 Zone을 가지고 있고 ngZone이라고 한다.) Zone.js는 JAVA의 ThreadLocal 개념과 비슷해서 각각의 비동기 기능에서 kick on과 kick off를 설정하지 않아도 Zone이 이를 감지하여 후킹할수 있게 해준다.

위 코드를 실행해보면 콘솔 창에 다음과 같이 나타단다. 1번에서 click event listener가 등록된것을 2번에서 setTimeout이 발생하여 Invoike 한것을 상세하게 알 수 있다. 이렇게 Zone.js는 비동기 코드들을 컨트롤하면서 사용자에게 편리한 후킹인터페이스를 제공해주는 것이고, Angular는 ngZone을 이용해서 컴포넌트의 변화를 감지하는 것이다.

Angular에서 구현된 소스는 다음과 같다.

this .zone.onMicrotaskEmpty

.subscribe(() => {

this .zone.run(() => this .tick()

})

})

tick ()

{

this .changeDetectorsRefs

.forEach((ref) => ref.detectChanges())

}

어떻게 변경하는가?

그렇다면 앞서 ngZone이 감지한 변화를 Angular는 어떻게 View에 적용할까? Angular의 컴포넌트들은 각자 자신만의 CD를 가지고 있어서, 각자 CD 수행하고 컨트롤할 수 있다. 예를 들어 클릭 이벤트가 발생하면, Zone.js가 이벤트를 감지하여 알려주고 Angular는 CD를 실행한다. 이 때 컴포넌트는 각자 자신의 CD를 가지고 있고 컴포넌트는 트리구조로 이루어져 있기 때문에 CD의 구조도 방향이 있는 트리구조로 구성된다. 방향성이 있는 트리구조 때문에 CD는 항상 TOP에서 ROOT로 전파된다.

http://pascalprecht.github.io/slides/angular-2-change-detection-explained/#/59

Angular 컴포넌트들은 자신만의 Change Detector를 가진다?

Runtime에 Angular는 각 컴포넌트마다 자신이 가진 속성들에 대하여 Change Detector 클래스를 생성한다. Change detector들은 컴포넌트의 각 속성에대한 Old Value를 가지고 있기때문에 독립적으로 자신의 컴포넌트에 대한 변경 여부를 확인 할 수 있고, 감지 속도를 높이기 위해 개발자가 전략을 작성하거나 속성값 등을 전달 할 수 있다.

/**

* @license

* Copyright Google Inc. All Rights Reserved.

*

* Use of this source code is governed by an MIT-style license that can be

* found in the LICENSE file at https://angular.io/license

*/

/**

* @stable

*/

export declare abstract class ChangeDetectorRef {

abstract markForCheck(): void;

/**

* Detaches the change detector from the change detector tree.

*/

abstract detach(): void;

/**

* Checks the change detector and its children.

*/

abstract detectChanges(): void;

/**

* Checks the change detector and its children, and throws if any changes are detected.

*/

abstract checkNoChanges(): void;

/**

* Reattach the change detector to the change detector tree.

*/

abstract reattach(): void;

}

Change Detection을 빠르게

그렇다면 어떻게 Chage Detection을 효율적으로 실행할 수 있을까? 앞서 설명했듯이 CD는 Runtime시 각 컴포넌트마다 생성되기 때문에 다양한 형태를 가질 수 있다. 각 컴포넌트의 구조에 맞게 CD 클래스를 정의 하고 생성하기 때문에, CD가 감시해야할 대상을 줄이면 성능을 높일 수 있다. Angular는 CD가 감시해야 할 대상을 줄이기 위해 Immutable 객체와 Observable 객체를 알아?보고 감시 대상에서 제외해 할 수 있는데, 이런 최적화는 Angular의 OnPush 전략(컴포넌트에 바인딩된 Input 값의 변화가 없는 경우에는 Subtree 에 대해 Change Detection을 하지 않음)으로 설정하여 처리한다.

Immutable

Immutable 객체는 값의 변화가 없음을 보장한다. 새로운 값을 지정하고 싶으면 객체를 변경하는 것이 아니라 새로 생성 해야만한다. 객체를 새로 생성한다는 것은 Angular 입장에서 값(상태) 비교가 필요 없다는 것을 뜻한다. Immutable 객체를 사용하는 바인딩된 컴포넌트들은 변경된 객체의 상태를 비교해 볼 필요 없이 객체의 레퍼런스만으로 변화를 감지할 수 있다. (Javascript Immutable lib들을 이용하여 손쉽게 Immutable 객체를 생성해 볼 수 있는다. Immutable.js Github)

http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

Observable

관찰할 수 있는, 식별할 수 있는 객체를 의미한다. 관찰할 수 있는 객체를 구독(subscribe)하면서 값을 참조하는 방법이다. 예를 들어 다음과 같은 코드가 있을 때, firstName만 변경하여도 fullName까지 함께 변경 되는 것이다.

var firstName = Observable( 'BK' ) ;

var lastName = Observable( 'KIM' ) ;

var fullName = Observable( function () {

return firstName.value + ' ' + lastName.value ;

}) ;

Angular에서는 Immutable 객체 반대 방식으로 CD를 생략한다. Immutable 객체의 레퍼런스 참조만으로 CD를 생략할 수 있었다면 반대로 Observabel 객체는 값의 변경점에서 이벤트를 발생시켜서 TOP CD까지 변경을 전파한다. 따라서 Angular는 ROOT에서 TOP으로 가는 길목에 있는 CD만 수행하면 되는 것이다. 이때 OnPush전략을 함께 사용하면 Observable 객체의 레퍼런스는 변경되지 않기 때문에, 이벤트 컴포넌트 Subtree의 CD가 생략 된다. 하위 컴포넌트들도 함께 업데이트 해야한다면 디텍터의 markForCheck()라는 API를 이용하여 Subtree까지 CD를 수행 할 수 있다.

http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

마치며

AngularJs 1.x에서 Angular 2로 넘어오면서 가장 관심이 갔던 부분은 성능이었다. Change Detection에 대해서 자세히 알아보기 전까진 ReactJS의 Virtual DOM과 비슷한 방식이라 생각했었는데, 깊이 들여다보니 Framewrok로서 장점을 잘 이용한 것 같다. 성능 저하와 복잡도 상승의 주 원인이던 양방향 바인딩을 없애고, Zone.js를 도입 함으로써 라이브러리 사용에 비해 자유롭지 못한 Framework의 단점을 보완하려 한 부분도 흥미로웠다. 들여다 볼수록 매력있는 Angular지만 제대로 사용하지 않으면 이러한 장점들의 존재조차 모르고 사용하게 될것이란 게 두렵기도 하다.

참조

Change Detection In Angular : https://vsavkin.com/change-detection-in-angular-2-4f216b855d4c Change Detection : https://angular-2-training-book.rangle.io/handout/change-detection/ Angular Change Detection Explained : https://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html OnPush 동작 방식 이해 : https://jongmoon.github.io/blog/2017/02/13/ng-onpush-mechanism/ 자바스크립트 프레임워크 소개 2 : Angulr2 - http://meetup.toast.com/posts/98

from http://bkim.tistory.com/10 by ccl(A) rewrite - 2020-03-06 07:20:33

댓글

이 블로그의 인기 게시물

[Angular] Router 라우터 정리

[Angular] Router 라우터 정리 Angular2 버전 이후를 기준으로 정리한 글입니다. 라우터는 URL을 사용하여 특정 영역에 어떤 뷰를 보여 줄지 결정하는 기능을 제공한다. 전통적인 서버사이드 렌더링을 하는 웹 사이트는 주소가 바뀔 때마다 서버에 전체 페이지를 요청하고 전체 페이지를 화면에 렌더링한다. 매 요청시 전체 페이지를 새로 랜더링하는 것은 비효율적이기 때문에 라우터를 이용하여 필요한 부분만 랜더링을 한다면 효율적일 것이다. 라우터는 URL에 해당하는 컴포넌트를 화면에 노출하고 네비게이션을 할 수 있는 기능을 가지고 있다. Router 구성 요소 Router – 라우터를 구현하는 객체이다. Navigate() 함수와 navigateByUrl() 함수를 사용하여 경로를 이동할 수 있다. RouterOulet – 라우터가 컴포넌트를 태그에 렌더링하는 영역을 구현하는 Directive이다. Routes – 특정 URL에 연결되는 컴포넌트를 지정하는 배열이다. RouterLink – HTML의 앵커 태그는 브라우저의 URL 주소를 변경하는 것이다. 앵귤러에서 RouterLink를 사용하면 라우터를 통해 렌더링할 컴포넌트를 변경할 수 있다. ActivatedRoute – 현재 동작하는 라우터 인스턴스 객체이다. Router 설정 라우터를 사용하기 위해서는 먼저 Router 모듈을 import 해야 한다. import { RouterModule, Routes } from '@angular/router'; 라우터에서 컴포넌트는 고유의 URL과 매칭된다. URL과 컴포넌트는 아래와 같이 Routes 객체를 설정하여 지정할 수 있다. 아래의 예에서는 디폴트 path에서는 MainComponent가 노출이 되고 product-list path에서는 ProductListComponent가 노출이 되도록 설정을 한 것을 볼 수 있다. const routes: Routes = [ { pa...

[Angular] Controller

[Angular] Controller Step02_controller.html // angular 모듈 만들기 var myApp = angular.module( "myApp" ,[]); // Ctrl1 이라는 이름의 컨트롤러 만들기 myApp.controller( "Ctrl1" ,[ "$scope" , function ($scope){ $scope. name = "김구라" ; $scope.clicked = function (){ alert ( "버튼을 눌렀네?" ); }; $scope.nums = [ 10 , 20 , 30 , 40 , 50 ]; }]); // Ctrl2 이라는 이름의 컨트롤러 만들기 myApp.controller( "Ctrl2" ,[ "$scope" , function ($scope){ $scope. name = "원숭이" ; $scope.friends = [ {num: 1 , name : "김구라" ,addr: "노량진" }, {num: 2 , name : "해골" ,addr: "행신동" }, {num: 3 , name : "원숭이" ,addr: "동물원" } ]; }]); 내이름은 : {{name}} 눌러보셈 {{tmp}} 너의 이름은 : {{name}} 번호 이름 주소 {{tmp.num}} from http://heekim0719.tistory.com/164 by ccl(A) rewrite - 2020-03-07 09:21:16

JQuery - $ 사용 유형.

JQuery - $ 사용 유형. 최근에 새로운 Javascript 프레임워크와 라이브러리들이 많이 등장했고 시장 점유율 또한 많이 변동 되었다고 한다. 특히 요새 대새는 Angular와 React라고들 한다. 그리고 Jquery 요즘 누가써? Jquery 퇴물이야 등등... 그런 이야기들을 종종 찾아볼 수 있는데, 유행은 돌고 돌듯이 결국 Jquery가 몰락할 일은 없다고 생각하는 바, 묵묵히 Jquery를 고집하기로 한다...ㅎㅎ 먼저 Jquery 교과서랄까.. 기본 문법을 배울 수 있는 링크를 걸어둔다. https://www.w3schools.com/jquery 여기서 기초들을 다 익힐 수 있다. 프로그래밍 문법을 한번이라도 봤다면 + - * / 같은 연산 while, for 등은 다 비슷하기 때문에 $ 사용법만 잘 알면 될 것 같다. $ syntax 사용유형 일단 기본적으로 $는 JQuery에서 미리 정해놓은 변수 값이다. : JQueryStatic 1. $( ) : JQueryObject 가장 많이 사용하는 형태이다. 괄호 안에 들어 갈 수 있는 것들은 클래스 이름, 아이디, 셀렉터 등이다. 예를 들어 $('p')이면 현재 html 페이지에 있는 모든 를 JqueryObject로 가져오겠다는 것이다. hide()는 제이쿼리 메서드이다. $('p')는 제이쿼리 오브젝트이기 때문에 제이쿼리 메서드를 사용할 수 있다. 그중의 하나인 hide()를 사용해 보았다. 결과이다. 에는 스타일이 적용이 되었다. 해당 태그에는 jquery의 메서드가 적용이 된 것을 확인할 수 있다. 2. $.함수 : 플러그인 개발 또는 기본 전역함수 플러그인을 개발 할 때나 Jquery가 갖고 있는 기본 전역함수들을 사용할 때 쓴다. 전역함수에는 여러개가 있는데 그중에 개인적으로 많이 쓰는 것은 $.ajax({}), $.each({}) 등이 있다. 이 함수들의 사용방법은 따...