Virtual DOM ?
Virtual DOM은 가상의 UI 요소를 메모리에 유지시키고, 그 유지시킨 가상의 UI 요소를 ReactDOM과 같은 라이브러리를 통해 실제 DOM과 동기화시킨다. 가상 DOM을 조작하는 것은 실제 DOM처럼 실제로 브라우저 화면에 그리는 것이 아니기 때문에 훨씬 속도가 빠르다. Virtual DOM은 DOM의 복사본이라고 생각하면 된다.
*DOM?
데이터를 효과적으로 탐색하기 위해 사용돼 빠른 자료 탐색 성능이 장점으로, 트리 구조로 된 DOM은 스크립팅 언어가 접근하고 탐색하는 속도가 빨라 변경 및 업데이트 속도 또한 빠르지만,업데이트 된 요소와 그에 해당하는 자식 요소들에 의해 DOM 트리를 재구축함으로써 재랜더링 과정을 거쳐 UI를 업데이트 해야 한다. 브라우저의 리플로우와 리페인트 과정은 다시금 재연산을 해야 하기 때문에 속도가 그만큼 느려지게 된다.
React Diffing Algorithm
React가 DOM 트리를 탐색하는 방법
React는 기존의 가상 DOM 트리와 새롭게 변경된 가상 DOM 트리를 비교할 때, 같은 레벨(위치)끼리 비교한다. 이는 너비 우선 탐색(BFS)의 일종이라고 볼 수 있다.
다른 타입의 DOM 엘리먼트인 경우
<div>
<Counter />
</div>
//부모 태그가 div에서 span으로 바뀝니다.
<span>
<Counter />
</span>
이렇게 부모 태그가 바뀌어버리면, React는 기존의 트리를 버리고 새로운 트리를 구축하기 때문에 이전의 DOM 노드들은 전부 파괴된다. 이전 div 태그 속 Counter 는 파괴되고 span 태그 속 새로운 Counter가 다시 실행된다. 새로운 컴포넌트가 실행되면서 기존의 컴포넌트는 완전히 해제(Unmount)되어버리기 때문에 Counter가 갖고 있던 기존의 state 또한 파괴된다.
같은 타입의 DOM 엘리먼트인 경우
타입이 바뀌지 않는다면 React는 최대한 렌더링을 하지 않는 방향으로 최소한의 변경 사항만 업데이트 한다.
<div className="before" title="stuff" />
//기존의 엘리먼트가 태그는 바뀌지 않은 채 className만 바뀌었다.
<div className="after" title="stuff" />
//className이 before인 컴포넌트
<div style={{color: 'red', fontWeight: 'bold"}} title="stuff" />
//className이 after인 컴포넌트
<div style={{color: 'green', fontWeight: 'bold"}} title="stuff" /
두 엘리먼트를 비교했을 때 React는 color 스타일만 바뀌고 있음을 눈치챈다. 따라서 color 스타일만 수정하고 다른 요소는 수정하지 않는다. 이렇게 하나의 DOM 노드를 처리한 뒤 React는 뒤이어서 해당 노드들 밑의 자식들을 순차적으로 동시에 순회하면서 차이가 발견될 때마다 변경하는데, 이를 재귀적으로 처리한다고 표현한다.
키(key)
만약 자식 노드들이 이 key를 갖고 있다면, React는 그 key를 이용해 기존 트리의 자식과 새로운 트리의 자식이 일치하는지 아닌지 확인할 수 있다.
<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>
React는 key 속성을 통해 ‘2014’라는 자식 엘리먼트가 새롭게 생겼고, ‘2015’, ‘2016’ 키를 가진 엘리먼트는 그저 위치만 이동했다는 걸 알게 된다. 따라서 React는 기존의 동작 방식대로 다른 자식 엘리먼트는 변경하지 않고 추가된 엘리먼트만 변경하게 된다. 키는 전역적으로 유일할 필요는 없고, 형제 엘리먼트 사이에서만 유일하면 된다.