프론트엔드/React

[React] map()에 key를 사용하는 이유 (index를 key로 쓰면 안되는 이유)

반응형
Warning: Each child in a list should have a unique "key" prop

우리가 자주 보는 이 경고는 리액트에서 map()에 key 속성을 부여하지 않았을 때 발생합니다.

이 글을 통해 map에 키가 필요한 이유와 index를 키로 쓰면 안되는 이유를 알아보겠습니다.

 

(Robin Pokorny의 Index as a key is an anti-pattern을 읽어보시는 것도 추천합니다.)

 


키가 필요한 이유

<!-- Before -->
<ul>
  <li>first</li>
  <li>second</li>
</ul>


<!-- After -->
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

위의 코드처럼 자식의 맨 끝에 엘리먼트를 추가하는 과정은 크게 문제 되지 않습니다.

리액트는 기존 트리와 이후 트리가 일치하는 것을 확인하고 third를 트리에 추가하기 때문입니다.

 

 

<!-- Before -->
<ul>
  <li>first</li>
  <li>second</li>
</ul>


<!-- After -->
<ul>
  <li>third</li>
  <li>first</li>
  <li>second</li>
</ul>

하지만 위의 코드처럼 리스트의 맨 앞에 엘리먼트가 추가되는 경우 비효율적으로 처리됩니다.

리액트는 first와 second를 유지하는 것이 아닌, 모든 자식을 변경하기 때문입니다.

 

이러한 비효율적인 렌더링 문제를 해결하기 위해 리액트는 key 속성을 활용합니다.

 


키의 역할

키는 엘리먼트에 고유성을 부여합니다.

리액트는 키를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인할 수 있습니다.

 

<!-- Before -->
<ul>
  <li key="1">first</li>
  <li key="2">second</li>
</ul>


<!-- After -->
<ul>
  <li key="3">third</li>
  <li key="1">first</li>
  <li key="2">second</li>
</ul>

위의 예시에 키를 추가하여 비효율적인 렌더링 문제를 해결할 수 있습니다. 

리액트는 키를 통해 first와 second는 기존의 자식을 이동만 하면 된다는 것을 알 수 있기 때문입니다.

 


적절한 키 후보

키의 가장 좋은 후보는 항목들을 고유하게 식별할 수 있는 문자열입니다.

주로 데이터의 id를 키로 많이 사용합니다.

 

리스트 항목에 명시적으로 키를 지정하지 않으면 React는 기본적으로 index를 키로 사용합니다.

하지만 인덱스는 키로 사용하면 다양한 문제가 발생할 수 있습니다.

 


Index를 키로 쓰면 안되는 이유

배열이 변경되면서 컴포넌트가 리렌더링되면 배열의 index가 다시 매핑됩니다.

만약 index를 키로 사용하고 있었다면 index는 기존과 다른 요소에 매핑될 수 있습니다. 

이렇게 되면 state가 의도치 않게 변경되어 원하는 결과를 얻지 못하게 됩니다.

 


 

💡 Index를 키로 써도 괜찮은 경우

다음 항목들을 모두 만족한다면 index를 키로 사용하여도 안전할 가능성이 있습니다.

  1. 리스트와 그 항목들이 정적이거나 변경의 여지가 없을 때
  2. 리스트의 항목들이 id를 갖고 있지 않을 때
  3. 리스트가 절대로 재정렬되거나 필터링되지 않을 때

 

 

반응형