TIL ( 8일차 2022-05-26 )

jigom·2022년 5월 26일
0

TIL

목록 보기
8/13

React Key Index 이슈

React는 DOM의 요소들을 map이나 from처럼 순회하면서 렌더링할 경우, key를 넣어주라고 경고한다.

예를 들어 3개의 리스트를 가진 변수를 통해 key가 없이 배열 랜더링을 진행하게 한다면 해당 리스트 변수에 1개 더 추가되는 경우라도 React 는 총 4개를 처음부터 다시 리 렌더링 하게 된다.
하지만 key 를 지정한다면 기존의 요소들은 변경되지 않았다는 걸 React 에서 자동으로 파악 후 새로 생기는 요소만 리 렌더링을 진행하게 된다.

단순히 key 요소만 추가한 것만으로도 더욱 최적화 된 랜더링을 진행할수 있다.

그럼 왜 map이나 from의 배열 index를 사용하지 말라고 하는 것일까?

다음 예시를 통해 알아보자.

import React, { useState } from "react";

const KeyEx = () => {
  const [list, setList] = useState([
    { name: "철수" },
    { name: "영희" },
    { name: "기철" },
  ]);

  const addList = () => {
    setList([{ name: "영수" }, ...list]);
  };

  const delList = () => {
      setList( list.filter( item => {
        return item.name !== "철수";
      } ));
  }

  return (
      <div className="wrapper">
        <button onClick={addList}>추가하기</button>
        <button onClick={delList}>삭제하기</button>

        {list.map((item, index) => {
           return <div>{index} : {item.name} <input type="text"/></div>
        })}

      </div>
  );
};

export default KeyEx;

이렇게 단순히 “영수”를 리스트 앞에 넣고 빼는 코드를 작성해보았다.

이 상태에서 추가하기 버튼을 누르면 “철수”가 밀려나고 “영수”가 생기면서 “아아아”는 철수와 함께 두번째 열에 생길 것으로 예상했다.

<예상 결과>

<실제 결과>

이유가 무엇인지 살펴보자. 추가 버튼을 누르기 전 상황에서 DOM의 key가 0인 곳에는 “아아아” 가 있다.

하지만 추가 버튼을 누르게 되면 list의 데이터가 변경되면서 compoent가 Re-render 되고, index에 맞게 다시 리스트를 생성한다. 결국 “영수”의 데이터key가 0으로 바뀌게 되는데,

React는 key가 동일할 경우, 동일한 DOM element를 보여주기 때문에 “아아아”는 0번째 key에 존재하게 된다.

데이터를 삭제할 때도 동일한 문제가 발생한다.

const delList = () => {
      setList( list.filter( item => {
        return item.name !== "철수";
      } ));
  }

name이 철수가 아닌 리스트만 필터링하기 때문에 삭제시, 예상 결과는

이라고 생각했다. 하지만 React는 key가 동일할 경우, 동일한 DOM element를 보여주기 때문에 ****

key가 0인 데이터는 영희에 씌워지게 되고, key가 1인 데이터는 기철에게 씌워지게 된다.

그럼 key가 index를 사용해도 되는 경우는?

  • 정적이거나 변경의 여지가 없을 때
  • ID가 없을 때
  • list가 절대로 재정렬되거나 필터링 되지 않을 때

결국 고유한 값을 가질 수 있도록 list가 변경되지 않을 때만 쓰라는 말로 보인다.

그럼 key를 index말고 쓸 수 있는 방법은?

  1. DB의 고유 ID 값을 사용하자

    대부분 DB에서 고유의 ID로 Data를 넘겨준다. 이럴 때 Data와 고유 ID는 한쌍으로 넘어오기 때문에 이용할 수 있다.

  2. nanoid()를 사용하자

    DB에서 고유한 ID를 주지 않을 경우, nanoid 패키지에서 제공하는 nanoId를 사용하여 key 값을 넣어주면 된다.

    yarn add nanoid

    해당 yarn 으로 프로젝트에 패키지를 설치하고

    import { nanoid } from "nanoId";

    Import 후에 key에 nanoId를 사용한다.

    import React, { useState } from "react";
    import { nanoid } from "nanoid";
    
    const KeyEx = () => {
      const [list, setList] = useState([
        { name: "철수" },
        { name: "영희" },
        { name: "기철" },
      ]);
    
      const addList = () => {
        setList([{ name: "영수" }, ...list]);
      };
    
      const delList = () => {
        setList(
          list.filter((item) => {
            return item.name !== "철수";
          })
        );
      };
    
      return (
        <div className="wrapper">
          <button onClick={addList}>추가하기</button>
          <button onClick={delList}>삭제하기</button>
    
          {list.map((item, index) => {
            return (
              <div key={nanoid()}>
                {index} : {item.name} <input type="text" />
              </div>
            );
          })}
        </div>
      );
    };
    
    export default KeyEx;

shortid 를 사용하는 방법도 있는데, nanoid 보다 효율성이 덜어진다고 한다.

React nanoid 활용법 링크

profile
일단 해보자

0개의 댓글