Jello's development blog

Jello's development blog

React DOM diff 알고리즘

React를 사용할 때, render()함수는 react의 새로운 엘리먼트를 반환한다. state나 prop이 바뀌어서 render()단계에서 다시 새로운 엘리먼트를 반환한다. 여기서 새롭게 렌더링이 될 때, react는 DOM diff 알고리즘을 이용해 효율적으로 UI를 업데이트한다.

일반적으로 사용되는 트리를 다른 트리로 바꾸는 알고리즘은 O(n3)의 알고리즘을 가지고 있어서, react는 그들만의 휴리스틱한 알고리즘을 만들었다. 이 알고리즘의 시간복잡도는 O(n)이다.

Dom diff 알고리즘

다른 타입의 요소일 때

렌더링된 DOM 트리가 전의 것과 비교해서 다른 타입의 요소일 때에, 전의 것은 없어지고 새로운 것을 완전히 새롭게 렌더링한다.

<!-- 전 -->
<div>
  <Counter />
</div>

<!-- 후 -->
<span>
  <Counter />
</span>

예를 들어, 위의 경우에는 divspan이 다른 타입이므로 Counter는 언마운트되고, 새로운 Counter가 마운트된다.

같은 타입의 DOM 엘리먼트일 때

바뀐 요소가 같은 타임의 DOM 엘리먼트(div, a, img 등)일 때에는 react가 그 엘리먼트의 property의 바뀐 부분만 감지하여 변경한다.


<!-- 전 -->
<div className="before" title="stuff" />

<!-- 후 -->
<div className="after" title="stuff" />

위와 같은 경우에 react는 div의 className만 변경된 것을 감지하고, 전체를 바꾸지 않고 className만 변경한다.

<!-- 전 -->
<div style= />

<!-- 후 -->
<div style= />

위와 같이 style의 color만 바뀐 경우에 react는 fontWeight를 포함한 style의 전체를 바꾸지 않고, color만 변경한다.

같은 타입의 컴포넌트 엘리먼트일 때

자식 엘리먼트 순회

React는 자식 엘리먼트를 순회하면서 차이가 있을 때마다 변경한다.

<!-- 전 -->
<ul>
  <li>first</li>
  <li>second</li>
</ul>

<!-- 후 -->
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

예를 들어 엘리먼트 트리의 변화가 위와 같을 때에, react는 첫번째 <li>first</li>를 비교하고, 두번째 <li>second</li>를 비교하고, 세번째에서 <li>third</li>를 삽입할 것이다.

하지만 다음 예제를 보자.

<!-- 전 -->
<ul>
  <li>first</li>
  <li>second</li>
</ul>

<!-- 후 -->
<ul>
  <li>Something</li>
  <li>first</li>
  <li>second</li>
</ul>

위와 같이 자식의 첫 번째에 새로운 엘리먼트가 삽입된 경우에 react는 모든 자식 엘리먼트들을 변경함으로써 낮은 퍼포먼스를 보이게 될 것이다.

Key

위와 같은 문제를 해결하기 위해서 react는 key 어트리뷰트를 지원한다. React는 key를 통해서 엘리먼트들을 비교한다.

<!-- 전 -->
<ul>
  <li key="1">first</li>
  <li key="2">second</li>
</ul>

<!-- 후 -->
<ul>
  <li key="0">Something</li>
  <li key="1">first</li>
  <li key="2">second</li>
</ul>

위와 같이 key를 심어주면 react는 <li key="0">Something</li>가 새롭게 추가된 엘리먼트임을 인식할 수 있다.

key는 부모 엘리먼트 안에서만 유일하면 된다 (전역에서 유일할 필요는 없다). 안정적이지 못한 값(Math.random() 등)을 사용하면 성능이 저하될 수 있다.