1 분 소요

리스트 렌더링 시 각 데이터의 unique key값을 추가하지 않을 경우 리액트가 렌더링할 컴포넌트를 잘못 판단하고, 브라우저에 의도치 않은 데이터를 노출할 수 있다.
서버 데이터를 그대로 그려주는거면 문제는 발생하지 않는다.
리스트를 추가/수정/삭제/정렬하여 리스트의 순서가 변경될 경우 문제가 발생한다.

key props

  • 리렌더링 발생 시 current tree와 workInProgress tree 간 변경 사항 확인 필요

    • 두 트리에서 같은 컴포넌트인지 구별하는 값 중 하나: key props
    • 만약 key가 없다면, 파이버 트리 구조에서 형제 컴포넌트 구분하기 위한 필드값인 sibling index만으로 판단하게 됨
  • 리스트나 조건부 렌더링 시, 리렌더링 간 변화한 컴포넌트를 추적하기 위해 key를 사용

    • key가 같으면 React는 컴포넌트를 재사용
    • key가 바뀌면 React는 이전 컴포넌트를 언마운트하고 새로운 걸 마운트함
<ul>
  {items.map((item) => (
    <li key={item.id}>{item.text}</li>
  ))}
</ul>

🚨 key props로 index를 사용하면 위험하다

  • 리스트가 추가/삭제/순서 변경될 때 문제 발생
  • React가 잘못된 컴포넌트를 재사용하거나 새로 만들게 됨
  • key를 안 쓰는 것과 key={index}는 동일
    • key를 안 쓰면 index가 자동으로 주입

key props를 설정하지 않아 발생하는 문제 예시

코드: index를 key값으로 설정하고, 버튼을 클릭하면 “Kiwi”를 “맨 앞”에 추가한다.
문제 상황: Kiwi를 맨 앞에 추가했는데, “Cherry”가 “맨 뒤”에 추가된다?

image

✅ 문제 상황 이해하기

  • 기존의 key값으로 들어간 index로 리렌더링 간 컴포넌트를 기억함
    • 0은 Apple, 1은 Banana, 2는 Cherry로 기억
  • 맨앞에 “Kiwi”가 추가되어 items의 상태는 변경되었는데, 렌더 단계에서 변경되지 않은 것으로 판단
    • 렌더 단계에서는 key, type, props로 변경이 필요한 컴포넌트를 체크 → 모두 같다 → pass
    • state가 브라우저에 반영되지 않음 (커밋 단계 생략)
  • items 리스트 렌더링할 때 idx 0, 1, 2는 변화 없으므로 Apple, Banana, Cherry가 DOM에 그려지고, items의 마지막 요소인 Cherry가 idx 3 요소로 들어옴 → Apple - Banana - Cherry - Cherry
key를 index로 사용할 경우, 리스트를 추가/수정/삭제했을 때 브라우저에 의도한 상태가 반영되지 않을 수 있다.
key를 안 쓰면 index가 자동으로 주입되므로 key를 안 쓰는 것과 key={index}는 동일하다.

image

💻 동작 코드

import { useState } from "react";

export default function App() {
  const [items, setItems] = useState([
    { id: 1, name: "🍎 Apple" },
    { id: 2, name: "🍌 Banana" },
    { id: 3, name: "🍒 Cherry" },
  ]);

  const handleAdd = () => {
    const newId = Math.max(...items.map((item) => item.id)) + 1;
    const newItem = { id: newId, name: `🥝 Kiwi ${newId}` };
    setItems([newItem, ...items]);
  };

  return (
    <div style=>
      <h3>✅ ID를 key로 사용한 안전한 리스트</h3>
      <button onClick={handleAdd}>맨 앞에 과일 추가</button>
      <ul>
        {items.map((item, idx) => (
          // idx를 key로 할 경우 문제 발생
          <li key={item.id}>
            <input defaultValue={item.name} />
          </li>
        ))}
      </ul>
    </div>
  );
}

📘 reference

댓글남기기