[React] 배열 렌더링 시 key를 사용해야하는 이유

Junyeong Kim·2024년 2월 7일
0

개발

목록 보기
10/16
post-thumbnail

리액트로 개발을 하면서, 위와 같은 경고 메시지를 만날 수 있다.

보통 배열 데이터의 요소를 map 함수를 통해 렌더링할 때, key 프로퍼티를 지정해주지 않았을 때 나타난다.

사실 지금까지는 크게 신경쓰지 않고 개발을 해왔던 터라, 이번 기회에 왜 이러한 경고가 나타나고, key 사용을 지향해야하는지 정리해본다.

Key를 사용하지 않으면 어떤 문제가 발생할까?

UI 관점

function ReviewList({ items, onDelete }) {
  return (
    <ul>
      {items.map((item) => {
        return (
          <li>
            <ReviewListItem item={item} onDelete={onDelete} />
            <input />
          </li>
        );
      })}
    </ul>
  );
}

items 배열의 요소들로 만드는 li 태그들에 key를 주지 않은 코드이다.
각 item들을 가지고 생성한 영화 정보 컴포넌트와, 해당 컴포넌트들에 대한 input으로 한 세트(?)가 구성되어있다.

내가 좋아하는 위대한 쇼맨 영화에 대한 input에 리뷰를 작성중이다.
작성 중에 위에 있는 언더 워터 영화는 너무 무서워서 삭제해버렸다.

그런데 언더워터 영화를 삭제하니, 내가 작성 중이던 위대한 쇼맨에 대한 input이 이상한 곳으로 이동했다.
정확히 말하면, 언더워터 영화를 삭제하면서 위대한 쇼맨 영화가 위로 밀려 올라간 것이다.

이렇게 key를 지정해주지 않으면, 요소들의 순서가 바뀌면서 다른 요소가 의도치 않은 엉뚱한 위치에 배치되어
한 세트인 요소들이 꼭 붙어있지 않는 부작용이 일어날 수 있다.

성능 관점

<>
	<Movie title="언더 워터">
	<Movie title="위대한 쇼맨">
</>

위와 같은 영화 요소들이 있을 때, 맨 끝에 한 영화를 더 추가해보자.

<>
	<Movie title="언더 워터">
	<Movie title="위대한 쇼맨">
   <Movie title="카페 벨에포크">
</>

리액트는 DOM 요소의 변화를 감지하고, 감지한 부분에 대해서만 변경을 생성한다.
위의 경우 언더 워터, 위대한 쇼맨은 일치하기 때문에 카페 벨에포크에 대한 변경을 생성한다.

<>
   <Movie title="카페 벨에포크">
	<Movie title="언더 워터">
	<Movie title="위대한 쇼맨">
</>

하지만 위와 같이 맨 위에 새로운 영화를 추가해주었을 때는,
밑의 언더 워터, 위대한 쇼맨이 이전과 동일하게 있음에도 전체 요소들이 변화한 것으로 보고,
모든 영화들에 대해 변경을 생성한다. 비효율적이다.

이와 같이 배열의 맨 앞에 엘리먼트를 추가할 경우 뒤의 요소들이 모두 일치하더라도 모든 요소들이 변화한 것으로 간주하여 불필요한 리렌더링을 하게 된다.

Key를 사용해야하는 이유

key를 사용해야 하는 이유 첫번째는 UI 관점에서의 부작용을 방지하기 위함이다.
key를 주고 위의 실험을 다시 진행해보자.

...
      {items.map((item) => {
        return (
          <li key={item.id}>
            <ReviewListItem item={item} onDelete={onDelete} />
            <input />
          </li>
...


위대한 쇼맨 영화의 위에 있는 언더워터 영화를 삭제해도, 작성하던 위대한 쇼맨 리뷰 input이 제자리에 잘 있다.

key를 사용해야하는 두번째 이유는 배열의 요소가 어떻게 바뀌었는지 직관적으로 확인할 수 있기 때문이다.
예를 들어 보자..

위와 같이 key가 존재하지 않는 [사과, 망고, 포도] 배열이 [사과, 포도]로 바뀌었을 때,

  • 단순히 망고만 삭제한건지,
  • 포도를 삭제하고 망고를 포도로 바꾼건지,

어떤 작업을 한건지 명확하게 확인하기가 어렵다.

반면에 아래와 같이 key를 지정해주면

key값이 2인 망고가 사라졌군, 망고를 삭제한거구나! 하고 직관적으로 알 수 있다.

만약 [ {key : 1, '사과'}, {key : 2, '포도'}] 와 같은 형태라면,
포도를 삭제하고 망고를 포도로 바꾼거구나, 알 수 있다.

이렇듯 key를 사용하면 배열의 변화를 리액트에 정확히 전달할 수 있다.
결과적으로 리액트가 key값을 기준으로 전체 요소의 변화를 파악할 수 있으므로, 성능 면에서의 문제를 해결할 수 있다.

Key값은 뭘로 줘야할까?

key 값은 배열 안에서 각 데이터를 고유하게 나타낼 수 있는 값이어야 한다.
한 key는 한 개의 데이터만 가리켜야 한다.

이 때 주의해야할 점은 index의 사용은 적합하지 않을 수 있다는 것이다.
언뜻 생각해보면, 배열의 인덱스는 각 요소를 고유하게 나타내므로 key값으로 적절하지 않을까? 라는 생각이 든다.

...
      {items.map((item, index) => {
        return (
          <li key={index}>
            <ReviewListItem item={item} onDelete={onDelete} />
            <input />
          </li>
...

하지만 위와 같이 index 값을 주고 실험을 진행해보면, 첫번째 실험(key를 안주었을 때)과 동일한 결과가 나타난다.

이러한 부작용은 배열의 요소가 바뀜에 따라 일어나는 현상인데,
index(배열의 순서값)는 배열의 요소가 달라짐에 따라 달라지는 값이기 때문이다.

따라서 위대한 쇼맨 영화의 인덱스가 처음에는 11번 이었어도,
언더워터 영화를 삭제하면 위대한 쇼맨 영화의 인덱스는 10번이 되어 버리는 것이다.

배열이 절대 변하지 않고, 따로 줄만한 id 값이 없을 때는 index 값을 key로 사용할 수 있겠지만,

결론적으로 가장 적합한 key 값은 고유한 값인 동시에, 어떤 변화가 일어나도 절대 변경되어서는 안되는 값이다.
id, 학번, 고객코드,, 등등의 예시를 들어볼 수 있겠다.

Key를 주는 위치

key는 보통 위의 예시와 같이 map 안에서 리턴하는 태그들을 감싸는 가장 바깥쪽 (ex. <li>) 태그에 지정한다.

참고

[React] key 값을 쓰는 이유(1)
key를 주지 않았을 때 일어나는 부작용에 대해 자세한 실험을 기록해주신 블로그이다.

profile
개발자가 되고싶어?? 네

0개의 댓글

관련 채용 정보