본문 바로가기

React

🚀 React 19 프리뷰: useFormState, useActionState 등 새 훅 분석

🧭 들어가며

작년 12월에, React 19는 드디어 공식 프리뷰(알파)를 통해 여러 신기능들을 공개했습니다.

그중에서도 특히 주목할 만한 기능은 폼 상태 처리와 서버 액션에 대한 새로운 접근 방식입니다.

 

 

서버와 클라이언트가 자연스럽게 협업할 수 있도록 만들어주는 훅,

바로 useFormState, useFormStatus, useActionState입니다.

 

이번 글에서는 이 새로운 훅들이 어떤 문제를 해결해주는지,
그리고 어떻게 사용할 수 있는지를 예제와 함께 살펴보도록 하겠습니다.

 

 

 


 

🎯 기존의 문제점: React에서 폼을 다룰 때 생기는 일들

React에서는 일반적으로 폼 상태를 이렇게 다룹니다:

  • useState로 입력값을 관리하고
  • onChange, onSubmit으로 이벤트를 제어하며
  • 서버 요청은 fetch나 axios로 따로 수행하고
  • 로딩, 에러, 성공 여부를 직접 상태로 만들어 처리함

복잡하죠? 특히 상태가 많아질수록 유지보수가 어려워집니다.
React 19는 이 과정을 더 직관적이고 선언적으로 바꾸고자 합니다.

 

 

 


 

🧪 1. useFormState: 서버 액션과 상태를 연결

📌 기본 구조

const [state, formAction] = useFormState(serverAction, initialState);

 

 

  • serverAction: 서버에서 실행되는 함수 (서버 액션)
  • initialState: 초기 상태 (예: { message: "" })
  • state: 서버에서 반환한 최신 상태
  • formAction: <form action={formAction}>에 연결

 

✅ 예시 코드

// app/actions.ts
export async function login(prevState: any, formData: FormData) {
  const username = formData.get("username");
  const password = formData.get("password");

  if (username !== "admin" || password !== "1234") {
    return { message: "로그인 실패" };
  }

  return { message: "로그인 성공" };
}

 

// app/page.tsx
'use client';

import { useFormState } from 'react-dom';
import { login } from './actions';

export default function LoginForm() {
  const [state, formAction] = useFormState(login, { message: '' });

  return (
    <form action={formAction}>
      <input name="username" placeholder="Username" />
      <input name="password" type="password" placeholder="Password" />
      <button type="submit">로그인</button>
      <p>{state.message}</p>
    </form>
  );
}

 

📌 핵심 포인트:

  • 별도 useState 없이도 서버 반환 상태 관리 가능
  • 폼 전송 결과는 자동으로 state에 반영됨

 


 

🧭 2. useFormStatus: 폼 상태를 쉽고 간결하게 확인

useFormStatus는 폼 전송 중인지, 성공했는지 등을 간단히 확인할 수 있는 훅입니다.

 

'use client';

import { useFormStatus } from 'react-dom';

function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button type="submit" disabled={pending}>
      {pending ? '로그인 중...' : '로그인'}
    </button>
  );
}

 

✅ 장점:

  • 별도로 로딩 상태 만들지 않아도 됨
  • 현재 전송 중인지 여부만으로 UX 향상 가능

 


 

⚙️ 3. useActionState: 폼 없이 서버 액션과 상태 연동

useFormState는 <form>과 함께 사용해야 하지만,
useActionState는 버튼 클릭 등 다른 인터랙션에도 사용 가능합니다.

'use client';

import { useActionState } from 'react-dom';
import { doSomething } from './actions';

export default function SomeComponent() {
  const [state, action] = useActionState(doSomething, { count: 0 });

  return (
    <div>
      <button onClick={() => action()}>+1</button>
      <p>Count: {state.count}</p>
    </div>
  );
}

 

  • 서버 액션에서 count를 1씩 증가시켜 반환할 수 있음
  • 별도 이벤트 핸들러 없이 상태 반영 가능

 

 


 

💡 왜 이게 중요한가?

  • 더 선언적인 폼 처리
  • 클라이언트-서버의 자연스러운 연결
  • 로딩/성공/실패 상태 관리 간소화
  • 코드량 감소 + 명확한 책임 분리

결국 이 흐름은 React 19가 추구하는 **서버 중심의 아키텍처(Server-first)**와 맞닿아 있어요.

바로 직전 포스트인 RSC와 연관이 있는..! 미래 지향적인 변화의 흐름이라고 볼 수 있습니다.


클라이언트는 최소한의 UI만 책임지고, 주요 로직은 서버에서 다룰 수 있게 됩니다.

 

 


 

🚨 현재 상태 및 주의점

  • React 19는 아직 알파(Alpha) 버전이에요.
  • Next.js 14의 App Router 환경에서 주로 실험 가능
  • 서버 액션은 서버 컴포넌트 안에서만 정의 가능

실무 도입은 아직 이르지만, 실험 프로젝트나 학습용으론 매우 추천할 만합니다!

 

 

 


 

✍️ 마무리하며

 

React 19의 새 훅들은 단순한 API 추가가 아닙니다.
**"클라이언트-서버의 경계를 허물고, 선언적으로 동작하는 React의 미래"**에 대한 방향성이 담겨 있어요.

폼 상태를 일일이 핸들링하며 고생했던 프론트엔드 개발자들에게는 아주 반가운 소식인 것 같습니다.

 

 

 

 

 

 

 

🔗 참고 자료