Virtual DOM
React는 UI의 상태를 추적하고 변화가 일어난 요소들을 빠르게 업데이트할 수 있도록 Virtual Dom 이라는 가상의 DIM 객체를 활용합니다. React는 실제 DOM 객체에 접근하여 조작하는 대신 이 가상의 DOM 객체에 접근하여 변화 전과 변화 후를 비교하고 바뀐 부분을 적용합니다.
Real DOM(Dom, Document Object Model)
DOM은 브라우저가 HTML 문서를 조작할 수 있도록 트리 구조화한 객체 모델입니다.
DOM의 조작 속도가 느려지는 이유
DOM이 변경되고 업데이트가 된다는 것은 결국 브라우저의 렌더링 엔진 또한 리플로우한다는 것을 의미합니다.
브라우저는 렌더링 과정에 DOM 트리와 Render 트리를 생성하고 각 요소가 배치될 공간을 계산한 뒤 이를 화면에 그려냅니다(Paint). 만약 DOM이 변경된다면 업데이트된 요소와 그에 해당하는 자식 요소들에 의해 DOM 트리를 재구축하는데, 이 과정에서 이에 대한 레이아웃 재연산을 수행하는 리플로우와 이를 화면에 그려내는 Repaint 과정을 거치게 됩니다. 이때 변화가 필요 없는 부분도 변경되면서 잦은 리플로우 발생으로 인해 성능을 떨어뜨리는 문제를 야기하게 됩니다.
Virtual DOM
가상 DOM은 실제 DOM과 동기화되어, 상태가 변경될 때마다 가상 DOM을 새로 생성하여 이전 상태와 비교합니다. 그리고 변경이 필요한 부분만 실제 DOM에 반영하여 업데이트하므로, 전체 UI를 다시 그리지 않아도 됩니다.
React 에는 DOM 객체에 대응하는 가상의 DOM 객체가 있습니다. 상대적으로 무거운 DOM에 비하여 React의 가상 DOM 객체는 자바스크립트 객체로 이루어져 있기 때문에 실제 DOM 객체와 동일한 속성을 가지고 있음에도 "훨씬 가벼운 사본"이라고 표현할 수 있습니다. 다만 가상 DOM 객체는 말 그래도 가상이기 때문에 비교를 위해 사용되며, 실제 DOM 객체처럼 화면에 표시되는 내용을 직접 변경하는 것은 아닙니다.
Virtual DOM 동작 과정
diffing 알고리즘을 통해 이전 상태와 현재 상태의 Virtual DOM을 비교합니다.
리액트는 상태를 변경하는 작업이 일어났을 때, 가상 DOM에 저장된 이전 상태와 변경된 현재 상태를 비교합니다.
이 비교 과정에서 React는 Diffing 알고리즘을 사용하여 변경된 부분을 감지합니다. 따라서 React에서 상태를 변경하는 경우에는 Diffing 알고리즘에서 이를 감지할 수 있도록 직접 할당이 아닌 setState 와 같은 메서드를 활용해 상태를 변경합니다.
동작 과정
- 이전 가상 DOM과 변경된 새로운 가상 DOM을 비교합니다.
- 재조정(Reconciliation)을 통해 변경된 부분만 실제 DOM에 업데이트합니다.
- 일괄 업데이트(Batch Update)로 여러 개의 상태 변경이 일어날 때 이를 한 번에 업데이트합니다.
React Diffing Algorithm
React가 DOM 트리를 탐색하는 방법
React는 기존의 가상 DOM 트리와 새롭게 변경된 가상 DOM 트리를 비교할 때, 트리의 레벨 순서대로 순회하는 방식으로 탐색, 즉 같은 레벨(위치)끼리 비교합니다. 이는 너비 우선 탐색(BFS)의 일종이라고 볼 수 있습니다.
- 다른 타입의 DOM 엘리먼트인 경우
- 부모 태그가 달라진다면 React는 이전 트리를 버리고 새로운 트리를 구출해 버립니다.
- 같은 타입의 DOM 엘리먼트인 경우
- 반대로 타입이 바뀌지 않는다면 React는 최대한 렌더링을 하지 않는 방향으로 최소한의 변경 사항만 업데이트합니다.
- 업데이트할 내용이 생기면 Virtual DOM 내부의 프로퍼티만 수정한 뒤, 모든 노드에 걸친 업데이트가 끝나면 그때 단 한번 실제 DOM으로의 렌더링을 시도합니다.
자식 엘리먼트의 재귀적 처리
만약 <ul>의 내용이 변경되면 React는 기존 <ul>과 새로운 <ul>을 비교할 때 자식 노드를 순차적으로 위에서부터 아래로 비교하면서 바뀐 점을 찾습니다. 이 방식때문에 처음의 노드를 제일 먼저 비교하는데, 처음 자식 노드를 비교할 때, 서로 다르다고 인지하면 React는 리스트 전체가 바뀌었다고 받아들이기 때문에 전부 파괴하고 새로이 렌더링을 구축합니다. 이 때문에, 이 동작 방식에 대해 고민하지 않고 리스트의 처음에 엘리먼트를 삽입하게 되면 이전의 코드에 비해 훨씬 나쁜 성능을 내게 됩니다.
키(key)
만약 자식 노드들이 이 key를 갖고 있다면, React는 그 key를 이용해 기존 트리의 자식과 새로운 트리의 자식이 일치하는지 아닌지 확인할 수 있습니다.
이 key 속성에는 보통 데이터 베이스 상의 유니크한 값(id)을 부여해 주면 됩니다. 키는 전역적으로 유일할 필요는 없고, 형제 엘리먼트 사이에서만 유일하면 됩니다.
'프론트엔드 > Section4' 카테고리의 다른 글
| Proxy (3) | 2023.06.07 |
|---|---|
| TypeScript (Enum,Interface,Type,Class) (2) | 2023.05.30 |
| TypeScript (타입, 함수, 연산자:유니온/인터섹션) (3) | 2023.05.30 |
| UI/UX 디자인 시스템 (2) | 2023.05.24 |
| React Hook (2) | 2023.05.19 |