Automatic Batching

Taegwan·2023년 1월 5일
0

Batching이란 React 가 더 나은 성능을 위해 여러개의 state 업데이트를 한 번의 리렌더링으로 묶어서 진행하는 것을 말한다.

Automatic batching이 없다면 React의 이벤트 핸들러 내부에서만 batched 업데이트가 가능했다.

  1. Batching의 이점

    • 당연히 성능적인 이점이다.
    • 여러개의 state 업데이트마다 발생하는 불필요한 리렌더링을 막아주기 때문이다.
    • 레스토랑에서 종업원이 손님이 주문하는 첫번째 메뉴를 듣자마자 주방으로 달려가지 않듯이 Batching은 반드시 필요한 하나의 리렌더링을 수행한다.
  2. 그럼 Batching이 일어나지 않을 경우 어떤 문제가 생길 수 있을까?

    여러개의 state 업데이트가 동시에 일어나지 않을 때 발생할 수 있는 일을 생각해보자.

    예를 들어 줄다리기. 한쪽 진영의 힘이 더 세지면 균형이 무너지기 마련이다.

    이러한 점에서 teamA 와 teamB 라는 state를 통해서 각 진영의 힘의 크기를 나타냈다.

    const [teamA, setTeamA] = useState(10);
    const [teamB, setTeamB] = useState(10);

    또한 힘의 균형이 무너지면 게임이 종료됨을 표현하기 위해 다음과 같이 useEffect를 정의했다.

    useEffect(() => {
    	if(teamA !== teamB) navigator("/end")
    },[teamA, teamB])

    한 명 줄이기라는 버튼을 누르면 teamA와 teamB 각각의 값을 1씩 줄일 것이다.

    React 의 이벤트 핸들러에서는 auto batching 을 지원하기에 Promise 의 콜백함수 안에 이 동작을 정의해서 batching이 발생하지 않도록 해보겠다.

    const delay = (ms) => new Promise((resolve) => setTimeout(resolve,ms));
    
    const decrease = async () => {
        await delay(1500);
        setTeamA((a) => a - 1);
        setTeamB((b) => b - 1);
    };
    
    <button onClick={decrease}>한명 줄이기</button>

    기대한 실행결과는 1500ms 이후에 teamA 와 teamB의 값이 1씩 감소한 모습일 것이다.

    하지만 실제 결과는 게임이 종료되어버린다.

  3. 리렌더링 흐름

    • Auto Batching 이 적용되지 않았기 때문에 setTeamA 와 setTeamB 호출마다 리렌더링을 유발하게 된다.
    • 첫 번째 setTeamA 호출에 의해 teamA의 값이 먼저 변경된다. 10 → 9
    • useEffect 의존성 배열에 teamA 가 포함되어있기 때문에 useEffect의 콜백함수가 실행된다.
    • 아직 teamB의 값이 변경된 상태가 아니기 때문에 teamA ≠= teamB(9 ≠= 10)로 게임이 종료된다.

    Auto Batching 이 적용되지 않는다면 이러한 상황처럼 의도치 않은 버그를 마주할 수도 있다.

    React 에서는 이러한 상태를 컴포넌트의 업데이트가 “half-finished” 된 상태라고 정의한다.

    우리가 의도한 로직 중 절반만 완성된 state에 의해 컴포넌트가 올바르게 동작하지 않는 상태를 말한다.

    그러나 위에서의 상황 설명과 이론들은 결국 React 18 에서는 신경 쓰지 않아도 되는 부분이 되었다.

    그 이유는 Automatic Batching 이 도입되었기 때문이다.

    React18에서는 tiemout, promise, native 이벤트 핸들러 등 업데이트가 어느 곳에서 야기되었는지와 관계없이 Batching을 지원한다.

    Automatic Batching을 사용하기 위해서는 React18에서 새롭게 등장한 ReactDOMClient의 CreateRoot 메서드를 사용해야 한다.

    기존의 React의 entry 파일에서는 아래와 같이 정의하였다.

    ReactDOM.render(
        <React.StrictMode>
            <App />
        </React.StrictMode>,
      	document.getElementById('root')
    )

    React18부터는 createRoot를 사용해 다음과 같이 정의하면 된다.

    	import {createRoot} from "react-dom/client";
    	const root = createRoot(document.getElementById("root"));
        root.render(
            <React.StrictMode>
                <App />
            </React.StrictMode>
        )

    참고 : https://nukw0n-dev.tistory.com/33

profile
React를 사용하는 Front-End 개발자입니다.

0개의 댓글