티스토리 뷰

왜 사용하게 됐나? 

이전에 커스텀 훅이나 유틸리티 기능을 따로 만들어 import하여 react 프로젝트에서 사용하였는데 react-hook-form을 사용하면 여러 form 상태에 관한 처리가 유용할 거 같아 찾아보게 됐습니다. form 양식에 커스텀 훅을 만드는 것보다 react hook form 이라는 라이브러리를 사용하면 form관련 작업을 수월하게 할 수 있습니다. 

 

사용해보기 

자바스크립트 패키지 매니저를 통해 리액트 훅 폼을 기존의 React 프로젝트에 설치해줍니다. 

yarn add react-hook-form 

npm install react-hook-form
  • 저는 yarn을 사용하기 때문에 yarn을 통해서 설치해주었습니다. 

실습 예제로 로그인 폼을 구현해봅시다. 

HookForm.jsx

export default function HookForm() {
 return(
  <form>
    <label htmlFor="email">이메일</label>
    <input type="email" id="email" name="email" placeholder="test@email.com" />
    <label htmlFor="password">비밀번호</label>
    <input type="password" id="password" name="password" placeholder="****" />
    <button type="submit">로그인</button>
  </form>
 )

 

1. react hook form 연결하기

리액트 훅 폼은 어떤 양식 UI에도 연결하기 쉬운 API를 제공합니다. 

react-hook-form 패키지로부터 useForm() 훅함수를 불러온 뒤, 컴포넌트 함수 안에서 이 함수를 호출합니다.

그러면 결과 객체로부터 register() 함수와 handleSubmit() 함수를 얻을 수 있습니다.

  • register() 함수를 이용하여 각 입력란을 등록하고, handleSubmit() 함수를 이용하여 form 요소에서 발생하는 submit이벤트를 처리해주도록 합니다. 

2. 기능 구현하기

2-1. 중복 제출 방지

로그인폼에서 사용자가 이벤트 처리가 미처 종료되기 전에 다시 로그인 버튼을 클릭할 경우, 양식이 중복해서 제출되는 문제가 발생할 수 있습니다. 

따라서 사용자가 로그인 버튼을 클릭하자 마자, 해당 버튼을 비활성화 시켰다가, 이벤트 처리가 완료되었을 때, 

제출 버튼을 다시 활성화 시켜주는 것이 안전합니다. 

 

useForm() 훅 함수가 반환하는 객체의 formState 속성은 양식이 현재 어떤 상태인지를 담고 있습니다. 

이 formState 로부터 isSubmitting 속성을 읽어서 양식이 현재 제출 중인 상태인지 아닌지를 판단할 수 있습니다.

->로그인 button의 disabled 속성에 이 isSubmitting 값을 설정해주면 로그인 버튼이 양식의 제출 처리가 끝날때까지 비활성화됩니다.

 

시각적으로 로그인 버튼이 비활성화되는 것을 확인하기 위해서 비동기 처리 코드를 작성해서 확인해봅시다. 

HookForm.jsx

import { useForm } from "react-hook-form";
import styled from "styled-components";

export default function HookForm() {
  const {
    register,
    handleSubmit,
    formState: { isSubmitting },
  } = useForm();

  async function submitHandler(formData) {
    await new Promise((r) => setTimeout(r, 3000));
    alert(JSON.stringify(formData));
  }
  return (
    <Wrapper>
      <h3 className="title">리액트 훅 폼</h3>
      <form onSubmit={handleSubmit(submitHandler)}>
        <label htmlFor="email">🕵️‍♀️ 이메일</label>
        <input
          type="email"
          id="email"
          name="email"
          placeholder="test@email.com"
          {...register("email")}
        />

        <label htmlFor="password">🕵️‍♀️ 비밀번호</label>
        <input
          type="password"
          id="password"
          name="password"
          placeholder="****"
          {...register("password")}
        />
        <button type="submit" disabled={isSubmitting}>
          로그인
        </button>
      </form>
    </Wrapper>
  );
}
  • handleSubmit 함수의 인자로 비동기 처리를 하는 함수 submithandler를 넘겨줍니다.

submit 이벤트 동안 비활성화되는 로그인버튼 모습

+ css 부분은 styled-components(CSS in JS)를 사용했습니다. 

본문 내용과는 무관하기 때문에 일회성으로 만들어둔 엉망인 css코드라는 점을 감안해주시긔..

const Wrapper = styled.div`
  background-color: gray;
  display: flex;
  flex-direction: column;
  border-radius: 10px;
  overflow: hidden;

  & > .title {
    color: white;
    text-shadow: 2px 2px 2px #2b90e2;
  }

  & > form {
    font-weight: 600;
    font-size: 17px;
    height: 200px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 10px 15px;
    background-color: #2b90e2;
    color: #f0efef;

    & > label {
      width: 100%;
      text-align: left;
    }

    & > * {
      margin-bottom: 10px;
    }

    & > input {
      padding: 5px 10px;
    }

    & > button {
      width: fit-content;
    }
  }
`;

2-2. 입력값 검증

로그인 폼에 기본적인 입력값 검증을 하는 validation 기능을 만들어봅시다. 보통은 따로 validation함수를 만들어 불러와서 사용하였습니다만 리액트 훅 폼 라이브러리와 같이 사용하면 입력값 검증이 손쉬워 집니다. 

[입력값 조건]

  • 이메일 & 비밀번호 필수 입력
  • 이메일 유효형식 
  • 비밀번호 최소 길이

입력값 검증은 입력란을 등록할 때, register() 함수의 두 번째 인자로 옵션을 넘기면 됩니다.

⭐ HTML에서 입력란 검증을 위해 기본적으로 제공되는 required, pattern, minLength 와 같은 검증 타입을 사용할 수 있으며, 각 검증 타입이 실패했을 때 보여줄 오류 메세지도 설정할 수 있습니다. 
import { useForm } from "react-hook-form";
import styled from "styled-components";

export default function HookForm() {
  const {
    register,
    handleSubmit,
    formState: { isSubmitting, isDirty, errors },
  } = useForm();

  async function submitHandler(formData) {
    await new Promise((r) => setTimeout(r, 3000));
    alert(JSON.stringify(formData));
  }
  return (
    <Wrapper>
      <h3 className="title">리액트 훅 폼</h3>
      <form onSubmit={handleSubmit(submitHandler)}>
        <label htmlFor="email">🕵️‍♀️ 이메일</label>
        <input
          type="email"
          id="email"
          name="email"
          placeholder="test@email.com"
          aria-invalid={!isDirty ? undefined : errors.email ? "true" : "false"}
          {...register("email", {
            required: "이메일은 필수 입력입니다.",
            pattern: {
              value: /\S+@\S+\.\S+/,
              message: "이메일 형식에 맞지 않습니다.",
            },
          })}
        />
        {errors.email && <small role="alert">{errors.email.message}</small>}
        <label htmlFor="password">🕵️‍♀️ 비밀번호</label>
        <input
          type="password"
          id="password"
          name="password"
          placeholder="****"
          aria-invalid={
            !isDirty ? undefined : errors.password ? "true" : "false"
          }
          {...register("password", {
            required: "비밀번호는 필수입니다.",
            minLength: {
              value: 8,
              message: "8자리 이상 비밀번호를 사용하세요.",
            },
          })}
        />
        {errors.password && (
          <small role="alert">{errors.password.message}</small>
        )}

        <button type="submit" disabled={isSubmitting}>
          로그인
        </button>
      </form>
    </Wrapper>
  );
}

입력란에 유효하지 않은 값을 입력했을 경우 검증이 실패하여 로그인 폼이 제출되지 않습니다. 

formState 속성의 errors 객체에 오류 내용이 저장되는데, 이 값을 읽어서 각 입력란 아래에 오류 메세지가 나오도록 설정했습니다. 

  • 스크린 리더 사용자를 위해서 각 입력란에 aria-invalid 라는 속성을 사용하고, 에러 메세지를 표시해주는 영역은 role="alert" 를 사용했습니다. aria-invalid 와 role="alert" 가 한 세트라고 생각하면 될 거 같습니다.  *이 부분은 유저 사용성을 고려한 코드같습니다.

2-2-1. isDirty 속성 

isDirty react-hook-form docs

isDirty는 어떤 필드든 사용자 입력이 있었는지 확인할 때 쓰입니다. 또한, form level에서 defaultValues 값과 현재 form의 값을 깊은 비교를 해서 나오는 상태값이기 때문에 useForm의 defaultValues를 설정해주라고 나와있네요. 타입은 boolean 으로 인풋값을 바꾸면 true로 변경하게 됩니다. 단, 기본적으로 설정한 값(초기값)과 현재값을 비교하여 동일하다면 false입니다. 

2-2-2. HTML small 태그

  • small 태그는 실제로 사용해 본적도 없고 왜 이 태그에 role속성을 붙인 것인지 궁금해서 찾아보게 되었습니다. 보통 덧붙이는 글이나, 저작권과 법률 표기 등의 작은 텍스트를 나타내는 요소라고 하는데요. 
  • role="alert" 를 붙일 수 있는 제한이 텍스트 요소이기 때문입니다(버튼, 링크 등은 x). Thealertrole should only be used for text content, not interactive elements such as links or buttons. 라는 MDN 설명에서 알 수 있습니다. 자세한 내용은 링크를 걸어두겠습니다. 

 

 

all ref: https://www.daleseo.com/react-hook-form/

 

React Hook Form 라이브러리 사용법

Engineering Blog by Dale Seo

www.daleseo.com

https://react-hook-form.com/api/useform/formstate

 

useForm - FormState

Performant, flexible and extensible forms with easy-to-use validation.

react-hook-form.com

 

댓글