자동 배칭(Automatic Batching)

332025년 09월 17일4

React 19의 거대한 아키텍처 변화가 진행되는 동안, 팀은 사용자 경험뿐만 아니라 개발자 경험을 저해하는 오래된 문제들에도 눈을 돌렸다. 그중 하나는 바로 ‘상태 업데이트의 배칭(Batching)’ 동작 방식에 관한 것이었다.

배칭이란, 여러 개의 상태 업데이트를 하나로 묶어 단 한 번의 리렌더링으로 처리하는 React의 최적화 기법이다. 예를 들어, 하나의 클릭 이벤트 핸들러 안에서 setCountsetName을 연달아 호출해도, React는 두 업데이트를 모아서 마지막에 딱 한 번만 화면을 다시 그린다. 이는 불필요한 렌더링을 막아 성능을 향상시키는 핵심적인 기능이었다.

문제는 이 똑똑한 배칭이 오직 ‘React 이벤트 핸들러’ 내부에서만 동작한다는 점이었다.

조쉬 스토리는 팀원들에게 이 불일치로 인해 발생하는 문제를 보여주었다.

// 클릭 이벤트 핸들러 (배칭 O)
function handleClick() {
  setCount(c => c + 1); // 리렌더링 X
  setFlag(f => !f);     // 여기서 한번에 리렌더링 O
}

// setTimeout 콜백 (배칭 X)
setTimeout(() => {
  setCount(c => c + 1); // 여기서 한번 리렌더링!
  setFlag(f => !f);     // 여기서 또 한번 리렌더링!
}, 1000);

“보시다시피, 똑같은 두 개의 상태 업데이트가 어디서 호출되느냐에 따라 렌더링 횟수가 달라집니다. setTimeout이나 Promise의 콜백, 또는 네이티브 이벤트 리스너 안에서는 배칭이 동작하지 않아, 각각의 setState가 별개의 리렌더링을 유발합니다.”

이것은 개발자들에게 큰 혼란을 주었다. 성능 최적화를 위해 때로는 불안정한 래핑(wrapping) 함수를 사용해야 했고, 코드가 어디서 실행되느냐에 따라 동작이 달라지는 예측 불가능성은 버그의 원인이 되기도 했다.

“왜 이런 불일치가 존재해야 하는 거죠?” 한 엔지니어가 물었다.

“과거의 React는 자신이 통제하는 이벤트 핸들러가 ‘끝나는 시점’을 알 수 있었기 때문에, 그 시점에 묶어뒀던 업데이트를 처리할 수 있었습니다.” 앤드류가 설명했다. “하지만 setTimeout이나 Promise 같은 비동기 작업은 React의 통제 밖에서 일어나기 때문에, 언제 작업이 끝나는지 알 수 없어 매번 즉시 렌더링을 할 수밖에 없었습니다.”

하지만 React 18에서 동시성 렌더러가 도입되면서 상황이 바뀌었다. 새로운 렌더러는 업데이트를 즉시 처리하는 대신, 짧은 시간 동안 기다렸다가 함께 처리할 수 있는 능력을 갖추게 되었다. React는 더 이상 이벤트 핸들러의 종료 시점에 의존할 필요가 없었다.

React 19 팀은 이 새로운 능력을 활용하여, 오랫동안 이어진 불일치를 끝내기로 결정했다.

“모든 상태 업데이트는 기본적으로 배칭되어야 합니다. 어디서 호출되든 상관없이요.”

이 결정에 따라, React의 핵심 로직이 수정되었다. 이제 setTimeout, Promise, 네이티브 이벤트 핸들러 등 React의 통제 밖에서 일어나는 상태 업데이트조차도 자동으로 배칭 처리되었다. React는 업데이트가 발생하면 즉시 렌더링하는 대신, 아주 짧은 시간(마이크로태스크 큐가 비워질 때까지)을 기다렸다가 그동안 쌓인 모든 업데이트를 모아 단 한 번에 처리했다.

결과는 마법과도 같았다.

개발자들은 더 이상 상태 업데이트가 어디서 일어나는지 신경 쓸 필요가 없었다. 언제나 일관되게 배칭이 동작할 것을 기대할 수 있었다. 불필요한 리렌더링이 사라지면서 애플리케이션의 성능은 눈에 띄게 향상되었고, 코드는 더 예측 가능하고 견고해졌다.

이 ‘자동 배칭(Automatic Batching)’ 기능은 서버 컴포넌트처럼 거대한 변화는 아니었다. 하지만 그것은 React가 개발자의 사소한 고통까지도 외면하지 않고, 프레임워크의 일관성과 예측 가능성을 얼마나 중요하게 생각하는지를 보여주는 상징적인 개선이었다. 마법처럼 보이는 이 변화 뒤에는, 개발자를 복잡성으로부터 해방시키려는 팀의 집요한 노력이 숨어 있었다.