--- id: react-form-state-patterns title: React Form 상태 패턴 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [react, forms, validation, react-hook-form, vibe-coding] tech_stack: { language: "TypeScript / React 18+ / react-hook-form / zod", applicable_to: ["Web"] } applied_in: [] aliases: [form library, validation schema, dirty fields] --- # React Form 상태 패턴 > 큰 form 에서 매 keystroke setState 면 재렌더 폭주. **react-hook-form (uncontrolled + ref)** 또는 **Server Action + form data** 가 답. 검증은 zod 스키마. ## 📖 핵심 개념 form 의 어려움: 검증 / dirty 추적 / 제출 / 에러 표시 / async 검증 / 의존 필드. 직접 구현하면 복잡 + 성능 함정. ## 💻 코드 패턴 ### 1. react-hook-form + zod ```tsx import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; const schema = z.object({ email: z.string().email(), age: z.coerce.number().int().min(13), }); type FormData = z.infer; function SignupForm() { const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm({ resolver: zodResolver(schema) }); return (
{ await api.signup(data); })}> {errors.email &&

{errors.email.message}

}
); } ``` ### 2. Next.js Server Action ```tsx // 'use server' async function signup(formData: FormData) { const parsed = schema.safeParse(Object.fromEntries(formData)); if (!parsed.success) return { error: parsed.error.flatten() }; await db.users.create(parsed.data); } // Client (또는 server-rendered form)
...
``` ## 🤔 의사결정 기준 | form 크기 / 복잡도 | 권장 | |---|---| | 1~3 input, 검증 단순 | useState controlled | | 5+ input, 다중 검증 | react-hook-form + zod | | Server-side validation 강제 | Server Action + zod (서버에서 재검증 필수) | | 다단계 wizard | react-hook-form + form context, 또는 zustand | | 동적 필드 추가/제거 | useFieldArray | ## ❌ 안티패턴 - **모든 input 에 useState**: 매 keystroke 전체 form 재렌더. - **클라이언트 검증만 신뢰**: 서버에서 반드시 재검증. 클라는 UX, 서버는 진실. - **중복 schema** (TS interface + 별도 validator): zod 의 z.infer 로 single source. - **submit 후 input 안 비움**: form.reset() 호출. - **disabled button 만으로 다중 제출 막기**: 빠른 더블 클릭에 race. Idempotency key 또는 mutation queue. - **에러 메시지를 toast로만**: 어떤 필드가 문제인지 보여야 함. 인라인. ## 🤖 LLM 활용 힌트 - "react-hook-form + zod 조합으로 작성, 검증은 클라+서버 둘 다" 명시. - 큰 form 일 때 LLM이 useState 다발 만들면 react-hook-form 으로 전환 요청. ## 🔗 관련 문서 - [[React_Controlled_vs_Uncontrolled]] - [[Idempotent_Operations]]