가상 DOM과 key
- 가상 DOM의 역할:
- React의 가상 DOM은 실제 DOM의 가벼운 복사본입니다.
- 변경사항을 먼저 가상 DOM에 적용한 후, 실제 DOM과 비교하여 필요한 부분만 업데이트합니다.
- key의 목적:
- key는 React가 어떤 항목이 변경, 추가 또는 제거되었는지 식별하는 데 도움을 줍니다.
- key는 가상 DOM의 요소들을 구분하는 데 사용됩니다.
- index를 key로 사용할 때의 문제:
- index를 key로 사용하는 것 자체가 가상 DOM의 재연산을 유발하지는 않습니다.
- 문제는 리스트 항목의 순서가 변경되거나 항목이 추가/제거될 때 발생합니다.
index를 key로 사용할 때의 실제 문제
- 불필요한 리렌더링:
- 리스트 항목의 순서가 변경될 때, index를 key로 사용하면 React는 많은 컴포넌트를 불필요하게 리렌더링할 수 있습니다.
- 이는 성능 저하로 이어질 수 있습니다.
- 컴포넌트 상태 문제:
- 각 리스트 항목이 자체 상태를 가지고 있을 때, index를 key로 사용하면 항목의 순서 변경 시 상태가 엉뚱한 컴포넌트와 연결될 수 있습니다.
- 재조정(Reconciliation) 문제:
- React의 재조정 과정에서, index를 key로 사용하면 항목 추가/제거 시 React가 컴포넌트를 올바르게 식별하지 못할 수 있습니다.
예시
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo, index) => (
<li key={index}>
<input type="checkbox" />
{todo.text}
</li>
))}
</ul>
);
}
이 예시에서, 만약 리스트의 첫 번째 항목이 제거되면:
- 모든 나머지 항목의 index가 변경됩니다.
- React는 모든 항목이 변경되었다고 판단할 수 있습니다.
- 결과적으로, 모든 <li> 요소를 다시 렌더링할 수 있습니다.
- 체크박스의 상태같은 내부 상태도 잘못 유지될 수 있습니다.
해결책
- 고유한 ID 사용:
- 가장 좋은 방법은 각 항목에 고유한 ID를 부여하는 것입니다.
- 예:
function TodoList({ todos }) return ( <ul> {todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> ); }
2. 안정적인 고유 값 생성:
- 데이터에 고유 ID가 없는 경우, 안정적인 고유 값을 생성할 수 있습니다.
- 예를 들어, 항목의 내용과 인덱스를 조합하여 사용할 수 있습니다
:
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo, index) => (
<li key={`${todo.text}-${index}`}>{todo.text}</li>
))}
</ul>
);
}
3. 라이브러리 사용:
- uuid나 nanoid 같은 라이브러리를 사용하여 고유 ID를 생성할 수 있습니다.
- 예:
import { v4 as uuidv4 } from 'uuid';
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={uuidv4()}>{todo.text}</li>
))}
</ul>
)
주의사항
- 인덱스를 key로 사용하는 것이 항상 잘못된 것은 아닙니다. 리스트가 정적이고 재정렬되지 않는 경우에는 인덱스를 사용해도 괜찮습니다.
- 그러나 대부분의 경우, 고유한 ID나 안정적인 고유 값을 사용하는 것이 더 안전하고 효율적입니다.