브라우저 렌더링 과정과 React의 Virtual DOM

·2022년 11월 17일
1

React

목록 보기
9/21

📌 브라우저 렌더링

📍 DOM 이란?

DOM(Document Object Model)은 브라우저 렌더링 엔진의 HTML parser에 의해 생성된 트리 구조의 Node 객체 모델이다.
DOM의 목적은 JS를 사용하여 문서에 대한 추가, 삭제, 이벤트 처리 등을 처리하는 인터페이스 제공이다.

📍 브라우저 렌더링 과정

1. DOM 트리 생성

HTML을 파싱하여 DOM 객체로 이뤄진 DOM 트리를 생성한다.

2. CSSOM (CSS Object Model) 트리 생성

CSS parser는 inline style과 CSS 코드를 파싱하여 CSSOM 트리를 생성한다.

3. 렌더 트리 생성

DOM과 CSSOM의 정보를 바탕으로 실제 브라우저의 화면에 노출돼야 하는 노드들에 대한 정보인 렌더 트리를 생성한다.

실제 브라우저의 화면에 노출된다는 것은 display: none인 보이지 않는 노드들은 렌더 트리를 생성할 때 제외한다는 것을 의미한다.
반면, 공간을 차지하는 visibility: hidden 속성은 렌더 트리에 포함된다.

4. Reflow (Layout)

렌더 트리의 각 노드들이 브라우저의 Viewport 내에서 어느 위치에 어떤 크기로 배치돼야 하는지에 대한 정보를 계산한다.
Layout 단계를 통해 %, vh, vw 등과 같은 상대적인 속성이 px 단위로 변환된다.

Viewport ? 그래픽이 표시되는 브라우저의 영역, 크기
디스플레이의 크기, 브라우저 창의 크기에 따라 달라진다.

5. Repaint (Paint)

렌더 트리의 각 노드들을 모니터에 실제 픽셀로 그리는 단계이다.

Reflow가 발생하면 Paint는 반드시 수행돼야 한다.
그러나 Reflow가 발생했을 경우에만 Paint가 발생하는 것은 아니다.
background-color, visibility 등과 같이 레이아웃에는 영향을 주지 않는 스타일 속성만 변경됐을 경우 Reflow를 수행할 필요가 없기 때문에 Paint 과정만 수행된다.

📍 브라우저 성능저하 원인

DOM을 수정할 때마다 렌더 트리 생성부터 Reflow, Repaint의 과정을 다시 수행해야 한다.
DOM 자체의 수정은 빠르지만, 브라우저가 수행해야 하는 이후의 과정이 느리다.
즉, 성능 저하의 주요 원인은 DOM을 수정할 때 발생하는 Reflow, Repaint 과정에 있다.
Reflow가 빈번하게 발생하는 경우 브라우저에서는 성능 저하가 발생하며, 웹 페이지의 DOM이 복잡하게 구성되어 있고 CSS가 많이 적용된 사이트일 수록 더욱 심해진다.

📌 React Virtual DOM

📍 Virtual DOM

📝 Virtual DOM이란?

DOM 수정은 수반되는 비용이 크기 때문에 성능 저하를 최소화하기 위해 DOM을 최소한으로 수정해야 한다.
이 문제를 해결하기 위해 Virtual DOM이 등장했다.
Virtual DOM이란 실제 DOM의 구조와 비슷한 React 객체의 트리다.

📝 Virtual DOM 장점

  • Virtual DOM의 장점 중 하나는 DOM을 직접 조작하지 않아도 된다는 점이다.
    Virtual DOM은 수백, 수천 개의 DOM을 직접 관리, 조작하는 복잡한 과정들을 자동화, 추상화해준다.
  • DOM의 업데이트를 batch로 처리하여 실제 DOM의 리렌더링 연산을 최소화할 수 있다.
    즉, 연쇄적으로 Reflow, Repaint가 발생하는 것을 줄이고 필요한 연산을 한 번에 묶어서 처리한다.

📍 React rendering 과정

1. render 단계

React.createElement로 생성

2. reconciliation 단계

이전 elements와 새로 생성된 elements 비교

3. commit 단계

필요한 경우 DOM update

📍 Diff Algorithm

컴포넌트 내에 state가 변경된 경우 React는 해당 컴포넌트를 dirty하다고 표시하고 batch에 추가한다.

Virtual DOM 엘리먼트와 실제 브라우저에 등록되어 있는 DOM 엘리먼트를 비교, 순회하며 dirty 체크된 엘리먼트들을 처리한다.
처리 과정에서 속성 값만 변경된 경우 속성 값만 업데이트하고, 엘리먼트의 태그 혹은 컴포넌트가 변경된 경우 해당 노드를 포함한 하위의 모든 노드를 언마운트(제거)한 뒤 새로운 virtual DOM으로 대체한다.
batch에 쌓인 모든 변경 혹은 업데이트가 모두 처리된 후 한 번 실제 DOM에 이 결과를 업데이트 한다.

📝 Level By Level

트리를 비교할 때 동일한 level의 node들끼리만 비교한다.

📝 같은 위치에서 엘리먼트 타입이 다른 경우

ex) div 태그가 span 태그로 바뀐 경우
1. 기존 트리를 제거 후 새로운 트리를 만든다.
2. 기존 트리 제거 시 트리 내부의 엘리먼트, 컴포넌트들은 모두 제거한다.
3. 새로운 트리를 만들 때 내부 엘리먼트, 컴포넌트들도 모두 새로 만든다.

📝 같은 위치에서 엘리먼트가 DOM을 표현하고 그 타입이 같은 경우

ex) class가 변경된 경우
1. 엘리먼트의 attributes를 비교한다.
2. 변경된 attributes만 업데이트 한다.
3. 자식 엘리먼트들에 diff 알고리즘을 재귀적으로 적용한다.

📝 같은 위치에서 엘리먼트가 컴포넌트이고, 그 타입이 같은 경우

ex) <Item price=100 /> => <Item price=200 />
1. 컴포넌트 인스턴스 자체는 변하지 않는다. (컴포넌트의 state 유지)
2. 컴포넌트 인스턴스의 업데이트 전 라이프 사이클 메서드들이 호출되며 props가 업데이트 된다.
3. render( )를 호출하고, 컴포넌트의 이전 엘리먼트 트리와 다음 엘리먼트 트리에 대해 diff 알고리즘을 재귀적으로 적용한다.

📝 자식 노드에 대한 재귀적 처리

DOM 노드의 자식들을 재귀적으로 처리할 때 React는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성한다.
이 때 비교하는 대상은 단순히 first-child to last-child로 비교한다.

  • 마지막 node가 추가된 경우 마지막 노트만 update된다.
// before
<ul>
  <li>ItemA</li>
  <li>ItemB</li>
</ul>

// after
<ul>
    <li>ItemA</li> // ItemA 유지
    <li>ItemB</li> // ItemB 유지
    <li>ItemC</li> // ItemC 추가
</ul>
  • 맨 앞 node가 추가된 경우 모든 node에 update가 발생한다.
    이러한 불필요한 성능 저하 발생 문제를 해결하기 위해 React에는 key가 존재한다.
    children이 key를 가지고 있으면 React는 동일한 key를 갖는 자식끼리 비교를 수행한다.

[ 불필요한 성능 저하 발생 ]

// before
<ul>
  <li>ItemA</li>
  <li>ItemB</li>
</ul>

// after
<ul>
    <li>ItemC</li> // ItemA -> ItemC 변경
    <li>ItemA</li> // ItemB -> ItemA 변경
    <li>ItemB</li> // ItemB 추가
</ul>

[ key 추가로 해결 ]

// before
<ul>
  <li key="A">ItemA</li>
  <li key="B">ItemB</li>
</ul>

// after
<ul>
    <li key="C">ItemC</li> // ItemC 추가
    <li key="A">ItemA</li> // ItemA 유지
    <li key="B">ItemB</li> // ItemB 유지
</ul>

key는 오로지 형제 사이에서만 유일하면 되고, 전역에서 유일할 필요는 없다.
리스트 배열이 재정렬되지 않거나 last-child에서만 추가, 변경, 제거가 일어난다면 인덱스를 key로 사용해도 되지만, 순서가 바뀌는 경우가 발생하면 key가 전부 바뀌기 때문에 key를 사용한 의미가 없다.

<참고 : https://velog.io/@1nthek/React-Virtual-DOM%EA%B3%BC-%EB%A0%8C%EB%8D%94%EB%A7%81
https://yujonglee.com/reactrendering.html
https://calendar.perfplanet.com/2013/diff/ >

profile
개발을 개발새발 열심히➰🐶

0개의 댓글