React
에서는 Vanilla JS
와 다르게 리스트를 다룰 때 key prop
을 필수로 요구한다.
Key
를 필요로 하는 이유는 뭔지, 기존에 사용하던 리스트와 어떻게 다른지 예제를 통해 알아보자.
React
는 리스트를 생성할 때, JSX
문법으로 작성한 리스트 엘리먼트들을 담고있는 배열
을 이용해서 리스트를 생성한다.
간단한 리스트 예제를 통해 살펴보자.
ListComponent
컴포넌트는 props
로 배열을 받는다.
그리고 해당 배열에 있는 요소들을 Array.map
메서드를 이용해 순회하며 JSX
문법으로 재가공한 뒤, ul
태그의 내부에 배열 형태로 넘겨주게 된다.
결과는 아래와 같다.
배열에 저장되어있던 li element
가 자동으로 전개되어 렌더링 된 것을 볼 수 있다.
이렇게 하나의 컴포넌트에서 모두 처리해도 되지만, 아래처럼 listItem
을 별도의 컴포넌트로 분리해서 사용하는 것도 가능하다.
결과는 위에서 작성했던 코드와 동일하다.
예제에 사용한 코드들은 React StrictMode
에서 실행하면 아래처럼 리스트의 각 항목에 key prop
을 필요로 한다는 경고가 출력된다.
해결하려면 아래처럼 각각 리스트 엘리먼트에 고유한 key
를 정의해주면 된다.
그럼 리스트에서 key
가 필요한 이유는 무엇인지 자세히 알아보자.
React
는 효율적인 렌더링을 하기 위해 비교 알고리즘(Diffing Algorithm)
을 이용해서 이전 트리와 현재 트리를 비교하고, 동일한 내용은 유지하며 변경된 내용만을 계산해서 최종적으로 렌더링을 수행한다.
(자세한 내용은 재조정 (Reconciliation) - React 를 참고하자.)
이 과정은 루트 노드부터 비교해서 자식 노드까지 재귀적으로 수행되는데, 리스트의 경우 새로운 요소가 리스트의 최상단에 추가되면 이전 노드와 다르다고 판단해서 하위에 있는 모든 자식 요소까지 다시 렌더링한다.
이렇게 불필요한 렌더링을 막기 위해서 key prop
을 통해 리스트 엘리먼트에 고유한 key
를 지정하고, key
의 비교를 통해서 리스트 엘리먼트를 렌더링한다.
또한, key
값은 형제 리스트 엘리먼트 사이에서 고유한 값을 가져야 하며, 문자열 형태로 지정한다.
만약 명시적으로 key prop
을 정의하지 않았다면 자동적으로 배열의 index
를 key
값으로 사용하게 되는데, React
는 index
를 key
로 사용하는 방법은 권장하지 않는다.
왜 그런지 아래의 예제에서 살펴보자.
배열의 index
를 key
로 사용하는 간단한 예제다.
버튼을 누르면 기존에 있던 list
배열의 index가 0인 위치({ number : 'one' }
이 있던 곳)에 새로운 값을 추가한다.
확인을 위해 가장 위에 있는 input
엘리먼트에 아무 값이나 입력하고, 버튼을 눌러 list
배열에 새로운 값을 추가해보자.
one의 input
에서 작성한 데이터가 새로 추가된 four의 input
으로 매핑된 것을 볼 수 있다.
이처럼 재배열 될 수 있는 배열의 index
를 key
로 사용하면 원하지 않는 UI
버그가 생길 수 있기 때문에 주의해야 한다.
그렇기 때문에 가능하면 배열의 index
를 key
로 사용하는 상황은 피하고, 아래처럼 고유한 id
를 key
로 사용하도록 하자.
고유한 key
를 이용하면 버그 없이 정상적으로 동작하는 것을 볼 수 있다.
참고 자료
리스트와 Key - React
https://ko.reactjs.org/docs/lists-and-keys.html
재조정 (Reconciliation) - React
https://ko.reactjs.org/docs/reconciliation.html
Index as a key is an anti-pattern
https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318