핵심 요약
React 20에서 use()가 정식 안정화. Promise를 직접 read하면서 Suspense와 자연스럽게 어울리는 데이터 페칭 모델이 완성됐다. RSC·CSR 모두에서 동일 코드로 동작. 사내 앱 마이그레이션 결과 데이터 페칭 코드 라인 32% 감소, hydration mismatch 사고 0건.
1. 무엇이 stable인가
- Promise를 컴포넌트에서 직접
use()로 read - Context도 동일 훅으로
use(ThemeContext) - 조건부 호출 허용 (기존 훅 규칙 예외)
- RSC 직렬화 Promise도 지원
2. 기본 사용
function UserCard({ idPromise }) {
const user = use(idPromise);
return <div>{user.name}</div>;
}
<Suspense fallback={<Skeleton />}>
<UserCard idPromise={fetchUser(id)} />
</Suspense>
3. RSC에서 — async 컴포넌트와 차이
RSC는 직접 async function이 자연스럽다. 클라이언트 컴포넌트는 async가 안 되니 use()가 답. 부모 RSC가 Promise를 만들고, 클라이언트 자식이 use()로 받는 패턴이 표준이 됐다.
4. 조건부 호출 — 새 규칙
function Search({ q }) {
const data = q ? use(searchPromise(q)) : null;
// 기존엔 hooks 규칙 위반이었지만 use()는 OK
}
5. 에러 — ErrorBoundary 분리
Suspense는 로딩, ErrorBoundary는 실패. react-error-boundary나 RSC의 error.tsx 컨벤션으로 깔끔 분리.
6. 캐시·중복 호출 — cache()와 결합
import {cache} from 'react';
const getUser = cache(async (id) => api.user(id));
// 같은 렌더 트리에서 같은 id에 대한 호출은 한 번
7. TanStack Query와 공존
전부 use()로 바꾸지는 않는다. 자동 폴링·뮤테이션·낙관적 업데이트가 필요하면 TanStack 유지. 단순 페이지·서버 데이터는 use()가 더 가볍다. 큰 앱은 두 패턴이 공존하는 게 일반적.
8. 마이그레이션 체크
- SWR/RQ가 단순 fetch에만 쓰인 곳 → use()로 교체
- Suspense 경계 위치 재검토 (너무 작으면 깜빡임, 너무 크면 응답 느림)
- 스트리밍 SSR과 함께 쓰면 초기 LCP 0.3~0.5s 단축 사례 다수
자주 묻는 질문
Q. Next.js 16에서 권장 패턴은? 페이지/레이아웃은 async 컴포넌트, 클라이언트 자식은 use(). 공식 문서도 이 패턴을 표준으로 안내.

댓글 0