ERRORS

Zod custom error message 설정 실패

진호우 2025. 4. 2. 10:18

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의 타입을 정확히 설정하기