[React] React 19에서의 폼 처리 방식: Form Action, useActionState, useFormStatus, useOptimistic

2025. 5. 2. 16:43·Programming/React
728x90
반응형

 

이 글은 Udemy의 【한글자막】 React 완벽 가이드 2025 with React Router & Redux 를 수강하고 정리한 내용입니다.

📌 Form Action이란?

React 19에서는 <form action={someFunction}>처럼 action 속성에 함수를 전달할 수 있습니다. 이때 React는 다음과 같은 동작을 자동으로 수행합니다:

  • preventDefault() 자동 호출 → 직접 막을 필요 없음
  • FormData 객체를 해당 함수에 인자로 전달
  • <input>, <select> 등에 설정된 name 속성을 기반으로 FormData 구성
  • 제출이 완료되면 자동으로 폼 초기화
<form action={myAction}>
  <input name="email" />
  <button>Submit</button>
</form>

주의: 모든 입력 요소에는 반드시 name 속성을 지정해야 합니다. 그래야 FormData에 포함됩니다.


 

📌 useActionState 훅

useActionState는 폼의 상태와 액션 함수를 연결하여, 액션 함수의 반환값을 쉽게 UI에서 사용할 수 있도록 도와주는 React 19의 새로운 훅입니다.

const [formState, formAction, isPending] = useActionState(actionFn, initialState);
  • actionFn: 폼 제출 시 실행될 함수. (prevState, formData)를 인자로 받습니다.
  • initialState: 초기 상태
  • 반환 배열 :
    • formState: 현재 상태
    • formAction: form 요소의 action에 전달할 수 있는 React가 감지 가능한 래핑된 함수
      • 리액트가 래핑된 함수를 만드는 이유
        더보기

        🧠 React는 다음과 같은 일을 자동으로 해줘야 해요:

        • FormData를 자동으로 꺼내서 넘겨주고,
        • 상태 변화를 감지하고 (isPending 같은),
        • 폼 제출 중 상태를 관리하고,
        • 렌더링도 적절히 트리거해야 하죠.

        ⚙️ 그래서 React는 우리가 전달한 함수를 직접 쓰지 않고, 내부적으로 새로운 함수를 만듭니다.

        이 새 함수는 다음과 같은 일을 합니다:

        function wrappedAction(formData) {
          // isPending = true 로 설정
          // 우리 액션(myAction)을 실행
          // 결과를 상태로 저장
          // isPending = false 로 변경
        }
        
        

        이렇게 감싼 함수를 React가 추적 가능한 형태로 만들어서 formAction으로 돌려주는 겁니다.

    • isPending: 비동기 요청 중 여부
      • 폼이 현재 제출 중일때 true, 그렇지 않을때는 false 값을 가진다.

✅ 예시: 유효성 검사 및 에러 처리

function signupAction(prevState, formData) {
  const email = formData.get("email");
  const password = formData.get("password");

  const errors = [];

  if (!email.includes("@")) errors.push("잘못된 이메일 형식입니다.");
  if (!password || password.length < 6) errors.push("비밀번호는 6자 이상이어야 합니다.");

  if (errors.length > 0) {
    return { errors, enteredValues: { email, password } };
  }

  return { errors: null };
}
const [formState, formAction] = useActionState(signupAction, { errors: null });

<form action={formAction}>
  <input name="email" defaultValue={formState.enteredValues?.email} />
  <input name="password" type="password" defaultValue={formState.enteredValues?.password} />
  {formState.errors && <ul>{formState.errors.map(e => <li key={e}>{e}</li>)}</ul>}
  <button>Sign Up</button>
</form>

 

📌 useFormStatus: 제출 중 상태 추적

useFormStatus는 현재 <form>의 제출 상태와 관련된 다양한 정보를 제공하는 React 19의 훅입니다. 특히 폼의 자식 컴포넌트에서 제출 상태에 따라 UI를 동적으로 제어할 수 있도록 해줍니다.

  • 제출 중일 때 버튼 비활성화
  • 로딩 스피너 표시
  • 상태에 따라 메시지 표시 등
import { useFormStatus } from "react";

function SubmitButton() {
  const { pending } = useFormStatus(); // 현재 폼의 제출 상태

  return (
    <button disabled={pending}>
      {pending ? "Submitting..." : "Submit"}
    </button>
  );
}

주의

  • useFormStatus는 <form> 태그 안에 위치한 컴포넌트에서만 사용할 수 있습니다. <form> 외부에서 호출하면 동작하지 않습니다.
  • 해당 폼 요소의 직접적인 하위 자식 컴포넌트여야 합니다. 즉, 같은 컴포넌트 트리 안에 있어야 합니다.

✅ 반환되는 객체 속성

useFormStatus()는 단순히 pending만 제공하는 것이 아니라 여러 상태 정보를 담고 있는 객체를 반환합니다:

속성명 설명
pending 폼이 제출 중일 때 true, 그렇지 않으면 false
data 폼 제출 시 함께 전송되는 FormData 객체
method 폼 요청 방식 (예: "post" 등)
action 폼에 지정된 액션 함수 또는 URL
enctype 폼 인코딩 타입 (기본은 application/x-www-form-urlencoded)

✅ 일반적으로 가장 많이 사용하는 속성은 pending이며, 제출 상태에 따라 버튼을 비활성화하거나 메시지를 띄우는 데 활용됩니다.


 

📌 useOptimistic: 낙관적 UI 업데이트

useOptimistic은 사용자의 액션이 서버에 반영되기 전, 예상 결과를 먼저 UI에 반영하여 즉각적인 피드백을 주는 방식입니다. 예를 들어 유저가 투표 버튼을 클릭하면, 서버에 반영되기 전에 일단 UI 상의 투표 수부터 바꿔 보여줄 수 있습니다.

✅ 사용법

const [optimisticState, updateOptimistically] = useOptimistic(
  initialValue,          
  (previousValue, action) => newValue 
);

1️⃣ 첫 번째 인자: 초기 상태 값

  • 예: 현재 투표 수 (votes)

2️⃣ 두 번째 인자: 업데이트 함수

  • React가 useOptimistic 내부적으로 폼 제출 중에 호출합니다.
  • 첫 번째 매개변수는 React가 전달하는 이전 상태(previousValue)입니다.
  • 두 번째 매개변수는 개발자가 updateOptimistically() 호출 시 전달한 사용자 정의 값 입니다.

🔁 반환값

  1. optimisticState
    • 낙관적으로 관리되는 상태입니다.
    • 폼이 제출되는 동안에만 이 값이 UI에 표시됩니다.
    • 폼 제출이 끝나면 이 상태는 사라지고, 앱의 원래 상태로 복귀합니다.
  2. updateOptimistically(action)
    • 낙관적 UI 상태를 적용하고 싶을 때 사용하는 함수입니다.
    • 폼 액션 내부에서 호출됩니다.
    • 인자로 넘긴 값은 useOptimistic()의 두 번째 인자인 함수의 두 번째 매개변수로 전달됩니다.

✅ 실제 예시: 투표 수를 즉시 반영하는 컴포넌트

import { OpinionsContext } from "../store/opinions-context.jsx";
import { use, useActionState, useOptimistic } from "react";

export function Opinion({ opinion: { id, title, body, userName, votes } }) {
  const { upvoteOpinion, downvoteOpinion } = use(OpinionsContext);

  // 낙관적 상태 설정: 'up'이면 +1, 'down'이면 -1
  const [optimisticVotes, setVotesOptimistically] = useOptimistic(
    votes,
    (prevVotes, mode) => (mode === "up" ? prevVotes + 1 : prevVotes - 1),
  );

  // 서버 요청 전 낙관적 업데이트 후 실제 서버에 반영
  async function upVoteAction() {
    setVotesOptimistically("up");
    await upvoteOpinion(id);
  }

  async function downVoteAction() {
    setVotesOptimistically("down");
    await downvoteOpinion(id);
  }

  // 각각의 폼 액션 상태 관리
  const [upVoteFormState, upVoteFormAction, upVotePending] =
    useActionState(upVoteAction);
  const [downVoteFormState, downVoteFormAction, downVotePending] =
    useActionState(downVoteAction);

  return (
    <article>
      <header>
        <h3>{title}</h3>
        <p>Shared by {userName}</p>
      </header>
      <p>{body}</p>
      <form className="votes">
        <buttonformAction={upVoteFormAction}
          disabled={upVotePending || downVotePending}
        >
          👍
        </button>

        <span>{optimisticVotes}</span>

        <buttonformAction={downVoteFormAction}
          disabled={upVotePending || downVotePending}
        >
          👎
        </button>
      </form>
    </article>
  );
}

  • useOptimistic(초기값, 업데이트 함수)를 사용하여 상태를 낙관적으로 관리합니다.
  • 실제 비동기 작업이 완료되기 전에 setVotesOptimistically("up") 등을 호출해 UI를 즉시 반영합니다.
  • 실제 서버 작업은 await upvoteOpinion(id)에서 진행됩니다.
  • 폼 제출 중에는 optimisticVotes 값이 화면에 표시되고, 제출이 완료되면 이 값은 사라지고 실제 상태로 대체 됩니다.
  • 만일 폼 제출 과정에서 오류가 발생했다면 이전 상태로 롤백되어집니다.

 

📌 버튼마다 다른 액션 처리하기

React의 폼은 각 버튼에 formAction 속성을 지정하면, 같은 <form> 안에 있어도 서로 다른 함수를 실행할 수 있습니다.

<form>
  <button formAction={saveDraft}>임시 저장</button>
  <button formAction={submitFinal}>최종 제출</button>
</form>

 

📌 Form Action vs onSubmit

비교 항목 Form Action onSubmit

선언 방식 <form action={function}> <form onSubmit={handler}>
기본 동작 방지 자동 수동 (event.preventDefault())
데이터 수집 방식 자동으로 FormData 제공 event.target에서 수동 수집
비동기 처리 지원 ✔ (Promise 지원) ✔ (직접 핸들링)
상태 관리 useActionState로 간편하게 처리 수동으로 useState 등 사용

 

 

 

 


도움이 되셨다면 ❤️ 좋아요와 댓글로 피드백 주세요!

궁금한 점이 있다면 언제든지 질문 환영입니다 😄

지금까지 읽어주셔서 감사합니다!

반응형

'Programming > React' 카테고리의 다른 글

[React] React에서 폼(form) 다루기 :: 추출, 유효성검증, 초기화, 제출  (0) 2025.04.29
[React] 왜 React에서는 form submit을 기본으로 두면 안 될까?  (1) 2025.04.29
[React] 리액트 컴포넌트 스타일링  (1) 2025.04.23
[React] Tailwind 사용하여 스타일링 하기  (0) 2025.04.23
[React] Styled Components 사용하기  (0) 2025.04.23
'Programming/React' 카테고리의 다른 글
  • [React] React에서 폼(form) 다루기 :: 추출, 유효성검증, 초기화, 제출
  • [React] 왜 React에서는 form submit을 기본으로 두면 안 될까?
  • [React] 리액트 컴포넌트 스타일링
  • [React] Tailwind 사용하여 스타일링 하기
제가안난데여♪(´ε`*)
제가안난데여♪(´ε`*)
기억의 유한함을 기록의 무한함으로 ✍️ 예비 개발자가 꿈꾸는 공간 여기는 안나의 개발 블로그 💻
  • 제가안난데여♪(´ε`*)
    안나의 전두엽 어딘가 🧠
    제가안난데여♪(´ε`*)
    기억의 유한함을 기록의 무한함으로 ✍️ 예비 개발자가 꿈꾸는 공간 여기는 안나의 개발 블로그 💻
  • 전체
    오늘
    어제
    • 분류 전체보기 (128)
      • 간단하게 한스푼🥄 (1)
      • Programming (56)
        • Spring (16)
        • Vue.js (6)
        • Deep Learning (3)
        • DataBase (5)
        • React (26)
      • DevOps (21)
        • Docker (12)
        • Linux (4)
      • Algorithm (46)
        • 알고리즘 정리 (5)
        • 자료구조 (0)
        • PS - 백준 (28)
        • 99클럽 코테 스터디 (13)
      • Project (0)
        • CampFire (0)
      • 안나의 취뽀일기 (˵ •̀ ᴗ - ˵ ) ✧ (4)
        • SSAFY_9기 (2)
        • SW 부트캠프 (2)
  • 잔디 달력

    «   2026/01   »
    일 월 화 수 목 금 토
    1 2 3
    4 5 6 7 8 9 10
    11 12 13 14 15 16 17
    18 19 20 21 22 23 24
    25 26 27 28 29 30 31
  • 인기 글

  • 태그

    Vue
    코딩테스트 준비
    java 백준
    백준 java
    자바 스택
    React
    til
    리액트 상태
    99클럽
    front-end
    Vue.js 입문하기
    도커컨테이너
    알고리즘
    java
    백준
    docker
    Vue.js
    springboot
    stack
    개발자 취업
    백준 구현문제
    도커
    greedy
    linux
    항해99
    Spring
    자바
    topology sort
    java stack
    김영한
  • 01-24 06:09
    반응형
  • hELLO· Designed By정상우.v4.10.3
제가안난데여♪(´ε`*)
[React] React 19에서의 폼 처리 방식: Form Action, useActionState, useFormStatus, useOptimistic
상단으로

티스토리툴바