React는 기존의 가상 DOM 트리와 새롭게 변경된 가상 DOM 트리를 비교할 때, 트리의 레벨 순서대로 순회하는 방식으로 탐색한다. 즉 같은 레벨(위치)끼리 비교한다는 뜻으로 이는 너비 우선 탐색(BFS)의 일종이라고 볼 수 있다.
<ul>
<li>first</li>
<li>second</li>
</ul>
//자식 엘리먼트의 끝에 새로운 자식 엘리먼트를 추가했습니다.
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
예를 들면 이렇게 자식 엘리먼트가 변경이 된다고 가정하면, React는 기존 <ul>
과 새로운 <ul>
을 비교할 때 자식 노드를 순차적으로 위에서부터 아래로 비교하면서 바뀐 점을 찾는다. 그렇기 때문에 예상대로 React는 첫 번째 자식 노드들과 두 번째 자식 노드들이 일치하는 걸 확인한 뒤 세 번째 자식 노드를 추가한다.
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
//자식 엘리먼트를 처음에 추가합니다.
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
반면 위와 같이 자식 엘리먼트가 변경이 된다고 가정하면, React는 우리의 기대대로 최소한으로 동작하지 못하게 된다.
처음의 자식 노드를 비교할 때, <li>Duke</li>
와 <li>Connecticut</li>
로 자식 노드가 서로 다르다고 인지하게 된 React는 리스트 전체가 바뀌었다고 받아들여 전부 버리고 새롭게 렌더링하게 된다. 이는 굉장히 비효율적인 동작 방식이다.
그래서 React는 이 문제를 해결하기 위해 key라는 속성을 지원한다. 이는 효율적으로 가상 DOM을 조작할 수 있도록 한다. 만일 개발할 당시 key라는 속성을 사용하지 않으면 노드는 비효율적으로 동작할 수 있기 React 에서 key값을 달라고 경고문을 띄우는 것이다.
만약 자식 노드들이 이 key를 갖고 있다면, React는 그 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>
React는 key 속성을 통해 ‘2014’라는 자식 엘리먼트가 새롭게 생겼고, ‘2015’, ‘2016’ 키를 가진 엘리먼트는 그저 위치만 이동했다는 걸 알 수 있다. 따라서 React는 기존의 동작 방식대로 다른 자식 엘리먼트는 변경하지 않고 추가된 엘리먼트만 변경한다.
이 key 속성에는 보통 데이터 베이스 상의 유니크한 값(ex. Id)을 부여한다. 키는 전역적으로 유일할 필요는 없고, 형제 엘리먼트 사이에서만 유일하면 된다.
만약 이런 유니크한 값이 없다면 최후의 수단으로 배열의 인덱스를 key로 사용할 수 있다. 다만 배열이 다르게 정렬될 경우가 생긴다면 배열의 인덱스를 key로 선택했을 경우는 비효율적으로 동작한다. 그 이유는 배열이 다르게 정렬되어도 인덱스는 그대로 유지되기 때문으로, 인덱스는 그대로지만 그 요소가 바뀌어버린다면 React는 배열의 전체가 바뀌었다고 받아들일 것이고, 기존의 DOM 트리를 버리고 새로운 DOM 트리를 구축해버리기 때문에 비효율적으로 동작하게 된다.
리액트 디핑 알고리즘을 사용해서 화면에서 바뀐 부분만 다시 그리는 작업을 수행하는데, DOM에서 바뀐 부분을 알아차리는데 중요한 역할을 하는게 key다. 근데 리스트 배열이 변경된다면 인덱스가 재매핑 되고, React는 키와 동일한 DOM Element를 보여주기 때문에 정상적으로 데이터가 들어가지 않을 수 있다.