React 18 알파 버전 - Automatic Batching 살펴보기
배경
어제 React 18 Working Group에서 알파 버전이 공개되었다. 가장 눈에 띄는 변경사항 중 하나가 Automatic Batching이었다. 지금까지는 React 이벤트 핸들러 내부에서만 setState가 자동으로 배칭되었는데, 이제는 Promise, setTimeout 내부에서도 배칭이 적용된다고 한다.
기존 동작 방식
React 17까지는 이런 코드가 두 번의 렌더링을 발생시켰다.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// 각각 렌더링 발생
}, 1000);
반면 이벤트 핸들러 내부에서는 배칭이 되어 한 번만 렌더링되었다.
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// 한 번만 렌더링
}
React 18의 변화
알파 버전을 설치해서 테스트해봤다. createRoot API를 사용하면 모든 경우에 자동 배칭이 적용되었다.
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
이제 setTimeout, Promise, 네이티브 이벤트 핸들러 어디에서든 여러 setState를 호출해도 한 번만 렌더링된다.
성능 측정
실제 대시보드 프로젝트에서 API 응답 후 여러 상태를 업데이트하는 부분에 적용해봤다. React DevTools Profiler로 측정한 결과 렌더링 횟수가 확실히 줄어들었다.
fetch('/api/data')
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
setError(null);
// React 17: 3번 렌더링
// React 18: 1번 렌더링
});
주의사항
만약 배칭을 원하지 않는 경우 flushSync를 사용하면 된다.
import { flushSync } from 'react-dom';
flushSync(() => {
setCount(c => c + 1);
});
// 즉시 렌더링
setFlag(f => !f);
아직 알파 버전이라 프로덕션에 적용하기는 이르지만, Concurrent 기능들과 함께 정식 릴리즈되면 유용할 것 같다.