react 성능개선 3

바다캔디·2025년 7월 1일

리액트공부

목록 보기
36/39

성능개선 3 : useTransition, useDeferredValue

1. 일관된 batching

batching : 무의미한 재렌더링 방지 기능.
state변경함수를 연달아서 여러 번 사용하면, 그 갯수만큼 렌더링이 일어남.
이걸 하나로 모아줌.
아래처럼 3번의 state 변경함수가 연달아 실행된다면,
이걸 마지막 변경함수 때 한 번만 재렌더링시켜줌.

setName("kim");
setCount(2);
setIsExist(true);

17버전 이전에는 ajax요청, setTimeout안에 state변경함수가 있는 경우에는 이 batching이 동작하지 않았음. 일관되지 않음.
그러나 18버전 이후 부터는 어디 있든 간에 재렌더링은 마지막에 1번만 변경됨. 일관됨.

의도적으로 재렌더링 시켜야하는 부분이 있다면?

batching 되는게 싫고 state변경함수 실행마다 재렌더링시키고 싶으면
flushSync라는 함수를 사용해야함.

useTransition

리액트 18버전 이후 부터 렌더링 성능이 저하되는 컴포넌트에서 쓸 수 있는 혁신적인 기능이 하나 추가됨.
useTransition 이라는 hook인데, 이걸로 오래걸리는 부분을 감싸면 렌더링시 버벅이지 않게 해줌.
정확히는, 오래 걸리는 코드 실행 시점을 뒤로 미루어 전체적인 유저 경험을 끌어올리는 방식.

아래처럼, 사용자가 input창에 무언가를 입력하면 그 입력값을 가지는 div를 10000개 수정하는 코드가 있다 가정.
유저가 input창에 무언가를 타이핑하면 그 글자에 따라 div도 10000개 수정됨.
div를 1만개 렌더링해주느라 input도 많은 지연시간이 발생.
일단 타이핑한 결과가 바로바로 반응이 와야하는데, 이게 10000개 수정을 할라니까 input도 겁나 느리게 쳐짐.
이럴때 사용하는 게 useTransition.

import { useState } from "react";

let a = new Array(10000).fill(0);

function App() {
  let [name, setName] = useState("");

  return (
    <div>
      <input
        onChange={(e) => {
          setName(e.target.value);
        }}
      />
      {a.map(() => {
        return <div>{name}</div>;
      })}
    </div>
  );
}

useTransition 적용 시

useTransition hook을 react에서 가져온 뒤, isPending, startTransition이라는 변수, 함수 가져오기.
시간이 지체되는, 1만개 state 수정 부분을 startTransition로 감싸주면
그 부분을 나중에 처리. 일단 input은 빠르게 입력됨.

import { useState, useTransition } from "react";

let a = new Array(10000).fill(0);

function App() {
  let [name, setName] = useState("");
  let [isPending, startTransition] = useTransition();

  return (
    <div>
      <input
        onChange={(e) => {
          startTransition(() => {
            //오래걸리는 부분
            setName(e.target.value);
          });
        }}
      />

      {a.map(() => {
        return <div>{name}</div>;
      })}
    </div>
  );
}

isPending은 언제쓰나요?

startTransition() 으로 감싼 코드가 처리중일 때 true로 변하는 변수.
이러면 input이 먼저 빠르게 나타나고,
div수정이 아직 덜됐다면 '로딩중입니다' 가 떴다가 수정 다 되면 수정된 값이 화면에 나타남.

{
  isPending
    ? "로딩중입니다."
    : a.map(() => {
        return <div>{name}</div>;
      });
}

useDeferredValue

startTransition() 이거랑 용도가 같다.
state 아니면 변수하나를 hook에 집어넣어 사용.
그 변수 or state에 변동사항이 생기면 그걸 늦게 처리해줌.

let [name, setName] = useState("");
let state1 = useDeferredValue(name); // name state가 변동사항이 생기면 늦게 처리

결론

성능저하가 일어나고, 느린 부분에는 위 기능을 적용해보자.
그리고 그 전에 일단 한 페이지에 html이 너무 많다면
다른 컴포넌트로 빼놓고 lazy import 거는게 나을 수도 있다.

0개의 댓글