Tech

Diary

Lecture

개발중

About Me

개발중

React key

JeongSeulho

2023년 06월 02일

준비중...
클립보드로 복사
thumbnail

0. 들어가며

map을 사용하여 컴포넌트를 렌더링 하는 경우가 많은데, 이때 key가 왜 필요하고 왜 인덱스를 권장하지 않는지 알아보자.

1. V-DOM 비교시 최적화

리액트는 current V-DOMworkInProgress V-DOM을 가지고 있다. 여기서 두 V-DOM을 비교하여 변경된 부분만 실제 DOM에 반영한다.
이때, key 속성은 이 2개의 V-DOM을 비교할 때 사용된다, 2개의 V-DOM에서 key를 기준으로 비교하여 변경된 부분만 반영할 수 있도록 한다.

(1) key가 없는 경우

copy
// current V-DOM
<ul>
  <li>first</li>
  <li>second</li>
</ul>

// workInProgress V-DOM
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

위와 같은 V-DOM을 가정하면 key가 없어도 2개의 V-DOMindex기준으로 순회하면 <li>third</li>만 추가하면 되는 것을 알 수 있다.
이 경우 <li>third</li>에대해서만 새로운 fiber node를 생성하고 나머지는 재사용하여 최적화 할 수 있다.

copy
// current V-DOM
<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

// workInProgress V-DOM
<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

다음과 같이 index = 0<li>Connecticut</li>를 추가하며 key가 없는 경우 <li>Duke</li><li>Villanova</li>를 모두 새로운 fiber node로 생성해야 한다.

(2) key가 있는 경우

copy
// current V-DOM
<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

// workInProgress V-DOM
<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

위와 같이 key를 사용하면 두 V-DOMkey를 기준으로 매칭하여 변경된 부분만 새로운 fiber node를 생성하고 나머지는 재사용하여 최적화 할 수 있다.

(3) key에 index를 지양하는 이유

map을 사용한 컴포넌트 렌더링에서 keyindex를 사용하는 것은 권장하지 않는다. 리스트 마지막에 새로운 항목을 추가하는 경우 문제가 없지만, 순서가 바뀌거나 중간에 항목을 추가, 삭제하는 경우 의도대로 두 V-DOM을 매칭할 수 없다.

2. fiber node 재사용의 의미

fiber node를 재사용하는 것은 컴포넌트가 동일하다는 뜻이며, 컴포넌트의 정보(상태)를 유지하는 것을 의미한다.
key 속성은 2개의 V-DOM에서 컴포넌트가 동일한지 판단하는 기준 중 하나이다. 또 다른 기준으로는 컴포넌트의 type과 위치가 있다. 동일한 컴포넌트(type)이어도 UI 트리에서 위치가 다르면 다른 컴포넌트로 판단한다.
즉 상태가 유지되지 않는다. 이에 대한 예시는 다음과 같다.

(1) 동일한 위치의 동일한 타입의 컴포넌트

copy
     {isFancy ? (
        <Counter isFancy={true} /> 
      ) : (
        <Counter isFancy={false} /> 
      )}

타입이 같고 위치가 같으므로 동일한 컴포넌트로 판단하며 상태가 유지된다.

copy
  if (isFancy) {
    return (
      <div>
        <Counter isFancy={true} />
        <label>
          <input
            type="checkbox"
            checked={isFancy}
            onChange={e => {
              setIsFancy(e.target.checked)
            }}
          />
          Use fancy styling
        </label>
      </div>
    );
  }

  return (
    <div>
      <Counter isFancy={false} />
      <label>
        <input
          type="checkbox"
          checked={isFancy}
          onChange={e => {
            setIsFancy(e.target.checked)
          }}
        />
        Use fancy styling
      </label>
    </div>
  );

이 또한 타입이 같고 위치가 같으므로 동일한 컴포넌트로 판단하며 상태가 유지된다.

(2) 동일한 위치의 다른 타입의 컴포넌트

copy
      {isFancy ? (
        <div>
          <Counter isFancy={true} /> 
        </div>
      ) : (
        <section>
          <Counter isFancy={false} />
        </section>
      )}

이 예시를 보면 Counter 컴포넌트의 위치는 동일 하지만 상위 태그의 타입이 다르므로 다른 컴포넌트로 판단한다.

(3) 의도적으로 다른 컴포넌트로 판단하고 싶은 경우

copy
      {isPlayerA &&
        <Counter person="Taylor" />
      }
      {!isPlayerA &&
        <Counter person="Sarah" />
      }

위 예시는 트리상 위치가 다르므로 다른 컴포넌트로 판단한다. image

copy
      {isPlayerA ? (
        <Counter key="Taylor" person="Taylor" />
      ) : (
        <Counter key="Sarah" person="Sarah" />
      )}

또한 다른 key를 사용하여 다른 컴포넌트로 판단하도록 유도 할 수 있다.

4. 마치며

key는 개발자가 해당 컴포넌트가 리렌더 이전과 이후 동일한 컴포넌트인지 알려주는 역할을 한다.
필요에 따라 fiber node를 재사용하여 최적화를 하거나 의도적으로 다른 컴포넌트로 판단하도록 유도하여 상태를 초기화 할 수 있다.

출처
goidlereact.dev.learnko.legacy.reactjsRobin Pokorny