리액트로 구현한 페이지에 반복되는 데이터를 렌더링하기 위해 map과 forEach 함수를 썼는데, 콘솔창에 이런 에러가 떴다.
페이지는 멀쩡하게 돌아가는 것 같은데... 왜 각 child가 "key" prop을 가져야 한다고 하는 걸까?
React 공식 홈페이지에는 Key는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕습니다
라고 명시되어 있다. 바로 이해가 안 가서 더 찾아보니까, 리액트의 렌더링과 관련이 있었다.
리액트는 state에 변경 사항이 있으면 이를 감지해 자동으로 리렌더링을 하는데, 배열 같은 경우 여러 데이터의 집합이기 때문에 한 요소에 변화가 일어났을 때 배열 전체를 리렌더링한다면 불필요한 작업들이 일어나게 된다. 그래서 각 데이터(each child in a list)에 key값을 부여해 key를 기준으로 요소의 변화를 감지하고, 변경된 요소만 리렌더링하면 좀 더 효율적인 프로그래밍이 될 것!
근! 데!
나는 대충 그럼 서로 안 겹치기만 하면 되겠지?! 하고 map 함수에서 두 번째 인자인 index를 받아 그대로 key값으로 부여했는데, 또 React 문서에서 항목의 인덱스를 key로 사용하는 걸 권장하지 않는다
고 나와 있었다...😓😓 그건 또 왤까?
Key는 리액트가 해당 데이터의 변화 여부를 비교하는 기준이 되기 때문에, 고유하고 영구적인(permanant) 값이어야 한다. 그런데 인덱스는 배열 내에서 자료의 위치를 나타내는 값이므로 배열의 순서가 바뀌면 인덱스도 바뀌게 된다.
예를 들어 let fruits = [apple, banana, grape, lemon]이라는 배열에 map을 사용하면서 index를 Key로 줄 경우
{fruits.map(i => (<div key={index}>{i}</div>)}
위 코드의 결과는
<div key=1>apple</div>
<div key=2>banana</div>
<div key=3>grape</div>
<div key=4>lemon</div>
이 된다. 그런데 여기서 melon이라는 데이터를 추가했을 때, 배열의 끝에 추가한다면 문제가 없지만 맨 앞이나 중간에 추가하게 되면 index에 변화가 생겨서 기존 요소의 key값 또한 바뀌는 문제가 생긴다.
<div key=1>apple</div> //변화 X
<div key=2>melon</div> //key=3의 이전 데이터는 banana, 현재 데이터는 melon
<div key=3>banana</div> //key=2의 이전 데이터는 grape, 현재 데이터는 banana
<div key=4>grape</div> //key=4의 이전 데이터는 lemon, 현재 데이터는 grape
<div key=5>lemon</div> //새로 추가된 것으로 간주
그래서 실제로는 melon만 추가되었음에도 불구하고 key값이 바뀐 banana, grape, lemon까지 전부 리렌더링하게 되는 비효율적인 상황이 발생하는 것......
만약 각 데이터별로 id가 존재하는 경우 id를 key로 주면 이러한 문제가 생기지 않는다.
let fruits = [{id: 1, name: apple},
{id: 2, name: banana},
{id: 3, name: grape},
{id: 4, name: lemon}]
{fruits.map(i => (<div key={index}>i</div>)}
//초기 결과
<div key=1>apple</div>
<div key=2>banana</div>
<div key=3>grape</div>
<div key=4>lemon</div>
여기서는 {id: 5, name: melon}을 맨 앞에 추가하더라도
<div key=5>melon</div> //추가됨
<div key=1>apple</div> //변화 X
<div key=2>banana</div> //변화 X
<div key=3>grape</div> //변화 X
<div key=4>lemon</div> //변화 X
따라서 이전의 결과와 다르게 melon만 리렌더링될 것~~~
그리고 React에서는 key가 동일한 경우 동일한 DOM Element를 보여주기 때문에, 사용자가 리스트의 데이터를 추가하고 삭제할 경우 각 요소의 Element가 꼬여버리는 현상도 발생할 수 있다. (여기 참고!)
결론적으로 id가 없는 경우라도 데이터가 추가될 때마다 1씩 count되는 변수를 따로 생성하는 방법 등으로 고유한 키값을 부여하는 것이 좋다고 한다!
리스트와 Key-React
엘리먼트 렌더링-React
리액트 배열의 key 값 존재 이유 · 쾌락코딩
You shouldn't use an index as key of the element