Virtual DOM
React 는 UI 상태를 추적하고 변화가 일어난 요소들을 빠르게 업데이트 할 수 있도록
Virtual DOM 이라는 가상의 DOM 객체를 활용합니다.
이는 실제 DOM 사본 같은 개념으로, React 는 실제 DOM객체에 접근하여 조작하는 대신
이 가상의 DOM 객체에 접근하여 변화 전과 변화 후를 비교하고 바뀐 부분을 적용합니다
<DOM 의 조작속도가 느려지는 이유>
DOM은 계층적 형태의 트리 구조로 구성되어 있습니다.
자료구조 중에서도 특히 트리는 “데이터 저장”의 의미보다는 “저장된 데이터를 더 효과적으로 탐색” 하기 위해 사용되므로, 빠른 자료 탐색 성능이 장점인 자료구조라고 볼 수 있습니다.
그러나 DOM 이 변경되고 업데이트 된다는 것은 결국 브라우저의 렌더링 엔진 또한 리플로우(reflow) 한다는 것을 의미합니다
reflow는 브라우저가 화면에 내용을 배치하기 위해 수행하는 계산 과정을 말합니다.
reflow는 요소의 크기, 위치, 레이아웃 등에 대한 계산을 수행하므로, 페이지의 레이아웃이 변경되거나 동적으로 요소가 추가/제거되는 경우에 발생합니다.
실제 DOM을 조작하는 경우, 변화가 필요하지 않은 요소도 변경될 수 있습니다.
또한, 극단적인 예로 프레임드롭(Frame Drop)과 같은 치명적인 UX문제가 발생할 수 있습니다.
프레임드롭은 웹에서 화면이 버벅대거나 부드럽게 표시되지 않은 것을 의미합니다.
“바뀐부분만 비교해서 그 부분만 렌더링할 수 없을까?” 라는 아이디어를 기반으로 React의 Virtual DOM 이 등장하게 되었습니다
Virtual DOM의 동작과정
diffing 알고리즘을 통해 두개의 가상 DOM 중 이전 상태와 현재 상태의 Virtual DOM 을 비교합니다.
이 비교과정에서는 React는 diffing 알고리즘을 사용하여 변경된 부분을 감지합니다.
따라서, React에서 상태를 변경하는 경우에는 diffing 알고리즘애서 이를 감지할 수 있도록 직접할당이 아닌 setState와 같은
메세드를 활용해 상태를 변경합니다.
그리고 가상 DOM과 새로운 가상DOM 을 비교하여 변경이 필요한 부분만 실제 DOM 에 반영하여 업데이트 합니다.
이것을 Reconciliation, 즉 재조정이라고 합니다.
이 과정에서 여러개의 상태 변화가 있을 경우 이를 일일이 수행하지 않고 일괄적으로 한번에 업데이트 합니다.
이를 통해 성능을 최적화하고 불필요한 리렌더링을 최소화할 수 있습니다
React Diffing Algorithm(비교 알고리즘)
너무 비싼 연산을 하지 않기 위해서 react는 두가지 가정을 가지고 휴리스틱 알고리즘(문제해결알고리즘)을 구현합니다.
1. 각기 서로 다른 두 요소는 다른 트리를 구축할 것이다.
2. 개발자가 제공하는 key 프로퍼티를 가지고, 여러번 렌더링을 거쳐도 변경되지 말아야 하는 자식 요소가 무엇인지를 알아낼 수 있을 것이다
react가 DOM 트리를 탐색하는 방법
react는 기존의 가상 DOM 트리와 새롭게 변경된 가상 DOM 트리를 비교할때, 트리의 레벨 순서대로 순회하는 방식으로 탐색합니다.
즉 같은 레벨(위치)끼리 비교한다는 뜻입니다.
동일선상의 있는 노드를 파악한 뒤 다음 자식세대의 노드를 순차적으로 파악해갑니다
*다른 타입의 DOM 엘리먼트인 경우*
<div>
<Counter />
</div>
//부모 태그가 div에서 span으로 바뀝니다.
<span>
<Counter />
</span>
DOM 트리는 각 html 태그마다 각각의 규칙이 있어 그 아래 들어가는 자식 태그가 한정적이라는 특징이 있습니다.
(예를들어, <ul> 태그 밑에는 <li> 태그만 와야 한다던가, <p> 태그 안에 <p> 태그를 또 쓰지 못하는 것입니다.)
자식 태그의 부모 태그 또한 정해져 있다는 특징이 있기 때문에, 부모 태그가 달라진다면 react는 이전 트리를 버리고 새로운 트리를 구축해버립니다.
*같은 타입의 DOM 엘리먼트인 경우*
<div className="before" title="stuff" />
//기존의 엘리먼트가 태그는 바뀌지 않은 채 className만 바뀌었습니다.
<div className="after" title="stuff" />
반대로 타입이 바뀌지 않는다면 react는 최대한 렌더링을 하지 않는 방향으로 최소한의 변경사항만 업데이트 합니다.
이것이 가능한 이유는 react는 실제 DOM이 아닌 가상 DOM을 사용해 조작하기 때문입니다. 업데이트할 내용이 생기면
vitual DOM 내부의 프로퍼티만 수정한 뒤, 모든 노드에 걸친 업데이트가 끝나면 그때 단 한번 실제 DOM으로의 렌더링을 시도합니다.
키(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>
만약 자식 노드들이 이 key를 가지고 있다면, react는 그 key를 이용해 기존 트리의 자식과 새로운 트리의 자식이 일치하는지 아닌지 확인할 수 있습니다
이 key 속성에는 보통 데이터 베이스 상의 유니크한 값(ex. ID)를 부여해주면 됩니다. 키는 전역적으로 유일할 필요는 없고,
형제 엘리먼트 사이에서만 유일하면 됩니다.
'React' 카테고리의 다른 글
React.lazy() 와 Suspense (0) | 2023.05.23 |
---|---|
useMemo 와 useCallback (0) | 2023.05.22 |
styled-component가 웹표준을 향상시킨다고? (0) | 2023.04.28 |
코드를 읽는 순차적인 흐름과 오류처리에 관하여 (Cmarket Redux) (6) | 2023.04.25 |
Redux란? ('store', 'reducer', 'action' ) (0) | 2023.04.24 |