⚡️ useState는 동기? 비동기?
- 삐삐 프로젝트를 진행하며, 멘토님과 얘기를 하던 도중 useState는 동기일까 비동기일까에 관련된 내용이 나오게되었다.
- 단순히 useState는 상태를 관리하는 훅으로만 알고 있었던 나를 반성하며, useState의 정체를 파헤쳐 보도록 할 예정이다.
📌 useState는 비동기 이다!
- 결론부터 얘기하자면 useState는 비동기 이다.
- 좀 더 자세히 얘기하자면, 상태를 변경시키는 setter 메소드인 setState가 비동기적으로 동작한다.
Q. 리액트는 왜 setState를 통해서만 상태를 변경시키는 걸까?
A. 직접 상태값을 변경하지 말아야하는 이유는, 이전 상태값을 바탕으로 업데이트된 상태값을 비교해 리액트가 감지하기 때문이다. 리액트는 가상 DOM(Virtual DOM)을 사용하여 변경된 부분만 실제 DOM에 반영하므로 모든 상태 변경이 바로 실제 DOM 조작으로 이어지지 않고, 변경 사항을 비교하고 변경된 값만 리랜더링 한다.
🧐 예시를 통해 살펴보자.
- 아래의 <useState 비동기 처리 예시> 코드를 보면 + 버튼 클릭시 setNum(num + 1)를 2번 실행하여 num을 2씩 증가시킬것만 같다.
- 하지만, 실제 + 버튼을 눌러보면 1씩 증가하는 걸 볼 수 있다.
🧐 Why ?
- 리액트 내부적으로 setState를 비동기로 처리하기 때문이다.
- 하나의 어플리케이션, 하나의 페이지나 컴포넌트에도 수많은 State가 있을텐데, State가 각각 바뀔 때마다 화면을 리렌더링 해야한다면 문제(일관성, 성능)가 생길 수 있다.
- 그래서 리액트는 이런 문제를 효율적으로 해결하기 위해 setState를 연속 호출하면 setState를 모두 취합(batch)한 후에 한 번에 렌더링하도록 한다.
- 아무리 많은 setState가 연속적으로 사용되었어도 배치 처리에 의해서 한 번의 렌더링으로 최신 상태를 유지한다.
Q. 배치(batch)란 뭘까?
A. 배치란 React가 너 나은 성능을 위해 여러개의 state 업데이트를 하나의 리렌더링으로 묶는 것을 의미한다. React는 16ms 단위로 배치를 진행하며, 그사이 변경된 상태값을 모아서(merge) 이전의 엘리먼트 트리와 변경된 state가 적용된 엘리먼트 트리를 비교하는 작업(reconciliation)을 거쳐 최종적으로 변경된 부분만 DOM에 적용시킨다.
- 리액트의 State는 객체이며, 객체이기 때문에 동일한 key에 대해 이전의 값을 계속 덮어 쓰기 때문에, 결국 마지막 명령어만 수행하게 된다.
💡 useState를 동기적으로 처리할 순 없을까?
- useState 동기처리 하는 방법에는 2가지가 있다.
- useEffeect 사용
- setState의 인자로 콜백함수 사용
🧐 1. useEffect 사용
- useEffect의 의존성 배열값으로 num값을 넣은 후, num 상태 변경시마다 +1을 한번 더 해주는 작업을 하였다.
- 아래와 같은 코드로 작성시 무한 루프가 도는 현상 발생
- 이를 해결하기 위해, + 버튼을 누른 상태를 따로 만들어 주고, 해당 상태가 true일때만 useEffect의 콜백함수가 실행되도록 하여 무한 루프 동작을 막았음
import { useState, useEffect } from "react";
export default function Example() {
const [num, setNum] = useState(0);
useEffect(() => {
setNum(num + 1);
}, [num]);
const numPlus = () => {
setNum(num + 1);
};
return (
<>
{num}
<button onClick={numPlus}>+</button>
<button onClick={() => setNum(0)}> reset </button>
</>
);
}
🧐 2. setState의 인자로 콜백함수 사용
- setState를 사용할 때 value값에 의존하는 것 보다 value => value + 1과 같이 함수로 인자를 전달하게 되면 항상 최신값을 참조하기 때문에 동기적으로 처리할 수 있다.
리액트 18 버전에서는 Automatic Batching 기능이 추가되어서 setState 코드를 두 번 실행해도 두 번의 렌더링이 일어난다.
🌱 결론
- 리액트의 useState는 비동기적으로 동작하는 훅이다.
- 비동기적으로 동작하는 이유는
- 컴포넌트의 일관성 유지: 여러 개의 setState 호출이 한 번에 처리되도록 비동기로 처리한다. 이것은 리액트가 여러 상태 업데이트를 하나의 업데이트로 묶어서 처리함으로써 컴포넌트의 일관성을 유지 할 수 있는 것이다.
- 성능 최적화 : setState를 배치(batch) 처리함으로써 가상 DOM(Virtual DOM)을 통해 변경사항을 비교하고 최적화된 렌더링 작업을 수행한다.
- useState를 동기적으로 처리하려면
- useEffect의 의존성 배열을 활용하는 방법과
- setState 의 인자로 콜백 함수를 넣는 방법이 있다.
'코딩 > React' 카테고리의 다른 글
useEffect를 이용한 커스텀훅을 만들어보자(feat. useAsyncEffect) (2) | 2024.02.29 |
---|---|
JSX는 무엇일까? (Feat. babel) (0) | 2023.10.11 |
[React] 실습 1회차 - Counter 구현하기 (0) | 2023.05.24 |
[React] 실습 Preview (0) | 2023.05.22 |