Development Study/Frontend

[Automatic Batching] 리액트 18에 등장한 렌더링 묶음판매

TMInstaller 2023. 10. 6. 21:44
728x90

Automatic Batching이란 무엇일까?

[[React]] 18에서 나온 새로운 기능, Automatic Batching에 대한 문서이다

여기서 Batching이란, 이벤트 핸들러나 hooks 안에서 상태 업데이트를 묶어서 동작하도록 만들어준다. 즉, 렌더링을 한 번만 일어나도록 해주는 것이다

따라서 Automatic Batching = 자동화된 일괄 렌더링 이라고 생각할 수 있다

사실 생각해보면 이상하다. 기존의 함수 내부에서 여러 setState()를 실행시키면 이미 Batching이 이루어졌을까?

  • 그 이유는 React에는 여러 번의 state update 작업을 Queue에 몰아넣고 일정 주기마다 Queue에 등록된 작업을 순차적으로 일괄 시행하면서 불필요한 리렌더링을 방지하는 특징이 있기 때문이다

코드를 통해 확인해보자

이전에는 batching 이 각각의 React event 마다 한 번씩 이루어졌었다

// Before: only React events were batched.
setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will render twice, once for each state update (no batching)
}, 1000);

결국 위 코드에서는 각각의 이벤트들마다 렌더링이 되므로, 두 번 렌더링 되었다

  • setCount()에서 한 번
  • setFlag()에서 한 번 더

이는 불필요한 렌더링을 야기한다는 점에서 문제를 일으킨다

기존(~React 17)에는 오직 React의 이벤트 핸들러 내부의 state update 작업에 대해서만 batching이 가능했다. 이 말은 Promise, setTImeout, native event handler 내부의 작업은 불가능했었다

React 18버전으로 넘어가면서 리액트 이벤트 핸들러 내부 뿐만 아니라 promises, setTimeout, 여러 native event handler 안에서도 배칭이 적용된다

// After: updates inside of timeouts, promises,
// native event handlers or any other event are batched.
setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
}, 1000);

이렇게 되면 setTimeout() 안에있는 내용들에 Automatic Batching이 적용되면서 렌더링을 한번만 발생시킨다

발동조건

하지만 언제나 이렇게 되는 것은 아니라는 것을 주의해야 한다

  • .createRoot() method 를 사용했을때만 automatic batching이 적용된다
  • 만약 이전 레거시인 .render() method 를 사용할 경우 React 18이어도 적용되지 않는다

이 설정을 적용해서 사용하려면?
App.js 와 이를 설정하는 index.js 두 파일로 설명을 해보겠다

App.js

import React from 'react';

export default function App() {
  const [count, setCount] = React.useState(0);

  const handleClick = () => {
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increase Twice</button>
    </div>
  );
};

.createRoot() 사용 시 Automatic Batching 적용

추가로 [[React Concurrent Mode]]의 기능을 활용할 수 있다

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const root = document.getElementById('root');
const reactRoot = ReactDOM.createRoot(root);
reactRoot.render(<App />);

.render() 사용 시 Automatic Batching 적용

단, React 18버전 이상일 경우 이는 적용되지 않는다

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

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

+ 만약 Automatic Batching을 무시해야 하는 상황이라면?

react-dom 라이브러리에 추가된 ReactDOM.flushSync() 메서드를 사용한다

  • 단, React에서는 공식적으로 해당 메서드의 사용을 추천하지는 않는다
    • 필요한 상황이 있을 경우에만 사용할 것을 권장하고 있다

아래에는 이 메서드를 활용한 렌더링 확인용 Counter와 렌더링이 될 때마다 로그를 찍도록 하는 간단한 예제를 만들어보았다

// App.js
import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';

export default function App() {
  const [count, setCount] = React.useState(0);

  const handleClick = () => {
    ReactDOM.flushSync(() => { // 여기서 첫 렌더링
      setCount(prev => prev + 1);
    });
    setCount(prev => prev + 1); // 이 이후 일괄 적용 렌더링
    setCount(prev => prev + 1);
  };

  useEffect(() => {
    console.log('Component rendered with count:', count);
  });

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increase Thrice</button>
    </div>
  );
};

실제로 확인해 볼 경우 .flushSync() 가 적용되어있는 경우에는 두 번씩 렌더링되며, 이 메서드를 빼고 확인해볼 경우에 한 번씩 렌더링되는 것을 볼 수 있다!

 

위의 코드처럼 .flushSync()를 적용하자 15부터 2번씩 렌더링되는 모습을 볼 수 있다


References

728x90