신청 폼
입력·선택·날짜 컴포넌트를 조합하고 오류 메시지를 처리하는 방법입니다.
프로그램 신청 폼을 예시로 폼 컴포넌트의 조합과 오류 메시지 처리 패턴을 다룹니다.
설치
레이블과 도움말
label을 전달하면 <label> 요소와 입력 요소가 자동으로 연결됩니다. hint는 입력 아래 도움말로 렌더링되며 aria-describedby로 연결됩니다.
import { TextInput } from "@/components/ui/krds/(input)/text-input"
export function NameField() {
return <TextInput label="이름" placeholder="홍길동" hint="주민등록상 이름을 입력하세요" />
}오류 메시지
검증에 실패하면 error에 메시지를 전달합니다.
export function EmailField() {
return <TextInput label="이메일" placeholder="you@example.com" error="이메일 형식이 아닙니다" />
}error 하나로 다음이 함께 처리됩니다.
- 입력 아래 오류 메시지 렌더링 (KRDS 규칙: 메시지는 항상 컨트롤 아래)
- 메시지에
role="alert"적용 — 스크린리더가 즉시 낭독 - 입력 요소에
aria-invalid="true"와aria-describedby자동 연결 - 오류 테두리 표시 — 시각 스타일이
aria-invalid셀렉터에 연결되어 있어 화면과 보조기술 상태가 항상 일치
success, information prop도 같은 방식으로 동작하며 우선순위는 error > success > information > hint입니다.
선택 컴포넌트
Select는 options 배열을 받습니다. Select와 Checkbox 모두 Radix Primitives 기반으로 키보드 인터랙션과 ARIA 속성이 내장되어 있습니다.
import { Checkbox } from "@/components/ui/krds/(selection)/checkbox"
import { Select } from "@/components/ui/krds/(selection)/select"
const PROGRAM_OPTIONS = [
{ value: "tour", label: "견학 프로그램" },
{ value: "edu", label: "교육 프로그램" },
{ value: "event", label: "체험 행사" },
]
export function ProgramFields() {
return (
<>
<Select options={PROGRAM_OPTIONS} label="신청 프로그램" />
<Checkbox label="개인정보 수집·이용에 동의합니다" />
</>
)
}날짜 입력
DateInput은 달력 팝오버로 값을 선택합니다. 직접 타이핑은 의도적으로 제한되어 있어 자유 입력으로 인한 형식 오류가 발생하지 않습니다.
import { DateInput } from "@/components/ui/krds/(input)/date-input"
export function VisitDateField() {
return <DateInput label="방문 희망일" />
}폼 조합
"use client"
import { useState } from "react"
import { Button } from "@/components/ui/krds/(action)/button"
import { DateInput } from "@/components/ui/krds/(input)/date-input"
import { TextInput } from "@/components/ui/krds/(input)/text-input"
import { Checkbox } from "@/components/ui/krds/(selection)/checkbox"
import { Select } from "@/components/ui/krds/(selection)/select"
const PROGRAM_OPTIONS = [
{ value: "tour", label: "견학 프로그램" },
{ value: "edu", label: "교육 프로그램" },
]
export default function ApplyPage() {
const [email, setEmail] = useState("")
const [submitted, setSubmitted] = useState(false)
const emailError = submitted && !email.includes("@") ? "이메일 형식이 아닙니다" : undefined
return (
<form
className="mx-auto flex max-w-[480px] flex-col gap-6 py-10"
onSubmit={(e) => {
e.preventDefault()
setSubmitted(true)
}}
>
<TextInput label="이름" placeholder="홍길동" />
<TextInput
label="이메일"
placeholder="you@example.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
error={emailError}
/>
<Select options={PROGRAM_OPTIONS} label="신청 프로그램" />
<DateInput label="방문 희망일" />
<Checkbox label="개인정보 수집·이용에 동의합니다" />
<Button type="submit" size="lg">
신청하기
</Button>
</form>
)
}입력 컴포넌트는 특정 폼 라이브러리에 의존하지 않으며 controlled/uncontrolled 모두 지원합니다. react-hook-form, TanStack Form과의 통합은 폼 문서를 참고하세요.
다음 단계
내비게이션에서 메뉴와 경로 표시를 구성합니다.