Suspense 는 suspension 잡는 경계. 로딩 UI 가 살 자리에 둬. 실제 페치하는 컴포넌트는 로딩에 대해 전혀 안 알아도 됨.
Mental model
컴포넌트 트리를 <Suspense fallback={...}> 경계의 계층으로 상상. 어떤 descendant 가 use(somePendingPromise) 호출하면 React 가 'throw' (suspend), 트리 올라가며 가장 가까운 Suspense 찾고, 그 경계의 fallback 렌더. Promise resolve 시 React 가 suspend 된 subtree 재렌더, fallback 이 진짜 컨텐츠로 교체.
경계 어디 둘지 결정. 페이지 상단 경계 = 페이지 전체 spinner; 단일 패널 주변 경계 = 그 패널만 spinner, 나머지는 정상 렌더.
Granularity
거친: <Suspense fallback={<FullPageSpinner />}><App /></Suspense>. 쉽지만 못생긴 UX.
고운: sidebar 주변 Suspense, 메인 패널 주변 또 하나, 각 리스트 row 주변. 페이지의 각 부분이 자기 페이스로 interactive 해지는 아름다운 스트리밍 UX.
현실적: 전략적 경계 두엇. 페이지 shell 즉시 렌더, 데이터 섹션 주변 Suspense 가 준비되면 스트림 in. 첫날부터 과도하게 granularize 시도 마.
중첩된 Suspense
경계가 자연스럽게 nest. 안쪽 경계가 자기 descendant 의 suspension 먼저 잡음; 안쪽 경계 없으면 바깥쪽이 인계. 의미 있는 가장 작은 영역으로 spinner 범위 잡으려고 nesting 사용.
Transition 트릭
사용자 액션 (검색, 필터 변경) 이 새 데이터 트리거하면 페이지가 보통 Suspense fallback 으로 flash. useTransition (Track 7) 이 React 한테 '이 state 변경은 비긴급; 새 UI 준비될 때까지 옛 UI 보여줘' 라고 말함. 결과: spinner flash 대신 부드러운 swap.