Suspense 와 use() 전엔 useEffect + useState. 패턴은 여전히 자리 있어 — Track 3 lesson 2 가 다룸. 이 레슨은 왜 더 나은 거 원하는지 보여줘.
3-state 춤
모든 fetch 가 세 state 생산: loading, error, success. Suspense 없이는 셋 다 명시적으로 추적:
const [data, setData] = useState<T | null>(null)const [error, setError] = useState<Error | null>(null)const [loading, setLoading] = useState(true)
+ 라이프사이클 오케스트레이트하는 useEffect + AbortController 로 cleanup + 언마운트 후 state 설정 방지 guard. Fetch 하나에 보일러플레이트 많음.
Discriminated-union 업그레이드
Track 2 lesson 5 의 discriminated union 이 세 state 를 하나로 축소:
type Fetch<T> =
| { status: 'loading' }
| { status: 'error'; error: Error }
| { status: 'success'; data: T };
컴포넌트가 state.status 기반 렌더; 불가능한 조합 (loading AND data) 존재 불가. 진전 — 다만 보일러플레이트는 여전.
Suspense + use() 업그레이드
Loading state 가 컴포넌트 위의 Suspense 경계에 (안 아니라), error state 가 위의 error boundary 에, success 경로가 컴포넌트 자체 안의 유일한 branch 면 어떨까? 그게 use() + Suspense 가 달성하는 것. 다음 네 레슨이 조각조각 조립.
Feature 쫓는 게 아니라 선언적 데이터로 가는 중. useState 체인 → discriminated union → Suspense + use() 의 궤적은 같은 호: loading/error 관심사를 컴포넌트 body 밖으로 밀어서 success 경로가 유일한 경로처럼 읽히게. 각 단계가 실재 가치.