React 19의 use() Hook과 Server Actions 도입 후기
배경
회사 대시보드 프로젝트에 React 19를 도입했다. 기존에는 Next.js 14 App Router에서 Server Component와 Client Component를 혼용하며 useEffect + fetch 패턴을 많이 사용했는데, 이번 기회에 use() Hook과 Server Actions를 본격적으로 적용해봤다.
use() Hook으로 Promise 처리
가장 눈에 띄는 변화는 비동기 데이터를 컴포넌트 레벨에서 자연스럽게 다룰 수 있게 된 점이다.
function UserProfile({ userPromise }) {
const user = use(userPromise);
return <div>{user.name}</div>;
}
기존에는 Suspense를 활용하려면 상위에서 fetch를 시작하고 prop으로 전달하는 패턴이 번거로웠다. use()는 Promise를 직접 unwrap하면서 Suspense와 자연스럽게 통합된다.
Server Actions 실전 적용
폼 처리 로직을 Server Actions로 전환했다. 이전에는 API Route를 만들고 클라이언트에서 fetch하는 방식이었다.
// actions.ts
'use server'
export async function updateUser(formData: FormData) {
const name = formData.get('name');
await db.user.update({ data: { name } });
revalidatePath('/profile');
}
// component
<form action={updateUser}>
<input name="name" />
<button type="submit">저장</button>
</form>
API Route 파일이 줄어들고 코드가 간결해졌다. revalidatePath로 캐시 무효화도 명시적으로 처리할 수 있어서 좋았다.
트레이드오프
use()는 조건부 렌더링 내에서 사용할 수 없어서, 기존 Hooks 규칙과 약간 다른 멘탈 모델이 필요했다. 팀원들과 코드 리뷰하며 패턴을 맞춰가는 중이다.
Server Actions는 편리하지만 에러 처리와 낙관적 업데이트를 구현할 때 useActionState(구 useFormState)를 함께 써야 해서 보일러플레이트가 생긴다.
결론
React 19는 서버-클라이언트 경계를 더 자연스럽게 만들어준다. 아직 생태계가 적응 중이지만, Next.js 15와 함께 사용하면 충분히 프로덕션에 적용할 만하다.