React 19의 use() 훅과 Server Actions 실전 도입기
배경
9월 중순 React 19가 정식 릴리즈되면서 use() 훅과 Server Actions가 안정화되었다. 사내 어드민 프로젝트를 Next.js 15와 함께 업그레이드하며 실제 도입해봤다.
use() 훅 적용
기존에는 Promise를 컴포넌트 밖에서 처리하거나 useEffect로 감싸야 했다. use()를 사용하면 컴포넌트 내부에서 직접 Promise를 unwrap할 수 있다.
// Before
function UserProfile({ userId }: Props) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
if (!user) return <Loading />;
return <div>{user.name}</div>;
}
// After
function UserProfile({ userPromise }: Props) {
const user = use(userPromise);
return <div>{user.name}</div>;
}
Suspense와 조합하면 로딩 상태 관리가 선언적으로 바뀐다. 다만 Promise는 부모에서 생성해야 한다는 제약이 있어서 데이터 페칭 로직 구조를 재설계해야 했다.
Server Actions 마이그레이션
폼 처리를 Server Actions로 전환했다. 기존 API 라우트 대비 타입 안정성이 확실히 좋았다.
// actions/user.ts
'use server'
export async function updateUser(formData: FormData) {
const name = formData.get('name') as string;
await db.user.update({ name });
revalidatePath('/profile');
}
// components/UserForm.tsx
function UserForm() {
return (
<form action={updateUser}>
<input name="name" />
<button type="submit">저장</button>
</form>
);
}
단, formData 파싱이 번거로워서 zod 스키마로 검증 레이어를 추가했다.
마주친 이슈
- use() 훅은 조건부로 호출할 수 없다. 분기 처리가 필요하면 컴포넌트를 분리해야 했다.
- Server Actions의 에러 핸들링이 명확하지 않아서 try-catch로 감싸고 결과 객체를 반환하는 패턴을 정립했다.
- 개발 환경에서 HMR 시 Server Actions가 간헐적으로 실패하는 버그가 있었다. Next.js 15.0.3에서 수정되었다.
결론
React 19의 새 기능들은 확실히 DX를 개선했다. 특히 Server Actions는 풀스택 개발 시 API 레이어를 줄여주는 효과가 컸다. 다만 프로덕션 적용 전 충분한 테스트가 필요하다.