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>
);
}
이 코드에서 폼이 제출되면
- formAction 함수가 호출되고 폼 데이터가 전달된다
- isPending이 true로 변경되어 버튼이 비활성화된다
- useActionState의 첫 번째 매개변수 함수가 실행되어 새 상태를 계산한다
- 결과가 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 |