React 18 beta의 Automatic Batching, 실제로 얼마나 달라지나

배경

React 18 beta가 지난 6월 공개된 후 팀 내에서 도입 검토를 시작했다. concurrent rendering보다 먼저 눈에 들어온 건 automatic batching이었다.

기존에는 React 이벤트 핸들러 내부에서만 batching이 동작했다. setTimeout이나 Promise 내부의 setState는 각각 리렌더를 발생시켰다.

기존 동작 (React 17)

function handleClick() {
  fetch('/api/data').then(data => {
    setLoading(false);  // 리렌더 1
    setData(data);      // 리렌더 2
  });
}

위 코드는 2번의 리렌더가 발생했다. React DevTools Profiler로 확인하면 두 개의 commit이 기록된다.

React 18에서의 변화

function handleClick() {
  fetch('/api/data').then(data => {
    setLoading(false);
    setData(data);
    // 자동으로 batching됨, 리렌더 1회만 발생
  });
}

실제 프로젝트의 데이터 페칭 로직에 적용해봤다. 평균적으로 Promise 콜백 내 3~4개의 setState가 있었는데, 각각이 모두 batching 처리됐다.

성능 측정

대시보드 페이지의 초기 로딩 시나리오를 비교했다.

  • React 17: 12회 렌더
  • React 18: 5회 렌더

체감 성능 차이는 크지 않았지만, Profiler 상으로는 명확한 개선이 보였다.

flushSync로 opt-out

특정 상황에서 즉시 렌더링이 필요하면 flushSync를 사용할 수 있다.

import { flushSync } from 'react-dom';

flushSync(() => {
  setCount(count + 1);
});
// DOM이 즉시 업데이트됨
setFlag(true);

아직 실무에서 필요한 케이스는 없었다.

결론

코드 변경 없이 얻는 최적화라 마이그레이션 부담이 적다. concurrent features는 아직 학습 중이지만, automatic batching만으로도 18 도입을 검토할 가치가 있어 보인다.