[react-hook-form + TypeScript] 리액트 훅 폼 사용법과 타입 주기

오늘은 react-hook-form 의 기본적인 사용법과 타입 주는 방법을 작성해보고자 한다.
바로 본론으로!!

1. 폼으로 입력받을 데이터 타입 정의하기

// SignupForm.tsx
interface FormValue {
	name: string
	nickname: string
  	email: string
  	password: string
  	password_confirm: string
}

회원가입을 하는 SignupForm.tsx 을 작성한다고 쳤을 때 타입스크립트에서는 먼저 회원가입 양식에 들어갈 각 데이터의 타입을 먼저 정의해주어야 한다.

2. useForm 사용하기

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

const SignupForm: FC = () => {
	const { register, handleSubmit, watch, formState: { errors } } = useForm<FormValue>()
    
    return (
      // ...
     )
}

그 다음에는 react-hook-form 에서 import 한 useForm 훅을 사용해서 객체 구조분해 할당으로 register, handleSubmit, watch, errors 등을 꺼낸다.

  • register 는 리액트훅폼에게, 이 인풋에 대해 이러한 항목을 입력받을 것이라는 것을 등록해준다.
  • handleSubmit 은 리액트훅폼에서 각 항목이 입력되었을 때 submit 이벤트를 처리하는 역할을 한다.
  • watchregister 한 항목의 변경사항을 추적한다
  • errors 는 유효성이 통과되지 않으면 에러 상태를 내보내준다.

중요한건 여기서 useForm 훅을 실행할 때 제너릭타입으로 <FormValue> 를 넣어줌으로써 입력받을 데이터의 타입들을 리액트훅폼에 전달해준다.

3. 각 항목에 적용하기

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

const SignupForm: FC = () => {
	const { register, handleSubmit, watch, formState: { errors } } = useForm<FormValue>()
    
    const onSubmitHandler: submitHandler<FormValue> = (data) => {
    	console.log(data)
    }
    
    return (
      	<form onSubmit={handleSubmit(onSubmitHandler)}>
	        <label>name</label>
        	<input {...register("name")} />
	        <label>nickname</label>
        	<input {...register("nickname")} />
	        <label>email</label>
        	<input {...register("email")} type="email" />
	        <label>password</label>
        	<input {...register("password")} type="password" />
	        <label>password_confirm</label>
        	<input {...register("password_confirm")} type="password" />
        </form>
     )
}

기본적으로 이런 형식으로 작성된다.

각 input 태그에는 {...register("name")} 처럼 속성으로 넣어주는데, 이렇게 하면 등록이 된다. register() 의 인자로는 이전에 작성한 FormValue 인터페이스에 맞게 각 데이터의 이름을 따옴표로 감싼 문자열로 넣어준다.
그럼 이제 각 항목의 데이터는 각 input 태그에 등록되어, 해당 input 은 해당 데이터만을 받게 된다.

그리고 onSubmitHandler 핸들러를 만들어서 타입은 submitHandler<FormValue> 로 작성해주고 모든 항목이 정상적으로 입력이 되었을 때의 처리할 로직을 내부에 작성해준다.
위의 핸들러는 회원가입의 로직이 등록될 것이다.
인자로 들어온 data 에는 register() 가 등록된 input 의 각 value 가 key-value 형태로 들어온다.

<form> 태그의 onSubmit 이벤트에는 바로 onSubmitHandler 를 등록하는게 아니라 리액트훅폼에서 꺼낸 handleSubtmit 의 인자로 넣어서 handleSubmit(onSubmitHandler) 를 걸어준다.

그럼 이제 이 <form> 태그 안에 있는 각 항목이 내가 직접 작성한 조건에 맞지 않으면 submit 이벤트가 발생하지 않는다.

4. 유효성 검증하기

단지 register 로 등록만 하는 것으로는 유효성 검증이 이루어지지 않는다. 이제 register 를 할 때 유효성 검증을 하는 코드를 추가해준다.

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

const SignupForm: FC = () => {
	const { register, handleSubmit, watch, formState: { errors } } = useForm<FormValue>()
    
    const onSubmitHandler: submitHandler<FormValue> = (data) => {
    	console.log(data)
    }
    
    return (
      	<form onSubmit={handleSubmit(onSubmitHandler)}>
	        <label>name</label>
        	<input {...register("name", { requried: true, maxLength: 20 })} />
	        <label>nickname</label>
        	<input {...register("nickname", { requried: true, maxLength: 10 })} />
	        <label>email</label>
        	<input {...register("email", { required: true, pattern: /^\S+@\S+$/i })} type="email" />
	        <label>password</label>
        	<input {...register("password", { required: true, minLength: 6 })} type="password" />
	        <label>password_confirm</label>
        	<input {...register("password_confirm", { ... })} type="password" />
        </form>
     )
}

우선 비밀번호 확인을 의미하는 password_confirm 을 제외하고는 이런 식으로 유효성 검증을 해줄 수 있다.
register() 의 첫번째 인자인 각 데이터항목의 이름 다음으로,
두번째 인자로 객체 형식으로 어떤 유효성 검증을 할지를 적어주면 된다.

우선 각 항목의 경우 모두 필수로 입력되어야 하기 때문에 required: true 를 넣어주었다.
또 이름과 닉네임의 경우 maxLength: Number 로 최대글자 수를 지정해주었고,
비밀번호의 경우는 minLength: Number 로 최소글자 수를 지정해주었다.
이메일은 pattern: RegExp 처럼 정규표현식으로 어떤 패턴을 받을지를 지정해주었다.

비밀번호 확인 유효성 검증하기

비밀번호 확인 (password_confirm) 의 경우 유효성 검증이 조금 추가된다.

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

const SignupForm: FC = () => {
	const { register, handleSubmit, watch, formState: { errors } } = useForm<FormValue>()
    
    // 비밀번호와 비밀번호 확인이 일치하는지 검증하기 위해 "password" input 의 value 를 추적함
    const passwordRef = useRef<string | null>(null)
    passwordRef.current = watch("password")
    
    const onSubmitHandler: submitHandler<FormValue> = (data) => {
    	console.log(data)
    }
    
    return (
      	<form onSubmit={handleSubmit(onSubmitHandler)}>
        	{/* 중략 */}
	        <label>password_confirm</label>
        	<input 
              {...register("password_confirm", {
          		required: true,
            	validate: (value) => value === passwordRef.current,
          	  })} 
              type="password"
             />
        </form>
     )
}

useRef 훅을 통해서 "password" input 의 입력된 값을 watch("password") 로 추적한다.
그리고 password_confirm 의 유효성 검증에서
validate: (value) => value === passwordRef.current 를 추가함으로써
passwor_confirm 의 value 가 passwordRef.current 와 일치하면 유효성이 검증되는 것으로 한다.

5. 유효성 검증이 통과되지 못한 경우 에러문구 내보내기

useForm 훅을 통해서 errors 객체를 객체 구조분해 할당으로 꺼내왔다.
만약 유효성이 통과되지 못하면 errors.해당데이터 에는 에러의 관한 내용이 담기게 된다.
따라서 만약 errors 객체 안의 내용이 있을 경우 에러문구를 직접 커스텀해줄 수 있다.

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

const SignupForm: FC = () => {
	const { register, handleSubmit, watch, formState: { errors } } = useForm<FormValue>()
    
    // ...
    
    return (
      	<form onSubmit={handleSubmit(onSubmitHandler)}>
	        <label>name</label>
        	<input {...register("name", { requried: true, maxLength: 20 })} />
        	{errors.name && errors.name.type === "required" && (
        		<div>이름을 입력해 주세요!</div>
        	)}
        	{errors.name && errors.name.type === "maxLength" && (
        		<div>이름은 최대 20자만 입력할 수 있습니다!</div>
        	)}
        	{ /*  중략  */}	
        </form>
     )
}

현재 name 에는 유효성 검증이 2가지 항목이 들어가있다.
필수 입력하라는 required: true 와 최대20자로 제한하는 maxLength: 20 이 있다.

따라서 하나는 errors.name 이 있고 errors.name.type 이 "required" 일 경우 이름을 입력해 달라는 에러문구를 내보내고 있고,
다른 하나는 errors.name 이 있고 errors.name.type 이 "maxLength" 일 경우 이름을 최대 20자만 입력해달라는 에러문구를 내보낸다.


이상 react-hook-form 으로 간단하게 유효성을 검증하고, 타입까지 지정하는 방법을 알아봤다.
리액트훅폼은 굉장히 유용하기 때문에 자주 쓰이고 있는 라이브러리다.
또한 리액트훅폼은 타입스크립트 친화적이기 때문에 따로 @types/... 같은 라이브러리를 추가로 설치할 필요가 없고, 세세한 타입 지정하는 법 또한 공식문서에 잘 나와있다.

react-hook-form 공식문서 with TypeScript

좋은 웹페이지 즐겨찾기