React | React-hook-form 가볍게 살펴보기

JU CHEOLJIN·2022년 8월 7일
1
post-thumbnail

들어가며

웹 어플리케이션을 만들다 보면 수 많은 Form 들을 만나게 된다. 상황에 따라서는 단순히 2~3개의 입력이 아니라 여러 개의 입력을 받고 유효성 검증까지 함께 해야하기 때문에 매번 골머리를 않는 부분이기도 하다. 특히, 사용자의 입력마다 유효성을 검증하고 에러 메세지를 보여줘야 하는 경우에는 렌더링 최적화에 대한 고민이 들어가게 되고, 이를 이해 코드를 작성하다 보면 또 엄청 복잡한 코드를 마주하고는 인상을 쓰게 된다. 팀 간에 협업을 진행하면서도 Form을 어떻게 작성할 지 협의가 쉽지 않아지는 포인트이기도 하다. 이를 개선하기 위해서 도움을 받을 수 있는 라이브러리가 있는 지 찾아보게 되었고 나는 React-hook-form 은 어떤 라이브러리인지, 어떤 문제를 도와줄 수 있는지 살펴보려고 한다.

설치하기

# 지원하는 리액트 버전
npm i react-hook-form

# 지원하지 않는 리액트 버전
npm i react-hook-form --legacy-peer-deps

기존 Form 방식

const App = () => {
  const [values, setValues] = useState({
    email: "",
    password:"",
    passwordCheck:"",
    phone:"",
    agreement: "",
  })
  
  const onChange = (e:React.ChangeEvent<HTMLInputElement>) => {
    const {name, value} = e.target;
    
    setValues(prev => ({
      ...prev, [name] : value.trim()
    }))
  }
  
  
  return( 
    <Container>
    <Input name="email" value={values.email} onChange={onChange}/> 
        <Input name="password" value={values.password} onChange={onChange}/>
        <Input name="passwordCheck" value={values.passwordCheck} onChange={onChange}/>
        <Input name="phone" value={values.phone} onChange={onChange}/>
        <Input name="agreement" value={values.agreement} onChange={onChange} type="checkBox"/>
    </Container>
   )
}

기존에는 폼을 작성하기 위해서 신경 쓸 부분이 많았다. 각 input에 대한 상태값을 관리할 필요가 있었고 이를 위해 각 상태를 따로 작성하거나 하나의 useState에 객체로 관리하거나 해야했다. 여기에 각 입력마다 error 상태를 관리하기 위해서는 error에 관한 상태를 또 만들고 매 입력마다 이를 확인하는 로직을 추가해야 했다. React-hook-from을 사용하면 이를 훨씬 간단하게 작성할 수 있다.

React-hook-Form 적용하기

useForm

useForm을 이용하면 번거롭고 복잡한 코드를 간단하게 작성할 수 있다.

import React from "react";
import { useForm } from "react-hook-form";

export default function App() {
  const { register, handleSubmit } = useForm();
  const onSubmit = data => console.log(data);
   
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("firstName")} />
      <select {...register("gender")}>
        <option value="female">female</option>
        <option value="male">male</option>
        <option value="other">other</option>
      </select>
      <input type="submit" />
    </form>
  );
}

우리가 연결하고자 하는 Inputregister 을 이용해서 연결할 수 있는데, 이때 인자로 넣어주는 문자열은 name이라고 생각하면 된다.

handleSubmit의 경우에는 submit의 기본 이벤트가 발생할 때 리프레쉬 되는 현상을 막아주면서 콜백으로 전달된 함수에 input에 담긴 value 들을 담아 실행시켜주기 때문에 복잡하게 추가 로직을 작성할 필요 없이 사용할 수 있다.

컴포넌트에 적용하기

위의 register 방식을 통하면 손쉽게 form을 관리할 수 있지만 다른 라이브러리에서 제공하는 컴포넌트나 내부에서 작성한 컴포넌트의 경우에는 바로 register을 적용할 수 없다. 이를 대비해서 Controller 가 제공된다.

const {
    control,
    getValues,
    formState: {errors, isValid}

} = useForm({mode: "onChange", reValidateMode: "onChange"})


return  <InputWrapper>
          <Controller
            render={({ field }) => (
            <WithLabelInput
              type="text"
              label="이름"
              {...field}
            />
          )}
            control={control}
            name="이름"
            rules={{ required: true }}
            defaultValue=""
          />
          <SpaceY space="8px" />
          {errors?.이름 && (
            <WarnMessage
              message="이름을 입력해주세요."
              isError
              fontSize="14px"
            />
          )}
        </InputWrapper>

Controller에 각• 요소는 다음과 같다.

  • render : 전달하고자 하는 컴포넌트를 전달할 수 있다. 에러메세지를 방지하기 위해서 직접 구현한 컴포넌트의 경우에는 ref를 전달받을 수 있는 형태로 작성하고, defaultValue를 통해서 밸류 관련 에러도 방지할 수 있다.
  • rules : 유효성 검증을 위해 필요한 내용을 입력해 줄 수 있다. 상황에 따라서 required, pattern 등을 사용할 수 있다. 타입과 메세지를 입력하는 경우에는 유효성 검증을 통과하지 못한 경우에 보여줄 메세지도 입력할 수 있다.
  • errors : 유효성 검증을 통과하지 못한 경우에는 에러 객체에 내용이 담기게 된다. type과 message의 형태이기 때문에 각 상황에 맞게 에러 메세지를 보여줄 수 있다.
  • isValid : 유효성 검증이 마무리 되어 있는지 상태를 boolean으로 알려주는 값이다. 제출 버튼의 활성화 상태와 관련해서 사용할 때 유용할 수 있다.

적용 모습

간단하게 작성해 본 입력 modal의 모습이다. 각 input의 유효성을 실시간으로 검색하면서도 리렌더링을 유발시키지 않고 있다. 또한, 동일한 동작을 하는 기능임에도 불구하고 훨씬 적은 코드를 작성할 수 있었다.

개선이 예상 되는 점

기존

  • 여러가지 입력의 상태를 관리하며 에러 메세지를 보여줘야 했기 때문에 많은 리 렌더링이 발생해야 했다. 입력이 적은 경우에는 문제가 없겠지만 많은 경우에는 불필요하게 많은 리렌더링이 성능에 나쁜 영향을 줄 수 있다고 생각했다.
  • 렌더링을 줄이기 위해서는 memo 등을 이용해 각자 최적화를 해야 했고, 이를 위해서 입력을 관리하는 로직이 지나치게 복잡해지는 경향이 있었다. 특히 여러 개의 입력을 관리하는 경우에는 상태를 계속 전개 연산자를 통해 복사하면서 진행해야 해서 실수를 유발하기 쉬웠다.

변경

  • React-hook-form의 경우에는 옵션에 따라서 리 렌더링을 발생시키지 않을 수 있기 때문에 불필요한 렌더링을 줄일 수 있었다.
  • 상태를 직접적으로 관리할 필요 없이 React-hook-form 에서 제공하는 함수를 통해 가져올 수 있었으며, 에러 메세지 또한 에러 객체를 통해서 쉽게 적용할 수 있었다.

마무리

이렇게 React-hook-form에 대해서 간단하게 살펴 봤다. 당장 프로덕트에 적용한 것이 아니라 검토하는 과정에서 간단하게 작성을 해봤기 때문에 실제로 사용하면서 많은 문제들이 발생할 여지는 있다. 항상 일하면서 느끼는 부분이지만 개발이 들어가게 되는 경우에 생각보다 많은 변수들이 발생하고 이를 해결하기 위해서 라이브러리와 고생을 해본 경험은 많으니까. 그렇기 때문에 직접 작성하고 최적화만 잘하면 되지 않겠냐는 생각이 들 수도 있고, 한 적도 있다. 그럼에도, 라이브러리를 사용해서 얻을 수 있는 이점은 불필요한 코드의 양을 줄이고 협업하는 과정에서 의사 소통에 들어가는 시간의 낭비를 줄일 수 있는 부분이라고 생각한다. 적은 코드를 작성하면 버그도 줄어들테니까. 다음 React-hook-form과 관련된 글은 실제로 적용하면서 겪었던 문제들과 이를 해결하는 과정에 대해서 정리해보려고 한다.

profile
사회에 도움이 되는 것은 꿈, 바로 옆의 도움이 되는 것은 평생 목표인 개발자.

0개의 댓글