Zod 완벽 마스터
Zod의 핵심 개념과 실전 활용법
TypeScript중급
8시간
4개 항목
학습 진행률0 / 4 (0%)
학습 항목
1. React
Shadcn|Form|완벽|가이드
퀴즈튜토리얼
2. TypeScript
Zod|런타임|타입|검증|완벽|가이드
퀴즈튜토리얼
3. TypeScript
초급Zod|성능|최적화|완벽|가이드
퀴즈튜토리얼
4. TypeScript
고급tRPC|타입_세이프|API|설계|패턴
퀴즈튜토리얼
1 / 4
이미지 로딩 중...
Shadcn Form 완벽 가이드
Shadcn UI의 Form 컴포넌트와 React Hook Form, Zod를 활용한 고급 폼 구현 방법을 다룹니다. 타입 안전성과 유효성 검증, 에러 핸들링까지 실전에서 바로 사용할 수 있는 패턴을 제공합니다.
카테고리:React
언어:TypeScript
메인 태그:#React
서브 태그:
#Shadcn#ReactHookForm#Zod#FormValidation
들어가며
이 글에서는 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
#React#Shadcn#ReactHookForm#Zod#FormValidation