REACT HOOK 프로젝트에 useForm 도입 / 유효성 검사

Duboo·2023년 10월 23일
1

REACT HOOK

목록 보기
12/16
post-thumbnail
import { useForm } from "react-hook-form";

const Register = () => {
  const { register, handleSubmit } = useForm();

  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>입력하세요</label>
      <input {...register("inputName")} />
      <input type="submit" />
    </form>
  );
};

export default Register;

먼저 React hook form의 사용방법에 대해서 익히기 위해서 기본적인 구조를 살펴봅니다.

const { register, handleSubmit } = useForm();

useForm hook에서 가장 기본적으로 사용하게 될 함수들

<input {...register("inputName")} />
<form onSubmit={handleSubmit(onSubmit)}>

register 함수를 위와 같은 형태로 추가하고

submit 이벤트를 핸들링 하기 위해 만들어준 onSubmit 함수를 useForm의 함수인 handleSubmit을 이용해서 사용합니다.

const onSubmit = (data) => console.log(data);

위 코드로 인해서 의도한 바와 같이 입력값을 가져올 수 있습니다.

useForm에서 가져온 register 함수를 통해서 필드를 등록하고 값을 가져올 수 있기 때문에 입력받을 값에는 register 함수를 필수로 사용해야합니다.

handleSubmit은 form을 submit 했을 때 실행할 함수로 Validation을 통과했을 때 실행할 콜백함수(SubmitHandler | 위에서는 onSubmit)가 반드시 필요하며, 실패했을시에 사용할 수 있는 SubmitErrorHandler를 옵션으로 추가로 사용할 수 있습니다.

유효성 검사 | validation

register 함수의 두번째 인자로 들어가는 validation을 사용하여 유효성 검사를 실행할 수 있습니다.

  • required : 필수 여부
  • minLength : 최소 길이
  • maxLength : 최대 길이
  • pattern : 정규 표현식
  • validate : 커스텀 함수
import { useForm } from "react-hook-form";

const Register = () => {
  const { register, handleSubmit } = useForm();

  const onSubmit = (data) => console.log(data);
  
  console.log("errors: ", errors);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>필수값</label>
      <input
        {...register("value", {
          required: true,
          minLength: 5,
        })}
      />
      <label>이름</label>
      <input {...register("name")} />
      <input type="submit" />
    </form>
  );
};

export default Register;

위와 같이 필수 값을 입력 받도록 하고 최소 길이를 설정해줬습니다.

필수값 입력이 5글자 미만일 때에는 submit을 할 수 없지만 5글자 이상일 때 출력이 정상적으로 나오고 필수값 지정을 하지 않은 값은 빈값으로 출력이 되는걸 확인할 수 있습니다. 콘솔을 통해 어떤 에러인지도 확인이 가능합니다.

하지만 버튼이 입력이 되지 않는 이유를 유저는 알지 못하기에 에러를 유저에게 확인시켜 줄 수 있도록 만들어줘야합니다.

useForm의 return 값으로 formState 객체를 받아올 수 있으며 해당 객체 안에는 errors 객체를 포함하고 있습니다.

const Register = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = (data) => console.log(data);

  console.log("errors: ", errors);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>필수값</label>
      <input
        {...register("value", {
          required: true,
          minLength: 5,
        })}
      />
      {errors?.value?.type === "minLength" && <span>필수 값은 5글자 이상</span>}
      {errors?.value?.type === "required" && <span>필수 값을 입력해야함</span>}
      <label>이름</label>
      <input {...register("name")} />
      <input type="submit" />
    </form>
  );
};
<input
	{...register("value", {
		required: true,
		minLength: 5,
	})}
/>
{errors?.value?.type === "minLength" && <span>필수 값은 5글자 이상</span>}
{errors?.value?.type === "required" && <span>필수 값을 입력해야함</span>}
필수값 입력 안함최소 5글자 이상 확인

위와 같이 에러의 타입에 따라 다르게 에러 메세지 핸들링 할 수 있어 매우 편리합니다.


여기서 추가로 이메일 입력값의 유효성 검사를 확인하기 위해 정규 표현식을 사용했습니다.

const Register = () => {
  // 이메일 검사 정규 표현식
  const EMAIL_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;
  
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = (data) => console.log(data);

  console.log("errors: ", errors);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Email</label>
      <input
        {...register("email", {
          required: true,
          pattern: EMAIL_REGEX,
        })}
      />
      {errors?.email?.type === "required" && (
        <div>이메일을 필수로 일력해주세요.</div>
      )}
      {errors?.email?.type === "pattern" && (
        <div>이메일을 형식을 맞춰서 작성하세요.</div>
      )}

      <input type="submit" />
    </form>
  );
};


혹은 다른 방법으로 에러를 유저에게 출력해줄 수 있습니다.

      <label htmlFor="email">Email</label>
      <input
        id="email"
        {...register("email", {
          required: "이메일을 필수로 입력해주세요.",
          pattern: {
            value: EMAIL_REGEX,
            message: "이메일 형식을 맞춰서 작성하세요.",
          },
        })}
      />
      {<div>{errors?.email?.message}</div>}

위와 같이 에러 메세지 값을 추가로 입력해주면 해당 에러에 맞게 메세지를 출력해줄 수 있으며 각 type에 맞춰 에러 메세지를 여러 줄 작성할 필요가 없습니다.


setError, watch

  const {
    register,
    handleSubmit,
    formState: { errors },
    setError,
    watch,
  } = useForm();

회원가입 입력값으로 email, nickname, password를 넘겨주려 합니다.

이때 password와 password confirm이 일치하는지
email과 nickname이 중복되진 않는지 중복검사를 실행하려 하는데요.

위 두가지 조건 중 비밀번호 체크는 useForm의 return 값으로 setError를 받아와 조건을 만들어 줄 수 있습니다.

  const onSubmit = ({ email, nickname, password, passwordConfirm }) => {
    if (password !== passwordConfirm) {
      setError(
        "passwordConfirm",
        { message: "비밀번호가 일치하지 않습니다." },
        { shouldFocus: true }
      );
      return;
    }
    console.log("API 호출");
  };

위 코드는 조건문에 따라 setError에서 passwordConfirm(비밀번호 체크)에 에러와 메세지 그리고 shouldFocus를 설정해주어 에러가 있는 input에 포커스를 줄 수 있습니다.

또한 이메일과 닉네임을 위한 중복 검사를 하고 싶다면 이메일과 이메일 값을 실시간으로 받아올 필요가 있는데 이때 사용할 수 있는 watch가 있습니다.

  const checkConflictEmail = () => {
    console.log("이메일 중복 검사 API 요청 값 : ", watch("email"));
  };

  const checkConflictNickname = () => {
    console.log("닉네임 중복 검사 API 요청 값 : ", watch("nickname"));
  };
<input
        {...register("email", {
          required: "이메일을 필수로 입력해주세요.",
          pattern: {
            value: EMAIL_REGEX,
            message: "이메일 형식을 맞춰서 작성하세요.",
            shouldFocus: true,
          },
        })}
      />
      <button onClick={checkConflictEmail}>이메일 중복 검사</button>
      <input
        type="text"
        {...register("nickname", {
          required: "이름을 필수로 입력해주세요.",
          maxLength: {
            value: 12,
            message: "이름은 12 글자 이내로 작성하세요.",
            shouldFocus: true,
          },
        })}
      />
      <button onClick={checkConflictNickname}>닉네임 중복 검사</button>

간단하게 버튼을 만들어 클릭시 이벤트를 걸어 해당 함수를 호출하면서 위의 watch에 각각의 input 값들을 넣어줘 해당 값들을 실시간으로 확인할 수 있습니다.

이제 중복 검사 실행 유무를 state hook을 사용해도 되고 익숙한 방법으로 체크를 해서 검사를 실행 했을 시에만 모든 form의 데이터가 넘어가도록 만들어주면 됩니다.


이제 대부분 필요한 기능들은 모두 react hook form을 사용해서 불필요한 리렌더링 없이 많은 state 상태를 관리할 필요 없이 구현할 수 있습니다.

profile
둡둡

1개의 댓글

comment-user-thumbnail
2023년 10월 25일

useform을 사용하는 흐름을 자세히 기재해주셨네요👍

답글 달기