[React] useState의 비동기 처리

혜린·2023년 1월 8일
2

React

목록 보기
18/18
post-thumbnail

✍🏻 비동기적으로 동작하는 useState를 알기 위해선
useState에 해당되는 Hook이 무엇인지 알아야 했다.
Hook이 무엇인지를 알고자 하니 Side Effect를 이해해야 했다.

🌊 따라서 Side Effet부터 Hook, useState까지의 흐름을 그대로 타고 내려가며
최종적으로 비동기적으로 작동하는 useState까지 정리해보고자 한다!



Side Effect


❓ React 컴포넌트가 화면에 처음으로 렌더링된 이후에,
비동기적으로 처리되어야 하는 부수적인 효과들을 Side Effect라고 부른다.

비동기적으로 처리되어야 하는 부수적인 효과..?
완전히 와닿지 않을 수 있으니 예시를 들어보자!

React 컴포넌트에서 외부 API를 호출할 때,
화면에 그려주는 것들을 먼저 렌더링해준 뒤, 📫실제 데이터📫는 비동기로 가져오는 것이 권장된다.
API 응답이 늦어지거나/없을 때의 영향을 최소화하기 위해
🎨화면에 그려주는 작업🎨을 먼저 하는 것이다.

이 때의 Side Effect는 📫실제 데이터📫를 받아오는 작업이 될 수 있겠다.
(첫 렌더링 후, 비동기적으로 처리되어야하는 작업이니까!)

Hook은 이런 Side Effect를 수행하는 역할을 한다.



Hook


❓ Hook이란?

Hook은 React v16.8부터 새로 추가되었다.
(22년 3월에 React v18이 출시된 상태)

함수형 컴포넌트에서 사용되는 기술을 Hook이라고 생각하면 되는데.

함수형 컴포넌트에서 상태관리를 할 수 있게 하는 useState,
렌더링 직후의 작업을 설정하는 useEffect가 Hook에 속한다.


🤷🏻‍♂️ 클래스형에서 함수형으로 굳이?

함수형 컴포넌트에서 사용되는 기술이 Hook이라고 했다.
그럼 클래스형으로 잘만 써왔는데 왜 굳이 함수형으로 바꿔 Hook을 사용해야 하는걸까란 의문이 생긴다.

우선 클래스는 진입장벽이 높다.
this 등 기본적인 JavaScript 문법을 알아야 하며, 코드의 재사용성과 코드 구성을 어렵게 한다.
그리고, React 최신 기술의 적용이 효과적이지 않다.

이런 클래스의 단점을 🚀함수형 컴포넌트로 극복🚀할 수 있다.
하지만, 클래스형 컴포넌트의 장점이었던 state 사용이나
Life Cycle을 직접 다루는 기능을 사용하지 못한다는 단점이 있었다.
이를 해결하기 위해 등장한 것이 바로 Hook이다.



useState


setState()호출 후 state가 바로 업데이트되지 않는 이슈를 겪은 적이 있을 것이다.
React를 배운 지 얼마되지 않았을 때 나도 굉장히 당황스러웠던 부분이었다.

이건 useState()가 비동기적으로 동작했기 때문이었다!
근데.. 그래서 비동기적인게 뭔데..?

useState과 setState 제대로 구분하고 있는지 CHECK!
useState : state의 초기값 설정 & return 값으로 state, setState를 돌려주는 hook
setState : state의 값을 변경할 때 사용하는 함수


😤 동기, 비동기가 뭔데!

헷갈리면 동기적으로 === 순차적으로라고 기억하면 편하다👍🏻

1. 동기

사장님 혼자만 있는 👦🏻개인카페👦🏻라고 생각하면 쉽다.
직원이 없으니 주문과 음료 제조를 동시에 처리하기 불가능할 것이다.
이렇게 동시에 작업을 처리하지 않고 순차적으로 처리하는 것이 동기적으로 처리하는 것이다.


2. 비동기

동기 처리와 다르게 직원이 많은 👨‍👨‍👧‍👦프렌차이즈 카페👨‍👨‍👧‍👦를 생각하면 된다.
주문 받는 것과 음료 제조를 동시에 처리할 수 있을 것이다.
이렇듯 한 가지 일을 처리하는 동안 다른 작업이 기다리지 않고 동시에 수행하는 것을 비동기적으로 처리한다고 한다.


📝 비동기 동작 예시

아래의 코드를 보면
증가 버튼을 누를 때마다 count가 1씩 증가할 것이다.

하지만, console에 찍히는 값을 보면 증가시키기 이전의 상태 값을 출력하는 것을 볼 수 있다.

function AsyncSetState() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
    console.log(count);
  };

  return (
    <>
     <div>현재 값 {count}</div>
     <button onClick={handleClick}>count+</button>
    </>
  );
}

💥 비동기적으로 동작하는 이유

1. state가 리렌더링 발생시킴

useState가 비동기적으로 동작하는 이유는 state와 연관이 깊다.
state는 값이 변경되면 리렌더링이 발생한다는 특징이 있다.

근데... 만약 값이 변경되는 state가 100개 있다면?
React는 매번 렌더링만 하다가 생을 마감할 것이다.


2. 해결방법

따라서 React는 렌더링을 줄이고자
상태값이 변한 state를 모두 취합(배치, Batch)한 후에 한 번에 렌더링이 되도록 한다.

🎀 배치(Batch)?

  • React가 너 나은 성능을 위해 여러개의 state 업데이트를 하나의 리렌더링으로 묶는 것
  • 16ms마다 변경된 상태값을 모아서(merge) 이전의 Element Tree와 변경된 state가 적용된 Element Tree를 비교하는 작업(reconciliation)을 거쳐 최종적으로 변경된 부분만 DOM에 적용시킨다.
  • state도 결국 객체다. 상태값이 모아지는 merge 단계에서 같은 키 값을 가진 state들이 있다면 가장 마지막 실행값으로 덮어씌워진다.

3. 효과

앞서 이야기 한 배치(Batch)와 같은 과정을 통해
100번의 상태값 변화를 단 1번의 렌더링으로 최신의 상태로 업데이트할 수 있게 된 것이다.



✨ 동기적으로 처리할 수 있는 방법

이처럼 비동기적으로 동작하는 setState를 동기적으로 처리할 수 있는 방법에는
아래와 같이 2가지의 방법이 있다.

1. useEffect의 의존성 배열 이용

2. 함수형 업데이트

// 이전
setCount(count + 1);

// 이후
setCount((prevCount) => prevCount + 1);



결론


💁🏻‍♀️ useState가 비동기적으로 동작하는 이유는,
렌더링 횟수를 줄여서 더 빠르게 동작하게 하기 위해서다.


참고


이 글은 아래의 글을 바탕으로 공부하며 개인적으로 정리한 글입니다.

profile
FE Developer

0개의 댓글