Virtual DOM

이동현·2022년 5월 28일
3

React

목록 보기
14/16

탄생 배경

우선 브라우저 렌더링 과정에 대해서 간략하게 살펴보겠습니다. 이 과정을 알아야 하는 이유는 화면에 변화가 많은 요즘 웹 애플리케이션에서 DOM 조작이 얼마나 번거로운 작업인지를 설명하기 위함입니다.

브라우저 렌더링 과정

1. Parsing

HTML 파일과 CSS 파일을 파싱해서 각각 Tree를 만든다.

2. Style

두 Tree를 결합하여 Rendering Tree를 만든다.

3. Layout

Rendering Tree에서 각 노드의 위치와 크기를 계산한다.

4. Paint

계산된 값을 이용해 각 노드를 화면상의 실제 픽셀로 변환하고, 레이어를 만든다.

5. Composite

레이어를 합성하여 실제 화면에 나타낸다.

기존에는 화면의 변화를 렌더링하기 위해서 DOM에 접근하여 DOM을 직접 수정해줬습니다. DOM의 상태에 변화가 생기면 html 을 파싱하는 과정부터 시작해서 렌더트리를 다시 생성하고 모든 요소들의 스타일이 다시 계산되고 실제 화면에 렌더링하는 과정까지 모두를 반복하게 됩니다.

이런 과정은 비용이 많이 들고 SPA와 같은 형태에서 클라이언트에서 화면의 상태 변화가 많은 애플리케이션의 경우에는 성능이 저하되는 문제가 있습니다.

Virtual DOM

기존의 방식과는 다르게 DOM을 직접 업데이트해주는 대신에 화면에 변화가 생길 때 변화된 모습의 가상의 DOM 트리를 생성합니다.

Virtual DOM은 실제 DOM node tree 를 복제한 자바스크립트 객체입니다.

Virtual DOM 은 실제 DOM의 가벼운 버전이라고 생각하면 됩니다. 가상돔은 실제 DOM과 같은 속성들을 갖고 있지만 화면에 변화를 직접 줄 수 있는 기능(예, getElementById 등 DOM api)들은 갖고 있지 않습니다.

화면에 보여질 상태에 변화가 생겼을 때 실제 DOM 을 업데이트 하기 이전에 가상의 DOM(Virtual DOM)에 먼저 적용을 시키고 그 최종적인 결과를 실제 DOM으로 전달합니다. 이러한 방식으로 브라우저 내에서 발생하는 연산의 양을 줄임으로써 성능이 개선되는 것입니다. 이러한 특징을 봤을 때 Virtual DOM은 DOM 캐싱, DOM 버퍼링 이라고 볼 수 있습니다.

DOM 조작에서 가장 비용이 많이 드는 작업은 레이아웃 변화, 트리를 재생성하고 렌더링을 새로 일으키는 부분입니다. 예를 들어 여러분이 30개의 노드를 하나 하나 수정하면 30번의 레이아웃 재계산, 30번의 리렌더링을 초래합니다.

그러나 Virtual DOM은 실제 DOM에 적용될 화면의 변화가 발생하면 가상돔 트리에 적용시킵니다. 가상돔은 렌더링을 하지 않기 때문에 실제 DOM 렌더링과 같은 높은 비용의 행동을 하지 않습니다. 가상돔은 변화를 하나로 묶어서 한 번만 DOM에 전달합니다. 이렇게 성능을 향상시키는 것입니다.

React 와 Virtual DOM

Virtual DOM (VDOM)은 UI의 이상적인 또는 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념입니다.

-React

React에서 상태 변화가 발생하여 Virtual DOM에 변화가 생기면 React는 직전의 Virtual DOM과 새로 업데이트된 Virtual DOM의 스냅샷을 비교하게 됩니다. 이 과정을 통해서 가상돔의 전체를 다시 만드는 것이 아니라 변화된 부분만 업데이트를 할 수 있는데 이 과정을 diffing이라고 합니다.

이렇게 변화된 가상돔만 활용하여 실제 DOM을 업데이트 해줍니다. 변화된 부분만 업데이트를 해주는 이 방식으로 React는 복잡한 상태변화 상황에서 성능을 개선할 수 있었습니다.

재조정 (Reconciliation)

Virtual DOM (VDOM)은 UI의 이상적인 또는 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념입니다. 이 과정을 재조정이라고 합니다.

-React

두 개의 트리를 비교할 때 React는 두 엘리먼트의 루트 엘리먼트부터 비교합니다.

엘리먼트의 타입이 다른 경우(<a><img>, 혹은 서로 다른 컴포넌트) React는 이전 트리를 버리고 완전히 새로운 트리를 구축합니다. 트리를 만들 때 이전 DOM 노드들은 전부 파괴됩니다. 컴포넌트의 경우는 언마운트되면서 삭제됩니다.

만약에 두 변경된 엘리먼트의 타입이 같은 경우(<div><dic>) 동일한 내용은 유지되고 변경된 속성들만 갱신합니다.

key값을 넣어야 하는 이유

React는 변경된 내용을 DOM에 업데이트하고 자식 노드에 대해서 재귀적으로 처리를 합니다.

//before
<ul>
  <li>first</li>
  <li>second</li>
</ul>

//after
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

위 코드와 같은 경우 <ul> 태그의 자식 노드를 처리하는데 first 태그가 변경사항이 없으므로 넘어가고 second 태그도 변경사항이 없으므로 넘어가고 third 가 새로 생겼기 때문에 이 부분을 업데이트할 것입니다.

그런데 다음의 경우를 보면 얘기가 다릅니다.

  // before
  <ul>
    <li>Duke</li>
    <li>Villanova</li>
  </ul>

  // after
  <ul>
    <li>Connecticut</li>
    <li>Duke</li>
    <li>Villanova</li>
  </ul>

이렇게 새로 추가된 노드가 첫 번째 위치로 들어가는 경우 자식노드를 처리할 때 전부 다 새롭게 업데이트되었다고 판단하여 자식노드를 전부 다시 업데이트하게 됩니다.

이런 문제를 방지하기 위해서 key prop을 사용하는 것입니다. 자식들이 key prop을 갖고 있다면 key를 통해 기존 트리와 이후 트리의 자식들이 일치하는지를 확인합니다.

출처

Virtual DOM github
[번역] 리액트에 대해서 그 누구도 제대로 설명하기 어려운 것 – 왜 Virtual DOM 인가?

profile
Dom Hardy : 멋쟁이 개발자 되기 인생 프로젝트 진행중

0개의 댓글