본문 바로가기
React

[React] 렌더링 배치, 리렌더링 최적화 기초

by LasBe 2023. 2. 11.
반응형

⚡️ 리액트의 렌더링 배치


리액트는 상태 변경을 감지해 렌더링 과정이 작동하여 변경사항을 반영합니다.

 

이러한 점은 상태변경에 대해 매번 렌더링 과정을 거치기엔 많은 리소스가 요구되기 때문에
리액트 18버전부터 여러 상태 변경을 묶어 batching(일괄) 처리 하여 리렌더링 하는 개념이 강화되었습니다.

 

이 개념을 몰랐을 때 API 호출을 위한 하나의 파라미터 상태 변경을
연관성(시간, 장비, 상태 등) 별로 묶은 컴포넌트에 위임해서 사용했는데,
여러 컴포넌트에서 하나의 상태를 변경하다보니 배치 타이밍을 조율하지 못해
API가 여러번 호출 됐던 경험이 있어 최적화 측면에서는 정말 중요한 개념이라 생각합니다.

 

그럼 어떻게 리액트가 상태 변경을 배치 처리 하는지 알아보겠습니다.

 

📌 상태 변경 배치 처리

const [state, setState] = useState(0);

console.log(state);

const onClickButton = async () => {
  setState(1);
  setState(2);
  setState(3);
  setState(4);
};

콘솔은 렌더링이 발생될 때 state를 데이터를 콘솔에 찍어줍니다.

 

기본적으로 4번의 상태가 함수에 의해 변경 되었으니
초기 렌더링 까지 5개의 로그가 찍힐 것이라 생각되지만,
리액트의 자동 렌더링 배치로 인해 여러번의 상태 변경이
하나의 렌더링 패스에 묶여 다음과 같은 결과를 보여줍니다.

이를 통해 어떻게 상태 변경이 일괄적으로 이루어지는지 직관적으로 확인할 수 있습니다.

 

그렇다면 이 렌더링 배치는 어떤 기준으로 묶일까요?

 

📌 배치로 묶이는 기준

const promiseFun = async () => {
  await new Promise((resolve) => setTimeout(resolve, 1000));
};

export default function App() {
  const [state, setState] = useState(0);

  console.log(state);

  const onClickButton = async () => {
    setState(1);
    setState(2);

    await promiseFun();

    setState(3);
    setState(4);
  };

  return <button onClick={onClickButton}>CLICK</button>;
}

이전 코드와는 다르게 상태 변경들 중간에 promise 함수가 실행되는 코드입니다.

 

과연 여기서는 아까와 같이 렌더링 패스가 하나로 묶여 2개의 로그만 찍힐까요?

초기 렌더링 로그를 제외하고 2개의 로그가 찍힌 것을 확인하니 2번의 리렌더링이 발생했고,
이를 통해 상태 변경들이 두개의 배치로 묶인 것을 알 수 있습니다.

 

리액트에서는 여러 상태 변경들을 배치로 묶을 때 중간에 await과 같은 동기적 행위가 있으면
다른 배치로 나누어 렌더링 패스를 진행하고 리렌더링 합니다.

 

📌 배칭에 의한 상태 업데이트

여러 상태를 변경하는 함수가 배칭 상태에 들어가면
그 이벤트 내에서 참조하는 상태는 동기적으로 업데이트 되지 않습니다.

 

쉽게 말해서 setState 사이에 state를 콘솔에 찍었을 경우
이벤트가 실행되기 전 state가 찍히게 됩니다.

 

const onClickButton = async () => {
  setState(1);
  console.log("inFun 1 ~ 2:", state);
  setState(2);

  await promiseFun();

  setState(3);
  console.log("inFun 3 ~ 4:", state);
  setState(4);
};

 

Class형 컴포넌트에서는 동기적으로 업데이트 된 state 값을 참조할 수 있었는데,
함수형에서는 더 이상 동작하지 않습니다.

 

이벤트가 끝나고 리렌더링이 발생 해 함수의 렉시컬 환경이 바뀌어야만 업데이트 된 값을 참조할 수 있으니
setState((prev)=>{}) 와 같이 이전 값을 참조하는 방법을 사용할 수 있습니다.

 

 

반응형

댓글


오픈 채팅