React의 Batching과 Automatic Batching
사내 지식 공유 시간에 다뤘던 React의 Batching과 Automatic Batching에 대한 내용을 정리해보려고 합니다.
React에서
setState
를 호출하면 컴포넌트가 다시 렌더링되는데, 여러 개의 상태 업데이트가 동시에 발생하면 어떻게 처리될까요?이 글에서는 Batching의 개념과 동작 방식을 알아보고, React 18에서 도입된 Automatic Batching이 어떻게 성능을 최적화하는지 예제와 함께 살펴보겠습니다.
1. React의 Batching이란?
🧐 Batching이란 무엇일까?
- Batching(배칭)은 영어 단어 batch에서 온 개념으로,
여러 개의 작업을 한 번에 묶어 처리하는 방식
을 의미합니다.
예를 들어, 음식 배달을 할 때 가게에서 주문이 들어올 때마다 한 건씩 바로 출발하는 것이 아니라,
"배달 건을 여러 개 모아서 한 번에 배달하는 방식"
이 훨씬 효율적이겠죠?
React에서도 이와 같은 원리로 여러 개의 상태 업데이트를 묶어서 한 번의 렌더링으로 처리하는 Batching(배칭) 기법을 사용합니다.
🛠 React의 Batching 개념
React에서는
setState
를 호출하면 컴포넌트가 다시 렌더링됩니다. 하지만 여러 개의 상태 업데이트가 연속적으로 실행될 경우, React는 이를 Batching(배칭)하여 한 번의 렌더링으로 묶어 처리합니다.이를 통해 불필요한 렌더링을 방지하고 성능을 최적화할 수 있습니다.
✅ React 18 이전 vs 이후의 차이점
React 18에서는 이 개념이 더욱 발전하여 Automatic Batching(자동 배칭)이 적용되었습니다.
- React 17 이하: 이벤트 핸들러 내부에서만 Batching이 동작 (비동기 코드에서는 개별 렌더링 발생)
- React 18: 모든 상태 업데이트를 자동으로 배칭 (비동기 코드에서도 배칭 적용)
🎯 Batching을 이해하는 간단한 예제
예제 1: 초기 렌더링 시 console.log 출력값
다음 코드를 실행하면 console.log에는 어떤 값이 출력될까요?
import React, { useState, useEffect } from "react"; export default function App() { const [count, setCount] = useState(0); useEffect(() => { console.log("count", count); }, [count]); return ( <div> <p>Count: {count}</p> <button>버튼 클릭</button> </div> ); }
🧐 정답
초기 렌더링 시,
console.log("count", count);
는 count
의 초기값인 0을 출력합니다.따라서, count 0이 출력됩니다.
2. 버튼 클릭 시 상태 업데이트와 Batching
그렇다면, 다음 코드를 실행한 후 버튼을 클릭하면 console.log에는 어떤 값이 출력될까요?
그리고 console.log는 몇 번 출력될까요?
예제 2: 상태 업데이트 배칭 테스트
import React, { useState, useEffect } from "react"; export default function App() { const [count, setCount] = useState(0); const handleClick = () => { setCount((count) => count + 1); setCount((count) => count + 1); setCount((count) => count + 1); }; useEffect(() => { console.log("count", count); }, [count]); return ( <div> <p>Count: {count}</p> <button onClick={handleClick}>버튼 클릭</button> </div> ); }
🧐 정답
버튼 클릭 시,
setCount
가 3번 호출됩니다.하지만 React는 배칭 처리를 수행하여 상태 업데이트를 한 번의 렌더링에서 묶어 처리합니다.
setCount((count) => count + 1)
을 3번 실행하면 최종적으로 count
는 3 증가합니다.따라서,
console.log("count", count);
는 3을 출력합니다.useEffect
는 count
값이 변경될 때만 실행되므로, console.log는 한 번만 출력됩니다.
📌 즉, 정답은 다음과 같습니다.
console.log
출력 값: "count 3"
console.log
실행 횟수: 1번
3. 왜 이렇게 동작할까? (React의 Batching 원리)
이 코드에서
setCount
를 3번 호출했음에도 불구하고, console.log
가 한 번만 실행된 이유는 React의 Batching 덕분입니다.React의 Batching 동작 방식
- React는
setState
가 여러 번 호출되더라도, 동일한 이벤트 핸들러 내에서는 한 번의 렌더링으로 묶어 처리합니다.
- 이를 통해 불필요한 렌더링을 방지하고 성능을 최적화합니다.
- 상태 업데이트가 함수형 업데이트(
setCount((count) => count + 1)
)로 이루어지면, 이전 상태를 기반으로 정확한 값이 계산됩니다.
- 최종적으로
count
는 0 → 3으로 업데이트되며,console.log
는 한 번만 실행됩니다.
📌 만약 React가 배칭을 하지 않는다면?
React가 배칭을 수행하지 않는다면,
setCount
가 호출될 때마다 렌더링이 발생하여 console.log
는 다음과 같이 여러 번 출력될 것입니다.count 1 count 2 count 3
하지만 React는 이를 배칭 처리하여 한 번의 렌더링으로 묶어 실행하므로, 최종적으로 count 3만 출력됩니다.
4. React 18의 Automatic Batching
React 18 이전에는 배칭이 이벤트 핸들러 내에서만 동작했지만, 비동기 코드(
setTimeout
, fetch
등)에서는 배칭이 적용되지 않았습니다. 그러나 React 18에서는 Automatic Batching이 도입되어 비동기 코드에서도 배칭이 적용됩니다.예제 3: 비동기 코드에서 Automatic Batching 테스트
import React, { useState, useEffect } from "react"; export default function App() { const [count, setCount] = useState(0); const [text, setText] = useState(""); const handleAsyncUpdate = () => { setTimeout(() => { setCount((prev) => prev + 1); setText("Updated!"); console.log("State updated!"); }, 1000); }; console.log("Component rendered!"); return ( <div> <p>Count: {count}</p> <p>Text: {text}</p> <button onClick={handleAsyncUpdate}>비동기 업데이트</button> </div> ); }
🧐 React 18 이전의 동작
setTimeout
내부에서setCount
와setText
가 호출되면, 각각 개별적인 렌더링을 트리거하여 두 번의 렌더링이 발생합니다.
🚀 React 18 이후의 동작 (Automatic Batching 적용)
- React 18에서는 비동기 작업에서도 Automatic Batching이 적용됩니다.
- 따라서
setCount
와setText
가 하나의 렌더링으로 처리되며,console.log("Component rendered!")
는 한 번만 실행됩니다.
5. 결론
- React의 Batching은 여러 상태 업데이트를 한 번의 렌더링으로 묶어 최적화합니다.
- React 18의 Automatic Batching은 이벤트 핸들러뿐만 아니라 비동기 코드에서도 배칭을 수행합니다.
- 이를 통해 불필요한 렌더링을 줄이고, 성능을 더욱 효율적으로 개선할 수 있습니다.
다음 글 예고
Batching에 대한 글을 작성하다보니 React가 어떻게 내부적으로 Batching을 처리하는지 궁금해집니다.
다음 글에서는 React의 소스 코드를 분석하며 Batching이 구현된 원리를 깊이 있게 살펴보겠습니다.
특히, Batching이 동작하는 대표적인 세 가지 경우를 중심으로 살펴보겠습니다.
1️⃣ 상태 업데이트 배칭 –
useEffect
또는 생명주기 메서드 내부에서 여러 개의 setState
가 호출될 때, React는 이를 어떻게 하나의 렌더링으로 묶을까?2️⃣ 이벤트 핸들러 내부 배칭 – 클릭, 입력 등의 이벤트 핸들러 내부에서 발생하는 여러 개의 상태 업데이트는 어떻게 배칭될까?
3️⃣ Automatic Batching (React 18 이후) – 기존에는 배칭되지 않던
setTimeout
, fetch
같은 비동기 코드 내부에서도 Batching이 동작하는 원리는?