배열로 이루어진 데이터를 처리하다 보면 연속된 "li" 요소를 사용해서 데이터를 나열하여 보여줄 때가 생긴다. 이럴 때 map, filter메서드를 이용해서 데이터를 파싱 하여 사용하는데 "li"요소 안에 key옵션을 정의해 주지 않으면 "key 옵션을 고유한 값으로 정의해 주세요."라는 에러가 뜨는 걸 확인할 수 있다. 이번 포스팅에서는 key옵션을 왜 사용해야 하고 데이터에 고유한 값이 없을 때 어떻게 해야 하는지에 대해서 알아보자.
key
옵션의 역할을 이해하기 위해서는 React가 렌더링 하는 방법에 대해서 알고 있어야 한다.
React는 요소의 업데이트가 일어날 시 이전의 Virtual DOM과 새롭게 생성된 Virtual DOM을 비교한다.
이 때 새롭게 생긴 Virtual DOM은 요소의 업데이트가 일어나고 난 후 해당 업데이트가 반영된 DOM이다.
2개의 Virtual DOM을 비교할때는 각 노드들의 React Element의 Type, Option등을 비교해서 변한게 있으면 최신 React Element의 Type과 Option등을 반영하게 된다.
//before
<ul>
<li>list 1</li>
<li>list 2</li>
</ul>
//after
<ul>
<li>list New</li>
<li>list 1</li>
<li>list 2</li>
</ul>
이때 연속된 데이터의 Type은 같은 요소로 정의되어 있기 때문에 Option을 통해서 비교해야 하는데 key을 정의하지 않을 시 비교할 대상이 없기 때문에 React는 해당 요소들이 제자리에 위치하지 않다고 판단하여 해당 요소들을 전부 새로 그리게 된다.
이러한 과정은 결국 성능 이슈와 생각지도 못한 에러가 발생할 확률이 있다.
이럴 때 필요한 옵션이 바로 key이다.
따라서 React는 연속적으로 나열된 요소에는 유니크한 값을 부여할 수 있는 key옵션을 정의할 수 있고 React는 해당 옵션을 통해서 2개의 Virtual DOM을 통해 구분되지 않는 요소에 대해 구분할 수 있게 되는 것이다.
각 데이터마다 고유값이 없다면 대표적으로 2가지 방법을 활용할 수 있다.
말 그대로 데이터가 배열에 위치한 인덱스를 key옵션의 값으로 활용하는 것이다.
const dummyData = ["apple", "banana", "watermelon", "cherry", "peach"];
const Info = (): JSX.Element => {
return (
<>
<ul>
{dummyData.map((el, index) => (
<li key={index}>{el}</li>
))}
</ul>
</>
);
};
export default Info;
index
를 활용해서 key에러 없이 렌더링 된 모습을 확인할 수 있다.
다만 이 방법에는 문제가 있다.
배열의 index는 0부터 n까지 새롭게 할당된다.
즉, 새로운 데이터가 앞쪽에 추가될 때마다 index
에 해당하는 원소가 변하게 된다.
그러면 기존에 0을 key로 가지고 있던 요소는 새로 추가된 "li"요소의 "key"로 전달되게 된다.
그럼 React는 "key"값으로 해당 요소의 변경 여부를 확인하기 때문에 "key"값이 "0"인 새로 추가된 "li"의 콘텐츠에 기존 Virtual DOM에서 키값이 "0"이었던 콘텐츠 값을 그대로 유지하게 된다.
따라서 index
로 key를 관리하려면 배열의 요소 삭제, 추가 등의 기능이 없는 경우에서만 사용하는걸 권장한다.
nanoid
는 고유한 값을 생성해주는 라이브러리이다.
uuid와 비슷해보이지만 총 길이가 36바이트인 uuid보다 짧고 빠른 라이브러이다.
nanoid : Spw9HFNQP5Idtpikarcmh
UUID : df6fdea1-10c3-474c-ae62-e63def80de0b
nanoid
를 사용하기 위해서는 해당 라이브러리를 설치해야 한다.
npm i nanoid
그 후, 고유값이 필요한 컴포넌트에 가서 해당 라이브러리를 import한다.
import { nanoid } from "nanoid"
key값을 정의해줘야 할 요소의 key옵션에다가 import한 nanoid
함수를 정의해주면 된다.
import { nanoid } from "nanoid";
const dummyData = ["apple", "banana", "watermelon", "cherry", "peach"];
const Info = (): JSX.Element => {
return (
<>
<ul>
{dummyData.map((el) => (
<li key={nanoid()}>{el}</li>
))}
</ul>
</>
);
};
export default Info;
key에러 없이 렌더링 되는걸 확인할 수 있다.
key옵션이 왜 필요하고 해당 에러가 발생 시 어떻게 해결해야 할지에 대해서 알아봤다.
데이터마다 고유의 값이 있다면 해당 값을 key로 활용하면 되지만 key로 활용할 값이 없다면 제한적인 상황에서 index
혹은 nanoid
를 활용해 보자.
https://developer-talk.tistory.com/102
https://blog.woolta.com/categories/1/posts/210