C.W.K.
Stream
Lesson 01 of 06 · published

<form action={fn}> — React 19 의 form 모델

~13 min · form-action, react-19, forms

Level 0React 입문자
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
React 19 가 HTML form 의 action 속성 오버로드. 문자열 전달 = 브라우저가 1993년부터 해온 대로 제출 처리. 함수 전달 = React 가 라이프사이클 소유.

두 모드

  • <form action="/save" method="POST"> — 풀-페이지 브라우저 제출. JS 없이 동작. 웹이 항상 가졌던 모드.
  • <form action={saveFn}> — React 가 제출 인터셉트, saveFn(formData) 호출, pending state 관리, 기본 브라우저 제출 방지. JS 필요. React 19 가 추가한 모드.

함수 모드가 Actions 스토리 잠금 해제. 다음 다섯 레슨의 hook (useActionState, useFormStatus, useOptimistic) 다 form 이 function-action 모드라고 가정.

입력으로 FormData

Action 함수가 FormData 객체 받음 — 직렬화된 form 필드의 표준 브라우저 타입. formData.get('name') 으로 필드 읽음; formData.get('avatar') instanceof File 로 파일 체크. JSON 파싱 없음, 수동 필드 수집 없음.

제출에 reset

Action 이 성공적으로 돈 후 React 가 form 의 uncontrolled input 지움 (form reset). 값 유지 원하면 (controlled input, 멀티-스텝 form) useState 로 본인이 값 관리.

네이티브 form 동작에 무슨 일

HTML 유효성이 먼저 발화 (required, type=email, minlength). 사용자 입력이 네이티브 유효성 fail 하면 Action 안 돔. Action 안에선 이미 유효성 검증된 입력 받음 — 다만 보안 위해 서버 사이드 유효성도 여전히 (lesson 6 Zod).

Form 이 모델, 함수가 핸들러. React 19 의 Action 모델이 네이티브 form 계약에 기댐 — input 이름 짓고, 브라우저가 수집하게, React 한테 실제 작업할 함수 줘. 결과: 옛 onSubmit + e.preventDefault + 필드 수집 + fetch + setState 춤보다 짧음.

Code

가장 단순한 Action — <form action> 에 전달된 함수·tsx
async function saveDraft(formData: FormData) {
  const title = formData.get("title") as string;
  const body = formData.get("body") as string;
  await fetch("/api/drafts", {
    method: "POST",
    body: JSON.stringify({ title, body }),
  });
}

function DraftForm() {
  return (
    <form action={saveDraft} className="space-y-4">
      <input name="title" required className="w-full px-3 py-2 border" />
      <textarea name="body" required className="w-full px-3 py-2 border" />
      <button type="submit" className="px-4 py-2 bg-brand text-bg rounded">
        Save draft
      </button>
    </form>
  );
}

// e.preventDefault 없음, title/body 의 useState 없음, onSubmit 의 fetch 없음.
// React 가 제출 시 FormData 와 함께 saveDraft 돌림.
Form 대신 단일 버튼에 동작·tsx
// 자기 action 가진 버튼 — 그 submitter 에 form 의 action 오버라이드.
async function deleteDraft() {
  await fetch("/api/drafts/current", { method: "DELETE" });
}

function DraftForm2() {
  return (
    <form action={saveDraft}>
      <input name="title" required />
      <textarea name="body" required />
      <div className="flex gap-2">
        <button type="submit">Save</button>
        <button type="submit" formAction={deleteDraft} className="text-danger">
          Delete
        </button>
      </div>
    </form>
  );
}

// 'Delete' 클릭이 saveDraft 대신 deleteDraft 호출.
// 같은 form, 버튼 둘, action 둘. 네이티브 HTML 이 지원; React 19 가 honor.

External links

Exercise

텍스트 input + submit 버튼 받는 <NewMessageForm> 빌드. <form action={sendMessage}> 연결 — sendMessage 가 가짜 endpoint 에 POST 하고 결과 로그하는 async 함수. 제출 후 form 지워지는지 확인 (uncontrolled input 패턴). 그 다음 POST 없이 저장하는 formAction={saveDraft} 가진 두 번째 버튼 추가. 각 버튼 클릭이 올바른 함수 돌리는지 확인.
Hint
Uncontrolled input (no value={...} prop) 으로 React 19 의 reset 동작 봐. useState 로 control 하면 표시 값 유지 — action 은 돌지만 state 본인이 reset 해야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.