잊지 않기 위해 포스팅 - rendering with key

Sal Jeong·2023년 7월 4일
0

가끔씩 사용하게 될 때마다 아 이거 어떻게 하더라? 하면서 인터넷을 검색하는 것은 참 좋지 않은 일이다.

const ExpCard = ({ expType }: { expType: (typeof experiences)[0] }) => {
  let content: JSX.Element;

  switch (expType) {
    case experiences[0]:
      content = (
        <MotionedDiv
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ delay: 0.1 }}
          className="w-full"
        >
          <h3 className="text-text-light dark:text-text-dark flex gap-1 font-medium text-xl font-title-font">
            Frontend Lead{' '}
            <span className="text-body-color-light dark:text-text-green tracking-wide">
              @Ibstech
            </span>
          </h3>
          <p className="text-sm mt-1 font-medium text-text-dark">
            Oct 2022 - June 2023
          </p>
          <ul className="mt-6 flex flex-col gap-3">
            <li className="text-base flex gap-2 text-text-light dark:text-text-dark">
              <span className="mt-1 dark:text-text-green text-body-color-light">
                <TiArrowForward />
              </span>
              <p>
                Wrote quality, robust code for a medical-exam managing web apps
                collaborating with colleges including Chonnam, Pusan.
                Collaborating with associates through collaborating and sharing
                & discussing knowledge about modern web structure.
              </p>
            </li>
          </ul>
        </MotionedDiv>
      );
      break;
...

export default React.memo(ExpCard);

위 컴포넌트는 사실 버튼을 누를 때마다 새로 렌더링되면서 이니셜 애니메이션을 플레이해야 하지만,

{switchState && <ExpCard expType={switchState} />}

실제로 위 코드에 따르면 expType이라는 프롭이 변경되는 것 뿐이기 때문에 사라졌다가 렌더링되는 것이 아니라, 안의 데이터가 달라져서 보이는 형태가 달라질 뿐 애니메이션이 다시 재생되지는 않는다.

간단하게 해결하고 싶다면

<div>
  {switchState === experiences[0] && <ExpCard expType={experiences[0]} />}
  {switchState === experiences[1] && <ExpCard expType={experiences[1]} />}
</div>

이런 식으로 아예 조건에 따라 같은 컴포넌트에 다른 prop을 넣어버리는 방법으로 문제를 해결할 수도 있겠지만 이 경우 if 처리를 부모 컴포넌트에서 한번 더 진행하기 때문에 그렇게 최적화되지 않는다는 것은 알 수 있겠다.

조금 더 나은 방법을 찾기 위해 react docs를 다시 한 번 보자.

https://react.dev/learn/rendering-lists

Why does React need keys? 
Imagine that files on your desktop didn’t have names. Instead, you’d refer to them by their order — the first file, the second file, and so on. You could get used to it, but once you delete a file, it would get confusing. The second file would become the first file, the third file would be the second file, and so on.

File names in a folder and JSX keys in an array serve a similar purpose. They let us uniquely identify an item between its siblings. A well-chosen key provides more information than the position within the array. Even if the position changes due to reordering, the key lets React identify the item throughout its lifetime.

리액트에서는 컴포넌트를 파일과 같이 취급하는데, 빠르게 리스트 추가 & 수정 및 삭제를 하기 위함이다.
이를 위해서 react에서는 key를 파일 이름으로 본다. 만약 렌더할 리스트에 변화가 일어날 경우, 리액트에서는 위의 key를 기억해서 더 빠르게 렌더할 수 있도록 한다.

여기서 중요한 점은, react에서 일반적으로 퍼포먼스를 위해 사용하는 React.memo나 useMemo처럼 key를 react가 기억하고 있다는 점이다. 그리고, key는 conditional rendering에서도 사용할 수 있기 때문에, 실제로는 이렇게 사용할 수 있다.

...
{switchState && <ExpCard expType={switchState} key={switchState} />}
...

그렇다면 위와 같은 경우의 퍼포먼스를 직접 체크하는 것이 도움이 될 것이다.

최근에 Vue나 Svelte와 같은 다른 프레임워크를 사용해 보면서 느낀 것이 있는데 React dev tool을 따라갈 만한 것이 없다는 것이었다.

사실 아직도 저 위 내용을 정확하게는 모른다.

RAF를 이용해서 performance에 로그를 기록해 렌더링 타임을 측정하는 방법이나, 각 프레임워크에서 제공해주는 툴을 사용하는 방법 등 사용해 보았지만, react dev tool보다 만족스러운 것은 없었다. 그래서 당연히 react dev tool로 렌더링 시간을 측정해 본다면

...
{switchState && <ExpCard expType={switchState} key={switchState} />}
...

의 경우 평균 render 시간은 0.8, 1.4, 1.8로 1.33333...ms인 반면

<div>
  {switchState === experiences[0] && <ExpCard expType={experiences[0]} />}
  {switchState === experiences[1] && <ExpCard expType={experiences[1]} />}
</div>

의 경우

1.3, 1.7, 2.2로 1.7333....ms가 되는것을 알 수 있었다.

위 예시에서 실제로 시간 차이는 거의 나지 않았다.

하지만 중요한 것은 위의 컴포넌트들의 양이 늘어나고, 실제로 그리게 되는 컴포넌트의 코드가 복잡해질 수록 위 시간 차이는 더욱 커질 것이므로 최대한 나은 방법을 처음부터 적용하는것이 좋다는 것이다.

profile
Can an old dog learn new tricks?

0개의 댓글