Next.js 앱에서 고급 React 상태 경계 설정하기

2026. 8. 12. 14:13카테고리 없음

반응형

대부분 정적인 Next.js 앱을 서버 렌더링 위주 + RSC + URL 상태 중심으로 구성했을 때, 경계(boundary)에서 생기는 실제 문제들을 다룬 글입니다. 훅 자체가 복잡한 게 아니라, React 상태 · 서버 데이터 · 브라우저 API · URL 상태 · 클라이언트 캐시가 한 제품에서 만날 때 “누가 상태를 소유하고, 무엇이 반응하게 하며, 변경을 누구에게 알려야 하는가”가 핵심 질문입니다.

주요 경계 사례

  1. URL 변경 시 Popover 자동 닫기
    • usePathname으로 경로 변화를 감지.
    • useEffectsetIsOpen(false) 하는 방식은 한 프레임 늦게 반영되고, ESLint도 경고.
    • key={pathname}으로 리마운트하면 애니메이션·자식 상태가 깨짐.
    • 최종 패턴: 열었을 때의 pathname을 저장해 두고, open={isOpen && openedAtPathname === pathname}으로 조건부로 닫기. Effect나 렌더 중 setState 없이 바로 반영.
  2. 브라우저 전용 API 감지 (navigator.share)
    • SSR에서는 존재하지 않음.
    • useSyncExternalStore 사용: getServerSnapshotfalse, 클라이언트 getSnapshot"share" in window.navigator, subscribe는 no-op.
    • 서버·클라이언트 모두 안전하게 값을 읽음.
  3. Infinite Query 캐시에서 파생 상태 만들기
    • 마스터-디테일 뷰에서 무한 스크롤 리스트의 이미 로드된 페이지를 기반으로 prev/next ID를 계산.
    • queryClient.getQueriesData는 단순 읽기라 캐시가 바뀌어도 리렌더가 안 됨.
    • InfiniteQueryObserver + useSyncExternalStore로 캐시 변경을 구독. 스냅샷 함수에서 파생값을 계산하고, 서버 스냅샷은 안정적인 빈 값 사용.
  4. RSC Auth 스냅샷을 클라이언트 Context로 전달
    • 서버에서 await withAuth()로 가져온 값을 ClientAuthProvider에 넘겨 Context로 노출.
    • 주의: 실시간 auth 상태가 아니라 요청 시점의 스냅샷. 로그인/로그아웃 후에는 해당 라우트를 새로고침하거나 revalidate해야 갱신됨.
    • 가능하면 라이브러리의 공식 클라이언트 Provider/훅을 쓰고, 이 패턴은 서버 전용 소스가 필요할 때만 제한적으로 사용.
  5. RSC + TanStack Query를 넘나드는 Optimistic Update
    • 좋아요 버튼처럼 초기값은 RSC, 같은 데이터가 클라이언트 쿼리에도 있는 경우.
    • Context + useOptimistic으로 즉각 피드백 제공.
    • 성공 시 로컬 상태 커밋 + 관련 쿼리 invalidate, 실패 시 롤백.
    • 서버 지속성, React 즉각 피드백, Query 캐시 일관성을 명확히 분리.

핵심 메시지

Next.js 앱이 복잡해질수록 상태 소유권이 중요해집니다. 값이 두 곳에 동시에 존재하면서 서로 동기화 책임이 불명확할 때 가장 까다로운 버그가 생깁니다. usePathname, useSyncExternalStore, RSC 스냅샷, optimistic state 등은 각각 “올바른 반응 경계”를 선택하는 도구입니다.

출처: https://www.nirtamir.com/articles/advanced-react-questions

반응형