[2022.08.22] 리액트 key값 작명의 중요성

REASON·2022년 8월 22일
1

React

목록 보기
24/27

리액트에서 map과 같이 반복문을 사용할 때 key값을 사용해야 한다.
이때 key 값은 유니크한 값을 넣어야 디버깅할 때 편리하다.
보통은 귀찮거나 경고 제거를 위해 index를 넣어서 사용하는 경우도 많은 것 같다.
(그치만 이것은 매우 안티패턴이라는 것!)

처음에는 key 값을 넣으라는 메시지만 나와서 생각없이 너무 당연하게도 인덱스를 key값으로 준적이 많았는데 이는 매우매우매우 좋지 않다. 😲

어디선가 주워들어서 대충 알고는 있었지만 왜??? 에 대해서는 크게 생각해보지 않았었다.

이번 기회에 key값 작명하는 방법과 디버깅의 상관관계를 제대로 짚고 넘어가보려 한다.

왜 유니크한 key를 사용해야 할까?

우선, 리액트 공식문서를 살펴보면 index 를 Key로 사용하는 것을 가장 최후의 수단으로 사용하라고 명시되어 있다.

바로 엘리먼트에 안정적인 고유성을 부여하기 위함이라고 한다.

안정적인 고유성?

말이 조금 어렵게 느껴지지만, 리액트가 DOM 요소를 식별할 때는 key를 통해 식별한다는 것이다.
여기서 만약 index를 사용한다면 리스트의 순서에 따라 index가 변경될 수도 있기 때문에 안정적이지 않다.

정적인 요소에 대해서 index를 사용해도 된다. 하지만 동적으로 무언가 추가되어 index가 변경될 여지가 조금이라도 있다면 index를 Key로 사용하면 안된다.

직접 코드로 확인해보기

const [listItems, setListItems] = useState(['가', '나', '다']);

먼저 유니크한 key의 중요성을 확인하기 위해 리스트 아이템을 만들어 map 반복문을 돌려볼 것이다.

<ul>
  {listItems.map((item) => {
    return <li>{item}</li>;
  })}
</ul>

현재 가, 나, 다가 들어있는 리스트 아이템 state를 li 태그에 하나씩 넣어서 요소로 만들어주었다.

그러면 예상했던 대로 리스트가 총 3가지가 생성되어 있음을 확인할 수 있다.

콘솔을 확인해보면 Warning: Each child in a list should have a unique "key" prop. 와 같은 경고 메시지가 나타나는데 이는 map 반복문 안에서 key를 부여하지 않았기 때문이다.

안티패턴🙅‍♀️ key값에 index 넣어보기

일단 리액트 공식문서에서 가장 최후의 수단이라 했던 index를 넣어보자. (정적 리스트면 상관없음)

<ul>
  {listItems.map((item, idx) => {
    return <li key={idx}>{item}</li>;
  })}
</ul>

당장은 이렇게 하면 경고 메시지는 나타나지 않는다.
어? 그럼 상관 없는거 아닌가 라고 생각될 지도 모르지만 아직 "정적" 리스트라서 key의 중요성을 체감하지 못하는 것 뿐이다.

import React, { useState } from 'react';

function ReactKey() {
  const [listItems, setListItems] = useState(['가', '나', '다']);
  const [inputvalue, setInputValue] = useState('');

  const addList = () => {
    setListItems((prevList) => {
      return [inputvalue, ...prevList];
    });
    setInputValue('');
  };

  const changeInputValue = (e) => {
    setInputValue(e.target.value);
  };

  return (
    <div>
      <input type="text" value={inputvalue} onChange={changeInputValue} />
      <button onClick={addList}>추가</button>
      <ul>
        {listItems.map((item, idx) => {
          return <li key={idx}>{item}</li>;
        })}
      </ul>
    </div>
  );
}

export default ReactKey;

index가 key값인 상태에서 "라" 를 리스트에 추가해보았다.
이때 DOM의 요소에 추가된 li 태그들이 모두 깜빡 거리는 것을 확인할 수 있는데 이는 li 태그가 모두 업데이트 되었다는 것을 의미한다.

즉, 맨 첫번째(인덱스 0)에 "라" 를 추가함으로 인해 인덱스가 모두 하나씩 뒤로 밀리면서 li 태그가 전부 업데이트가 된 것이다.

"라"를 맨 뒤에 추가했다면 위처럼 li가 모두 업데이트되지 않고 추가한 "라"만 업데이트 됐겠지만, 인덱스를 key값으로 가지고 있는 다른 요소들도 key값이 변경됨으로 인해 모두 함께 업데이트되어 버린 것이다.

성능 이슈 발생!

만약 li가 몇개가 아니라 몇 백개, 몇 천개 였다면 그 많은 li가 전부 업데이트 된다는 의미와도 같다.

그렇다면 key를 item으로 넣으면 어떨까?

<ul>
  {listItems.map((item, idx) => {
    return <li key={item}>{item}</li>;
  })}
</ul>

이번에는 인덱스가 아닌 item을 key값으로 사용해보았다.

당연히 "라" 라는 키를 가진 li는 없으므로 이전과 같은 상황은 일어나지 않았다.

그런데 지금 만약 가, 나, 다, 라 중에 똑같은 것을 한개라도 더 추가한다면 중복 되는 키가 발생한다.

🙅‍♀️ 중복되는 키 발생!

중복되는 키가 발생하면 어떻게 될까?

뭔가 추가할 수록 잘못됨을 알 수 있다..
중복된 키를 가진 li가 보너스도 아니고 덤마냥 딸려온다.

그리고 콘솔에서 다음과 같은 중복 키에 대한 에러를 발견할 수 있다.

Warning: Encountered two children with the same key, 가나다. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.

중복된 키를 가지고 있으니 키를 고유하게 만들어라 라는 내용이다.
고유하지 않으면 위처럼 똑같은 애가 자꾸 쫓아오는 (?) 문제가 발생된다는 것이다.

결론

하지 말란거 했다고 리액트가 자꾸 이상한 애를 만들어서 선물하거나 성능에 발작을 일으킬 수 있으니 key값을 유니크하게 짓는 습관을 들이자.
애초에 유니크한 id가 있으면 이 id를 가지고 key 값을 부여하면 되서 매우 좋을 것이다.
예를 들면 객체에 { id : 1 } 이런 식으로ㅎㅎ

귀찮다고 key값에 index를 때려 박다가는 나중에 디버깅하는데 고통의 시간을 보내야 될 수도 있다.


참고 자료
리액트 공식문서 리스트와 key
리액트 공식문서 재조정
React - List와 Key의 중요성. 디버깅의 악몽을 피하자!
Index as a key is an anti-pattern

0개의 댓글