동기
- state나 prop이 갱신되면 리액트 컴포넌트는 새로운 리액트 엘리먼트 트리를 반환함
- 이 때 리액트는 갱신된 트리에 맞게 효과적으로 UI를 갱신하고자 함
- 하나의 트리를 다른 트리를 변환하기 위한 최소한의 연산수를 구하는 알고리즘이 O(n^3)이기 때문에 두가지 가정하에 O(n) 복잡도를 가지는 휴리스틱 알고리즘을 사용해 속도를 높임
- 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다
- 개발자가 key Prop을 통해 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 말아야 할지 표시해줄 수 있다.
비교 알고리즘
- 두 개의 트리를 비교할 때 리액트는 두 엘리먼트의 루트 엘리먼트부터 비교하게 됨
루트 엘리먼트의 타입이 다른 경우
- 두 루트 엘리먼트 타입이 다르면 (ex: div -> section) 리액트는 이전 트리를 버리고 새로운 트리를 구축함
컴포넌트 마운트가 해제되면서 기존 DOM 노드들이 파괴되고 새로운 DOM 노드들이 삽입됨
- 루트 엘리먼트 아래에 컴포넌트, state가 있었을 경우 모든 컴포넌트들도 언마운트되고, state도 사라지게 됨

루트 엘리먼트의 타입이 같은 경우
- 타입이 같은 경우는 두 엘리먼트의 속성을 확인해 동일한 내역은 유지하고 변경된 속성들만 갱신함
- DOM 노드의 처리가 끝나면 리액트는 이어서 해당 노드의 자식들을 재귀적으로 처리함

<div className="" title="stuff" />
<div className="isFancy" title="stuff" />
- 이 경우 리액트는 className만을 갱신시킴
같은 타입의 컴포넌트 엘리먼트
- 커포넌트가 갱신되면 인스턴스는 동일하게 유지 => 렌더링간 state는 유지됨
- 새로운 엘리먼트 내용을 반영하기 위해 현재 컴포넌트의 prop을 갱신하고, 컴포넌트를 반환하며 비교 알고리즘이 재귀적으로 호출됨
자식에 대한 재귀적 처리
- DOM 노드의 자식을 재귀적으로 처리할 때 두 리스트를 동시에 순회하며 차이점이 있을 때 변경을 생성함
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
- 위의 예시의 경우 트리를 순화하면서 third List만을 트리에 추가하게 됨
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
- 하지만 위의 예시는 Connecticut만 트리에 추가하면 됨에도 불구하고, 모든 자식을 변경해야 하는 문제가 생김
Key
- 리액트는 문제를 해결하기 위해 key를 사용
- key를 비고해 기존 트리와 이후 트리의 자식이 일치하는지를 확인
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>
- 컴포넌트의 키는 데이터의 id, 해시, 배열의 인덱스 등을 사용하게 됨
배열의 인덱스를 사용하는 경우 배열이 재배열되는 경우 항목의 순서가 바뀔 때 key가 바뀌기 때문에 state가 엉망이 되는 경우가 생길 수 있음에 주의
고려 사항
- 알고리즘에서 다른 컴포넌트 타입을 갖는 트리들의 일치 여부는 확인하지 않음 => 비슷한 결과물을 출력할 경우 둘을 같은타입으로 만드는 것도 고려해볼 필요가 있음
- key는 반드시 변하지 않고, 예상 가능하며, 유일해야 함 -> 지켜지지 않으면 필요없는 DOM 생성으로 성능이 저하되거나, state에서 문제가 생길 수 있음
출처:
https://ko.reactjs.org/docs/reconciliation.html
https://beta.reactjs.org/learn/preserving-and-resetting-state