React의 Key! 아직도 index로 사용하니?

NaReum·2021년 9월 15일
1

Frontend

목록 보기
1/1
post-thumbnail

우리는 리액트를 개발하다가, 특히 초기에 이런 경고문구를 많이 보았을 것이다.
Warning: Each child in a list should have a unique "key": prop.
Warning: Each child in a list should have a unique "key" prop. !!!
바로 map 등을 사용해서 컴포넌트를 반복적으로 생성할 때, key값을 지정해주지 않았다라는 경고이다.
그렇다면 이 key 값이 왜 필요할까?

요소 트리

리액트는 컴포넌트의 상태나 속성이 변할때마다 render 함수를 호출하는데, render 함수는 새로운 리액트 요소를 반환하고 이를 기존 요소 트리와 비교해 새로운 변경점에 대해서만 리렌더링을 수행합니다.

리액트는 O(N)의 시간복잡도로 두 트리를 비교하기 위해 key 속성을 사용하고, 자식 요소들을 반복적으로 렌더링하는 상황에서 명시적으로 key를 사용합니다.

즉, 트리를 비교할 때 key 값을 비교하고 key가 달라지면 렌더링도 다시하게 된다. 또한 key 값을 통해 추가되는 데이터 및 요소의 위치를 잡아주기도 합니다.

예시

key를 index로 사용했을때

데이터를 뒤에 추가

import { useState } from "react";

const StudyPage = () => {
    const [data,setData]= useState([
        {
            id: 1,
            name: "나름"
        },
        {
            id:2,
            name: "콘스트"
        },
        {
            id:3,
            name:'조이'
        }
    ]); 

    const clickButton = () => {
        setData([...data,{id:4,name:"합체"}])
        console.log(data)
    }
    return(
        <>
            <button onClick={clickButton}> 추가 </button>
            <ul>
                {data?.map((item,index)=><li key={index}>{item.name}</li>)}
            </ul>
        </>
    )
}

export default StudyPage;

일단 이 상황을 가정해서 예시를 들어보겠습니다.
key값을 index로 사용하고, 새로운 데이터를 뒤에 붙여보겠습니다.

마지막 데이터만 렌더링 되는것이 보이시나요? 이렇게 마지막 데이터를 추가하는 것은 문제가 되지 않습니다.

데이터를 앞에 추가

문제가 되는것은 데이터가 앞에 추가 될 때 입니다.

    const clickButton = () => {
        setData([{id:4,name:"합체"},...data])
        console.log(data)
    }

clickButton 함수를 이렇게 수정하면 새로운 데이터가 앞에 추가가 되겠죠?
데이터를 앞에 추가한 모습

그리고 전체가 리렌더링 되는 모습을 볼 수 있습니다.

  <ul>
      <li key=1>나름</li>
      <li key=2>콘스트</li>
      <li key=3>조이</li>
  </ul>

에서 앞에 데이터가 추가 됬기 때문에 index가 변경되어

  <ul>
      <li key=1>합체</li>
      <li key=2>나름</li>
      <li key=3>콘스트</li>
      <li key=4>조이</li>
  </ul>

key 값이 변경되었기 때문에 react에서 다르다고 판단되어 리렌더링이 된것을 확인할 수 있습니다.
이래서 key값은 변경되지 않는 유일한 값으로 설정해야한다는 것입니다.
그래서 저는 1차원적으로 생각해서 key 값에 item 을 넣으면 데이터는 안변하니까 리렌더링이 안되겠지? 라고 생각이 들어 해보았습니다.

key값을 item 데이터 자체를 넣어보았다!

<>
	<button onClick={clickButton}> 추가 </button>
	<ul>
        	{data?.map((item)=><li key={item}>{item.name}</li>)}
    </ul>
</>

이렇게 수정해보았다! 그 결과는?

짜란... 전체가 리렌더링 되었다. 그리고 경고메세지가 하나 떠있다.


경고: [object Object] 라는 동일한 키를 가진 두 개의 자식이 발생했습니다. 구성 요소가 업데이트 간에 ID를 유지하도록 키는 고유해야 합니다. 고유하지 않은 키를 사용하면 하위 항목이 중복 및/또는 생략될 수 있습니다. 이 동작은 지원되지 않으며 향후 버전에서 변경될 수 있습니다.

즉, react key에서는 무조건 문자열로 변환하기때문에, 객체를 문자열화하면 [object Object]가 되어 모든 키 값이 동일해진것이다. 그럼 react가 추가되야할 위치를 정확히 찾지 못하여 전체를 다시 렌더링하는 것입니다.

해결

보통 서버에서 데이터를 받아오면 데이터에 해당하는 고유 idx 값이 있을것이다. 그 값을 사용하면 쉽게 해결됩니다.
예제에서 사용했던 데이터의 포맷은

[
        {
            id: 1,
            name: "나름"
        },
        {
            id:2,
            name: "콘스트"
        },
        {
            id:3,
            name:'조이'
        }
]

이러한 모습이기에 id 가 각 데이터의 고유값, 고유 idx이다. 이 id값을 key 값으로 지정하면,

        <>
            <button onClick={clickButton}> 추가 </button>
            <ul>
                {data?.map((item)=><li key={item.id}>{item.name}</li>)}
            </ul>
        </>

이러하겠다. 이 코드의 실행 결과를 보면

깔끔하게 추가된 부분만 데이터가 리랜더링됨을 알 수 있습니다.
이 key의 값이 전역적으로 고유할 필요는 없지만, 같이 반복되는 형제요소 내에서는 고유해야 react의 성능을 이처럼 망치지 않고 잘 사용할 수 있습니다.

profile
나름 프론트엔드 개발자입니다.

0개의 댓글

관련 채용 정보