React, Reconciliaion, React 렌더링 알고리즘

date
Apr 18, 2022
slug
react-reconciliation
summary
React, Reconciliaion 알고리즘
thumbnail
status
publish

Virtual DOM

렌더링이 발생할 때마다 DOM을 조작하는 것은 계속해서 브라우저에게 reflow, repaint 요청을 하기 때문에 부담이 가게 됩니다.
따라서, React는 곧 바로 DOM을 직접적으로 조작하지 않고 Virtual DOM을 통해 렌더링 변화를 묶어서 변화된 부분만 DOM에 반영하게 됩니다.
우리가 흔히 index.jsx (tsx)에서 사용하는 render 함수가 이 과정을 하게되죠. 🤓
ReactDOM.render(<App />);

React의 DOM 비교, Diffing Algorithm

그렇다면! 기존의 Virtual DOM (DOM 과 동기화된 상태) 과 새로운 변경사항이 있는 Virtual DOM의 비교 는 어떻게 진행될까요? 🤔
단순히 DOM 트리 비교를 위한 순차적인 알고리즘은 O(n^3) 의 시간 복잡도를 갖게 됩니다. 만약의 1000개의 요소가 있는 DOM에서 10억번의 비교 연산을 하게 되겠죠..?
이를 해결하기 위해 React 팀은 휴리스틱 알고리즘을 활용해서 O(n) 시간 복잡도를 갖게되는 Reconciliaion 알고리즘을 구현했습니다.

Reconciliaion 알고리즘

1. 부모 타입이 다르면 자식 비교를 멈춘다.

// before <div> <Component /> </div> // after <span> <Component /> </span>
기본적으로 Level by Level 비교를 진행하면서 트리 구조의 특징을 이용하여 부모 엘리먼트가 바뀌게 되면 해당 엘리먼트(자식 포함)는 바뀌었다고 인식을 하게되고 하위로의 비교를 멈춥니다. 자식 엘리먼트들은 state가 사라지고 새로 마운트가 일어납니다.
notion image
어찌보면 Root 밑에 2개의 DOM 요소가 존재하며, BFS 방식을 사용하는 느낌?

2. 부모 타입이 같으면 속성(attributes)을 비교한다.

// before <div className="before" title="stuff" /> // after {/* className 속성만 수정 */} <div className="after" title="stuff" />
속성 값이 다르면 전체 요소를 변경하지 않고 변경된 속성만 수정합니다!

3. key 를 이용하면 변경 될 엘리먼트를 감지할 수 있다.

자식 엘리먼트의 리스트를 비교할 때, 2개의 리스트를 순회하면서 비교를 진행하게 됩니다.
// before <ul> <li>first</li> <li>second</li> </ul> // after <ul> <li>third</li> {/* first -> third, 새로 렌더링 */} <li>first</li> {/* second -> third, 새로 렌더링 */} <li>second</li> {/* 새로운 요소, 새로 렌더링 */} </ul>
key를 사용하지 않았을 경우 변경점이 발견되었을 때, 모든 리스트를 수정하기 때문에 굉장히 비효율적이게 됩니다.
// before <ul> <li key="first">first</li> <li key="second">second</li> </ul> // after <ul> <li key="third">third</li> {/* 새로운 요소, 새로 렌더링 */} <li key="first">first</li> {/* key를 이용, 순서만 변경 */} <li key="second">second</li> {/* key를 이용, 순서만 변경 */} </ul>
하지만, key를 이용하면 기존에 존재했던 요소들을 기억하고 불필요한 렌더링을 줄일 수 있습니다.
인덱스를 key로 사용하면 재배열, 정렬에서 문제가 생긴다!

4. 컴포넌트는 Props의 변화를 본다.

// before <Component value="1" /> // after <Component value="100" />
컴포넌트의 인스턴스는 그대로 유지하게 되고 Props 변경을 탐지하여 업데이트합니다. 변경된 Props로 인한 컴포넌트 내부 엘리먼트들을 다시 재귀적 Reconciliaion 알고리즘을 통해 비교를 진행합니다.

마무리

React 내부의 Reconciliaion 알고리즘을 통해 최적화된 DOM 렌더링을 이루어 낼 수 있게 되었지만 결국, React의 렌더링 최적화를 위한 코드 작성은 개발자 몫! 😎