한가로이 기존에 개발한 기능을 테스트하다가 찾은 이슈 기록
올 초에 리팩토링한 칸반 보드를 테스트하다가 보니 API로 받은 데이터는 분명 1건인데, 화면엔 같은 태스크가 2번씩 표시되는 이상한 현상을 겪었다. (벌써 v3.. 그 이유는 더보기에서)
1. 최초 기능 개발
2. 라이브러리 교체 (beautiful-dnd -> dnd-kit)
3. 디자인 전환작업으로 인한 리팩토링
이번에 전체 검색 기능을 넣으면서, 검색 기능을 전반적으로 테스트하다가 발견했다.
처음에는 계층 구조 때문에 백엔드에서 중복 데이터를 잘못 가져오는 줄 알았지만 실제로는 렌더링 로직의 문제였다.
API 응답 자체에는 이상이 없었으며, 실제 원인은 React 내부의 reconciliation
알고리즘과 컴포넌트에 할당한 key
사용 방식에 있었다.
현상 요약
- API에서 수신된 태스크(task.id)는 분명 1건
- 하지만 UI에는 같은 태스크가 2개 이상 중복 표시됨
- 콘솔을 찍어보면 task는 1개인데, 컴포넌트는 반복 렌더링됨
원인: 중복된 key로 인한 잘못된 재사용
React는 리스트를 렌더링할 때 key
값을 기준으로 컴포넌트의 identity를 판단하고,
같은 key
가 있으면 DOM
을 재사용한다.
{tasks.map((task) => (
<KanbanTask key={task.id} ... />
))}
이 구조만 보면 문제가 없어 보이지만, 실제로는:
- columnTasksList [columnId]로 여러 column에서 같은 task가 포함될 수 있음
- 그리고 각 column 별로 map()을 돌리기 때문에, 동일한 id가 여러 column의 서로 다른
map
안에서 반복 사용됨
❗ 이때는 React가 중복 key
경고를 출력하지 않지만,
내부적으로는 동일한 컴포넌트로 오인하고 잘못 재사용하여
결과적으로 UI에 중복 렌더링이 발생한다.
중복 key
는 주로 콘솔에 에러 찍히는 것만 보고 처리하는 경우가 많았어서, 이 부분을 놓친 것 같다.
해결: key를 고유하게 구성하기
React가 올바르게 diffing 하도록 하기 위해, key
를 아래처럼 고유하게 조합해 주었다:
<KanbanTask
key={`${task.id}-${column.knbnSn}-${i}`}
/>
이렇게 column과 index를 함께 조합해서 고유한 key
를 만들었더니, 정상적으로 동작하는 것을 확인할 수 있었다.
정리
항목내용
문제 | 동일한 key로 인해 React가 컴포넌트를 잘못 재사용함 |
원인 | 서로 다른 리스트(map()) 안에서 동일 key 사용 시 React 경고 없음 |
증상 | 데이터는 1건인데, UI에는 중복 표시됨 |
해결 | key={id-columnId} 형태로 고유한 값으로 변경 |
마무리
React의 key
는 단순히 반복 렌더링을 위한 인덱스가 아니라,
컴포넌트의 정체성을 보장하는 중요한 기준이다.
특히 리스트가 여러 개(map 여러 번) 존재하거나, 동일한 데이터가 다른 위치에 중복 포함될 수 있다면 key
는 반드시 유일하고 의미 있는 값으로 만들어야 한다.
https://ko.legacy.reactjs.org/docs/reconciliation.html
재조정 (Reconciliation) – React
A JavaScript library for building user interfaces
ko.legacy.reactjs.org
'Development > React' 카테고리의 다른 글
[React] ReactQuery 순환 참조 (1) | 2025.06.09 |
---|---|
[React] useId()를 사용하다가 삽질한 기록 (4) | 2025.05.25 |
[TypeScript] 고급 타입 활용법: 조건부 타입, 제네릭 심화와 Exclude/Extract 활용 (0) | 2025.04.01 |
[React] 상태 (State) (0) | 2024.02.28 |
[React] JSX 사용하기 (0) | 2024.02.28 |