React에서 리스트를 렌더링할 때 key를 부여하는 건 필수적이다. 종종 input 요소를 map으로 뿌려야하는 경우가 있는데 이때 key를 nanoid()와 같은 랜덤한 고유값으로 주면 한 글자 입력 시 포커스가 풀리는 현상이 발생한다.

🔍 발생 원인
React에서 map으로 요소를 렌더링할 때 nanoid()를 사용하면 충돌 가능성이 없는 고유한 key를 쉽게 부여할 수 있다. 하지만 nanoid()는 호출할 때마다 새로운 key를 생성하기 때문에, input을 map으로 뿌릴 때 매번 key가 바뀌게 된다. 이 경우 React는 기존 DOM을 재사용하지 않고 새로운 DOM으로 인식해 다시 그리게 되므로, 입력 중이던 값이나 포커스가 사라지는 문제가 발생한다.
즉, 해당 현상은 map 때문이 아니라 unstable key 값으로 인해 발생한다.
✅ 해결 방법
1. 데이터에 있는 고유 id 사용
리스트 아이템이 DB id와 같은 고유 식별자를 갖고 있다면 그것을 key로 사용하는 것이 가장 이상적인 해결 방법이다.
{items.map((item) => (
<input key={item.id} value={item.value} />
))}
2. 초기화 시 한 번만 nanoid 생성
만일 아이템에 id가 없다면, 컴포넌트 초기 렌더링 시에 한 번만 nanoid를 만들어 상태에 저장한 뒤 사용할 수 있다.
const [inputs, setInputs] = useState(
Array.from({ length: 3 }, () => ({ id: nanoid(), value: '' }))
);
return (
<div>
{inputs.map((item) => (
<input key={item.id} value={item.value} />
))}
</div>
);

해당 현상이 발생하는 문제의 본질은 map 사용 여부가 아니라 key의 안정성(stability)에 있다.
key 값이 매번 바뀌게 되면 React는 해당 요소를 이전과 동일한 노드로 인식하지 않고, 새로운 노드로 판단하여 기존 DOM을 제거하고 새로 생성한다. 그 결과 input에 주어져 있던 포커스가 풀리게 되는 것이다.
따라서 리렌더링이 발생하더라도 변하지 않는 안정적인 key를 부여하는 것이 중요하다.
'Frontend' 카테고리의 다른 글
| query 기반 필터 안전하게 가져오는 방법 (0) | 2025.10.13 |
|---|---|
| 얕은 복사(shallow copy) vs 깊은 복사(deep copy) (0) | 2025.09.08 |
| Zod z.preprocess 활용 (1) | 2025.08.14 |
| 공통 로직 처리: 커스텀 훅 호출 위치 (1) | 2025.07.08 |
| Skeleton UI를 이용한 로딩 상태 개선 (1) | 2025.06.16 |