본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 10. 31. · 59 Views
Shadcn Form 완벽 가이드
Shadcn UI의 Form 컴포넌트와 React Hook Form, Zod를 활용한 고급 폼 구현 방법을 다룹니다. 타입 안전성과 유효성 검증, 에러 핸들링까지 실전에서 바로 사용할 수 있는 패턴을 제공합니다.
들어가며
이 글에서는 Shadcn Form 완벽 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- 기본_폼_설정
- Form_컴포넌트_구조
- 에러_메시지_표시
- 복잡한_스키마_검증
- 동적_폼_배열
- 비동기_검증
- FormDescription_활용
- 조건부_필드_렌더링
- 제출_상태_관리
- Select_컴포넌트_통합
- 파일_업로드_처리
- 전역_에러_처리
1. 기본 폼 설정
개요
Shadcn Form은 React Hook Form과 Zod를 기반으로 타입 안전한 폼을 구축합니다. useForm 훅에 zodResolver를 연결하여 스키마 기반 검증을 수행합니다.
코드 예제
```tsx
const formSchema = z.object({
email: z.string().email("유효한 이메일을 입력하세요"),
password: z.string().min(8, "8자 이상 입력하세요"),
})
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: { email: "", password: "" },
})
### 설명
zodResolver가 Zod 스키마를 React Hook Form과 연결하고, z.infer로 타입을 자동 추론하여 타입 안전성을 보장합니다.
---
## 2. Form_컴포넌트_구조
### 개요
Form, FormField, FormItem, FormControl 등 계층적 구조로 폼 UI를 구성합니다. FormField는 Controller를 래핑하여 상태 관리를 단순화합니다.
### 코드 예제
```typescript
```tsx
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>이메일</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
</FormItem>
)}
/>
</form>
</Form>
### 설명
FormField의 render prop이 field 객체를 제공하며, 이를 Input에 spread하여 value, onChange 등을 자동 연결합니다.
---
## 3. 에러_메시지_표시
### 개요
FormMessage 컴포넌트가 필드별 에러를 자동으로 표시합니다. Zod 스키마의 에러 메시지가 검증 실패 시 렌더링됩니다.
### 코드 예제
```typescript
```tsx
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>사용자명</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
### 설명
FormMessage는 해당 필드의 에러 상태를 자동 감지하여 에러 메시지를 렌더링하며, 별도의 조건부 로직이 필요 없습니다.
---
## 4. 복잡한_스키마_검증
### 개요
Zod의 refine과 superRefine으로 커스텀 검증 로직을 추가할 수 있습니다. 여러 필드 간 관계를 검증할 때 유용합니다.
### 코드 예제
```typescript
```tsx
const schema = z.object({
password: z.string().min(8),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: "비밀번호가 일치하지 않습니다",
path: ["confirmPassword"],
})
### 설명
refine 메서드로 두 필드를 비교하고, path 옵션으로 에러를 특정 필드에 연결하여 정확한 위치에 메시지를 표시합니다.
---
## 5. 동적_폼_배열
### 개요
useFieldArray로 동적으로 추가/제거 가능한 폼 필드 배열을 관리합니다. append, remove 메서드로 간편하게 조작할 수 있습니다.
### 코드 예제
```typescript
```tsx
const { fields, append, remove } = useFieldArray({
control: form.control,
name: "items",
})
{fields.map((field, index) => (
<FormField key={field.id} name={`items.${index}.name`}
render={({ field }) => <Input {...field} />} />
))}
<Button onClick={() => append({ name: "" })}>추가</Button>
### 설명
useFieldArray가 배열 상태를 관리하고, 각 필드는 고유 id로 추적되어 React의 key 최적화를 활용합니다.
---
## 6. 비동기_검증
### 개요
Zod의 .refine 또는 superRefine에서 Promise를 반환하여 API 호출 등 비동기 검증을 수행할 수 있습니다.
### 코드 예제
```typescript
```tsx
const schema = z.object({
username: z.string().refine(
async (val) => {
const res = await fetch(`/api/check?name=${val}`)
return res.ok
},
{ message: "이미 사용중인 사용자명입니다" }
),
})
### 설명
refine 콜백에서 async 함수를 사용하면 폼 제출 전 비동기 검증이 실행되며, 결과에 따라 에러를 표시합니다.
---
## 7. FormDescription_활용
### 개요
FormDescription으로 필드 설명을 추가하여 사용자 가이드를 제공합니다. 접근성과 UX를 동시에 개선할 수 있습니다.
### 코드 예제
```typescript
```tsx
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>자기소개</FormLabel>
<FormControl>
<Textarea {...field} />
</FormControl>
<FormDescription>
200자 이내로 작성해주세요.
</FormDescription>
</FormItem>
)}
/>
### 설명
FormDescription은 FormMessage와 별개로 항상 표시되는 도움말 텍스트로, aria-describedby로 자동 연결됩니다.
---
## 8. 조건부_필드_렌더링
### 개요
watch를 사용하여 특정 필드 값에 따라 다른 필드를 동적으로 표시/숨김 처리할 수 있습니다.
### 코드 예제
```typescript
```tsx
const accountType = form.watch("accountType")
{accountType === "business" && (
<FormField
control={form.control}
name="companyName"
render={({ field }) => (
<FormItem>
<FormLabel>회사명</FormLabel>
<FormControl><Input {...field} /></FormControl>
</FormItem>
)}
/>
)}
### 설명
watch가 필드 값 변경을 구독하여 리렌더링을 트리거하며, 조건부 렌더링된 필드도 자동으로 폼 상태에 통합됩니다.
---
## 9. 제출_상태_관리
### 개요
formState를 통해 제출 중 상태, 에러, 터치 여부 등을 추적하고 UI에 반영할 수 있습니다.
### 코드 예제
```typescript
```tsx
const { isSubmitting, isValid } = form.formState
const onSubmit = async (data: z.infer<typeof schema>) => {
await fetch("/api/submit", { method: "POST", body: JSON.stringify(data) })
}
<Button type="submit" disabled={isSubmitting || !isValid}>
{isSubmitting ? "처리중..." : "제출"}
</Button>
### 설명
isSubmitting은 비동기 onSubmit 실행 중 자동으로 true가 되며, isValid는 모든 검증 통과 여부를 실시간 반영합니다.
---
## 10. Select_컴포넌트_통합
### 개요
Shadcn Select를 FormField와 통합할 때 onValueChange를 field.onChange에 연결하여 상태를 동기화합니다.
### 코드 예제
```typescript
```tsx
<FormField
control={form.control}
name="country"
render={({ field }) => (
<FormItem>
<FormControl>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<SelectTrigger><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="kr">한국</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
### 설명
Select의 onValueChange가 field.onChange와 연결되어 값 변경이 폼 상태에 자동 반영되며, defaultValue로 초기값을 설정합니다.
---
## 11. 파일_업로드_처리
### 개요
Input type="file"을 사용할 때 FileList 타입을 Zod로 검증하고, onChange를 커스터마이징하여 파일 상태를 관리합니다.
### 코드 예제
```typescript
```tsx
const schema = z.object({
file: z.instanceof(FileList).refine(
(files) => files.length > 0, "파일을 선택하세요"
),
})
<FormControl>
<Input type="file" {...field} value={field.value?.filename}
onChange={(e) => field.onChange(e.target.files)} />
</FormControl>
### 설명
FileList 인스턴스를 Zod로 검증하고, onChange에서 e.target.files를 직접 전달하여 파일 객체를 폼 상태에 저장합니다.
---
## 12. 전역_에러_처리
### 개요
setError로 서버 응답 에러를 특정 필드나 root에 수동으로 설정할 수 있습니다. API 에러를 폼 UI에 통합할 때 유용합니다.
### 코드 예제
```typescript
```tsx
const onSubmit = async (data: FormData) => {
try {
await api.submit(data)
} catch (error) {
form.setError("root", {
message: "서버 오류가 발생했습니다",
})
}
}
{form.formState.errors.root && <p>{form.formState.errors.root.message}</p>}
### 설명
setError의 root 키는 특정 필드가 아닌 폼 전체 에러를 나타내며, formState.errors.root로 접근하여 표시합니다.
---
## 마치며
이번 글에서는 Shadcn Form 완벽 가이드에 대해 알아보았습니다.
총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
### 관련 태그
#React #Shadcn #ReactHookForm #Zod #FormValidation
댓글 (0)
함께 보면 좋은 카드 뉴스
UX와 협업 패턴 완벽 가이드
AI 에이전트와 사용자 간의 효과적인 협업을 위한 UX 패턴을 다룹니다. 프롬프트 핸드오프부터 인터럽트 처리까지, 현대적인 에이전트 시스템 설계의 핵심을 배웁니다.
자가 치유 및 재시도 패턴 완벽 가이드
AI 에이전트와 분산 시스템에서 필수적인 자가 치유 패턴을 다룹니다. 에러 감지부터 서킷 브레이커까지, 시스템을 스스로 복구하는 탄력적인 코드 작성법을 배워봅니다.
Feedback Loops 컴파일러와 CI/CD 완벽 가이드
컴파일러 피드백 루프부터 CI/CD 파이프라인, 테스트 자동화, 자가 치유 빌드까지 현대 개발 워크플로우의 핵심을 다룹니다. 초급 개발자도 쉽게 이해할 수 있도록 실무 예제와 함께 설명합니다.
실전 MCP 통합 프로젝트 완벽 가이드
Model Context Protocol을 활용한 실전 통합 프로젝트를 처음부터 끝까지 구축하는 방법을 다룹니다. 아키텍처 설계부터 멀티 서버 통합, 모니터링, 배포까지 운영 레벨의 MCP 시스템을 구축하는 노하우를 담았습니다.
MCP 동적 도구 업데이트 완벽 가이드
AI 에이전트의 도구를 런타임에 동적으로 로딩하고 관리하는 방법을 알아봅니다. 플러그인 시스템 설계부터 핫 리로딩, 보안까지 실무에서 바로 적용할 수 있는 내용을 다룹니다.