React Virtual DOM 과 React Diffing Algorithm

최정석·2022년 7월 28일
0
post-thumbnail

Real DOM(DOM)

Virtual DOM이 나오게 된 배경을 보기 전에, DOM의 개념을 다시 정리해 보도록 하겠습니다.

DOM은 Document Object Model의 약자로, 뜻을 그대로 풀자면 문서 객체 모델을 의미합니다.

문서 객체란 브라우저가 JavaScript와 같은 스크립팅 언어가 <html>, <head>, <body>와 같은 태그들에 접근하고 조작할 수 있도록 태그들을 트리 구조로 객체화 시킨 것을 의미합니다.

DOM은 브라우저가 트리 구조로 만든 객체 모델

프로그래밍 언어로 조작하는 DOM은 애플리케이션의 UI 상태가 변경될 때마다 해당 변경 사항을 나타내기 위해 업데이트가 됩니다.

만약 이런 DOM을 조작하는 정도가 잦다면 성능에 영향을 미치게 될 것이고,DOM의 렌더링은 브라우저의 구동 능력에 의존하기 때문에 DOM의 조작 속도는 느려지게 됩니다.

React Virtual DOM에 대하여

React에는 Virtual DOM이라고 하는 가상의 DOM 객체가 있습니다.

이는 실제 DOM의 사본 같은 개념으로, React는 실제 DOM 객체에 접근하여 조작하는 대신 이 가상의 DOM 객체에 접근하여 변화 전과 변화 후를 비교하고 바뀐 부분을 적용합니다.

가상 DOM은 가상의 UI 요소를 메모리에 유지시키고, 그 유지시킨 가상의 UI 요소를 ReactDOM과 같은 라이브러리를 통해 실제 DOM과 동기화시킵니다.

가상 DOM이 어떻게 더 빠를까?

React는 새로운 요소가 UI에 추가되면 트리 구조로 표현이 되는 가상의 DOM이 만들어집니다. 이러한 요소의 상태가 변경이 되면 다시 새로운 가상의 DOM 트리가 만들어집니다.

그리고 이전의 가상의 DOM과 이후의 가상의 DOM의 차이를 비교합니다

가상의 DOM은 실제 DOM에 변경을 수행할 수 있는 최상의 방법을 계산하기 시작합니다.

이렇게 하면 실제 DOM은 최소한의 작업만 수행해도 렌더링을 할 수 있게 됩니다.
따라서 실제 DOM의 업데이트 비용을 줄일 수 있게 됩니다.


React Diffing Algorithm

React는 두 가지의 가정을 가지고
시간 복잡도 O(n)의 새로운 휴리스틱 알고리즘(Heuristic Algorithm)을 구현해냅니다.

  1. 각기 서로 다른 두 요소는 다른 트리를 구축할 것이다.

  2. 개발자가 제공하는 key 프로퍼티를 가지고, 여러 번 렌더링을 거쳐도 변경되지 말아야 하는 자식 요소가 무엇인지 알아낼 수 있을 것이다.

실제 이 두 가정은 거의 모든 실제 사용 사례에 들어맞게 됩니다.
여기서 React는 비교 알고리즘(Diffing Algorithm)을 사용합니다.

React는 기존의 가상 DOM 트리와 새롭게 변경된 가상 DOM 트리를 비교할 때,
트리의 레벨 순서대로 순회하는 방식으로 탐색합니다.
즉 같은 레벨(위치)끼리 비교한다는 뜻입니다.
이는 너비 우선 탐색(BFS)의 일종이라고 볼 수 있습니다.


  • 다른 타입의 DOM 엘리먼트인 경우
<div>
	<Counter />
</div>

//부모 태그가 div에서 span으로 바뀝니다.
<span>
	<Counter />
</span>

HTML 태그마다 각각의 규칙이 있어 그 아래 들어가는 자식 태그가 한정적이라는 특징이 있습니다. (ex) ul과 li , <p>태그 안에 <p>태그
자식 태그의 부모 태그 또한 정해져 있다는 특징이 있기 때문에,
부모 태그가 달라진다면 React는 이전 트리를 버리고 새로운 트리를 구축해버립니다.


  • 같은 타입의 DOM 엘리먼트인 경우

React는 최대한 렌더링을 하지 않는 방향으로 최소한의 변경 사항만 업데이트합니다.
업데이트 할 내용이 생기면 virtual DOM 내부의 프로퍼티만 수정한 뒤,
모든 노드에 걸친 업데이트가 끝나면 그때 단 한번 실제 DOM으로의 렌더링을 시도합니다.


  • 자식 엘리먼트의 재귀적 처리
<ul>
  <li>JeongSeok</li>
  <li>Hansoo</li>
</ul>

//자식 엘리먼트를 처음에 추가합니다.
<ul>
  <li>JaeYoon</li>
  <li>JeongSeok</li>
  <li>Hansoo</li>
</ul>

이렇게 구현하게 되면 React는 우리의 기대대로 최소한으로 동작하지 못하게 됩니다. React는 원래의 동작하던 방식대로 처음의 노드들을 비교하게 됩니다.

처음의 자식 노드를 비교할 때,
<li>JeongSeok</li><li>JaeYoon</li>로 자식 노드가 서로 다르다고 인지하게 된 React는 리스트 전체가 바뀌었다고 받아들입니다.

React는 이 문제를 해결하기 위해 key라는 속성을 지원합니다.
개발할 당시 key라는 속성을 사용하지 않으면
React 에서 key값을 달라고 경고문을 띄우는 것도 이 때문인 것입니다.

<ul>
  <li key="2015">JeongSeok</li>
  <li key="2016">Hansoo</li>
</ul>

//자식 엘리먼트를 처음에 추가합니다.
<ul>
  <li key="2014">JaeYoon</li>
  <li key="2015">JeongSeok</li>
  <li key="2016">Hansoo</li>
</ul>

React는 key 속성을 통해 ‘2014’라는 자식 엘리먼트가 새롭게 생겼고,
‘2015’, ‘2016’ 키를 가진 엘리먼트는 그저 위치만 이동했다는 걸 알게 됩니다.

따라서 React는 기존의 동작 방식대로 다른 자식 엘리먼트는 변경하지 않고 추가된 엘리먼트만 변경합니다. 이 key 속성에는 보통 데이터 베이스 상의 유니크한 값(ex. Id)을 부여해주면 됩니다.

profile
코딩, 널 가지러 왔다!

0개의 댓글