Form Action

1. Form Action이란?

React 19에서는 기존 HTML 폼의 action 속성을 재해석하여 새로운 방식의 폼 처리 메커니즘을 도입했다.

이전에는 폼 제출과 관련된 작업을 처리하기 위해 onSubmit 이벤트 핸들러를 사용하고 event.preventDefault()를 호출하여 페이지 새로고침을 방지해야 했다.

React 19의 formAction은 더 간결하고 직관적인 방식으로 폼 처리를 가능하게 한다.

 

1.1. 기존 HTML vs React의 차이점

기존 HTML에서 action 속성은 폼이 제출될 URL을 지정하는 방식이다.

<form action="/submit-form" method="post">
  <input name="username" type="text">
  <button type="submit">제출</button>
</form>

 

반면, React 19에서는 action 속성에 JavaScript 함수를 직접 할당할 수 있다.

이 접근법은 폼 처리 로직을 더 직관적으로 작성할 수 있게 해준다.

function handleSubmit(formData) {
  const username = formData.get('username');
  console.log(`사용자명: ${username}`);
}

function LoginForm() {
  return (
    <form action={handleSubmit}>
      <input name="username" type="text" />
      <button type="submit">제출</button>
    </form>
  );
}

 

1.2. FormData 객체

React 19의 formAction 함수는 FormData 객체를 매개변수로 받는다.

FormData는 웹 API의 일부로, 폼 데이터를 쉽게 다루기 위한 인터페이스를 제공한다.

 

  • formData.get('name'): 특정 이름의 필드 값을 가져온다
  • formData.getAll('name'): 같은 이름을 가진 모든 필드 값을 배열로 가져온다
  • formData.has('name'): 특정 이름의 필드가 있는지 확인한다
  • formData.append('name', 'value'): 새 필드를 추가한다
  • formData.delete('name'): 특정 이름의 필드를 삭제한다

각 입력 필드는 name 속성을 통해 FormData 객체 내에서 식별된다.

 

1.3. 기본 사용법

React는 폼 제출 시 자동으로 event.preventDefault()를 실행하여 페이지 새로고침을 방지한다.

폼 요소의 name 속성은 FormData 객체에서 해당 값에 접근하는 키가 된다.
모든 폼 요소에 고유하고 의미 있는 name 속성을 지정하는 것이 중요하다.

 

ex)

function handleSubmit(formData) {
  // formData에는 폼 필드의 값들이 포함됨
  const name = formData.get('name');
  const email = formData.get('email');
  console.log(`이름: ${name}, 이메일: ${email}`);
}

function MyForm() {
  return (
    <form action={handleSubmit}>
      <input name="name" type="text" />
      <input name="email" type="email" />
      <button type="submit">제출</button>
    </form>
  );
}

 

 

2. useActionState 훅

React 19는 폼 제출 상태와 결과를 관리하기 위한 새로운 훅인 useActionState를 도입했다.

이 훅은 폼 제출 과정의 상태 관리를 간소화하고, 로딩 상태를 쉽게 처리할 수 있게 해준다.

 

2.1. 기본 구조와 반환값

const [state, formAction, isPending] = useActionState(actionFunction, initialState);

 

반환되는 3개의 값:

  • state: 현재 상태 값 (폼 제출 결과를 포함)
  • formAction: 폼의 action 속성에 바인딩할 함수
  • isPending: 폼 제출이 진행 중인지 나타내는 불리언 값

 

2.2. 매개변수

useActionState는 두 개의 매개변수를 받는다.

  • 첫 번째 매개변수: (previousState, formData) => newState 형태의 함수
    • previousState: 이전 상태 값
    • formData: 제출된 폼 데이터
    • 반환값은 새로운 상태가 된다
  • 두 번째 매개변수: 초기 상태 값
    • 첫 렌더링 시 사용될 상태의 초기값

 

2.3. 예시 코드

ex)

import { useActionState } from 'react';

function MyForm() {
  const [state, formAction, isPending] = useActionState(
    (previousState, formData) => {
      // 폼 처리 로직
      const name = formData.get('name');
      return `Hello, ${name}!`; // 새로운 상태 반환
    }, 
    '초기 상태' // 초기 상태 값
  );

  return (
    <div>
      <form action={formAction}>
        <input name="name" type="text" />
        <button type="submit" disabled={isPending}>
          {isPending ? '제출 중...' : '제출'}
        </button>
      </form>
      <p>상태: {state}</p>
    </div>
  );
}

 

이 코드에서 폼이 제출되면

  1. formAction 함수가 호출되고 폼 데이터가 전달된다
  2. isPending이 true로 변경되어 버튼이 비활성화된다
  3. useActionState의 첫 번째 매개변수 함수가 실행되어 새 상태를 계산한다
  4. 결과가 state 값으로 업데이트되고 isPending은 다시 false가 된다

 

2.4. 주의사항

useActionState의 첫 번째 매개변수 함수는 반드시 다음 형태여야 한다:

(previousState, formData) => newState

 

이전 상태를 사용하지 않더라도 매개변수 구조는 유지해야 한다.

 

 

3. 사용 패턴

3.1. 중첩된 폼과 버튼별 action

React 19에서는 각 제출 버튼에 개별 formAction을 지정할 수 있어 하나의 폼에서 여러 작업을 처리할 수 있다.

 

ex)

function MyForm() {
  return (
    <form>
      <input name="username" />
      <button 
        formAction={(formData) => console.log('저장됨:', formData.get('username'))}>
        저장
      </button>
      <button 
        formAction={(formData) => console.log('삭제됨:', formData.get('username'))}>
        삭제
      </button>
    </form>
  );
}

 

이 패턴은 사용자가 어떤 버튼을 클릭했는지에 따라 다른 작업을 수행할 수 있게 한다.

 

3.2. 폼 유효성 검사

formAction과 함께 클라이언트 측 유효성 검사를 구현할 수 있다.

 

ex)

function validateAndSubmit(formData) {
  const email = formData.get('email');

  if (!email || !email.includes('@')) {
    // 유효성 검사 실패 처리
    document.getElementById('error').textContent = '유효한 이메일을 입력하세요';
    return;
  }

  // 유효성 검사 통과, 정상 제출 처리
  console.log('제출 성공:', email);
}

function MyForm() {
  return (
    <form action={validateAndSubmit}>
      <input name="email" type="email" />
      <div id="error" style={{ color: 'red' }}></div>
      <button type="submit">제출</button>
    </form>
  );
}

 

HTML5의 기본 폼 유효성 검사와 함께 사용하면 더 강력한 검증 체계를 구축할 수 있다.

 

3.3. 비동기 처리

formAction 함수는 비동기 함수일 수 있으며, 이를 통해 API 호출 등의 작업을 수행할 수 있다.

 

ex)

async function handleSubmit(formData) {
  const username = formData.get('username');
  const password = formData.get('password');

  try {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify({ username, password }),
      headers: { 'Content-Type': 'application/json' }
    });

    const result = await response.json();

    if (result.success) {
      window.location.href = '/dashboard';
    } else {
      document.getElementById('loginError').textContent = result.error;
    }
  } catch (error) {
    console.error('로그인 오류:', error);
  }
}

 

useActionState와 함께 사용하면 로딩 상태와 에러 처리가 더욱 간편해진다.

'학습 > React' 카테고리의 다른 글

Form과 사용자 입력 처리  (0) 2025.05.01
리액트 기초 - 강의 정리 4 : fetch, 커스텀  (0) 2025.04.26
Million.js  (0) 2025.04.23
React key  (0) 2025.04.23
useMemo, memo, useCallback  (0) 2025.04.22