Batching 그래서 넌 뭔데?

dev.horang🐯·2024년 7월 8일
1
post-thumbnail

💡 Batch : 일괄

React 개발자라면 batching 단어를 한번쯤은 들어봤으리라 생각한다! 나 같은 경우도 직접 겪으면서 아! batching 이라는게 일어나는구나~ 라고 알아본적이 있다. 하지만 이번에 batching 에 대해 깊게 학습해볼 기회가 생겼고 React 공식 깃허브에 있는 batch 에 대해 설명해둔 글을 해석 + 분석 해보려고 한다!

Batching 이란?

Batching은 여러개의 스테이트값들이 단 한번의 리랜더로 업데이트되는 현상을 말한다.

이렇게만 들으면 잘 이해가 되지 않으니 예시를 한번 살펴보자라

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);
  const [count2, setCount2] = useState(0)

  function handleClick() {
    setCount(c => c + 1); // 랜더 안됨
    setFlag(f => !f); // 랜더 안됨
    // 리액트는 배칭을 통해 마지막에 한번만 리랜더링 된다!
  }
  
  function handleClick2() {
  setCount(2); // 랜더 안됨
  setCount(5); // 랜더 안됨
  setCount(1); // 랜더 안됨
  // 리액트는 배칭을 통해 마지막에 한번만 리랜더링 된다!
  }
  
  console.log(count) // 0 -> 1 

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <button onClick={handleClick2}>Now</button>
      <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1>
    </div>
  );
}

즉, state인 count와 flag의 값을 변경함에 따라 2번의 리랜더링이 일어나는 것이 아닌 handleClick이 끝나는 시점에 리랜더링이 일어나는 것을 뜻한다.
배칭의 장점은 불필요한 리랜더링은 줄임으로써 퍼포먼스를 올릴 수 있게 된다. 또한 배칭은 아직 끝나지 않은 작업에 대한 업데이트를 막음으로써 효율을 높일 수 있게된다. 공식 문서에서 재밌는 예시를 들어놨다. 우리가 레스토랑 알바를 할 때 손님이 음식을 주문하면 첫번째 음식을 주문하자마자 주방으로 달려가 주문을 넣는것이 아닌 손님이 주문하는 모든 음식을 듣고 차후에 주방으로 가서 주문을 넣는 것 또한 효율 때문이 아니겠는가 하는 것이다.

React18 이전의 batch는 오직 브라우저 이벤트가 발생할 때만 자동적으로 batching 을 실행해 주었지만 18이후부터는 브라우저 이벤트 뿐만 아니라 promises, setTimeout, native event handlers 등의 다른 이벤트에서도 자동적으로 batching을 처리해준다!

function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 이때만 리랜더링
}

setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 이때만 리랜더링
}, 1000);

fetch(/*...*/).then(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 이때만 리랜더링
})

elm.addEventListener('click', () => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // 이때만 리랜더링
});

난 싹 다 업데이트 하고 싶은데..?

batching을 하는것이 사실 이론상은 안전하지만 난 따로따로 업데이트 하고싶은데? 한다면? ReactDOM.flushSync() 를 사용하면 batching 처리를 하지 않고 state 변경이 될 때마다 업데이트를 할 수 있게 해준다.

import { flushSync } from 'react-dom'; 

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // 업데이트 됨
  flushSync(() => {
    setFlag(f => !f);
  });
  // 업데이트 됨
}

🤔 흠... 그럼 도대체 어떤식으로 동작하는거지?

한번 생각을 해보자! 어떻게 진행이 되는걸까?

  1. 하나의 이벤트 내에서 여러번의 setState가 발생했을때 이것들을 한번에 처리하는 것을 Batching이라고 한다.
  2. 이것을 구현하기 위해서는 어떻게 해야할까?
  3. setState 가 여러번 발생 했을때 해당 이벤트를 어디에 모아 뒀다가 가장 마지막에 들어오는 애만 1frame 후에 실행되게 해야한다. 그럼 이건 후입선출 이니까 stack을 사용한다. stack에 넣어두고 1frame 후에 stack에서 pop을 해서 실행하면 된다.
  4. 근데 그러면 이 과정들이 1frame 안에 일어났으며 한개의 스테이트값에 대한 변경만 발생했음을 알아야한다.
  5. 그러면 해당 스테이트값에 대한 인덱스를 키로 가지면서 밸류값으로 스택을 가지고 있어야하지..? 그리고 처음 딱 이벤트가 발생하는 시점에 setTimeout을 걸어두고 1frame 후에 실행되게 해야한다.

이런 의식의 흐름에 이르렀다. 여기서 1frame 은 react에서 처리하는 타이밍을 인위적으로 표시하기 위해 설정한 조건이다. 실제 리액트는 훨씬 더 복잡하게 처리하겠지..?

공부하면서..

공부를 하면서 느낀점은 이런식의 동작이 자동으로 돌아가는 React에 대한 경외감과 재밌다!!! 는 생각이였다. 물론 왠만하면 자동적으로 batching 이 되기 때문에 의식하면서 사용하지 않아도 되겠지만 사용하면서 음~ 배칭됐겠네! 하는 일말의 생각을 하면서 사용할 수 있을것같다. 재밌따!

profile
좋아하는걸 배우는건 신나🎵

0개의 댓글