react-hook-form(Controller) + yup을 활용하여 유효성 검사하기

kim yeseul·2023년 10월 11일
2

라이브러리

목록 보기
4/6
post-thumbnail

Schema Validation

리액트 훅 폼은 Yup, Zod, Superstruct & Joi를 통해 스키마 기반 폼 유효성 검사를 지원한다. 이를 통해 스키마에 대한 입력 데이터를 유효하게 처리하고 오류 또는 유효한 결과를 반환한다.
<출처 - react-hook-form 공식문서 번역>

yup?

런타임 값 구문 분석 및 유효성 검사를 위한 스키마 빌드. 스키마를 정의하거나, 일치하도록 값을 변환하거나, 기존 값의 shape을 주장하거나, 둘 다 지정합니다. 스키마는 표현력이 뛰어나며 복잡한 상호 의존적인 유효성 검사 또는 값 변환을 모델링할 수 있다.
<출처 - yup 깃허브 번역>

어떻게 시작하나요?

npm install @hookform/resolvers yup react-hook-form

🟡 schema

1. yup : 개별 적용

import * as yup from "yup"

const firstName = yup
  .string()
  .required()
// firstName은 string이어야 하며 필수 조건이다.

const age = yup
  .number()
  .positive()
  .integer()
  .required()
// age는 number 타입이어야 하며, 양수, 정수, 필수 조건이다.

2. yup.object() : 객체 스키마 정의

import * as yup from "yup"
const schema = yup
  .object({
    firstName: yup.string().required(), 
    age: yup.number().positive().integer().required(),
  })
  .required() // 

// yup.object를 선언하여 키(검사할 속성): 값(유효성 로직)을 schema에 담아준다. 

2. yup.object().shape() : 중첩 객체 스키마 정의

import * as yup from "yup"
const schema = yup.object().shape({
	firstName: yup
      .string()
      .required,
  
    age: yup
  	  .number()
 	  .positive()
  	  .integer()
  	  .required()
})

참고: 스키마 코드는 주로 consts/schema.js 에 정의 하여 export하여 사용한다.

object나 shape()을 사용할 경우 해당하는 모든 조건이 맞아야 유효성 검사를 통과하게 됨으로 주의할 것!

🟢 matches

주로 정규 표현식으로 복잡한 유효성 검사가 필요할 때 matches() 안에 해당 로직을 넣어준다.

1. matches() 안에 직접 적용

email: yup
    .string()
    .required("이메일을 필수로 입력해주세요")
    .matches(
      /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i,
      "email 형식에 맞지 않습니다"
    ),

2. consts/regex.js 에 분리 후 export 하여 사용

// consts/regex.js
export const REGEX = {
  email: /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/i,
  password: //생략,
  phone: // 생략,
  age:
}

// consts/schema.js
import { REGEX } from "./regex";
email: yup
    .string()
    .required("이메일을 필수로 입력해주세요")
    .matches(REGEX.email),

개인 의견, 2번 처럼 로직을 분리하여 사용하는 것이 가독성 및 유지보수에 더 좋을 것 같다

🔵 Controller

나는 먼저 재사용 가능한 <input /> 태그를 Controller로 만들어주었다.

import { FormHelperText, TextField } from "@mui/material";
import { Controller } from "react-hook-form";

const OneInput = ({ control, name, label, value, errors, ...rest }) => {
  return (
    <Controller
      control={control}
      name={name}
      render={({ field: { onChange, value } }) => (
        <div style={{ marginBottom: "20px" }}>
          <TextField
            style={{ width: "400px" }}
            label={label}
            variant="standard"
            value={value}
            onChange={onChange}
            error={errors[name]} // mui ui error 밑줄 빨갛게
          />
          {errors[name] && (
            <FormHelperText style={{ textAlign: "center" }} error>
              {errors[name].message}
            </FormHelperText>
          )}
        </div>
      )}
    />
  );
};

export default OneInput;

react-hook-form에서 Controller를 import하여 control, name, render props를 넘겨준다.

mui 라이브러리에 Controller와 연결할 수 있는 에러메세지 컴포넌트가 있어 사용해보았다.

// Form.js
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import OneInput from "./one-input";
import { schema } from "consts/schema";
import { Button } from "@mui/material";

const SignUpForm = () => {
  const {
    handleSubmit,
    formState: { errors },
    control,
  } = useForm({
    resolver: yupResolver(schema),
  });

  /*
    1. yupResolver: yup 라이브러리의 스키마 유효성 검사를 react-hook-form과 통합
   */

  const onSubmit = (data) => {
    console.log("onSubmit", data);
  };
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <h2>회원가입</h2>
      <OneInput control={control} name="email" label="Email" errors={errors} />
      <OneInput
        control={control}
        name="password"
        label="Password"
        errors={errors}
      />
      <OneInput
        control={control}
        name="passwordConfirm"
        label="Password Confirm"
        errors={errors}
      />
      <OneInput control={control} name="phone" label="phone" errors={errors} />
      <OneInput control={control} name="age" label="Age" errors={errors} />
      <Button variant="outlined" type="submit">회원가입</Button>
    </form>
  );
};

export default SignUpForm;

기존 리액트 훅 폼은 useForm() 이었다면

yup에서는 yupResolver를 import하여 유효성 검사 로직을 작성한 schema를 import하여 매개 변수로 넣어주면 검사 준비 끝!
useForm({ resolver: yupResolver(schema) }

구현 화면 예시

리액트 훅 폼에는 이 외에도 여러 많은 속성들이 있어 다음에 추가 공부를 하며 경험해 볼 예정이다.

profile
출발선 앞의 준비된 마음가짐, 떨림, 설렘을 가진 신입개발자

0개의 댓글