<aside>

</aside>

개요

react-hook-form으로 입력값의 유효성 검증을 처리하고 있다.

<TextInput
  label="이름(닉네임)"
  required
  {...register('name', {
    required: {
      value: true,
      message: '필수로 입력해 주세요.',
    },
    maxLength: {
      value: 10,
      message: '10자 이내로 입력해 주세요.',
    },
  })}
  errorMessage={errors.name?.message as string}
/>
<TextInput
  label="이메일"
  type="email"
  required
  {...register('email', {
    required: {
      value: true,
      message: '필수로 입력해 주세요.',
    },
  })}
  errorMessage={errors.email?.message as string}
/>
<TextArea
  label="자기소개"
  description="개설할 티클과 관련하여 자신에 대해 알리고 싶은 내용을 적어주세요."
  size="md"
  required
  maxLength={500}
  {...register('selfIntroduction', {
    required: {
      value: true,
      message: '필수로 입력해 주세요.',
    },
    maxLength: {
      value: 500,
      message: '500자 이내로 입력해 주세요.',
    },
  })}
  errorMessage={errors.selfIntroduction?.message as string}
/>

여기서 세 가지 문제를 느꼈다.

1. 유효성 검증 로직이 한 눈에 파악되지 않는다.

각 input 태그마다 검증 로직을 따로 넣어주어야하니, 각 로직이 파편화되어있어서 실제로 이 form 내부에서 어떤 값들을 어떤 규칙으로 관리하고 있는지 파악하기 어려운 점이 아쉽다. 또한 UI 렌더링 부에 검증 로직이 포함되니 코드가 너무 길어지고 실제로 어떤 UI가 렌더링되는지 추측하기 어려운 코드가 되었다.

2. 동일한 유효성 검증 메시지를 반복적으로 입력해주어야 한다.

‘필수로 입력해 주세요’, ‘~자 이내로 입력해 주세요’ 와 같은 동일한 오류 메시지를 복사 붙여넣기 하고 있다. 만약 UX 라이팅이 변경된다면 하나하나 찾아서 바꿔주어야할 것이다.

물론 상수로 빼서 재사용한다면 조금 더 안정적이어지겠지만, 여전히 상수를 반복적으로 삽입해주어야한다는 것이 아쉽다. 더 스마트한 방법은 없을까 고민이 된다.

3. 타입 검증이 안된다.

register 메소드를 호출할 때 필드의 이름을 넣어주어야하는데, 여기서 오타가 날 가능성이 있다. 또한 errors 객체의 message의 타입이 string | FieldError | Merge<FieldError, FieldErrorsImpl<any>> | undefined 이어서 as string으로 타입 단언이 필요한 점이 불편하다.

이 문제들을 해결하기 위해, 타입 정의와 런타임 데이터 검증(validation)을 한 번에 할 수 있게 해주는 zod를 도입해보았다.

zod schema 정의하기

export const ticleOpenFormSchema = z.object({
  name: z.string().min(1, '필수로 입력해 주세요.').max(10, '10자 이내로 입력해 주세요.'),
  email: z.string().min(1, '필수로 입력해 주세요.').email('올바른 이메일 형식이 아닙니다.'),
  selfIntroduction: z
    .string()
    .min(1, '필수로 입력해 주세요.')
    .max(500, '500자 이내로 입력해 주세요.'),
  title: z.string().min(1, '필수로 입력해 주세요.').max(30, '30자 이내로 입력해 주세요.'),
  ticleIntroduction: z
    .string()
    .min(1, '필수로 입력해 주세요.')
    .max(1500, '1500자 이내로 입력해 주세요.'),
  hashtags: z.string().min(1, '필수로 입력해 주세요.'),
});

zod를 이용해서 form에서 필요로하는 유효성 검증 로직을 한번에 선언하자. formData가 객체 형태로 만들어지기 때문에 z.object()로 객체 형태로 만들어준다.

이제 유효성 검증 로직이 한 눈에 볼 수 있도록 분리가 되었다.

export type OpenFormInputs = z.infer<typeof ticleOpenFormSchema>;