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의 조건은 두 가지입니다.
- 고유해야 합니다 — 형제 항목 사이에서 중복되면 안 됩니다.
- 안정적이어야 합니다 — 렌더링할 때마다 바뀌면 안 됩니다.
실무에서는 보통 데이터의 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에서 상태를 다룰 때 꼭 지켜야 하는 원칙, 불변성에 대해 알아보겠습니다.