Zod 검증이 실행되기도 전에 실패하는 이유와 해결 방법
Source: Dev.to

react-hook-form과 함께 Zod를 사용하고 있다면, 최소 한 번은 다음과 같은 메시지를 보았을 것입니다:
Invalid input: expected number, received NaN
언뜻 보기엔 단순한 검증 문제처럼 보입니다.
그게 아닙니다.
실제 문제
폼 입력을 다룰 때는:
- 모든 값은 문자열로 들어옵니다
z.coerce.number()가 이를 변환하려고 시도합니다- 빈 입력(
"")이나 잘못된 값 →NaN
그리고 여기서 문제는:
Zod는 .min() / .max() 검증이 실행되기 전에 실패합니다.
따라서 커스텀 메시지 대신 일반적이고 (그다지 도움이 되지 않는) 오류를 얻게 됩니다.
상황이 더 복잡해집니다
TypeScript를 사용한다면:
z.input≠z.outputreact-hook-form은 입력 타입을 사용합니다- Zod는 파싱 후 출력 타입을 제공합니다
이 불일치는 혼란스러운 타입 오류와 데이터에 대한 잘못된 가정으로 이어질 수 있습니다.
해결 방법
Zod가 검증을 시도하기 전에 잘못된 입력을 처리해야 합니다:
readTime: z.preprocess((val) => {
if (val === "" || val === undefined) return undefined;
const num = Number(val);
return isNaN(num) ? undefined : num;
},
z.number()
.min(2, 'Minimum read time is 2 minutes')
.max(60, 'Maximum read time is 60 minutes')
)
Enter fullscreen mode
전체 화면 모드로 전환
Exit fullscreen mode
전체 화면 모드 종료
이 해결책이 해결하는 문제
- 빈 입력 → 올바르게 처리
- 잘못된 숫자 → 더 이상 NaN 문제 없음
- 커스텀 검증 메시지 → 실제로 표시됨
- 전반적으로 깔끔한 UX
핵심 정리
폼에서 z.coerce.number()를 사용한다면, 무조건 신뢰하지 마세요.
항상 입력을 먼저 정규화하세요.
때때로 버그는 검증 규칙에 있는 것이 아니라… 검증이 시작되기 전 데이터가 들어오는 방식에 있습니다.
