React18의 useSyncExternalStore 훅으로 전역상태 관리하기

Youngjin
Dong-gle
Published in
6 min readOct 18, 2023

이 글은 useSyncExternalStore를 이미 알고 있다는 가정 하에 작성되었습니다.

처음 들어보셨다면 공식문서를 읽고 오시는 것을 추천합니다.

1. useGlobalState 를 만들었다.

공식문서에서는 useSyncExternalStore를 이렇게 정의한다.

useSyncExternalStore is a React Hook that lets you subscribe to an external store.
useSyncExternalStore 는 외부 store를 구독할 수 있는 React 훅입니다.

옵저버 패턴을 이용하는 이 훅을 이용해 외부 store에 데이터를 저장하고, 리액트 컴포넌트가 store를 구독하게하여

store가 바뀔 때 마다 컴포넌트를 리렌더링하는 기능을 만들 수 있다.

따라서 전역상태관리를 할 수 있다고 생각했고, 실제로 많은 사람들이 그렇게 사용하는 걸 봤다.

그래서 나도 팀원들이 익숙한 useState, recoil과 사용법이 유사한 전역상태관리 훅 useGlobalState 를 만들었다.

사용 흐름은 다음과 같다.

  1. Component에서 GlobalState를 이용해 useGlobalState를 호출한다.
  2. useGlobalState은 내부적으로 GlobalState 에게 자신을 호출한 Component의 re-render 함수를 넘겨준다.
  3. Component 는 useGlobalState에게 받은 setState를 호출하여 GlobalState를 변경한다.
  4. GlobalState는 state를 변경하며 동시에 내부적으로 emitChange를 호출하여 자신을 구독중인 Component들을 re-render한다.

밑에서 직접 테스트 해볼 수 있습니다.

사용흐름을 코드에 주석으로 달아놓았습니다😎

2. 라이브러리 배포

@yogjin/react-global-state

간단한 전역상태관리 훅이지만 npm에 배포해보았다.

typescript를 지원하고, react18 버전에서만 사용할 수 있다.

yarn add @yogjin/react-global-state-hook

3. 학습계기

동글 프로젝트(글을 관리하고 블로그에 포스팅할 수 있는 서비스)를 진행하면서 전역상태가 필요한 부분이 있었다.

동글은 오른쪽 사이드바 부분에서 글의 정보를 확인하고 블로그에 발행할 수 있다.

그런데 휴지통에 있는 글은 블로그에 발행하는 버튼이 없어야하고, 글의 정보만 확인할 수 있어야했다.

이를 구현하기 위해 글 컴포넌트에서 현재 보고있는 글의 성격에 따라 isDeleted 상태를 set하고 사이드바에서는 isDeleted 상태를 이용해 조건부 렌더링을 해야했다.

기존 useState를 이용하면 props drilling 문제가 있었다.

사이드바는 글 컴포넌트에 종속적이지 않았기 때문에, 글과 사이드바의 공통 최상위 컴포넌트에서 현재 보고있는 글의 정보(activeWritingInfo)를 넘겨줘야했기 때문이다.

대신 react-router-dom에서 제공하는 useOutletContext 를 활용해서 Outlet 에 간단하게 activeWritingInfo context를 넘겨주어서 해결했었다.

Outlet을 감싸는 context API를 따로 만들 필요가 없었고 간편해서 꽤 만족스러웠다.

위 코드에서 아쉬운 점은 딱 2가지 정도였다.

  1. state의 setter를 그대로 넘겨주고 있음 -> reducer를 이용해 개선가능
  2. 처음 저 코드를 본다면 익숙하지 않음 (실제로 리뷰가 달렸었다)

위 경우에는 제공하는 훅을 이용했기 때문에 깔끔했지만,

전역 상태를 만들 때 Context API를 이용하면 상태가 많아질 수록 감싸줘야하는 Context Provider가 많아진다는 게 살짝 아쉬웠다.

(감싸고 있는 내부 컴포넌트들에서만 context를 이용한다는 걸 선언적으로 알 수 있어서 좋은 점은 나도 강력히 동의한다)

또한 recoil를 이용해도 됐지만, 단순한 전역상태를 관리하기 위해 큰 라이브러리를 설치한다는 점이 약간 불편했다.

주저리주저리 말이 많았지만, 한 마디로

“Context API와 reducer를 이용한 상태관리 대신 새로운 시도를 해보고 싶었다” 로 정리가능하다😁

4. 마치며

공식문서에도 나와있듯이 React 상태관리는 기존 usestateuseReducer를 이용하는걸 권장한다.

useSyncExternalStore 는 리액트의 전역상태관리를 위해 사용하라고 나온 훅은 아니다.

하지만 훅 특성상 충분히 전역상태관리로도 이용할 수 있다고 생각한다.

상태관리를 지원하는 다른 라이브러리들도 useSyncExternalStore를 이용하고 있기도 하다.

tanstack-query

zustand

직접 간단한 전역상태 라이브러리를 구현하면서 배운 점은 다음과 같다.

  • 리액트의 useState 타입선언
  • tanstack-query 의 동작방식
  • 옵저버 패턴

--

--