key
- 엘리먼트 리스트를 만들 때 포함해야 하는 특수한 문자열 attribute.
- **React가 어떤 항목을 변경, 추가, 삭제할지 식별하는 것을 돕는다.**
자식에 대한 재귀적 처리
React는 기본적으로 기존 트리와 이후 트리의 자식들을 동시에 보고, 차이점이 있으면 DOM을 변경한다.
예로 들어서 자식의 끝에 엘리먼트를 추가하면 변경은 잘 작동한다.
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
React는 두 트리에서 <code><li>first</li></code> 가 일치하는 것을 확인하고, <code><li>second</li></code>가 일치하는 것을 확인한다. 그리고 마지막을 <code><li>third</li></code>를 트리에 추가한다.
문제점
하지만 위와 같이 단순히 리스트 맨 아래에 추가하면 괜찮지만, 리스트 맨 앞에 엘리먼트를 추가하는 경우에는 성능이 좋지 않다. 예를 들면 다음과 같은 아래 두 트리 변환은 형편없이 작동한다.
<ul>
<li>Duke</li>
<li>Villanova</li>
</ul>
<ul>
<li>Connecticut</li>
<li>Duke</li>
<li>Villanova</li>
</ul>
`<li>Duke</li>`와 `<li>Villanova</li>` 종속 트리를 그대로 유지하고 있는데에도 React는 **모든 자식을 변경**한다. 이러한 비효율은 문제가 될 수 있다.
문제점 해결: Keys
이 문제를 해결하기 위해서 React는 key 속성을 지원한다. 자식들이 key를 가지고 있으면 React는 이 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>
이제는 모든 자식을 다 옮길 필요 없이, '2014' key를 가진 엘리먼트를 새로 추가하고, '2015', '2016' key를 가진 엘리먼트는 그냥 이동만 하면 된다는 걸 알게 된다.
key값 정하기
리스트의 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 문자열을 사용하는 것이 가장 좋다. 그래서 대부분 데이터의 ID를 key로 사용한다.
<li key={item.id}>{item.name}</li>
근데 만~~약에 유닉한 데이터를 가지고 있지 않다면?
- 데이터 구조에 ID라는 속성을 추가해서 key 생성하기
- 데이터 일부에 해시를 적용해서 key 생성하기
**key는 전역에서 유일할 필요가 없다. 오로지 형제 사이에서만 유일하기만 하면 된다!**
map의 index를 key로 주면 안되는 이유는?
만약 데이터에 안정적인 ID가 없다면, 최후의 수단으로 index를 key로 사용할 순 있다.
하지만 항목의 순서가 바뀔 수 있는 경우(재배열)에는 key에 인덱스를 사용하면 비효율적으로 동작하고 컴포넌트의 state 관련 문제가 발생할 수 있다.
컴포넌트 인스턴스는 key를 기반으로 갱신되고 재사용되는데, 인덱스를 key로 사용하면 항목의 순서가 바뀌었을 때 key 또한 바뀔 것이기 때문이다. 그래서 결론적으로 모든 자식들이 재렌더링될 수도 있고(겁나 비효율적임), 컴포넌트의 state가 엉망이 되거나 의도하지 않는 방식으로 바뀔 수도 있다.
- 인덱스를 key로 주어서 문제가 발생하는 예시 : https://ko.reactjs.org/redirect-to-codepen/reconciliation/index-used-as-key
- 인덱스를 key로 사용하지 않고, 재배열, 정렬, 이어서 발생하는 문제들을 해결한 예시 : https://codepen.io/pen?editors=0010
참고 자료
- https://ko.reactjs.org/docs/lists-and-keys.html
- https://ko.reactjs.org/docs/reconciliation.html#recursing-on-children
'Front-End: Web > React.js' 카테고리의 다른 글
forwardRef 사용법 (+여러 ref들 전달하기) (0) | 2023.07.22 |
---|---|
특정 동작 시, useQuery하기 (0) | 2023.04.09 |
[코딩애플] 성능개선 3: useTransition, useDeferredValue (0) | 2022.11.09 |
[코딩애플] 성능개선 2: 재렌더링 막는 memo, useMemo (0) | 2022.11.09 |
[코딩애플] 성능개선 1: 개발자 도구 & lazy import (0) | 2022.11.08 |