Next.js + TypeScript + Zod: defaultValues 타입 문제 해결하기
Zod를 활용해 폼 검증을 설정할 때, defaultValues의 타입을 잘못 지정하면 예상치 못한 검증 오류가 발생할 수 있다.
error 메세지를 별도로 지정해주었는데, 검증 실패시 !Required 로 나오는 문제가 있었다.
이전까지는 defaultValues를 주지 않아서 발생하는 문제로만 알고있었는데, defaultValues를 줘도 계속 문제가 발생.
https://github.com/orgs/react-hook-form/discussions/9063
위 질문을 보면 별도로 defaultValues를 주지 않아도 custom error message가 정상적으로 동작한다.
문제 상황
Zod 스키마를 아래와 같이 정의했다고 가정해보자.
const UserSchema = z.object({
userIdx: z.number().refine((value) => !!value, { message: '사용자를 선택해주세요' }),
isActive: z.boolean().default(true),
age: z.number(),
role: z.string().default('user'),
});
그리고 defaultValues를 설정하는 함수의 결과를 보면 userIdx가 string 타입으로 지정되었다.
예시
const defaultValues = {
userIdx: '', // ❌ 잘못된 타입 (string)
isActive: true,
age: 0,
role: 'user',
};
문제 원인
- z.number()는 string을 자동 변환하지 않는다.
- defaultValues.userId가 string('')이므로 refine 함수에서 !!value가 false가 되어도, Zod가 number 타입이 아님을 먼저 감지해 refine의 메시지를 반환하지 않는다.
- 따라서 "사용자를 선택해주세요" 메시지가 표시되지 않는다.
해결 방법
1. 올바른 타입으로 defaultValues 설정하기
const defaultValues = {
userId: 0, // ✅ 올바른 타입 (number)
isActive: true,
age: 25,
role: 'user',
};
2. Zod의 coerce를 사용해 자동 변환하기
coerce를 사용하면 string 타입의 값도 number로 변환할 수 있다.
const UserSchema = z.object({
userId: z.coerce.number().refine((value) => !!value, { message: '사용자를 선택해주세요' }),
isActive: z.boolean().default(true),
age: z.number(),
role: z.string().default('user'),
});
하지만 원하는 custom message는 나오지 않는다.
Expected number, received nan 로 발생
결론
Zod를 사용할 때 defaultValues의 타입이 스키마와 일치하지 않으면 예상치 못한 검증 오류가 발생할 수 있다. 이를 방지하려면:
✅ defaultValues의 타입을 정확히 설정하기
'ERRORS' 카테고리의 다른 글
API 응답 타입이 예상과 다를 때 (0) | 2025.04.01 |
---|---|
tsconfig에서 paths 설정 시 eslint 에러 해결 (1) | 2024.07.23 |
Error: NextRouter was not mounted. (0) | 2024.05.17 |