[React] 배열의 index를 key로 쓰면 안되는 이유
React 공식 Document
를 보다가 아래 노란색으로 캡쳐한 부분을 보게 되었고 확인한 사항들을 정리합니다.
우선 문제가 발생하는 소스
를 보겠습니다. Create-react-app 을 이용해 React 프로젝트를 생성했으며 App.js
단순히 아래 컴포넌트를 보여줄 뿐입니다.
코드를 간단하게 설명하면..
- line 22, 23:
추가, 삭제
버튼.추가
버튼을 누르면정국
데이터를 list 앞에 추가하고삭제
를 누르면철수
를 삭제한다. - line 28: component가 관리하는 list 데이터를 map으로 loop 돌며 div 태그의
key
를index
로 지정한다.
코드를 실행한 결과는 아래와 같습니다.
여기서 철수 input
부분에 아래와 같이 데이터를 입력합니다.
그리고 추가
버튼을 누릅니다. 우리가 예상한 결과는 아래와 같을 겁니다.
하지만 실제 결과는 아래와 같습니다.
왜 이런 결과가 나왔는지 추가 버튼
을 누르기 전 상황으로 돌아갑니다. DOM에서 key가 0 인 곳에 여기는 철수 데이터
가 있습니다.
우리가 추가 버튼
을 누르면 list 데이터가 변경되면서 component가 re-render되고 index
를 다시 mapping
합니다. 맨 앞에 추가된 정국
데이터에 key가 0이 mapping 됩니다. React는 key가 동일 할 경우, 동일한 DOM Element를 보여주기 때문에 아래와 같은 현상이 발생합니다.
이런 현상은 데이터를 삭제하는 부분에도 발생하게 됩니다.
이 상태에서 삭제
를 누르면 철수 데이터를 삭제 하게 됩니다. 이 때 결과를 보면 아래와 같습니다. 마찬가지로 데이터가 변경
되면서 re-rendering
되고 index
가 0부터 다시 mapping
되면서 이전의 index가 0 이였던 철수 데이터가 영희에게 mapping 됩니다. 그리고 이는 분명히 우리가 원하는 결과와는 다릅니다.
Q1. 그럼 어떤 데이터를 key로 사용하면 될까요
List 데이터에서 Unique
한 값을 key
로 사용합니다. 제 경험에 비춰서 가장 사용하기 쉬운 건 Database의 id 입니다. AUTO_INCREMENT
된 id값을 서버로 부터 받아서 사용합니다. id값이 아니더라도 unique 하다면 key로 사용할 수 있습니다.
만약 데이터에 Unique한 값이 없다면
아래와 같이 global 변수를 이용해 id를 생성하고 이를 key로 사용할 수도 있습니다.
counter = 1;function createNewTodo(text) {
return {
id: counter++,
text
}
}
Production
모드에서는 위 방법보다는 shortid를 사용하는게 좋다고 하네요.
var shortid = require('shortid');function createNewTodo(text) {
return {
id: shortid.generate(),
text
}
}
Q2. 그럼 절대로 배열의 index를 key로 사용하면 안되나요?
제가 참고한 Robin Pokorny 말에 의하면 아래 3가지를 만족 할 경우 안심하고 index를 key로 써도 된다
고 이야기 했습니다.
- 배열과 각 요소가 static이며
computed
되지 않고변하지 않아야
한다. - 데이터 내부에 id로 쓸만한 unique 값이 없을 경우
- 데이터가 결코
reordered
orfiltered
되지 않을 경우
위 3가지를 만족하면 배열의 index를 key로 써도 된다는 것 같은데.. 어째 저는 데이터가 절대 변하지 않을 경우에 쓰라는 말 같기도 하네요..