dev-guide
React 19 use() 훅 완벽 가이드 — Promise와 Context를 다루는 새로운 방법
React 19에서 도입된 use() 훅의 Promise 처리 패턴, Context 읽기, Suspense와의 통합, 기존 데이터 페칭 방식과의 차이점을 정리하였다.
- ·React 19에서 정식 도입된 use() 훅
- ·Promise와 Context 두 가지 타입을 처리하는 범용 훅
- ·Hooks 규칙 예외: 조건문과 반복문 내부에서 사용 가능
- ·Suspense와 자동으로 통합되어 로딩 상태 처리
기존 프로젝트에서 useEffect 내부에 복잡하게 작성하던 비동기 데이터 로딩 로직을 use() 훅과 Suspense 조합으로 교체하였다. 로딩·에러 상태를 컴포넌트마다 개별 관리하던 보일러플레이트가 대폭 줄었고, 선언적으로 비동기 상태를 처리하는 패턴에 익숙해지면서 코드 가독성이 눈에 띄게 향상되었다.
use() 훅이란 무엇인가
Promise를 읽는 새로운 방법
use(promise)는 Promise를 인자로 받아 resolved된 값을 동기적으로 반환하는 훅이다. Promise가 아직 pending 상태라면 컴포넌트 렌더링을 일시 중단하고 가장 가까운 Suspense 경계의 폴백을 표시한다. Promise가 fulfilled되면 Suspense 경계 내의 컴포넌트들이 다시 렌더링된다. 이 동작은 기존 Suspense의 동작 방식을 일반화한 것으로, 개발자가 직접 비동기 상태를 관리하지 않고 선언적으로 데이터를 읽을 수 있게 한다.
// use()로 Promise 읽기
import { use, Suspense } from 'react'
async function fetchUser(id: string) {
const res = await fetch(`/api/users/${id}`)
return res.json()
}
function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
const user = use(userPromise) // Promise가 resolve될 때까지 suspend
return <h1>{user.name}</h1>
}
// Suspense로 로딩 상태 선언
<Suspense fallback={<Skeleton />}>
<UserProfile userPromise={fetchUser('1')} />
</Suspense>기존 데이터 페칭 방식과의 비교
기존 useEffect + useState 패턴은 로딩, 에러, 성공 세 가지 상태를 컴포넌트 내부에서 직접 관리해야 했다. 이로 인해 컴포넌트에는 항상 조건부 렌더링 분기가 포함되어 실제 UI 로직이 상태 관리 코드에 묻혔다. use(promise) 패턴은 로딩 상태를 Suspense에, 에러 상태를 Error Boundary에 위임하여 컴포넌트가 "데이터가 이미 있다"는 가정 하에 UI만 집중적으로 표현하도록 한다. 코드가 단순해지고 로딩·에러 처리를 재사용 가능한 경계 컴포넌트로 추출할 수 있다.
Context를 읽는 새로운 방법
use(Context)로 Context 읽기
use(Context)는 useContext(Context)와 동일하게 Context 값을 읽지만, 가장 중요한 차이는 조건문과 반복문 내부에서 호출할 수 있다는 점이다. 기존 useContext는 Hooks 규칙에 따라 컴포넌트 최상단에서만 호출해야 했지만, use는 이 제약에서 자유롭다. 조건부로 다른 Context를 읽거나 반복문 내에서 Context 값을 사용하는 복잡한 시나리오에서 코드 구조를 자연스럽게 표현할 수 있다.
서버 컴포넌트에서의 use() 활용
React Server Components에서는 컴포넌트 자체가 async 함수이므로 await를 직접 사용할 수 있어 use()가 필요하지 않다. use()는 주로 클라이언트 컴포넌트에서 서버로부터 받은 Promise를 처리하거나, 다른 클라이언트 컴포넌트로 Promise를 props로 전달받아 처리할 때 유용하다. 서버 컴포넌트에서 시작된 데이터 페칭을 클라이언트에서 스트리밍으로 받아 처리하는 패턴에서 use()가 핵심 역할을 한다.
Suspense와의 통합 전략
Suspense 경계 설계
Suspense 경계를 어디에 배치하느냐에 따라 사용자 경험이 달라진다. 페이지 전체를 하나의 Suspense로 감싸면 모든 데이터가 로드될 때까지 전체 폴백이 표시된다. 중요한 콘텐츠와 부가적인 콘텐츠를 별도의 Suspense로 분리하면 핵심 콘텐츠가 먼저 표시되고 나머지가 순차적으로 채워지는 스트리밍 경험을 제공할 수 있다. Next.js의 PPR과 결합하면 서버 스트리밍과 클라이언트 Suspense가 협력하여 최적의 로딩 경험을 만들 수 있다.
Error Boundary와의 조합
use(promise)로 읽은 Promise가 reject되면 가장 가까운 Error Boundary로 에러가 전파된다. Error Boundary를 Suspense와 함께 적절히 배치하면 로딩 상태와 에러 상태를 모두 선언적으로 처리할 수 있다. React 19에서는 클라이언트 컴포넌트에서도 Error Boundary를 더 세밀하게 제어할 수 있는 API가 추가되어, 에러 복구(retry) 로직도 표준 방식으로 구현할 수 있다.
자주 묻는 질문
use()는 useEffect를 완전히 대체하나요?+
데이터 페칭 패턴에서는 상당 부분 대체 가능하지만, DOM 이벤트 구독, 타이머, 브라우저 API 같은 사이드 이펙트는 여전히 useEffect가 필요합니다.
use()는 React Query나 SWR을 대체하나요?+
기본적인 데이터 페칭은 대체 가능하지만, 캐싱, 재검증, 폴링, Optimistic Updates 같은 고급 기능은 React Query나 SWR이 여전히 강점을 가집니다.
관련 글
React 19 Server Actions 완벽 가이드 — API Route를 대체하는 새로운 표준
React 19에서 도입된 Server Actions의 작동 원리부터 폼 처리, 낙관적 업데이트, 캐시 재검증 전략까지 실무 중심으로 정리하였다.
Next.js 15 Partial Prerendering 완벽 가이드 — 정적과 동적 렌더링의 통합
Next.js 15에서 도입된 Partial Prerendering(PPR)의 개념, Suspense 경계 설계, 캐싱 전략 변경사항을 실무 관점에서 정리하였다.
TanStack Router v1 완벽 가이드 — 완전 타입 안전 React 라우터
TanStack Router v1의 완전 타입 안전 라우팅, 파일 기반 라우트 생성, 데이터 로더, 검색 파라미터 스키마를 실무 예제와 함께 정리하였다.