React의 key, 제대로 알고 사용하자!
key prop이 React 성능에 미치는 영향과 올바른 사용법에 대해 알아보자
2024.11.10
20분 소요
글을 시작하며
React 개발을 하다 보면 반드시 마주치게 되는 경고 메시지가 있습니다.
바로 "Warning: Each child in a list should have a unique 'key' prop." 입니다.
단순히 경고를 없애기 위해 key를 넣는 것이 아닌, 왜 key가 필요한지 그리고 어떻게 사용해야 하는지 자세히 알아보려 합니다.
key가 필요한 이유
Virtual DOM의 비교 과정 최적화
React는 Virtual DOM을 사용하여 실제 DOM 업데이트를 최적화합니다. 이 과정에서 key는 매우 중요한 역할을 합니다.
재조정 과정은 이전 가상 DOM 트리와 새로운 가상 DOM 트리를 비교하여 변경된 부분만 실제 DOM에 반영하는 과정입니다. 이는 최소한의 변경 사항만 파악하고 실제 DOM에 반영하기 위함입니다.
key는 각 리스트 항목을 고유하게 식별하는데 사용되며, 어떤 항목이 변경되었는지 빠르게 추적할 수 있습니다. 이를 통해 변경된 부분만 업데이트 하므로 재조정 과정을 효율적으로 수행할 수 있습니다.
// key가 없는 경우
<ul>
<li>사과</li>
<li>바나나</li>
</ul>
// 맨 앞에 '포도' 추가 시
<ul>
<li>포도</li>
<li>사과</li>
<li>바나나</li>
</ul>
key가 없다면, React는 모든 li 요소가 변경되었다고 판단하여 불필요한 리렌더링이 발생합니다.
- 첫 번째 요소에서 "사과"와 "포도"가 다르므로 리액트는 첫 번째 요소를 업데이트합니다.
- 두 번째 요소에서 "바나나"와 "사과"가 다르므로 두 번째 요소를 업데이트합니다.
- 새로운 요소 "바나나"를 발견하고 DOM에 새 요소를 추가합니다.
// key를 사용한 경우
<ul>
<li key="apple">사과</li>
<li key="banana">바나나</li>
<li key="orange">오렌지</li>
</ul>
// 맨 앞에 '포도' 추가 시
<ul>
<li key="grape">포도</li>
<li key="apple">사과</li>
<li key="banana">바나나</li>
<li key="orange">오렌지</li>
</ul>
key를 사용하면 React는 각 요소를 정확히 식별할 수 있어, 새로운 요소만 추가하고 나머지는 그대로 유지합니다.
자식 요소의 순서가 바뀌더라도 key를 통해 React는 각 요소를 식별할 수 있습니다. 동일한 타입과 key를 갖는다면 재조정 과정에서 비교 작업을 수행할 때 동일한 요소가 사용된다는 것을 알 수 있기 때문에 더 적은 비용으로 최소한의 변경만 실제 DOM에 반영하고 연산 작업을 수행할 수 있습니다.
컴포넌트의 상태 유지
key는 컴포넌트의 identity를 유지하는 데 중요한 역할을 합니다.
const TodoList = ({ todos }) => {
return (
<ul>
{todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
/>
))}
</ul>
);
};
각 TodoItem 컴포넌트는 고유한 key를 가짐으로써 각 컴포넌트 인스턴스를 식별하고 추적할 수 있습니다.
- 컴포넌트의 local state가 보존
- 불필요한 리렌더링을 방지
- 애니메이션이나 트랜지션이 의도대로 동작
key 사용 시 주의사항
index를 key로 사용하지 말 것
// ❌ 잘못된 사용
{
todos.map((todo, index) => (
<TodoItem
key={index}
todo={todo}
/>
));
}
// ✅ 올바른 사용
{
todos.map((todo) => (
<TodoItem
key={todo.id}
todo={todo}
/>
));
}
index를 key로 사용하면
- 항목 순서가 바뀌거나 중간에 항목이 삽입/삭제될 때 index로 지정한 key도 변경되기 때문에 문제가 발생
- 그 결과, 항목을 올바르게 추적하지 못하게 되고 의도하지 않은 방식으로 동작할 수 있음
key를 지정하지 않으면 React는 기본적으로 index를 key로 사용합니다. 항목들이 재배열되지 않고 상태를 갖지 않는다면 정상 작동할 수 있습니다.
고유하고 안정적인 key 사용하기
// ❌ 잘못된 사용
<TodoItem key={Math.random()} />
// ✅ 올바른 사용
<TodoItem key={`${todo.id}-${todo.createdAt}`} />
Math.random()을 키로 사용하면 매 렌더링마다 새로운 key가 생성되기 때문에 고유한 값이 공유되지도 않을뿐더러 예측할 수도 없습니다. React는 매번 해당 자식 요소들을 새로운 요소로 간주하게 되어 불필요한 리렌더링이 발생할 수 있습니다.
따라서 key는 다음과 같은 조건을 충족해야 합니다.
- 형제 요소 사이에서 고유해야 함
- 렌더링이 일어나도 변하지 않아야 함
- 예측 가능해야 함
결론
key prop은 단순한 React 경고 메시지를 없애기 위한 것이 아니며 React 애플리케이션의 성능과 정확성을 보장하는 중요한 요소입니다.
올바른 key 사용은 다음과 같은 이점을 제공합니다.
- Virtual DOM 비교 과정을 최적화
- 컴포넌트의 상태를 올바르게 유지
- 불필요한 리렌더링 방지
참고 자료