본문 바로가기

프로젝트

react-hook-form과 zod 선택한 이유

react-hook-form

 

프로젝트 구현중에

로그인과 회원가입 폼에 관련해서 

input추가시마다 state선언 및 관리를 많이 해야 하고,

그만큼 단순 반복 로직이 길어진다고 느꼈고 효율적이지 못해 보였다.

가독성도 떨어져보였다.

이것이 최선일까? 하는 의문이 들었다. 

그리고 이것이 변경될 때마다 렌더링이 되는 점도 아쉬웠다. 

 

react-hook-form의 가장 큰 장점은 ref로 스테이트를 관리하기 때문에 

사용자가 입력시마다 리렌더링이 발생하지 않는다는 것이었지만

 나는 결국 에러메시지를 바로바로 반영해주는 것이 

사용자 관점에서 맞다고 생각해서 mode를 onChange로 변경하긴 했다. 

 

또하나의 큰 장점은 validation 로직을 구현하는 것이라고 생각했다. 

유효성 검증 로직을 따로 만들어서 그걸 입력할 때마다 실행해야 했었으나, 

react-hook-form은 아래와 같이 입력하기만 하면 

검증로직을 등록해주고 

해당 로직에 맞지 않으면 error를 내보내준다

 

    <Input
        variant={errors.email ? 'danger' : 'passed'}
        type="text"
        placeholder="이메일 (이메일 형식)"
        {...register('email', {
          required: '반드시 입력해주세요',
          pattern: {
            value:
              /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i,
            message: '이메일 형식에 맞지 않습니다.',
          },
         })}
     />

 

input을 추가하려고 한다면 register에 이름을 명시해서 등록해주면 되고, 

에러는 해당 이름의 키값으로 나오게 된다. 

 

 {errors.email && (
     <p className="text-red-500">{errors.email.message}</p>
)}

 

 

zod

 

현재 프로젝트에서 타입스크립트를 쓰고 있는데 타입스크립트는 컴파일 시점의 타입 에러만 잡을 수 있다. 

만일 컴파일 시점에서 잡더라도 런타임에서는 타입에러를 잡을 수 없는게 단점이다. 

이것을 커버할 수 있는 것이  zod다. 

react-hook-form으로 유효성검증 및 렌더링 이슈를 해결하기 위해 도입하던 도중 

zod라는 라이브러리를 알게되었고 해당 라이브러리를 활용하여 

런타임시점 에러를 함께 잡고, 

패스워드 일치 검증 로직까지 함께 쉽게 구현할 수 있었다. 

 

 

zod의 장점은 이뿐만이 아니라 따로 type을 선언할 필요 없이 

zod object에 type을 검증하는 로직을 작성해두면 
해당 type으로 추론할 수 있게 된다. 

 

 

스키마 작성 

const LoginSchema = z
  .object({
    name: z.string().min(2, { message: '2글자 이상 입력해주세요' }),
    email: z
      .string()
      .min(1, { message: '반드시 입력해주세요' })
      .email({ message: '이메일 형식에 맞게 입력해주세요' }),
    password: z
      .string()
      .min(5)
      .max(20)
      .regex(
        pwdRegex,
        '영어소문자, 숫자 포함 5자 이상 20자 미만으로 입력해주세요',
      ),
    checkPwd: z.string().min(5),
  })
  .superRefine(({ checkPwd, password }, ctx) => {
    if (checkPwd !== password) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: '패스워드가 일치하지 않습니다.',
        path: ['checkPwd'],
      });
    }
  });

 

 

해당 스키마를 바탕으로 react-hook-form을 사용하여 form을 작성 

 

 

const LoginForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting, isValid },
    reset,
  } = useForm<LoginType>({
    mode: 'onChange',
    resolver: zodResolver(LoginSchema),
    defaultValues: {
      name: '',
      email: '',
      password: '',
      checkPwd: '',
    },
  });
  
      return  (
      <Input
        variant={errors.name ? 'danger' : 'passed'}
        type="text"
        placeholder="이름 (2글자 이상)"
        {...register('name', {
            required: '반드시 입력해주세요',
            minLength: { value: 2, message: '2글자 이상 입력해주세요.' },
        })}
       />
       (...생략)