개발/Javascript & Typescript

[React] React에서 key로 index를 쓰면 안 되는 이유

devhooney 2025. 7. 4. 08:33
728x90

React로 리스트를 렌더링하다 보면 .map() 안에서 key 값을 필수로 지정하게 된다. 이때 초보자부터 숙련자까지 흔히 하는 실수가 바로 index를 key로 사용하는 것이다.

// 흔히 하는 실수
items.map((item, index) => <div key={index}>{item.name}</div>);

 

간단해 보이지만, 이 코드는 예기치 않은 버그의 원인이 된다. 이번 포스팅에서는 왜 index를 key로 쓰면 안 되는지, 그리고 어떨 때는 써도 되는지를 예제와 함께 정리해본다.

 

 


 

 

 

📌 key란 무엇인가?
React는 리스트를 렌더링할 때 어떤 항목이 추가되었는지, 삭제되었는지, 혹은 순서가 바뀌었는지를 파악하기 위해 key라는 식별자를 사용한다. 이 key는 각 요소를 고유하게 식별해야 하고, 이를 기반으로 효율적으로 DOM을 업데이트한다.

 

 

728x90

 

 


 

❌ index를 key로 쓰면 발생하는 문제들
1. 순서 변경 또는 삽입/삭제 시 잘못된 컴포넌트 재사용
React는 key를 기준으로 DOM을 재사용한다. 그런데 index는 데이터의 실제 위치를 반영하지 않기 때문에, 항목의 순서가 바뀌거나 새로 삽입되면 잘못된 컴포넌트가 재사용될 수 있다.

예시

const items = ['A', 'B', 'C'];

items.map((item, index) => <div key={index}>{item}</div>);

 

이 상태에서 B를 삭제하면?
- 기존: A(key=0), B(key=1), C(key=2)
- 변경: A(key=0), C(key=1)
React는 key=1을 가진 요소를 B로 인식하고, 그걸 그대로 C로 바꿔치기함 → 원래 B 컴포넌트가 C로 재사용됨

💥 내부 상태(state)나 <input> 같은 컨트롤 요소가 있다면? → 상태가 엉뚱한 컴포넌트에 붙어버리는 현상 발생!

 

 

2. 입력 상태나 내부 state가 꼬임
특히 <input>, <checkbox> 같은 상태를 가지는 요소에서 index를 key로 쓰면 입력값이 다른 항목에 섞여버리는 현상이 자주 발생한다.


예시

items.map((item, index) => (
  <input
    key={index}
    value={item.name}
    onChange={e => handleChange(index, e.target.value)}
  />
));

 

리스트 중간 항목을 삭제하거나 순서를 바꾸면 입력하던 값이 엉뚱한 위치로 옮겨간다.

 

 

3. React의 diff 알고리즘이 비효율적으로 작동
index는 위치에 따라 바뀌기 때문에, 실제로는 같은 데이터인데도 매번 다른 key로 인식되어 불필요한 리렌더링이 발생한다.

 

 

 


 

✅ 언제는 index를 key로 써도 괜찮을까?

모든 경우에 index를 피해야 하는 건 아니다. 아래 조건을 모두 만족하면 예외적으로 사용할 수 있다.
- 리스트가 절대 변경되지 않음 (삽입, 삭제, 정렬 없음)
- 각 항목이 상태를 가지지 않음 (예: <input>, <checkbox> 없음)
- UI 상에서 간단한 표시용 리스트 (예: 메뉴 목록, 네비게이션 등)

 

예시

const menu = ['홈', '소개', '문의하기'];

menu.map((label, index) => <li key={index}>{label}</li>);

 

 

 


 

🔁 좋은 key의 조건
가능한 한 고유한 식별자를 사용하자.
- DB에서 오는 경우: id, uuid
- 백엔드에서 고유 키 제공 못 하는 경우: 해시값 생성하거나 클라이언트에서 고유 ID 할당
- key로 사용하는 값은 중복되지 않아야 하며, 렌더링마다 일관성 있게 유지되어야 한다

 

 

 

 

 

728x90