본문으로 건너뛰기
개발 머꼬
개발 노트React
hohyeon.dev17

리스트에 key={index}를 썼다가 입력값이 엉뚱하게 섞인 이유

  • #Common Pitfall
  • #Engineering Note
  • #React

문제 발생

입력 필드가 있는 리스트에서 가운데 항목을 삭제했더니, 삭제 이후의 입력값들이 한 칸씩 밀려서 잘못된 항목에 표시되는 버그가 발생했습니다.

{items.map((item, index) => (
  <input key={index} defaultValue={item.text} />
))}

원인 분석

React는 리스트를 다시 렌더링할 때 key를 기준으로 "이전에 있던 어떤 엘리먼트와 지금 이 엘리먼트가 같은 것인지"를 판단합니다. key가 같다고 판단되면 React는 그 DOM 노드와 컴포넌트 상태(input의 defaultValue가 반영된 실제 DOM 값, 포커스 등)를 재사용합니다 — 새로 만들지 않습니다.

배열 인덱스를 key로 쓰면, 항목을 삭제하거나 순서를 바꿨을 때 인덱스는 그대로인데 실제 데이터는 다른 항목으로 바뀝니다. 예를 들어 3번째 항목을 지우면, 4번째였던 항목이 이제 인덱스 3을 갖게 됩니다. React 입장에서는 "key=3인 엘리먼트는 그대로 있다"고 판단해 그 DOM(과 그 안의 사용자 입력값, 포커스 상태)을 그대로 재사용하지만, 실제로는 전혀 다른 데이터 항목입니다 — 그래서 입력값이 엉뚱한 항목에 남아있는 것처럼 보입니다.

해결 방안

  1. 각 항목을 고유하게 식별하는 안정적인 값(DB의 id 등)을 key로 씁니다.
{items.map((item) => (
  <input key={item.id} defaultValue={item.text} />
))}
  1. 안정적인 id가 정말 없는 경우에만(예: 렌더링 시점에 한 번 생성되고 절대 재정렬/삭제되지 않는 정적 리스트) 인덱스를 key로 써도 괜찮습니다 — React 공식 문서도 이 조건에서는 인덱스 사용을 허용합니다.
  2. 새로 추가하는 항목에 아직 서버가 부여한 id가 없다면, 클라이언트에서 생성 시점에 임시 고유 id(crypto.randomUUID() 등)를 만들어 부여합니다 — 배열 인덱스보다 안전합니다.
  3. 이 문제는 key가 잘못되어도 콘솔에 명확한 에러가 뜨지 않고 그냥 조용히 상태가 잘못 섞이는 방식으로 나타나므로, 리스트 항목에 입력값/체크박스/포커스처럼 "항목별 상태"가 있다면 처음부터 안정적인 key를 쓰는 습관이 중요합니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.