key 잘 사용하기
key 잘 사용하기
key를 작성하지 않으면 콘솔에 경고가 출력됨.
key를 왜 사용해야 할까?
key: 리렌더링이 발생하는 동안 형제 요소들 사이에서 동일한 요소를 식별하기 위한 값.
리렌더링이 발생하면 업데이트 전(current), 후(workInProgress) 트리를 비교하여 어떤 컴포넌트에 변경이 있었는지 구별해야 하는데,
이 때 두 트리 사이에서 같은 컴포넌트인지 구별할 때 사용하는 값이 key다.
key가 존재하는 경우 이를 기준으로 컴포넌트를 구별할 수 있지만,
key가 없으면 sibling index만을 기준으로 구별해야함.
(sibling index에 관해서는 자세한 설명이 나와있지 않지만, 이로 충분한 최적화가 되지 않는 걸로 추정)
sibling: 트리에서 형제 컴포넌트를 구별하기 위해 사용하는 속성값.
current: 현재 UI 렌더링을 위해 존재하는 트리.
workInProgress: 업데이트가 발생하여 새로 받은 데이터로 빌드된 트리. 다음 렌더링에 사용된다.
두 트리 비교 시 최첨단 알고리즘을 사용하더라도 n개의 엘리먼트에 대한 비교 연산은 O(n3) 복잡도를 가짐.
리액트는 다음과 같은 방식으로 비교 연산에 대한 시간 복잡도를 줄였다.
key를 사용하면 더 빠르게 비교 연산을 수행할 수 있게 되는 것이다.
React는 대신, 두 가지 가정을 기반하여 O(n) 복잡도의 휴리스틱 알고리즘을 구현했습니다.
- 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다.
- 개발자가
keyprop을 통해, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할지 표시해 줄 수 있다.
// before
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
// after
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
리스트가 추가될 때 마지막에 새로운 요소가 추가되는 경우 새롭게 추가된 마지막 엘리먼트만 새로 렌더한다.
그러나, 위의 코드와 같이 맨 앞에 요소가 추가되는 경우는 다르다.
<li>Connecticut</li> 만 추가 되었을 뿐인데 key가 없으면 변경되지 않은 엘리먼트까지 포함해서 모든 <li>를 업데이트 해야 한다.
리스트가 맨 앞에 추가되면서 이후의 리스트들의 순서가 밀렸기 때문이다.
2번째 리스트는 Villanova에서 Duke가 되었고 존재하지 않았던 3번째 리스트가 추가된 것이다.
이렇게 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>
따라서 위와 같이 key를 추가하면 2014라는 key를 가진 엘리먼트가 새로 추가되었고, 나머지 요소에 대해서는 key 참조를 통해 변경이 없음을 인식하며 요소를 이동만 하면 된다는 사실을 알 수 있게 된다.
index를 key로 사용하면 안 되는 이유
key를 지정하지 않으면 React는 기본적으로 인덱스를 key로 사용한다.
그러나 항목이 삽입되거나, 삭제하거나, 배열의 순서가 바뀌면 시간이 지남에 따라 항목을 렌더링 하는 순서가 변경된다.
// before
<ul>
<li key="1">1</li>
<li key="2">2</li>
<li key="3">3</li>
<li key="4">4</li>
</ul>
// after
<ul>
<li key="1">1</li>
<li key="2">2</li>
<li key="3">new</li>
<li key="4">3</li>
<li key="5">4</li>
</ul>
위의 코드를 보면 li new가 중간에 삽입이 되면서 이후 요소들의 key가 변경이 되었다. 실제로는 변경 사항이 없는 요소임에도 다시 연산을 해야 한다.
항목들이 재배열되지 않는다면 index를 사용하는 것도 괜찮음, 그러나 재배열되는 경우 비효율적으로 동작함.
인덱스를 key로 사용 중 배열이 재배열되면 컴포넌트의 state와 관련된 문제가 발생할 수 있습니다. 컴포넌트 인스턴스는 key를 기반으로 갱신되고 재사용됩니다. 인덱스를 key로 사용하면, 항목의 순서가 바뀌었을 때 key 또한 바뀔 것입니다. 그 결과로, 컴포넌트의 state가 엉망이 되거나 의도하지 않은 방식으로 바뀔 수도 있습니다.
즉석에서 key를 생성하지 말기
key={Math.random()}처럼 즉석에서 key를 생성하면 렌더링 간에 key가 일치하지 않아 모든 컴포넌트와 DOM이 매번 다시 생성될 수 있다. 속도가 느려질뿐 아니라, 리스트 항목 내부의 모든 사용자의 입력도 손실된다.