React에서 배열을 렌더링할 때 각 요소에 key prop을 지정해야 한다는 경고를 마주친 적이 있을 것입니다. Key는 단순한 경고 해결용 속성이 아니라, React가 리스트를 효율적으로 업데이트하기 위해 반드시 필요한 식별자입니다.
Key는 React가 리스트 내 각 요소를 고유하게 식별하기 위해 사용하는 특별한 prop입니다. 같은 리스트 안에서 중복되지 않는 문자열이나 숫자 값을 사용해야 합니다. Key는 React 내부에서만 사용되며 실제 컴포넌트 props로는 전달되지 않습니다.
const items = ["사과", "바나나", "딸기"];
return (
<ul>
{items.map((item, index) => (
<li key={item}>{item}</li>
))}
</ul>
);
React는 상태가 변경될 때 이전 Virtual DOM과 새 Virtual DOM을 비교하여 실제로 변경된 부분만 DOM에 반영합니다. 이 과정을 Reconciliation이라고 합니다.
리스트가 있을 때 Key가 없다면 React는 요소의 위치(인덱스)만으로 변경 여부를 판단합니다. 항목이 중간에 추가되거나 삭제되면 그 이후의 모든 요소가 변경된 것으로 인식되어 불필요한 DOM 업데이트가 발생합니다.
Key 없는 경우 (항목 앞에 삽입 시):
이전: [A, B, C]
새로: [X, A, B, C]
React 판단:
위치 0: A → X (변경)
위치 1: B → A (변경)
위치 2: C → B (변경)
위치 3: 없음 → C (추가)
→ 4번 DOM 조작 발생
Key 있는 경우 (항목 앞에 삽입 시):
이전: [key=A, key=B, key=C]
새로: [key=X, key=A, key=B, key=C]
React 판단:
key=A: 이동
key=B: 이동
key=C: 이동
key=X: 추가 (신규)
→ 1번 DOM 조작 발생
Key를 올바르게 지정하면 React는 각 요소의 정체성을 추적하여 실제로 추가, 삭제, 이동된 요소만 정확히 처리합니다.
배열의 인덱스를 Key로 사용하는 것은 가장 흔한 실수 중 하나입니다. 정적인 리스트라면 문제가 없어 보이지만, 리스트가 재정렬되거나 중간 항목이 삭제될 때 심각한 버그를 유발합니다.
// 위험한 패턴
items.map((item, index) => <Item key={index} value={item} />)
인덱스를 Key로 사용하면 항목을 삭제하거나 재정렬할 때 Key와 실제 데이터의 매핑이 어긋납니다. 예를 들어 0번 항목을 삭제하면 기존 1번이 0번 Key를 받게 되고, React는 이 컴포넌트를 "동일한 컴포넌트"로 인식하여 내부 상태를 재사용합니다. 입력 필드의 값이나 로컬 상태가 엉뚱한 항목에 남아 있게 되는 것입니다.
올바른 Key는 데이터 자체의 고유 식별자를 사용해야 합니다. 서버에서 받아온 데이터라면 데이터베이스 ID를, 그렇지 않다면 uuid 같은 라이브러리로 생성한 고유 값을 사용하는 것이 안전합니다.
// 올바른 패턴
items.map((item) => <Item key={item.id} value={item.name} />)
Key는 형제 요소 사이에서만 고유하면 됩니다. 다른 리스트에서 같은 Key를 사용하는 것은 문제가 없습니다.