DOM 객체는 브라우저에서 HTML문서를 조작하기 위한 인터페이스이다. DOM 객체는 트리구조로 되어 있으며, Document 라는 최상위 객체 아래 html element들이 자식으로 이루어진 모습이다.
DOM은 조작 속도가 느리다.
일부를 바꿔야한다면 전부를 바꾼다. -DOM 객체-
DOM이 변경되고 업데이트 되면 브라우저의 렌더링 엔진 또한 리플로우(reflow) 한다. DOM 트리 내부에 요소 속 상태가 변화할때 브라우저는 DOM 트리를 재구축하기 위해 재렌더링과 UI업데이트가 이루어진다는 것이고, 이는 레이아웃 및 페인트에 해당하는 재연산을 해야하기 때문에 속도가 많이 느려지게 된다
React를 사용하는데에 장점은 가상 DOM 객체를 다루는 것이라고 어렴풋이 알고는 있었지만 이 가상 DOM 이라는 것이 어떻게 생긴 것인지 구조나 원리를 자세히 알아볼 필요가 있었다.
일단 React를 통해 생성되는 DOM 트리 객체는 객체 안에 객체가 들어있는 모양이다.
const vDom = {
tagName: "html",
children: [
{ tagName: "head" },
{ tagName: "body",
children: [
tagName: "ul",
attributes: { "class": "list"},
children: [
{
tagName: "li",
attributes: { "class": "list_item" },
textContent: "List item"
}
]
]
}
]
}
이러한 가상 DOM을 조작한다고 해서 브라우저의 렌더링이나 재연산을 수행하지 않기 때문에 훨씬 수월한 것은 당연하다. React는 이러한 가상 DOM의 UI요소들을 메모리에 유지시키고 있다가, ReactDOM과 같은 라이브러리를 통해 실제 DOM과 동기화 시킨다.
리액트에서는 DOM 상태가 업데이트가 되면 이전 가상 DOM 과 다른 새로운 가상 DOM을 만들어낸다.
이 두개의 트리를 차이를 비교하며 React는 가장 효율적으로 교체하는 방법을 계산해내고 적용하는데 이렇게 하면 실제 DOM은 최소한의 작업만으로 렌더링을 수행할 수 있게 된다.
이 과정을 Diffing 알고리즘이라고 한다.
DOM 트리 노드를 가장 효율적으로 교체하는 방법은 어떻게 알 수 있는가? 리액트는 이를 처리하기 위해 두가지 가정을 하고 알고리즘을 돌리는데
- 각기 서로 다른 두 요소는 다른 트리를 구출할 것이다
- 개발자가 제공하는 key 프로퍼티를 가지고, 여러번 렌더링을 거쳐도 변경되지 말아야 하는 자식요소가 무엇인지 알아낼 수 있을 것이다.
기본적으로 같은 레벨끼리 비교하는 BFS(너비 우선탐색)을 선택하여 각 노드를 순차적으로 파악해 간다.
<div>
<Counter />
</div>
//부모 태그가 div에서 span으로 바뀝니다.
<span>
<Counter />
</span>
<div className="before" title="stuff" />
//기존의 엘리먼트가 태그는 바뀌지 않은 채 className만 바뀌었습니다.
<div className="after" title="stuff" />
<ul>
<li>first</li>
<li>second</li>
</ul>
//자식 엘리먼트의 끝에 새로운 자식 엘리먼트를 추가했습니다.
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
//key가 2014인 자식 엘리먼트를 처음에 추가합니다.
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>