🎉 디자인 시스템 v5.0 — 본문 16px · 컴포넌트 47종 · 스타터 킷 제공
개발패턴 5종 ★③ 폼 · 검증 · 저장

③ 폼 · 검증 · 저장

입력받고, 틀린 것을 알려주고, 저장합니다. 오류는 색이 아니라 문장으로 알리는 것이 핵심입니다.

배치 순서

#구획쓰는 컴포넌트
1단계 표시기 (3단계 이상일 때만)단계 표시기
2섹션별 카드 — 라벨·입력·도움말·오류카드 · 입력
3하단 액션 — 취소(ghost) / 저장(primary)버튼
4저장 후 알림토스트

규칙

오류는 문장으로

<label class="jt-label" for="code" data-required>품목코드</label>
<input class="jt-input" id="code" aria-invalid="true" aria-describedby="e1">
<div class="jt-error" id="e1">코드는 CP-JT + 숫자 4자리입니다. 예: CP-JT0299</div>

세 가지가 한 세트입니다 — aria-invalid · aria-describedby · .jt-error 문장. 빨간 테두리만으로는 색각 이상 사용자가 무엇이 틀렸는지 알 수 없습니다.

좋은 오류 문장은 예시를 포함합니다. “형식이 올바르지 않습니다”보다 “CP-JT + 숫자 4자리입니다. 예: CP-JT0299”가 낫습니다.

사과하지 마세요

이렇게 ✅이러지 마세요 🚫
코드는 CP-JT + 숫자 4자리입니다죄송합니다. 입력값에 문제가 있습니다
제조일자가 유통기한보다 늦습니다오류가 발생했습니다
이미 등록된 LOT입니다잘못된 요청입니다

저장 중에는 버튼을 잠급니다

<button class="jt-btn" data-loading="true">저장 중</button>

data-loading="true" 하나로 스피너와 pointer-events:none이 함께 걸립니다. 두 번 눌려 중복 저장되는 사고가 실제로 가장 흔합니다.

필수 표시는 라벨에

<label class="jt-label" data-required>품목명</label>

필드 옆에 별표를 흩뿌리지 말고 라벨에 붙입니다. 그리고 필수가 아닌 것이 더 많다면 반대로 “(선택)“을 붙이는 편이 읽기 쉽습니다.

고정 height 금지

번역하면 라벨과 도움말이 길어집니다. min-height만 쓰세요. 여러 줄 입력은 resize:vertical로 사용자가 늘릴 수 있게 둡니다.

저장 결과 알림

상황무엇을
저장 성공 (계속 작업)토스트 — 3초 뒤 사라짐
저장 성공 (흐름 종료)결과 화면
저장 실패토스트 금지. 알림이나 오류 문장 — 사라지면 안 됩니다

하지 말 것

  • placeholder를 라벨 대신 쓰기 — 입력을 시작하면 사라져 무슨 칸인지 잊습니다
  • 오류를 폼 맨 위에만 모아두기 — 어느 칸이 문제인지 찾아야 합니다. 칸 옆에도 붙이세요
  • 저장 버튼을 화면 밖으로 밀어내기 — 긴 폼은 하단 액션바를 position:sticky로
  • 실패를 토스트로 알리기

접근성

  • 모든 입력에 <label for> 연결. 아이콘만 있는 컨트롤은 aria-label
  • 오류 요약을 role="alert"로 두면 저장 실패 시 즉시 읽힙니다
  • 도움말·placeholder는 --color-text-secondary(4.6:1). --color-text-tertiary는 글자에 쓰지 않습니다
  • 날짜는 OS 기본 달력을 씁니다 — 직접 만든 달력은 키보드·다국어에서 거의 항상 집니다