React Hook Form & Zod

류지승·2024년 10월 30일
3

React

목록 보기
15/19
post-thumbnail

Form 라이브러리 React-Hook-Form

React에서 Form을 효율적으로 관리하기 위한 라이브러리이다. 이 라이브러리는 폼 상태 관리와 유효성 검사를 간편하게 처리할 수 있도록 설계되었으며, 비제어 컴포넌트(uncontrolled components)를 기반으로 성능을 최적화한다. 특히, 다중 필드를 가진 대규모 폼이나 고성능이 요구되는 상황에서 React-Hook-Form은 매우 유용하다.

주요기능 및 특징

  • 성능 최적화 : react-hook-form은 폼 요소를 직접 관리하는 ref를 활용하여, 필드 값이 변경될 때 불필요한 리렌더링을 방지한다. 이를 통해 폼의 성능을 최적화하여, 대규모 폼에서도 성능 저하 없이 동작한다.

  • 비제어 컴포넌트 기반 설계 : 라이브러리는 비제어 컴포넌트를 사용하여, 기본적인 폼의 상태를 DOM에 저장하고 필요한 시점에만 상태를 가져온다. 이는 컨트롤된 컴포넌트보다 코드가 간결해지고 성능이 향상되는 장점이 있다.

  • 간편한 유효성 검사 : react-hook-form은 HTML5 기본 유효성 검사와 커스텀 유효성 검사를 함께 지원한다. 또한, Yup이나 Zod와 같은 유효성 검사 라이브러리와도 쉽게 통합할 수 있어 복잡한 검증 로직도 간편하게 구현할 수 있다.

  • 작고 가벼운 라이브러리 : react-hook-form은 의존성이 거의 없고 크기가 작아, 프로젝트에 추가해도 번들 크기 증가가 적다.

비제어 컴포넌트 vs 제어 컴포넌트

  • 비제어 컴포넌트 : 바닐라 자바스크립트처럼 submit함수를 실행할 때 ref 로 input값을 한번에 변경한다.
  • 제어 컴포넌트 : 사용자의 입력을 기반으로 state를 실시간으로 관리(setState 사용). 쉽게 말해 입력이 될 때마다 state값이 실시간으로 변경된다.

React-Hook-Form 예시

export default function GraphqlMutationPage() {
	const {register , handleSubmit} = useForm()

	const onClickSubmit = (data)=>{
		console.log(data)
	}

	return (
    <form onSubmit={handleSubmit(onClickSubmit)}>
      작성자: <input type="text" {...register("writer")} /><br />
      제목: <input type="text" {...register("title")} /><br />
      내용: <input type="text" {...register("contents")} /><br />
      주소: <input type="text" {...register("boardAddress.addressDetail")} /><br />
      <button type="submit">클릭</button>
    </form>
  );
}

register : state를 등록하는데 필요한 모든 기능이 들어있다.
handleSubmit : register에 적힌 state를 등록해주는 함수.
formState : 폼 상태를 포함하며, errors, isDirty, isSubmitting과 같은 속성을 제공한다. 각 속성은 폼의 상태를 나타내며 유효성 검사와 에러 처리를 쉽게 해준다.
reset : 폼의 상태를 초기화하거나 특정 값을 설정하여 리셋할 수 있다.

React Hook Form 주요 메서드 및 속성

useForm

React Hook Form의 기본 훅으로, 폼 상태와 메서드들을 제공한다.

defaultValues - form 초기값 설정 / API를 통해 데이터를 초기값으로 설정하거나 폼 초기 상태를 기본값으로 설정하고 싶을 때 사용한다.
mode - 유효성 검사가 실행되는 타이밍을 설정한다. onSubmit / onChange / onBlur / all , 실시간으로 유효성 검사를 해야하는 경우 onChange를 사용하고 form 성능이 중요하거나 간단한 유효성 검사를 할 때는 onSubmit을 사용한다.
resolver - Zod, Yup 등과 같은 유효성 라이브러리와 통합할 때 사용한다. 주로 복잡한 유효성 검사를 처리하거나 데이터 스키마와 타입 정의를 함께 사용하고 싶을 때 사용한다.
shouldFocusError - 유효성 검사 실패 시, 오류가 발생한 필드로 포커스를 이동할지 여부를 설정한다. default 값으로 true이며 주로 다중 필드 폼에서 오류가 난 필드로 자동으로 포커스 이동을 원치 않는 경우 false로 설정한다.
shouldUnregister - 폼에서 필드가 UnMount되었을 때, 해당 필드를 상태에서 제거할지 여부를 설정한다. default 값으로 false이며 주로 동적으로 추가/삭제되는 필드가 많을 때, 상태를 초기화하려는 경우 true로 설정한다.
reValidateMode - 이미 유효성 검사를 받은 필드가 다시 검사를 받을 조건을 설정한다. onChange, onBlur , 입력값 수정 중에는 검사를 피하고 싶을 때 onBlur를 사용한다.
defaultValuesFromResolver - resolve에서 가져온 값을 초기값으로 설정할지 여부를 지정한다. default 값으로 false이며 외부 유효성 검사 라이브러리에서 초기값을 자동으로 가져오고 싶을 때 true로 설정한다
criteriaMode - 에러 메세지를 수집하는 방식 설정으로 default로는 firstError이지만 모든 에러를 반환하고 싶으면 all 설정하면 된다.
delayError - 에러 메시지를 일정 시간 지연 후 표시한다. 주로 에러 메세지가 실시간으로 나타나 사용자를 방해하지 않도록 조정할 때 사용한다.
shouldUseNativeValidation - 브라우저의 기본 HTML5 유효성 검사를 활성화할 때 사용한다. default로 false이며 기본 유효성 검사를 활용해 간단한 유효성 검사를 구현할 때 사용한다.

import React from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

const schema = z.object({
  username: z.string().min(3, "Username must be at least 3 characters long"),
  email: z.string().email("Invalid email address"),
  password: z.string().min(6, "Password must be at least 6 characters long"),
});

const FormExample = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
    watch,
    setValue,
  } = useForm({
    mode: "onChange", // 실시간 유효성 검사
    reValidateMode: "onBlur", // 필드 수정 후 blur 시 재검사
    defaultValues: {
      username: "defaultUser", // 초기값 설정
      email: "",
      password: "",
    },
    resolver: zodResolver(schema), // Zod를 사용한 유효성 검사
    shouldFocusError: true, // 에러가 발생한 필드로 포커스 이동
  });

  const onSubmit = (data) => {
    console.log("Submitted Data:", data);
  };

  const username = watch("username"); // 특정 필드 관찰
  const setDefaultEmail = () => setValue("email", "default@example.com"); // 필드값 설정

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>Username</label>
        <input {...register("username")} />
        {errors.username && <p>{errors.username.message}</p>}
        <p>Currently Typed: {username}</p>
      </div>

      <div>
        <label>Email</label>
        <input type="email" {...register("email")} />
        {errors.email && <p>{errors.email.message}</p>}
        <button type="button" onClick={setDefaultEmail}>
          Set Default Email
        </button>
      </div>

      <div>
        <label>Password</label>
        <input type="password" {...register("password")} />
        {errors.password && <p>{errors.password.message}</p>}
      </div>

      <button type="submit">Submit</button>
    </form>
  );
};

export default FormExample;

register

Form FleidReact Hook Form에 등록할 때 사용한다.

register(name: string, options?: {
  required?: boolean | string;
  min?: number | { value: number, message: string };
  max?: number | { value: number, message: string };
  maxLength?: number | { value: number, message: string };
  minLength?: number | { value: number, message: string };
  pattern?: RegExp | { value: RegExp, message: string };
  validate?: Validate | Record<string, Validate>;
  valueAsNumber?: boolean;
  valueAsDate?: boolean;
  setValueAs?: (value: any) => any;
  disabled?: boolean;
})
<input {...register("username", { required: "Username is required" })} />

handleSubmit

Form Data를 처리하는 함수로 제출 시 유효성 검사를 실행하고 데이터 전달하는 역할을 한다.

const onSubmit = (data) => console.log(data);
<form onSubmit={handleSubmit(onSubmit)}>
  <button type="submit">Submit</button>
</form>

formState

FormState를 제공하며 유효성 검사와 관련된 여러 속성을 포함한다. 주요 속성으로 errors(필드별 에러 정보를 담고 있음) / isSubmitting(제출 중 여부) / isDirty(폼 값이 변경되었는지 여부) / isValid(모든 유효성 검사가 통과했는지 여부)

const { formState: { errors, isSubmitting, isValid } } = useForm();

watch

특정 필드 값이나 전체 폼 데이터를 실시간으로 관찰한다.

const watchedValue = watch("username");
console.log(watchedValue);

reset

폼 상태를 초기화하거나 특정 값을 설정하여 리셋할 수 있다.

const { reset } = useForm();

<button onClick={() => reset({ username: "default" })}>Reset</button>

setValue

특정 필드의 값을 프로그래밍 방식으로 특정 필드의 값을 설정한다.

const { setValue } = useForm();
setValue("username", "new value");

getValues

현재 폼 상태에서 특정 필드 값 또는 전체 값을 가져온다.

const { getValues } = useForm();
console.log(getValues("username")); // 특정 필드 값
console.log(getValues()); // 전체 값

clearErrors

특정 필드 또는 전체 폼의 에러를 제거한다.

const { clearErrors } = useForm();
clearErrors("username");

trigger

특정 필드 또는 전체 폼에 대해 유효성 검사를 수동으로 실행한다.

const { trigger } = useForm();
await trigger("email");

setError

필드에 수동으로 에러를 설정한다

const { setError } = useForm();
setError("username", { type: "manual", message: "Custom error" });

유효성 검증 라이브러리 Zod

Zod는 타입스크립트와 함께 유용하게 사용할 수 있는 스키마 기반 유효성 검사 라이브러리다. 주어진 스키마에 맞춰 데이터의 구조와 유효성을 확인할 수 있다. Zod는 타입 정의뿐만 아니라 데이터 유효성 검사를 한 번에 처리해주기 때문에, 폼 데이터나 API 요청에서 데이터를 다룰 때 안전하게 사용할 수 있다.

Zod의 장점
타입 안전성 : TypeScript와 완벽히 통합되어, 런타임과 컴파일 타임 모두에서 타입을 강제한다.

간결하고 직관적인 API : 데이터 스키마와 유효성 검사 코드를 간결하게 작성할 수 있어 가독성이 뛰어난다.

유연한 에러 처리 및 커스터마이징 : 유효성 검사 실패 시 사용자 정의 에러 메시지를 제공할 수 있다.

다양한 데이터 구조 지원 : 단순한 문자열, 숫자뿐 아니라 객체, 배열, 중첩된 구조 등 복잡한 데이터 구조의 유효성 검사도 가능하다.

다양한 유효성 검증 라이브러리

라이브러리주요 특징장점단점
Zod스키마 기반 유효성 검사 라이브러리.
TypeScript와 완벽히 통합.
- TypeScript 타입 정의와 동시 사용
- 직관적 API
- 런타임 타입 안전성 제공
- 다른 라이브러리에 비해 상대적으로 간단한 기능
Yup선언형 스키마 정의와 유효성 검사 제공.
포괄적인 데이터 유형 지원.
- 쉬운 사용법
- 널리 알려진 라이브러리
- 친숙한 API
- TypeScript와의 통합이 제한적
Joi강력하고 유연한 스키마 정의를 지원.
Node.js 환경에서 자주 사용.
- 정교한 유효성 검사 가능
- 정규식, 조건부 검증 등 고급 기능 지원
- 브라우저 환경에서 사용하려면 번들 크기가 큼

Zod에서의 스키마 정의

Zod는 z.object, z.string, z.number 등을 사용해 다양한 필드와 데이터 구조를 정의할 수 있다.

문자열 필드 유효성 검사
min( ): 문자열 최소 길이 설정
max( ): 문자열 최대 길이 설정
email( ): 이메일 형식 검사

숫자 필드 유효성 검사
min( ): 최소 숫자 값을 설정
max( ): 최대 숫자 값을 설정
positive( ): 양수인지 검사
int( ): 정수인지 검사

배열과 객체의 유효성 검사
배열 요소에 대한 유효성 검사 z.array( )도 가능하며, 중첩된 객체 z.object( )도 쉽게 설정할 수 있다.

react-hook-form와 Zod 통합

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const schema = z.object({
  username: z.string().min(3, "Username must be at least 3 characters long"),
  email: z.string().email("Invalid email address"),
});

const MyForm = () => {
  const { register, handleSubmit, formState: { errors, isValid } } = useForm({
    resolver: zodResolver(schema),
  });

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("username")} />
      {errors.username && <p>{errors.username.message}</p>}

      <input {...register("email")} />
      {errors.email && <p>{errors.email.message}</p>}

      <button type="submit" disabled={!isValid}>Submit</button>
    </form>
  );
};

useForm을 선언할 때 options로 resolver를 Zod schema를 넣으면 submit이 될 때 자동으로 유효성 검사가 이뤄진다.

profile
성실(誠實)한 사람만이 목표를 성실(成實)한다

0개의 댓글

관련 채용 정보