React의 `useSyncExternalStore` 실전 활용: 탭 간 쇼핑 카트 구현
카테고리
프로그래밍/소프트웨어 개발
서브카테고리
웹 개발
대상자
- React 개발자 (중급 이상): 상태 동기화, 탭 간 데이터 공유, SSR 호환성 문제 해결에 관심 있는 분
- 난이도: 중급 (React Hooks, 상태 관리, 콘커런시 이해 필요)
핵심 요약
useSyncExternalStore
의 핵심 기능:localStorage
기반 탭 간 상태 동기화 (StorageEvent
활용)- 콘커런시 안전한 렌더링 (렌더링 중 상태 변화 시
snapshot
동기화) - SSR 호환성 해결 (
getServerSnapshot
옵션 제공) - 구현 핵심 코드:
cartStore
에서getSnapshot
,subscribe
메서드 정의 (StorageEvent
리스너 연결)useSyncExternalStore
를 통해 React 컴포넌트에서 외부 상태를 읽음useCart
커스텀 훅으로useSyncExternalStore
를 캡슐화- 비교 우위:
useEffect + useState
방식보다 메모리 누수 방지, 하이드레이션 경고 제거, 동기화 안정성 향상
섹션별 세부 요약
1. 문제 정의: React 18의 콘커런시와 외부 상태 동기화의 어려움
- Tearing 문제: 탭 간 상태 변경 시 DOM 일관성 손상
- SSR 하이드레이션 불일치: 서버와 클라이언트 상태 불일치로 인한 경고 발생
- 구독 로직 중복: 각 컴포넌트가 별도
useEffect
로 이벤트 리스너 추가
2. `useSyncExternalStore` 활용한 탭 간 쇼핑 카트 구현
cartStore.ts
설계:readCart()
/writeCart()
함수로localStorage
와의 상호작용 정의StorageEvent
를 통해 탭 간 상태 변경 알림getSnapshot
,subscribe
메서드를useSyncExternalStore
에 전달useCart.ts
에서의 적용:useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
호출- 서버에서
getServerSnapshot
을 통해 초기 상태 로드
3. `useSyncExternalStore` vs. `useEffect + useState` 비교
useEffect
기반 접근의 단점:- Tearing 가능성: 콘커런시 렌더링 중 상태 불일치
- 리스너 중복 생성: 각 컴포넌트별 이벤트 리스너 추가
- SSR 하이드레이션 문제: 서버/클라이언트 상태 불일치 시 경고 발생
useSyncExternalStore
의 장점:- 자동 구독/구독 해제: 리스너 관리 최소화
- 하이드레이션 경고 제거:
getServerSnapshot
으로 서버 상태 동기화 - 동일한
snapshot
사용: 렌더링 중 모든 컴포넌트가 동일한 상태 읽음
4. 확장성과 활용 사례
- 다양한 외부 상태 소스 지원: Redux, WebSocket,
matchMedia
등과 호환 - 아키텍처 패턴:
subscribe
와getSnapshot
인터페이스만 변경하면 상태 소스 교체 가능localStorage
대신Redux
로 교체 시subscribe
메서드 재구현
결론
- 실무 적용 팁:
useSyncExternalStore
를 사용하여 외부 상태 동기화 로직의 복잡성과 오류 위험을 줄임- 타브 간 상태 공유 시
StorageEvent
와 결합하여 실시간 업데이트 구현 - SSR 환경에서는
getServerSnapshot
을 반드시 정의하여 하이드레이션 문제 방지 - 핵심 메시지:
useSyncExternalStore
는 React의 선언적 세계와 외부 상태 간의 격차를 해소하는 최소한의 API로, 탭 간 상태 공유, SSR 호환성, 콘커런시 안정성 문제를 한 번에 해결 가능.