기술 포스트

key가 필요한 이유 — 리스트 렌더링의 숨은 함정


Article

React로 목록을 그려본 적이 있다면, 이런 경고를 본 적 있을 겁니다.

Warning: Each child in a list should have a unique "key" prop.

"리스트의 각 자식에 고유한 key prop이 있어야 한다"는 경고입니다. 처음 보면 대수롭지 않게 넘기기 쉬운데, 이 key는 React가 화면을 효율적으로 업데이트하는 데 정말 중요한 역할을 합니다.

map으로 리스트 그리기

React에서 목록을 그릴 때는 보통 map을 사용합니다.

const fruits = ["사과", "바나나", "포도"];

function FruitList() {
  return (
    <ul>
      {fruits.map((fruit) => (
        <li>{fruit}</li>
      ))}
    </ul>
  );
}

화면에는 잘 나옵니다. 하지만 콘솔에 경고가 뜹니다. key가 없기 때문입니다.

key가 하는 역할

이전 글에서 React가 가상DOM을 비교(Diffing)해서 달라진 부분만 업데이트한다고 배웠습니다. 리스트도 마찬가지입니다.

그런데 리스트는 문제가 있습니다. 항목이 추가되거나, 삭제되거나, 순서가 바뀔 수 있습니다. 이때 React가 "어떤 항목이 바뀌었는지"를 어떻게 알 수 있을까요?

바로 key를 보고 판단합니다.

{fruits.map((fruit) => (
  <li key={fruit}>{fruit}</li>
))}

key는 각 항목의 신분증 같은 것입니다. React는 key를 보고 "이 항목은 이전에도 있었고, 이 항목은 새로 추가됐고, 이 항목은 사라졌다"를 판단합니다.

비유: 학생 출석부

30명이 앉아 있는 교실을 떠올려보세요.

key가 없는 경우 (이름표 없음)

  • 자리 순서만으로 학생을 구분합니다.
  • 2번째 자리에 새 학생이 앉으면, 2번부터 끝까지 전부 바뀐 것으로 인식합니다.
  • 실제로는 한 명만 추가됐는데, 나머지를 전부 다시 확인해야 합니다.

key가 있는 경우 (학번으로 구분)

  • 각 학생에게 고유한 학번이 있습니다.
  • 새 학생이 들어와도 "학번 31번이 추가됐네" 하고 바로 알 수 있습니다.
  • 나머지 학생은 건드릴 필요가 없습니다.

React도 똑같습니다. key가 있으면 변경된 항목만 정확히 파악할 수 있습니다.

index를 key로 쓰면 안 되는 경우

"그럼 배열의 index를 key로 쓰면 되지 않나?"라고 생각할 수 있습니다.

{fruits.map((fruit, index) => (
  <li key={index}>{fruit}</li>
))}

이건 목록이 절대 바뀌지 않을 때만 괜찮습니다.

항목이 추가되거나 삭제되거나 순서가 바뀌면 문제가 생깁니다. index는 위치 번호일 뿐이라, 항목이 바뀌면 index도 밀려버립니다.

예를 들어 목록 맨 앞에 "딸기"를 추가하면:

이전: 0-사과, 1-바나나, 2-포도
이후: 0-딸기, 1-사과, 2-바나나, 3-포도

React는 "0번이 사과에서 딸기로 바뀌었고, 1번이 바나나에서 사과로 바뀌었고..." 전부 바뀐 걸로 인식합니다. 실제로는 하나만 추가된 건데요.

더 심각한 건, 각 항목에 입력 필드 같은 상태가 있을 때 상태가 엉뚱한 항목에 붙어버릴 수 있습니다.

좋은 key란

좋은 key의 조건은 두 가지입니다.

  1. 고유해야 합니다 — 형제 항목 사이에서 중복되면 안 됩니다.
  2. 안정적이어야 합니다 — 렌더링할 때마다 바뀌면 안 됩니다.

실무에서는 보통 데이터의 ID를 key로 사용합니다.

const todos = [
  { id: 1, text: "장보기" },
  { id: 2, text: "운동하기" },
  { id: 3, text: "공부하기" },
];

{todos.map((todo) => (
  <li key={todo.id}>{todo.text}</li>
))}

데이터베이스에서 가져온 데이터라면 대부분 고유한 ID가 있습니다. 그게 가장 이상적인 key입니다.

정리

  • 리스트를 렌더링할 때 각 항목에 고유한 key를 넣어야 합니다.
  • key는 React가 어떤 항목이 바뀌었는지 식별하는 데 사용합니다.
  • index를 key로 쓰는 건 목록이 변하지 않을 때만 괜찮습니다.
  • 가장 좋은 key는 데이터의 고유 ID입니다.

다음 글에서는 React에서 상태를 다룰 때 꼭 지켜야 하는 원칙, 불변성에 대해 알아보겠습니다.