- Incremental Rendering (2)
- 변경 감지틀 통한 렌더링이 최적화 기법일 수 있는 이유
- Layered Rendering
Incremental Redering은 어떻게 구현할 수 있을까?
실제 DOM
과 가상 DOM
을 분리해서 관리하는 방식으로 Incremental Rendering
을 구현할 수 있다.
실제 DOM
을 기준으로 가상 DOM
을 생성가상 DOM
에 적용실제 DOM
과 가상 DOM
을 비교하여 변동이 있는 부분만 동기화해서 반영// 가상 DOM을 생성
let virtualDOM = {
counter: 0,
};
// 실제 DOM 요소
const counterElement = document.getElementById('counter');
// 카운터를 증가시키는 함수
function incrementCounter() {
// 가상 DOM의 값을 업데이트
virtualDOM.counter += 1;
// 변경된 부분만 실제 DOM에 업데이트
counterElement.textContent = virtualDOM.counter;
}
// 버튼 클릭 이벤트에 카운터를 증가시키는 함수를 연결
const buttonElement = document.getElementById('incrementButton');
buttonElement.addEventListener('click', incrementCounter);
최신 브라우저들은 기본적으로 dirty bit
라는 속성을 활용해서 변경사항만 렌더링하는 기능을 지원하고 있다.
브라우저도
dirty bit
를 활용해서 변경된 사항만 렌더링하는 기능을 지원하는데, Incremental Rendering 기법을 적용하는 것이 무슨 의미가 있을까?
dirty bit
를 확인해서 렌더링하는 것은 주기적으로 일어난다.dirty bit
확인 주기는 브라우저마다, 사용환경마다 달라질 수 있다.dirty bit
로 감지되지 않고, 확인 주기와 무관하게 실제 DOM에 바로 적용된다.변경 감지를 통한 브라우저 렌더링에는 여러 방식이 존재한다.
const targetNode = document.getElementById('el_id');
/*
* childList: 대상 노드의 자식 요소 추가, 제거 감지 - subtree 값이 true인 경우 하위 노드에 대한 자식요소 변경사항도 감지 (default: false)
* subtree: 대상 노드를 기준으로 하위 트리 전체에 대한 변경 감지 여부 (default: false)
* attributes: 대상 노드의 속성 변경 여부 감지 (default: false, attributeOldValue가 true면 true)
* attributeOldValue: 대상 노드의 속성 변경을 감지했을 때 바뀌기 전 값을 기록 (default: false)
* characterData: 대상 노드의 텍스트 변경 감지 (default: false, characterDataOldValue가 true면 true)
* characterDataOldValue: 대상 노드의 텍스트 변경을 감지했을 때 바뀌기 전 값을 기록 (default: false)
*/
const config = { attributes: true, childList: true, subtree: true };
const callback = function(mutationList, observer) {
/*
* mutationList : 변경사항 객체
* observer : callback을 호출한 MutationObserver 인스턴스
*/
for (const mutation of mutationList) {
if (mutation.type === "childList") {
console.log("A child node has been added or removed.");
} else if (mutation.type === "attributes") {
console.log(`The ${mutation.attributeName} attribute was modified.`);
}
}
}
const observer = new MutationObserver(callback);
MutationObserver
는 DOM의 변경사항을 감지해서 비동기적으로 콜백을 호출한다.
변경 감지는 대부분 비동기적으로 동작하고, 콜백이 동작했을 때에는 이미 실제 DOM에 반영되었거나, 반영될 예정일 경우가 많다.
그렇기 때문에 변경 감지를 통한 렌더링은 하나의 동작으로 여러 번의 렌더링이 발생할 수 있기 때문에 중복 렌더링을 방지하기 위한 조치가 필요하다.
그럼에도 변경 감지를 통한 브라우저 렌더링이 최적화 기법이라고 하는 이유는 뭘까.
이 부분은 찾아보면서도 이해가 되지 않아 공부가 더 필요해 보이지만, 그나마 이해할 수 있었던 2가지 내용이 있었다.
디바운스(debounce), 쓰로틀(throttle) 메커니즘을 사용해서 불필요한 렌더링을 줄일 수 있다.
변경 사항을 감지하여 UI를 동적으로 업데이트 할 수 있다.
중복으로 일어날 수 있는 렌더링을 제어하면서, 특정 요소의 변경에 따라 영향을 받을 수 있는 주변 요소에 대한 업데이트를 즉시 반영할 수 있게 된다.
이를 통해서 렌더링을 최소화하고 사용자 경험을 개선할 수 있게 된다.
브라우저 렌더링 과정 중 paint 단계에서는 Render Tree를 레이어로 분리해서 그린다.
Incremental Rendering
이나 Layered Rendering
이나 모두 변경사항이 발생한 부분만을 새롭게 그린다는 개념에서는 차이가 없다.
Layered Rendering
은 이 paint 단계에서 그리는 개별 Layer를 기준으로 변경사항이 발생한 Layer만을 새로 그리는 기법이다.
position
속성을 명시적으로 가지는 요소 (absolute, fixed, sticky)will-change
속성을 가진 요소동일한 <iframe>
이지만 will-change
속성이 적용된 경우 개별 레이어로 분리된 모습을 확인할 수 있다.
will-change
는 대상 요소를 개별 레이어로 만들어 관리하기 때문에 무분별하게 사용하게 되면 오히려 성능에 악영향을 줄 수 있다.
opacity
와 transform(3D 변환 x)
만을 사용하는 애니메이션은 변경사항이 발생한 레이어더라도 전체를 새로 그리지 않기 때문에 상황에 따라 구분해서 적용할 수 있다.
그러나 will-change
속성을 가진 요소는 개별 레이어로 분리되어 렌더링 된다는 점을 이용하여 변경사항이 생긴 레이어를 개별 레이어로 분리하여 변경사항을 반영한 후 will-change
속성을 없애는 방식으로 활용할 수 있다.