[F-Lab 모각코 챌린지 3일차]

milknsoda·2023년 6월 3일
0

F-Lab

목록 보기
2/5

TIL

  1. Incremental Rendering (2)
  2. 변경 감지틀 통한 렌더링이 최적화 기법일 수 있는 이유
  3. Layered Rendering

1. Incremental Rendering (2)

Incremental Redering은 어떻게 구현할 수 있을까?

1-1. 가상 DOM

실제 DOM가상 DOM을 분리해서 관리하는 방식으로 Incremental Rendering을 구현할 수 있다.

  1. 최초 렌더링 후, 실제 DOM을 기준으로 가상 DOM을 생성
  2. 변경사항이 발생하는 경우, 가상 DOM에 적용
  3. 실제 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 기법을 적용하는 것이 무슨 의미가 있을까?

  1. 브라우저가 dirty bit를 확인해서 렌더링하는 것은 주기적으로 일어난다.
  2. dirty bit 확인 주기는 브라우저마다, 사용환경마다 달라질 수 있다.
  3. javascript를 이용해 변경한 내용은 dirty bit로 감지되지 않고, 확인 주기와 무관하게 실제 DOM에 바로 적용된다.
    • 변경사항에 대한 세밀한 제어와 사용자 경험 개선에 도움을 줄 수 있다.

1-2. 변경 감지

변경 감지를 통한 브라우저 렌더링에는 여러 방식이 존재한다.

1) MutationObserver API

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의 변경사항을 감지해서 비동기적으로 콜백을 호출한다.

2) 이벤트 리스너

3) requestAnimationFrame

4) setInterval, setTimeout

5) 가상 DOM

변경 감지는 대부분 비동기적으로 동작하고, 콜백이 동작했을 때에는 이미 실제 DOM에 반영되었거나, 반영될 예정일 경우가 많다.

그렇기 때문에 변경 감지를 통한 렌더링은 하나의 동작으로 여러 번의 렌더링이 발생할 수 있기 때문에 중복 렌더링을 방지하기 위한 조치가 필요하다.


2. 변경 감지 렌더링

그럼에도 변경 감지를 통한 브라우저 렌더링이 최적화 기법이라고 하는 이유는 뭘까.

이 부분은 찾아보면서도 이해가 되지 않아 공부가 더 필요해 보이지만, 그나마 이해할 수 있었던 2가지 내용이 있었다.

  1. 디바운스(debounce), 쓰로틀(throttle) 메커니즘을 사용해서 불필요한 렌더링을 줄일 수 있다.

    • 디바운스(debounce): 연속적으로 발생하는 이벤트의 처음 혹은 마지막만 처리
    • 쓰로틀(throttle): 일정 시간동안 동일한 이벤트에 대해서 한번만 처리하도록 제한
  2. 변경 사항을 감지하여 UI를 동적으로 업데이트 할 수 있다.

중복으로 일어날 수 있는 렌더링을 제어하면서, 특정 요소의 변경에 따라 영향을 받을 수 있는 주변 요소에 대한 업데이트를 즉시 반영할 수 있게 된다.

이를 통해서 렌더링을 최소화하고 사용자 경험을 개선할 수 있게 된다.

3. Layered Rendering

브라우저 렌더링 과정 중 paint 단계에서는 Render Tree를 레이어로 분리해서 그린다.

Incremental Rendering이나 Layered Rendering이나 모두 변경사항이 발생한 부분만을 새롭게 그린다는 개념에서는 차이가 없다.

Layered Rendering은 이 paint 단계에서 그리는 개별 Layer를 기준으로 변경사항이 발생한 Layer만을 새로 그리는 기법이다.

Layer가 나뉘는 기준

  1. Root Element
  2. position 속성을 명시적으로 가지는 요소 (absolute, fixed, sticky)
  3. will-change 속성을 가진 요소
  4. 3D 변환이 이루어지는 요소 (transform)

will-change 미적용

will-change 적용

동일한 <iframe>이지만 will-change 속성이 적용된 경우 개별 레이어로 분리된 모습을 확인할 수 있다.


will-change는 대상 요소를 개별 레이어로 만들어 관리하기 때문에 무분별하게 사용하게 되면 오히려 성능에 악영향을 줄 수 있다.

opacitytransform(3D 변환 x) 만을 사용하는 애니메이션은 변경사항이 발생한 레이어더라도 전체를 새로 그리지 않기 때문에 상황에 따라 구분해서 적용할 수 있다.

그러나 will-change 속성을 가진 요소는 개별 레이어로 분리되어 렌더링 된다는 점을 이용하여 변경사항이 생긴 레이어를 개별 레이어로 분리하여 변경사항을 반영한 후 will-change 속성을 없애는 방식으로 활용할 수 있다.


참고
Layers, layers, layers… Be careful!

0개의 댓글