React 리스트 렌더링에서 key가 필수인 이유와 index를 쓰면 안 되는 이유

Kingmo·2022년 3월 26일
post-thumbnail

React에서 map 함수를 사용해 여러 컴포넌트를 렌더링할 때, 흔히 콘솔에서 다음과 같은 경고를 마주한다.

React가 왜 고유한 key를 요구하는지, 그리고 왜 단순히 배열의 index를 넘기면 문제가 될까?

React에서 key가 필요한 이유

React는 상태가 변경되면 새로운 Virtual DOM 트리를 만들고, 이전 트리와 비교(Diffing)하여 실제 변경된 부분만 브라우저 DOM에 반영한다.

단순 텍스트나 고정된 컴포넌트와 달리, 동적 리스트에서는 항목이 새로 추가되거나 삭제되고 순서가 뒤바뀌는 일이 빈번하다.

만약 리스트의 각 노드에 고유한 식별자가 없다면 React는 항목의 순서만 보고 이전 항목과 일대일로 단순 비교를 수행한다. 즉, 맨 앞에 항목 하나가 추가되었을 뿐인데 뒤따르는 모든 자식 노드가 통째로 바뀌었다고 판단하여 불필요한 DOM 재생성을 일으킨다.

각 항목에 고유한 key를 부여하면, React는 렌더링 전후로 어떤 항목이 유지되었고, 어떤 항목이 이동했거나 추가/삭제되었는지 단번에 식별할 수 있어 렌더링 성능을 최적화할 수 있다.

// ❌ key가 없어 콘솔 경고 발생 및 비효율적 갱신
const list = ['사과', '딸기', '바나나'];

return (
  <>
    {list.map((data) => (
      <Test>{data}</Test>
    ))}
  </>
);

//  고유한 key 부여
return (
  <>
    {list.map((data) => (
      <Test key={data}>{data}</Test>
    ))}
  </>
);

배열의 index를 key로 사용할 때 발생하는 문제

경고를 없애기 위해 흔히 (item, index) => <Item key="{index}"/> 형태로 작성하곤 한다. 하지만 이는 단순한 성능 저하를 넘어 심각한 UI 버그와 상태 왜곡을 유발한다.

React는 key를 기준으로 컴포넌트의 인스턴스와 내부 state를 유지하고 재사용하기 때문이다.

삭제 시 발생하는 문제 예시

아래와 같이 체크박스 상태를 가진 리스트가 있다고 가정하자.

  • 초기 상태: [0: 사과(체크됨), 1: 딸기, 2: 바나나]
  • 사용자가 사과를 삭제한다.
  • 새 배열: [0: 딸기, 1: 바나나]

React 입장에서는 key=0인 노드가 여전히 존재한다고 인식한다. 따라서 key=0 노드의 인스턴스와 DOM을 그대로 재사용하고, 텍스트만 딸기로 바꾼다.

그 결과, 삭제된 사과의 체크 상태가 새로 0번 인덱스가 된 딸기에 그대로 남아있는 치명적인 버그가 발생한다. 삭제되지 말아야 할 데이터나 인풋 상태가 엉뚱한 항목으로 대물림되는 것이다.

추가/정렬 시 발생하는 문제

배열의 맨 앞에 새 항목을 추가하면 기존 모든 항목의 인덱스가 1씩 밀린다.

  • 기존: 사과(key=0), 딸기(key=1)
  • 앞에 '포도' 추가: 포도(key=0), 사과(key=1), 딸기(key=2)

React는 key=0의 내용이 포도로 바뀌었고, key=1이 딸기에서 사과로 바뀌었으며, 새 항목 key=2가 추가되었다고 오판한다. 사실상 리스트 전체를 파괴하고 다시 그리는 것과 다름없어 key를 사용하는 본래 목적인 렌더링 최적화가 무력화된다.

index를 key로 사용해도 되는 예외적인 상황

공식 문서에서는 다음 조건을 모두 충족할 때만 최후의 수단으로 index 사용을 허용한다.

  1. 리스트와 항목들이 완전히 정적이며, 절대 계산되거나 변하지 않을 때
  2. 리스트에 항목이 추가, 삭제, 역순 정렬 등의 재배열이 일어나지 않을 때
  3. 리스트의 항목 컴포넌트가 자체 내부 state(체크박스, 인풋 텍스트 등)를 전혀 갖지 않는 순수 프레젠테이션 컴포넌트일 때
  4. 데이터에 고유한 ID 필드가 전혀 없을 때

실무에서는 백엔드 DB에서 내려주는 고유 식별자(item.id)나, 고유한 문자열 값을 key로 지정하는 것이 원칙이다.
index와 key에 관한 자세한 내용

profile
Developer

0개의 댓글