본문으로 바로가기

[React] react-hook-form yup으로 validation 처리하기

이전 게시글에서 react-hook-form을 사용하면 얼마나 간단하게 form 처리를 할 수 있는지 알아보았습니다.

그 과정에서 에러 핸들링에 대해서도 간단하게 처리할 수 있는 것을 확인하였습니다.

그래도 불편한 점이 있습니다.

 

<input
  {...register("name", {
    required: "이름은 필수 값입니다.",
    minLength: {
      value: 5,
      message: "이름은 5글자 이상이어야합니다."
    },
    maxLength: {
      value: 10,
      message: "이름은 10글자 이하이어야 합니다."
    }
  })}
/>;

위 코드를 확인해보면 코드 중간에 에러 처리를 해주어야 한다는 것입니다.

물론 최상단에 변수로 따로 빼서 처리하면 되지만 불편합니다.

이러한 불편함을 해결해주고 더 간단하게 에러 핸들링을 할 수 있도록 도와주는 yup 라이브러리가 있습니다.

이번 게시글에서는 yup을 통해 이전 게시글에서 했던 에러 핸들링을 더 깔끔하게 해보겠습니다.

 

yup 설치하기

yup을 사용하기 위해 필요한 설치 명령어입니다.

yup : npm install yup or yarn add yup

yupResolver : npm install @hookform/resolvers/yup or yarn add @hookform/resolvers/yup

 

yup 문서입니다.

자세한 사용법해당 문서를 보시기 바랍니다.

yup 문서 이동하기

 

yup 사용하여 기존 코드 리팩토링하기

◆ yup schema 생성 및 react-hook-form 연결

우선 schema 생성react-hook-form에 설정하는 방법을 알아보겠습니다.

 

schema는 간단하게 설명하면 yup에서 validation을 할 수 있게 우리가 작성해주는 것입니다.

따라서 schema를 type개발자가 원하는 validation에 맞게 잘 작성해주어야 합니다.

여기서 required, max, min, pattern 등 많은 함수 활용이 가능합니다.

 

그리고 구현한 schema를 react-hook-form에 등록해주어야 합니다.

해당 작업들은 모두 간단하니 아래 코드를 확인해봅시다.

import { yupResolver } from "@hookform/resolvers/yup";
import { object, SchemaOf } from "yup";
import { IUserData } from "./types";

const schema: SchemaOf<IUserData> = object({
    // ... 추후 작성해야함. 
});

const {
   register,
   handleSubmit,
   formState: { errors }
} = useForm<IUserData>({
   mode: "onSubmit",
   resolver: yupResolver(schema)
});

 

위 코드를 보시면 tsx이기 때문에 schema에 SchemaOf를 이용하여 type을 작성한 것을 확인할 수 있습니다.

현재 데이터 구조는 객체이기 때문에 object로 감싸주었습니다.

(추후 상세 schema 작성)

react-hook-form에 연결하는 방법은 resolver에 추가해주시면 끝입니다.

 

◆ yup schema 구현하기

이제 schema를 상세 구현해보겠습니다.

name, age, id, password, email 순서로 구현하겠습니다.

 

- name

name의 경우 조건이 required, minLength, maxLength가 있었습니다.

아래처럼 작성하시면 됩니다.

const schema: SchemaOf<IUserData> = object({
  name: string()
    .required("이름은 필수 값입니다.")
    .min(5, "이름은 5글자 이상이어야합니다.")
    .max(10, "이름은 10글자 이하이어야 합니다.")
});

위 코드만 보면 쉽게 이해가 가능하여 따로 설명은 하지 않겠습니다.

(해당 함수 사용은 공식 문서에 잘 나와있습니다.)

 

추가로 schema에 validation을 모두 작성하였기 때문에 react-hook-form에서 input에 적어주었던 validation들은 모두 지워주어도 됩니다.

아래 yup 적용 전/후 코드를 비교해봅시다.

yup 적용 전 코드

<input
  {...register("name", {
    required: "이름은 필수 값입니다.",
    minLength: {
      value: 5,
      message: "이름은 5글자 이상이어야합니다."
    },
    maxLength: {
      value: 10,
      message: "이름은 10글자 이하이어야 합니다."
    }
  })}
  placeholder="name"
/>

 

yup 적용 후 코드

<input {...register("name")} placeholder="name" />

 

yup 적용 전/후 코드를 보시면 코드 길이가 많이 차이 나는 것을 확인할 수 있습니다.

가독성이 굉장히 좋아진 것을 확인할 수 있습니다.

(추후 위처럼 상세 비교는 없음)

(schema 작업은 계속 합쳐짐)

 

- age

age의 경우 조건이 required, min, max가 있었습니다.

해당 조건을 작성한 schema는 아래와 같습니다.

const schema: SchemaOf<IUserData> = object({
  name: string()
    .required("이름은 필수 값입니다.")
    .min(5, "이름은 5글자 이상이어야합니다.")
    .max(10, "이름은 10글자 이하이어야 합니다."),
  age: number()
    .required("나이는 필수 값입니다.")
    .min(10, "나이는 10살 이상이어야 합니다.")
    .max(100, "나이는 100살 이하이어야 합니다.")
});

 

- id

id의 경우 required만 필요합니다.

const schema: SchemaOf<IUserData> = object({
  name: string()
    .required("이름은 필수 값입니다.")
    .min(5, "이름은 5글자 이상이어야합니다.")
    .max(10, "이름은 10글자 이하이어야 합니다."),
  age: number()
    .required("나이는 필수 값입니다.")
    .min(10, "나이는 10살 이상이어야 합니다.")
    .max(100, "나이는 100살 이하이어야 합니다."),
  id: string().required("id는 필수 값 입니다.")
});

 

- password

password의 경우 required, 영어만 가능 조건이 있습니다.

여기서 영어를 체크하는 것은 정규식을 사용했습니다.

const schema: SchemaOf<IUserData> = object({
  name: string()
    .required("이름은 필수 값입니다.")
    .min(5, "이름은 5글자 이상이어야합니다.")
    .max(10, "이름은 10글자 이하이어야 합니다."),
  age: number()
    .required("나이는 필수 값입니다.")
    .min(10, "나이는 10살 이상이어야 합니다.")
    .max(100, "나이는 100살 이하이어야 합니다."),
  id: string().required("id는 필수 값 입니다."),
  password: string()
    .required("비밀번호는 필수 값입니다.")
    .matches(/^[a-zA-Z]*$/, { message: "비밀번호는 영어만 가능합니다." })
});

yup에서는 정규식을 사용할 때 matches 함수를 사용하시면 됩니다.

보여주고 싶은 message는 2번째 인자로 넘겨주시면 됩니다.

 

- email

email의 경우 gmail만 가능하도록 되어 있습니다.

이 경우 test 함수를 사용하시면 쉽게 validation 하실 수 있습니다.

const schema: SchemaOf<IUserData> = object({
  name: string()
    .required("이름은 필수 값입니다.")
    .min(5, "이름은 5글자 이상이어야합니다.")
    .max(10, "이름은 10글자 이하이어야 합니다."),
  age: number()
    .required("나이는 필수 값입니다.")
    .min(10, "나이는 10살 이상이어야 합니다.")
    .max(100, "나이는 100살 이하이어야 합니다."),
  id: string().required("id는 필수 값 입니다."),
  password: string()
    .required("비밀번호는 필수 값입니다.")
    .matches(/^[a-zA-Z]*$/, { message: "비밀번호는 영어만 가능합니다." }),
  email: string()
    .required("email은 필수 값 입니다.")
    .test("domainCheck", "gmail만 가능합니다.", email => {
      if (!email) return false;
      return email.split("@")[1] === "gmail.com";
    })
});

test 함수를 사용하는 방법도 매우 쉽습니다.

인자test명, message, validation function을 넘겨주시면 됩니다.

 

위 모든 작업을 끝낸 코드는 아래와 같습니다.

import { yupResolver } from "@hookform/resolvers/yup";
import { FieldErrors, useForm } from "react-hook-form";
import { number, object, SchemaOf, string } from "yup";
import { IUserData } from "./types";

const schema: SchemaOf<IUserData> = object({
  name: string()
    .required("이름은 필수 값입니다.")
    .min(5, "이름은 5글자 이상이어야합니다.")
    .max(10, "이름은 10글자 이하이어야 합니다."),
  age: number()
    .required("나이는 필수 값입니다.")
    .min(10, "나이는 10살 이상이어야 합니다.")
    .max(100, "나이는 100살 이하이어야 합니다."),
  id: string().required("id는 필수 값 입니다."),
  password: string()
    .required("비밀번호는 필수 값입니다.")
    .matches(/^[a-zA-Z]*$/, { message: "비밀번호는 영어만 가능합니다." }),
  email: string()
    .required("email은 필수 값 입니다.")
    .test("domainCheck", "gmail만 가능합니다.", email => {
      if (!email) return false;
      return email.split("@")[1] === "gmail.com";
    })
});

const UserRegisterRhfWithYup = () => {
  const {
    register,
    handleSubmit,
    formState: { errors }
  } = useForm<IUserData>({
    mode: "onSubmit",
    resolver: yupResolver(schema)
  });

  const onValid = (data: IUserData) => {
    console.log("# onValid", data);
  };

  const onInValid = (errors: FieldErrors) => {
    console.log("# onInValid", errors);
  };

  return (
    <form onSubmit={handleSubmit(onValid, onInValid)}>
      <input {...register("name")} placeholder="name" />
      <span>{errors.name?.message}</span>
      <br />

      <input type="number" {...register("age")} placeholder="age" />
      <span>{errors.age?.message}</span>
      <br />

      <input {...register("id")} placeholder="id" />
      <span>{errors.id?.message}</span>
      <br />

      <input {...register("password")} type="password" placeholder="password" />
      <span>{errors.password?.message}</span>
      <br />

      <input {...register("email")} type="email" placeholder="email" />
      <span>{errors.email?.message}</span>
      <br />

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

export default UserRegisterRhfWithYup;

한눈에 딱 봐도 return 문이 훨씬 간단해졌습니다.

그리고 validation 부분이 따로 분리되어 있어 가독성도 좋습니다.

 

정리

이번 게시글에서는 yup에 대해 알아보았습니다.

react-hook-form으로도 충분히 좋지만 yup을 사용하면 validation을 쉽고 간결하게 할 수 있습니다.

yup은 공식문서가 나름 잘 되어 있으니 사용하시기 전 한번 읽어보시길 바랍니다.

 

참고자료

 

마지막

해당 내용은 틀릴 수도 있습니다. 틀린 내용이 있으면 조언 부탁드립니다.

반응형

'기타 (+ Legacy) > Legacy' 카테고리의 다른 글

[Nest] NestJS + GraphQL + MongoDB 구현하기  (0) 2023.03.23
[React] 키보드 이벤트 처리  (0) 2023.02.09
[React] useEffect 실습  (0) 2022.06.17
[React] useEffect 개념  (0) 2022.06.15
[React] useRef 개념 및 실습  (0) 2022.06.13