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 StructureFigure: 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/#/59http://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.htmlhttp://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.htmlhttp://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지만 제대로 사용하지 않으면 이러한 장점들의 존재조차 모르고 사용하게 될것이란 게 두렵기도 하다. 

참조

 

 

 

'ANGULAR' 카테고리의 다른 글

IntelliJ 에서 Angular 2 프로젝트 시작하기  (499) 2017.05.16

Angular를 시작하기 위해 문서를 찾아보면 대부분 편집 툴로 Visual Studio Code 를 추천한다.

처음 접해보는 용어와 Node라는 런타임, 그리고 npm 사이에서 새로운 편집툴은 Angular의 진입 장벽을 더욱 높인다.

평소 사용하고 있는 IDE인 IntelliJ에서 Angular2(이하 Angular) 프로젝트를 생성할 수 있으면 조금이나마 진입 장벽을 낮출 수 있을것 같아서 내용을 정리한다.

해당 문서는 Window 환경을 기준으로 작성했다.


1. Node.js 설치

https://nodejs.org/ko/ 에 접속하여 stable 버전인 v6.10.3 LTS 를 다운받고 설치한다.


Node.js라는 개념이 쉽게 와닿지 않는다.  노드란 무엇일까?

"Node.js 는 Chrome V8 JavaScript 엔진으로 빌드된 JavaScript 런타임" 이라고 공식페이지에서 말한다.

런타임이란 프로그램이 실행되고 있을 때 존재하는 곳을 뜻하는데, 우리가 익숙하게 사용하고 있는 JRE를 떠올리면 쉽다.

JRE는 Java Runtime Environment의 준말로 우리가 개발한 JAVA 어플리케이션을 구동하기 위한 환경인 것이다.

이처럼 노드는 JavaScript로 개발된 어플리케이션을 구동하기 위한 환경이다.


또한 Node.js의 패키지 생태계를 지원하기 위해 npm 이란 것이 있다.

리눅스에서 흔히 사용하는 yum, apt-get, rpm과 비슷한 개념의 패키지 매니져이며 npm은 JavaScript를 위한 패키지 매니저라고 보면 된다.

Node.js를 설치할때 npm도 같이 설치 된다.


2. Angular CLI 설치 (https://cli.angular.io/)


cmd 창에서 

npm install -g @angular/cli@latest 

npm install --save-dev @angular/cli@latest


여기까지 설치 후 cmd 창에서 ng -v를 날려보면 다음과 같은 버전 정보가 나와야한다.

#최근 angular 패키지의 이름이 변경되었고, 이전버전을 디프리케이트 했다. 

골뱅이가 붙어서 시작하는게 변경된 버전이니 책이나 구버전 문서들을 볼때 주의해야겠다.


Angular 프로젝트 생성, 빌드, 테스트를 도와주는 command line interface(CLI)이다.

Angular CLI 로 프로젝트를 생성하면 각각 따로 설정해줘야하는 polyFill, tsConfig, typings, karma 등등 각종 의존성등을 설정해준다.

NodeJs를 처음 접하면 온갖 의존성 관리에 좌절하기 마련인데, 이러한 문제를 

Angular CLI는 가장 최적화된 상태로 프로젝트를 생성함으로써 진입 문턱을 낮춰준다.


이밖에 쉽게 테스트할 수 있는 로컬 서버과 테스트까지 제공해주니 각각 따로 세팅하는것 보다 Angular CLI 사용하는것을 추천한다.


3. IntelliJ 플러그인 설치

 

IntelliJ에는 Angular 프레임워크가 없다. (Web Storm 에는 있음)

플러그인으로 설치 하기 위해 IntelliJ Settings -> Plugins -> Browse Repositories -> AngularJS 를 검색 설치한다.

재시작 후 File -> New -> Project -> Static Web 을 보면 Angular CLI 를 확인할 수 있다. 


4. 프로젝트 생성


File -> New -> Project -> Static Web 을 보면 Angular CLI 후 Next를 클릭하면,

프로젝트 이름과 위치를 지정할 수 있으며, Node Interpreter 와 Angular CLI를 선택 할 수 있다. 

Node 는 1번 단계에서 설치한 node.exe.의 위치를, Angular CLI 는 npm으로 설치한 Angular CLI 패키지 위치를 지정해주면된다.

프로젝트 생성을 마치면 IntelliJ가 자동으로 Angular CLI에 new 명령어를 날려 필요한 라이브러리들을 다운받고 설치한다.

 

 

 

Angular CLI 위치가 목록에 보이지 않는다면 다음 사이트를 참조해보자.

https://www.jetbrains.com/help/idea/2017.1/using-angular.html

http://revf.tistory.com/entry/IntelliJ%EC%97%90%EC%84%9C-Angular2-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-with-Angular-CLI


5. output 세팅


AngularJs는 타입스크립트를 사용하기 때문에 타입스크립트를 자바스크립트로 컴파일 하는 빌드과정이 필요하다.

IntelliJ 터미널 또는 좌측 하단 npm 창에서 build 를 클릭하면 명시적으로 컴파일할 수 있다. 

이때 컴파일 결과물들을 임의의 위치에 저장하려면  angular-cli.json 설정을 수정하면 된다.

추후 스프링과 연동할때 필요할것으로 보이므로 관심이 있으면 수정해보자.

"outDir": "dist",  --> "outDir": "output",  

 

빌드 시작

 

빌드 성공

 

빌드 결과물

 

6. 프로젝트 실행


생성한 프로젝트를 실행하기 위해 Angular CLI 는 serve 명령어를 지원해준다.

IntellJ의 npm 창에는 start로 표시되어있는데, 클릭해보면 터미널 창에서 ng serve 명령어를 날리는 것을 볼 수 있다.

ng serve 를 하면 콘솔창에서 연결정보 (localhost:4200)를 확인 할 수있다.

(ng serve 는 ng build 를 포함한다.)

 



7. 실행 확인


별다른 설정을 하지 않았다면, ng serve로 시작한 어플리케이션은 라이브 코딩을 지원한다. (자동 빌드)

src/app/app.component.ts 의 title 변수를 변경하고 저장하면 자동으로 업데이트 되는것을 확인 할 수 있다.

 




'ANGULAR' 카테고리의 다른 글

Angular Change Detection & Zone.js  (455) 2017.06.01

+ Recent posts