January 24, 2023
zone.js
은 비동기 실행들을 하나의 맥락(실행 컨텍스트)으로 다룰 수 있도록 해준다.
쉬운 말로 하면, 브라우저의 비동기 작업(예: setTimeout, Promise, XHR 요청)을 감싸고, 각각의 작업이 완료된 후 애플플리케이션 상태를 다시 확인할 수 있도록 도와주는 라이브러리 !
앵귤러는 zone.js를 사용하여 앱의 상태가 변화했는지 감지하고, 그 상태에 따라 DOM을 업데이트한다. 앵귤러는 개발자가 변경 감지를 수동으로 호출하지 않아도 알아서 자동으로 DOM 업데이트를 처리한다.
더욱 자세히는,
몽키패치
) 해서,후킹
),비동기 컨텍스트를 하나로 관리 할 수 있도록 도와준다.
예시
//Zone.js의 핵심 아이디어는 브라우저의 비동기 API를 감싸서(override 또는 monkey-patch) "언제 상태가 바뀌는지 알아내는 것"이다.
//Zone.js는 브라우저의 주요 비동기 함수들(예: setTimeout, addEventListener, fetch, Promise)를 가로채서(intercept) 내부적으로 자신의 "Zone" 컨텍스트로 래핑한다.
//예를 들어, setTimeout이 호출된 경우:
const originalSetTimeout = window.setTimeout
window.setTimeout = function(callback, delay) {
return originalSetTimeout(() => {
Zone.current.run(callback) // Zone에서 callback 실행
}, delay)
}
//이 패칭된 setTimeout은 콜백 실행 직전에 등록된 Zone의 감시 툴을 실행. 이를 통해 비동기 작업 이후의 상태 변화를 감지가 가능하다.
리액트를 사용할 때 재 랜더링에 대해 생각을 하면서 코딩을 하곤 했었다.
state, props의 변경과 부모컴포넌트의 재랜더링은 자식 컴포넌트를 다시 랜더링시키는데 그 이유는 화면의 데이터를 바꾸기 위함이다.
앵귤러가 화면의 데이터를 바꾸는 방식은 변경감지(change detection)를 통해 이루어진다.
앵귤러는
상황
을 감지한다. (특정 데이터 자체의 변경을 통지 받는것이 아님)다만 zone은 언제 변경감지(change detection)을 해야하는지만 알려준다.
변경 감지는 (viewRef가 상속하고 있는 changeDetectorRef를) 루트부터 아래로 내려가면서 순차적으로 진행되어야 어떤것이 변경되었는지 알고, 바꿀 수 있다.
앵귤러는 change detection이 시작되면, 루트부터 모든 컴포넌트를 순회한다.
(ngOnChange의 경우 @input이 변경될때만 호출되지만 ngDoCheck는 매번 호출되는걸 생각한다면 이해하기 쉽다.)
이벤트가 발생할 때마다 모든 컴포넌틀를 순회하니, 데이터가 변경되는곳만
확인한다면 성능 향상을 바랄 수 있다.
부모 컴포넌트, 자식 컴포넌트와의 관계에서
자식은 부모에게서 받은 데이터만 보여준다고 할 때,
부모의 데이터가 변경되지 않는다면
→ 자식의 데이터도 변경되지 않는다.
이 때 자식 컴포넌트는 change detection에서 제외 되어도 된다.
따라서 부모의 데이터의 레퍼런스 비교를 통해 데이터가 바뀌었는지 안바뀌었는지 아는게 핵심이다. (객체가 통째로 바뀔땐 변경감지 프로세스가 작동하고, 객체의 프로퍼티가 바뀔땐 작동하지 않는다)
앵귤러 컴포넌트 데코레이터에 changeDetection 프로퍼티로 전략을 설정할 수 있는데, 이는 changeEtectionStarategy.Default, changeEtectionStarategy.OnPush 두가지가 있다. 위에서 계속 설명했던, 데이터가 변경되는곳만 확인하기 위해선 OnPush 전략을 사용하면 된다.
알아두면 좋은 onpush tmi
변경감지를 세밀하게 조정할 수 있는 객체이다.
detach
메소드를 통해 변경감지 트리에서 분리할 수 있다.루트부터 순회하는 변경감지 순회에서 분리되어 따로 변경감지 트리를 만드는것이므로, 해당하는 컴포넌트와 자식 컴포넌트는 zone에 의해 루트부터 시작된 변경감지가 수행되지 않는다.
reattach
메소드를 통해 분리되었던 변경감지 트리를 다시 붙일 수 있다.detectChanges
메소드는 즉시
자신과 자식 컴포넌트의 변경감지 순회를 시작한다. 즉시 해당 컴포넌트를 재랜더링 시키고 싶을 때 사용한다.markForCheck
는 변경감지 순회를 시작하는것이 아니라
, 다음번 변경감지 순회때 순회를 시키도록 체크해 두는것이다.따라서 전역적인 변경감지로 인해 편하지만 성능상 단점이 존재한다.
모든 비동기 작업에서 변경 감지가 발생할 가능성이 있고(일정 시간이 지난 타이머라도 변경 감지를 실행하는 등), 컴포넌트의 상태가 전혀 바뀌지 않았더라도 불필요한 DOM 계산과 검사 비용이 발생할 수 있다.
특정 비동기 작업(Promise, 이벤트 핸들러, HTTP 요청 등)이 실제로는 DOM 업데이트를 필요로 하지 않지만, Zone.js는 무조건 변경 감지를 실행한다.
Zone.js로 인해 변경 감지가 언제, 어디서, 왜 발생했는지 추적하기가 어렵다. 이는 복잡한 애플리케이션에서 큰 문제가 될 수 있다.
따라서 앵귤러 개발팀은 앵귤러 16부터 시그널과 같은 방식으로 변경감지를 개선하고 있다.
참고
https://angular.io/api/core/ChangeDetectorRef
https://www.thisdot.co/blog/zone-js-deep-diving-execution-context
제대로 배우는 앵귤러4