Next.js
React 폼 에러 메시지를 입력창 바로 아래에 두는 이유
에러 메시지를 폼 상단에 몰아두면 모바일에서 어디가 잘못됐는지 찾기 힘들다. 각 입력창 바로 아래에 두는 것이 사용성 면에서 훨씬 낫다.
회원가입 폼을 만들 때 흔히 보이는 패턴이 있다. 에러 메시지를 폼 최상단이나 버튼 위에 한꺼번에 모아서 보여주는 것이다. 데스크톱에서는 어느 정도 괜찮지만 모바일에서는 문제가 된다. 키보드가 올라와 있는 상태에서 에러를 읽고, 다시 스크롤해서 해당 입력창을 찾아야 한다.
에러 메시지는 입력창 바로 아래에
각 필드 에러는 그 필드 바로 아래에 붙여두는 것이 원칙이다. React Hook Form과 Zod를 같이 쓰는 예시:
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const schema = z.object({
email: z.string().email('올바른 이메일 형식이 아닙니다'),
password: z
.string()
.min(8, '비밀번호는 8자 이상이어야 합니다')
.regex(/[A-Z]/, '대문자를 하나 이상 포함해야 합니다'),
})
function SignupForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ resolver: zodResolver(schema) })
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="email">이메일</label>
<input id="email" type="email" {...register('email')} />
{errors.email && (
<p className="mt-1 text-sm text-red-600">{errors.email.message}</p>
)}
</div>
<div>
<label htmlFor="password">비밀번호</label>
<input id="password" type="password" {...register('password')} />
{errors.password && (
<p className="mt-1 text-sm text-red-600">{errors.password.message}</p>
)}
</div>
<button type="submit">가입하기</button>
</form>
)
}
에러 표시 시점
제출 전부터 실시간으로 에러를 보여주면 사용자가 아직 입력 중인데 에러 메시지가 뜨는 것처럼 보여 불편하다. mode: 'onBlur'를 쓰면 포커스를 벗어날 때 검증한다.
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(schema),
mode: 'onBlur',
})
'onSubmit'(기본값)은 제출 버튼을 눌렀을 때만 검증한다. 서비스 성격에 따라 고르면 된다. 일반적으로는 'onBlur'가 무난하다.
모바일에서 확인해야 할 것
에러 메시지 위치가 맞더라도 모바일에서 레이아웃이 깨지면 소용없다. 체크해볼 포인트:
- 에러 문구가 한 줄에 안 들어와서 잘리거나 줄바꿈이 이상한지
text-sm(14px) 기준으로 읽기 충분한지 —text-xs(12px)는 너무 작을 수 있다- 입력창과 에러 메시지 사이 간격(
mt-1)이 너무 붙어 있지는 않은지
브라우저 DevTools의 반응형 뷰보다 실제 기기에서 확인하는 것이 여전히 더 정확하다. 특히 iOS Safari는 폼 동작이 다른 경우가 있다.