useSyncExternalStore: React와 localStorage를 동기화하는 올바른 방법
Source: Dev.to
Introduction
React 18은 대부분의 개발자가 직접 건드리지 않지만 거의 모든 최신 상태 관리 라이브러리가 의존하는 저수준 훅을 도입했습니다: useSyncExternalStore.
이 훅은 React가 외부 상태 소스—React 외부에 존재하는 상태—와 연결되는 공식적인 방법입니다. 예를 들면:
localStorage- Redux / Zustand 스토어
- 브라우저 API
- WebSocket 데이터
Why useSyncExternalStore?
React 18 이전에는 외부 스토어에 구독할 때 보통 다음과 같이 작성했습니다:
useEffect(() => store.subscribe(forceUpdate), []);
이 패턴은 동시 렌더링(concurrent rendering) 하에서 깨집니다. useSyncExternalStore는 React가 다음을 제어하도록 함으로써 문제를 해결합니다:
- 구독(subscription) 관리
- 현재 값 읽기(reading)
- 안전한 재렌더링 트리거(triggering)
const value = useSyncExternalStore(
subscribe,
getSnapshot,
getServerSnapshot? // optional, for server‑side rendering
);
subscribe– React가 업데이트를 듣는 방법getSnapshot– React가 현재 상태를 읽는 방법
스냅샷이 변경되면 React가 자동으로 재렌더링합니다.
Creating a localStorage store
function createLocalStorageStore(key, initialValue) {
const listeners = new Set();
const getSnapshot = () => {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : initialValue;
};
const setValue = (value) => {
localStorage.setItem(key, JSON.stringify(value));
listeners.forEach((l) => l());
};
const subscribe = (listener) => {
listeners.add(listener);
const onStorage = (e) => {
if (e.key === key) listener();
};
window.addEventListener("storage", onStorage);
return () => {
listeners.delete(listener);
window.removeEventListener("storage", onStorage);
};
};
return { getSnapshot, setValue, subscribe };
}
Hook wrapper for external stores
import { useSyncExternalStore } from "react";
function useExternalStore(store) {
return useSyncExternalStore(
store.subscribe,
store.getSnapshot,
store.getSnapshot // same function for server snapshot if needed
);
}
Example: Counter synced with localStorage
const counterStore = createLocalStorageStore("counter", 0);
function Counter() {
const count = useExternalStore(counterStore);
return (
<>
Count: {count}
{/* The original snippet omitted the button element; kept as‑is */}
counterStore.setValue(count + 1)}>+
</>
);
}
이제 카운터 상태는:
- 영구적(
localStorage에 저장) - 공유(여러 컴포넌트 간)
- 탭 간 동기화(
storage이벤트를 통해) useSyncExternalStore덕분에 동시 안전
When to use useSyncExternalStore
- 상태가 React 외부에 존재할 때(예: 브라우저 스토리지, 외부 스토어, WebSocket 데이터).
- 여러 컴포넌트가 같은 데이터를 공유해야 할 때.
- 스토어, 영속성 레이어, 혹은 라이브러리를 만들고 다른 컴포넌트가 이를 사용할 경우.
일반적인 컴포넌트 로컬 상태에는 useState를 계속 사용하세요. useState는 React가 소유하는 상태를 관리하고, useSyncExternalStore는 React를 외부 세계와 연결합니다. 로컬 컴포넌트 상태를 넘어서는 무언가를 구축한다면, 이 훅이 기본이 됩니다.