8단계
8단계 — 폼·검증·UX 마무리
25 분
8단계 — 폼·검증·UX 마무리
화면을 예쁘게 그리는 데서 한 발 더 — 사용자가 실수해도 친절하게 알려주는 게 진짜 프론트엔드입니다.
zod 로 입력값 검증
zod 는 입력 모양을 스키마 로 정의하는 라이브러리예요.
import { z } from "zod";
const SignupSchema = z.object({
email: z.string().email("이메일 형식이 아니에요"),
password: z.string().min(8, "비밀번호는 8자 이상"),
age: z.number().int().min(14, "14세 이상만 가입 가능"),
});
const result = SignupSchema.safeParse(formData);
if (!result.success) {
// result.error.issues 에 어떤 필드가 왜 틀렸는지 들어 있음
}
이 한 패턴으로 모든 폼 의 검증이 통일됩니다.
로딩 UX — 사용자 시야에 머무는 0.4 초
서버 응답을 기다리는 사이 화면이 멈춰 있으면 사용자는 고장났다 고 느껴요. 세 가지가 큰 차이를 만듭니다.
- 버튼 비활성화 + 텍스트 변경 —
<button disabled>{loading ? "보내는 중…" : "보내기"}</button> - 스켈레톤 UI — 콘텐츠 자리에 회색 박스 (
animate-pulse) - 낙관적 업데이트 — 응답 전이라도 화면을 미리 갱신, 실패 시 되돌림
const [loading, setLoading] = useState(false);
async function onSubmit() {
setLoading(true);
try {
await fetch("/api/signup", { method: "POST", body: ... });
} finally {
setLoading(false);
}
}
접근성 (a11y) 한 줄
<input> 옆엔 항상 <label> 을, <button> 텍스트는 행동(submit / cancel) 이 드러나게.
<label for="email">이메일</label>
<input id="email" type="email" required>
스크린리더 사용자도 어떤 항목을 채우는지 알아야 해요.
직접 해 보기
5~7단계의 Counter 를 증가/감소/리셋 세 버튼으로 확장해 보세요. 각 버튼에 aria-label 을 붙이고, 0 일 때 감소 버튼은 disabled 로.
더 깊이
다음 단계
이 강좌는 끝났어요. 다음으로 nextjs-fullstack 강좌 에서 Next 에 DB 를 잇거나, backend-with-spring 강좌 에서 진짜 백엔드를 만나 보세요.
🎉 HTML/CSS/JS 부터 React, Next, Tailwind 까지 완주를 축하해요
이어서 어떤 걸 배워 볼까요?