Just Do IT!

[TypeScript] react-hook-form 사용하기 본문

개발 공부/TypeScript

[TypeScript] react-hook-form 사용하기

MOON달 2023. 2. 17. 17:08
728x90
react hook form이란?

React 내에서 form, 양식을 만들 때 사용하는라 이브러리다. 복잡한 폼을 만들 때 간결하고 쉬운 코드 작성에 도움이 된다.

기존 폼에서 입력해야 하는 번거롱룬 작업을 줄여주며, 타입스크립트로 작성된 라이브러리이기 때문에 타입스크립트와 잘 맞는다. 불필요한 렌더링을 최소화한다.

 

공식 문서

https://react-hook-form.com/

 

Home

React hook for form validation without the hassle

react-hook-form.com

 

 

 

 

 

 

설치하기
npm install react-hook-form

 

 

 

 

 

react-hook-form 적용 전
import { useState } from "react";
const Login = () => {
  // 1. state로 관리하기
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  
  // input 태그에서 아이디와 비밀번호 받아와, state 변경해주기
  const onValidEmail = (event) => {
    setUsername(event.target.value)
  }
  const onValidPassword = (event) => {
    setUsername(event.target.value)
  }
  
  // 2. state값 가져오기
  const handleSubmit = (event) => {
    event.preventDefault();
    // login 함수
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <Input
        value={email}
        type="text"
        placeholder="이메일"
        onChange={onValidEmail}
      />
      <Input
        value={password}
        type="password"
        placeholder="비밀번호"
        onChange={onValidPassword}
      />
      <Input type="submit" value="로그인" />
   </form>
  );
};
export default Login;

라이브러리 없이 로그인/회원가입 같은 폼을 만드려면 과정이 번거롭다.

1. 입력 필드에 관한 state 선언

2. 입력 필드에 onChange 이벤트로 state 변경

3. state를 입력 필드의 value로 등록

 

기존에는 위의 과정을 거쳐 state 선언하고 event를 연결하고, value를 받아와야 한다.

그런데 로그인, 회원가입 등 입력 필드가 많아질 수록 제어하기 힘들어지고 유효성 검사를 처리하기 어려워진다.

 

나 역시 이번에 유효성 검사를 구현하면서 생각치 못한 버그들을 만나게 되면서 이 라이브러리를 쓰기로 마음 먹었다.

 

react hook form을 사용하게 되면 value, onChange로 각 입력 필드에 대한 처리를 추가할 필요가 없고, state를 직접 관리할 필요가 없어진다.

따라서 코드의 양이 줄고 좀 더 효율적으로 코드를 작성할 수 있었다.

 

 

 

 

 

공식 문서 예제 살펴보기
import { useForm, SubmitHandler } from "react-hook-form";

type Inputs = {
  example: string,
  exampleRequired: string,
};

export default function App() {
  const { register, handleSubmit, watch, formState: { errors } } = useForm<Inputs>();
  const onSubmit: SubmitHandler<Inputs> = data => console.log(data);

  console.log(watch("example")) // watch input value by passing the name of it

  return (
    /* "handleSubmit" will validate your inputs before invoking "onSubmit" */
    <form onSubmit={handleSubmit(onSubmit)}>
      {/* register your input into the hook by invoking the "register" function */}
      <input defaultValue="test" {...register("example")} />
      
      {/* include validation with required or other standard HTML validation rules */}
      <input {...register("exampleRequired", { required: true })} />
      {/* errors will return when field validation fails  */}
      {errors.exampleRequired && <span>This field is required</span>}
      
      <input type="submit" />
    </form>
  );
}

공식 문서 예제에는 자바스크립트, 타입스크립트 두 가지 버전이 있는데 내가 typescript에 적용했으므로 (ㅋㅋ)

타입스크립트 버전 예제를 가져왔다.

 

  • register
    • ref로 사용되는 함수
    • 입력 필드를 react hook form에 등록하고 변경사항에 대해 값을 추적한다
    • name 속성이 있어야 오류가 생기지 않으며, 해당 값은 고유해야 한다
  • handleSubmit : form을 서버로 제출할 때 사용하는 함수
  • errors: 유효성 검사를 포함하는 객체

 

validation 추가

  • required: 입력 필드에 대한 필수 여부 검사
  • minLength / maxLength : 문자열 입력 값의 최소, 최대 길이 설정
  • min / max : 숫자 입력 값의 최소값, 최대값 설정
  • type: 입력 필드에 대한 유형
  • pattern: 정규식을 이용한 패턴 정의 (유효성 검사)

 

 

이러한 속성들을 사용하여 form을 작성할 수 있고, errors를 통해 유효성 검사 성공 여부를 알 수 있다.

유혀성 검사는 handleSubmit 함수가 실행될 때 진행되며 유효성 검사를 실패하면,

  • 해당 입력 필드에 자동으로 focus
  • erros가 하나라도 존재할 경우 submit 함수 실행 x (손쉽게 버튼 비활성화 가능)

이러한 기능을 제공해주기 때문에 유효성 검사하기도 쉽다.

 

 

 

 

 

사용법 (타입스크립트로 작)

1. 폼에 들어가는 데이터 타입 정의

// Register.tsx

interface AuthForm {
  userName: string;
  email: string;
  password: string;
  confirm: string;
}

우리 프로젝트에 적용한 예시인데, 회원가입 양식에 들어갈 데이터 타입을 정해주었다.

 

2. useForm() 사용하여 필요한 함수, 객체 불러오기

const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<AuthForm>({ mode: "onBlur" });

register : input 요소를 react-hook-form과 연결시키는 메소드

handleSubmit : form을 submit 했을 때 실행할 함수

formState : form state에 관한 정보를 담고 있는 객체

errors: 유효성 검사 실패 시 error message

 

mode : form의 유효성 검사를 어느 동작 때 실행할 지 결정하는 props

onBlur : input에서 focus out 될 때를 의미 (input에서 작성한 뒤) => onBlur가 실행되면 유효성 검사가 실행된다

 

3. form 에 적용하기

	<input
            className="shadow appearance-none border rounded w-full py-2 px-1 text-black"
            {...register("email", {
              required: "이메일을 올바르게 입력해주세요.",
              pattern: {
                value:
                  /^(([^<>()\[\].,;:\s@"]+(\.[^<>()\[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i,
                message: "이메일 형식에 맞게 입력해주세요,",
              },
            })}
            name="email"
            type="email"
            id="email"
            value={email}
            onChange={(event) => setEmail(event.target.value)}
            placeholder="이메일"
            onKeyUp={(e) => {
              if (e.key === "Enter") {
                loginBtn(e);
              }
            }}
          />
          <div className="text-red-600 font-sm mb-6">
            {errors?.email?.message}
          </div>

 

실제로 적용한 로그인 폼 중 이메일 input 부분만 가져왔다.

 

여기서 register() 안에는 폼 안에 들어갈 데이터 중 필요한 걸 가져온다. 여기서는 email이므로 email을 가져온다.

email은 이전에 이전에 데이터 타입을 지정했었다. (1번 참고)

 

register를 통해 input 태그에 데이터가 등록되어 해당 input은 해당 데이터만을 받게 된다. register 태그는 name 속성이 필수이기 때문에 name 역시 지정해주었다.

 

또한 pattern을 통해 input 태그의 유효성 검사를 지정할 수 있다. 위의 예제는 이메일 형식에 맞도록 정규 표현식으로 구현했으며, 유효성 검사 실패 시 message를 input 태그 아래 구현하도록 했다.

 

이러한 과정을 거쳐 각 항목이 내가 작성한 조건에 맞지 않으면 submit 이벤트가 전혀 발생하지 않는다. 버튼이 비활성화 되는 것과 마찬가지이다.

 

 

비밀번호 input 창 역시 아래 예제처럼 직접 custom할 수가 있다.

	<input
            className="shadow appearance-none border rounded w-full py-2 px-1 text-black"
            {...register("password", {
              required: "비밀번호를 입력해주세요",
              minLength: {
                value: 8,
                message:
                  "비밀번호는 영문 대소문자, 숫자를 혼합하여 8~15자로 입력해주세요.",
              },
              pattern: {
                value: /^[A-Za-z0-9]{8,15}$/,
                message:
                  "비밀번호는 영문 대소문자, 숫자를 혼합하여 8~15자로 입력해주세요.",
              },
            })}
            name="password"
            type="password"
            id="password"
            value={password}
            onChange={(event) => setPassword(event.target.value)}
            placeholder="비밀번호"
            onKeyUp={(e) => {
              if (e.key === "Enter") {
                loginBtn(e);
              }
            }}
          />
          <div className="text-red-600 font-sm mb-6">
            {errors?.password?.message}
          </div>

 

 

 

 

 

 

 

 

 

 


유효성 검사 버그 때문에 이것저것 찾아보다가 이런 좋은 라이브러리를 발견하게 되었다.

처음에는 이게 뭔가 싶어서 공식 문서를 보면서 헤맸었는데 한번 적용하고 나니 한결 코드가 간결해졌다.

덕분에 버그 해결하면서 리팩토링까지 하게 되었다.

 

나중에도 편리하게 적용하고 싶어서 블로그로 정리했다. 새로운 걸 알게 되다니...,tmi지만 프로젝트 하면서 더 많이 공부하는 것 같다.