React-Hook-Form을 이용한 input 관리 및 유효성 검사

김인태·2024년 9월 25일
0

사용하게 된 계기

공식문서 링크 : https://react-hook-form.com/

아주 전통적인 방법으로, form 에 있는 input, button 등에 대한 컨트롤을 하기 위해선 state가 필요하다.

근데 우리 프로젝트 같은 경우에는 약 9개 정도의 input이 필요하다.

그리고 몇 개는 유효성 검사도 필요하다. 그러면 각자 다른 value에 대해 유효성 검사도 해야하지,

저장도하고, 서버에도 보내고 실패하면 거기에 따른 처리도 해줘야한다.

그렇다면 이 것을 모두 생 react 를 구현한다고하면 꽤나 번거로울 것이다.

이 때 사용할 수 있는 것이 이 react-hook-form 이다.

사용하는 방법

필요한 것은

  1. input이 변화했을 때의 value를 저장하고 기억하고 있을 것.
  2. input에 대한 조건들에 대해 validate를 할 것.
  3. 각각의 input에 대한 조건을 모두 충족시켰을 때 button을 활성화 시킬 것.

각각의 사용법에 따른 코드의 예시를 확인해보자.

1. input이 변화했을 때 value를 저장하고 기억한다.

import ReactDOM from "react-dom"
import { useForm, SubmitHandler } from "react-hook-form"

enum GenderEnum {
  female = "female",
  male = "male",
  other = "other",
}

interface IFormInput {
  firstName: string
  gender: GenderEnum
}

export default function App() {
  const { register, handleSubmit } = useForm<IFormInput>()
  const onSubmit: SubmitHandler<IFormInput> = (data) => console.log(data)

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>First Name</label>
      <input {...register("firstName")} />
      <label>Gender Selection</label>
      <select {...register("gender")}>
        <option value="female">female</option>
        <option value="male">male</option>
        <option value="other">other</option>
      </select>
      <input type="submit" />
    </form>
  )
}

control 하고 싶은 input에 {...register("firstName")} 를 적어준다.

이렇게 input에 적으면 폼의 검증과 제출에 모두 값을 사용할 수 있다.

2. input에 대한 조건들에 대해 validate를 할 것.

 const {
    register,
    handleSubmit,
    formState: { errors, isValid },
  } = useForm<LoginDTO>({
    mode: "onChange",
  });

<form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName", { required: true, maxLength: 20 })} />
      <input {...register("lastName", { pattern: /^[A-Za-z]+$/i })} />
      <input type="number" {...register("age", { min: 18, max: 99 })} />
      <input type="submit" />
    </form>
    
   
<label htmlFor="password" className="text-sm font-medium text-gray-700">
          비밀번호
        </label>
        <input
          id="password"
          type="password"
          placeholder="비밀번호를 입력해주세요"
          className="mt-1 w-full border p-2 rounded-lg"
          {...register("password", { required: "비밀번호는 필수 항목입니다." })}
        />
        {errors.password && (
          <span className="text-red-500 text-sm">
            {errors.password.message}
          </span>
        )}

위처럼 정규식 또는 required 프로퍼티를 추가해서 이 value가 필수적일 때

즉 input이 비어있을 때에 대한 message 등을 전달할 수 있다.

이런식으로! 에러 메세지를 전달하고 싶다면 useForm의 formState : { errors} 를 꼭 추가할 것.

3. 각각의 input에 대한 조건을 모두 충족시켰을 때 button을 활성화 시킬 것.

 const {
    register,
    handleSubmit,
    formState: { errors, isValid },
  } = useForm<LoginDTO>({
    mode: "onChange",
  });
  
   <button
        type="submit"
        disabled={!isValid}
        className={`w-full p-2 rounded-lg ${
          isValid
            ? "bg-blue-600 hover:bg-blue-700 text-white"
            : "bg-gray-300 text-gray-500"
        }`}
      >
        로그인
      </button>

isValid를 추가해서 input들의 모든 유효성 검사를 통과했을 때 위의 버튼 같이 isValid를 통해 조건문 레이아웃을 설정할 수도 있다!

isValid하나로 state를 확 줄일 수 있는 것이다!

근데 고민이 하나 있다면 너무 많은 input에 대한 처리를 해야되는데 그것은 어렵지 않다.

하지만, 컴포넌트를 어떻게 나눌지 고민하고 있다.

  1. input 별로 컴포넌트를 나눈다. ( 8~9개의 input을 전부 컴포넌트로 만든다고?)
  2. constant를 만들어서 map 돌려서 좀 깔끔하게 만든다. (어짜피 유지보수 할 떄는 다 수정해야 되는 거 아냐? )

이런 고민에 빠져있다 더 좋은 결과물을 위해서 생각해보자.

⇒ constant를 만들어서 map 돌리는 것으로 결정!

이유 1. 필드들이 크게 다르지 않아서

이유 2. 반복되는 필드를 한 곳에서 관리하기 떄문에 유지보수 하기 쉬워서.

내일 리팩토링 해보자!

profile
새로운 걸 배우는 것을 좋아하는 프론트엔드 개발자입니다!

0개의 댓글