[Next.js] React Hook Form

안셩·2024년 10월 17일

복습내용

목록 보기
27/27
post-thumbnail

1. React Hook Form

: 폼을 효율적으로 관리하고 검증하기 위한 비제어 컴포넌트 기반의 라이브러리

비제어 컴포넌트

  • 즉각적인 피드백이 불필요하고 제출시에만 값이 필요하거나, 불필요한 렌더링과 값 동기화가 싫을 때 사용
  • 상태를 직접 관리하지 않기 때문에 리렌더링을 줄이고 성능을 향상시킴

2. React-Hook-Form로 로그인 기능 구현

강의 자료에서 로그인 페이지를 리액트훅폼을 사용한 코드이다.

"use client";

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

const SignInForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

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

  return (
    <form
      onSubmit={handleSubmit(onSubmit)}
      className="flex flex-col gap-4 p-5 items-center w-full m-auto"
    >
      <div className="flex flex-col gap-2">
        <label htmlFor="email">Email</label>
        <input
          id="email"
          type="text"
          {...register("email", {
            required: "Email is required",
            pattern: {
              value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
              message: "Invalid email address",
            },
          })}
          placeholder="Email"
          className="text-black"
        />
        {errors.email && (
          <p className="text-red-500">{errors.email.message}</p>
        )}
      </div>

      <div className="flex flex-col gap-2">
        <label htmlFor="password">Password</label>
        <input
          type="password"
          {...register("password", {
            required: "Password is required",
          })}
          placeholder="Password"
          className="text-black"
        />
        {errors.password && (
          <p className="text-red-500">{errors.password.message}</p>
        )}
      </div>
      <button
        className="bg-gray-800 text-white px-4 py-2 rounded-md"
        type="submit"
      >
        로그인
      </button>
    </form>
  );
};

export default SignInForm;

1) useForm 함수 import

useForm : 폼의 상태, 입력값 검증, 제출 등을 간단하게 관리할 수 있는 React-Hook-Form의 핵심함수

2) useForm 훅 사용

const {register, handleSubmit, formState: { errors }} = useForm();
  • register : 각 입력 필드를 React Hook Form에 등록하는 역할, 이걸로 입력값을 추적하고 검증 규칙을 적용가능
  • handleSubmit : 폼이 제출될 때 호출되는 함수, 폼 데이터를 받아서 처리해 줌.
  • formState: { errors } : 각 입력 필드의 검증 결과를 저장하는 객체.
    에러가 있으면 이곳에 들어가고, 이를 통해 오류 메시지를 표시가능.

3) form태그의 onSubmit 함수

const SignInForm = () => {
  // onSubmit 함수 : 폼 제출 시 실행될 함수
  const onSubmit = (data) => {
    console.log(data); // 사용자가 입력한 데이터를 인자로 받아서 콘솔에 출력
  };

  return (
    // form태그의 onSubmit
    <form onSubmit={handleSubmit(onSubmit)}></form>
	);
};
  • onSubmit은 폼 제출 시 실행될 함수이다. 사용자가 입력한 데이터를 인자로 받아서 콘솔에 출력한다.
  • handleSubmit(onSubmit)로 폼 제출을 관리한다.
    폼이 제출되면 검증이 먼저 실행되고, 검증을 통과하면 onSubmit 함수가 호출된다.

4) 이메일 입력창(input)

<input
  id="email"
  type="text"
  // 이메일 입력 필드를 폼에 등록하는 역할
  {...register("email", {
    required: "Email is required",
    pattern: {
      value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
      message: "Invalid email address",
    },
  })}
  placeholder="Email"
/>
  • register("email", {...}) : 이메일 입력 필드를 폼에 등록하는 역할. 이때 'required'나 'pattern'으로 검증 규칙 설정가능.
  • required : 이메일 입력이 필수라는 규칙을 설정할 수 있다.
    사용자가 이메일 인풋창을 비워두면 "Email is required"라는 오류 메시지가 나타난다.
  • pattern : 이메일 형식을 정규 표현식으로 검증. 이메일 형식이 잘못되면 "Invalid email address"라는 오류 메시지가 나타난다.

5) 비밀번호 입력창(input)

{...register("password", {
    required: "Password is required",
})}

이메일 입력창과 비슷하게,

  • register("password", {...}) : 비밀번호 입력 필드를 폼에 등록하는 역할.
  • required : 비밀번호 입력이 필수라는 규칙을 설정할 수 있다.
    사용자가 비밀번호 인풋창을 비워두면 "Password is required"라는 오류 메시지가 나타난다.

6) 오류 메시지 처리

// 이메일 오류 메시지
{errors.email && <p className="text-red-500">{errors.email.message}</p>}
// 비밀번호 오류 메시지
{errors.password && (<p className="text-red-500">{errors.password.message}</p>)}
  • 입력 검증에 실패하면 errors.email / errors.password 객체에 오류 메시지가 저장됨. 이 코드는 오류가 있을 때 해당 메시지를 화면에 출력한다.

7) 제출 버튼

<button
  className="bg-gray-800 text-white px-4 py-2 rounded-md"
  type="submit"
>
  로그인
</button>
  • 이 버튼은 폼을 제출하는 역할.
    버튼을 누르면 폼이 제출되고, 입력값에 오류가 없으면 onSubmit 함수가 호출된다.

3. React-Hook-Form 코드 요약

  • useForm : 폼 상태와 검증을 관리하는 훅.
  • register : 각 입력 필드를 폼에 등록하고 검증 규칙을 설정.
  • handleSubmit : 폼 제출 시 검증을 처리한 후 데이터를 넘겨줌.
  • errors : 검증 오류를 저장하는 객체로, 이를 통해 오류 메시지를 출력.

이 코드를 통해 사용자 입력을 쉽게 검증하고, 오류를 처리할 수 있다.
React Hook Form 덕분에 더 깔끔하고 효율적으로 폼을 관리할 수 있다.


4. 제어컴포넌트로 로그인 기능 구현

제어컴포넌트로 작성 시,
입력값이 변경될 때마다 React의 useState를 통해 상태를 업데이트하고, 그 상태를 폼 입력 필드에 바인딩하는 방식으로 동작하며,
상태 업데이트 및 검증도 수동으로 처리해야 한다.

import { useState } from "react";

const SignInForm = () => {
  // useState로 상태를 직접 관리해야 함.
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [errors, setErrors] = useState({});

  const validate = () => {
    const newErrors = {};
    
    // 이메일 검증
    if (!email) {
      newErrors.email = "Email is required";
    } else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)) {
      newErrors.email = "Invalid email address";
    }

    // 비밀번호 검증
    if (!password) {
      newErrors.password = "Password is required";
    }

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const onSubmit = (e) => {
    e.preventDefault(); // 폼의 기본 제출 동작 방지

    if (validate()) {
      // 검증이 통과되면 데이터 처리
      console.log({ email, password });
    }
  };

  return (
    <form onSubmit={onSubmit} className="flex flex-col gap-4 p-5 items-center w-full m-auto">
      <div className="flex flex-col gap-2">
        <label htmlFor="email">Email</label>
        <input
          id="email"
          type="text"
          value={email}
          onChange={(e) => setEmail(e.target.value)} // 상태 업데이트
          placeholder="Email"
          className="text-black"
        />
        {errors.email && <p className="text-red-500">{errors.email}</p>} {/* 오류 메시지 */}
      </div>

      <div className="flex flex-col gap-2">
        <label htmlFor="password">Password</label>
        <input
          id="password"
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)} // 상태 업데이트
          placeholder="Password"
          className="text-black"
        />
        {errors.password && <p className="text-red-500">{errors.password}</p>} {/* 오류 메시지 */}
      </div>

      <button className="bg-gray-800 text-white px-4 py-2 rounded-md" type="submit">
        로그인
      </button>
    </form>
  );
};

export default SignInForm;

비제어 컴포넌트 라이브러리인 'React-Hook-Form'과의 주요 차이점

1) 상태 관리

  • React Hook Form : useForm 훅이 내부적으로 입력값을 관리해 주기 때문에 상태를 별도로 선언하지 않아도 됨.
  • 제어 컴포넌트 : useState를 사용하여 각 입력 필드의 값을 직접 상태로 관리.

2) 입력 핸들링

  • React Hook Form : register를 통해 입력 필드를 자동으로 관리하고, onChange 핸들러를 따로 작성할 필요가 없다.
  • 제어 컴포넌트 : onChange 이벤트 핸들러를 작성해서 입력값이 변경될 때마다 상태를 업데이트해줘야 함.
    예시코드에서는 onChange={(e) => setEmail(e.target.value)}와 같은 방식으로 상태를 업데이트하고 있다.

3) 검증 처리

  • React Hook Form : 폼 검증 규칙(required, pattern 등)을 register 함수에서 쉽게 설정할 수 있고, 폼 제출 시 자동으로 검증이 이루어짐.
  • 제어 컴포넌트 : 검증을 수동으로 해야한다. 예시코드에서는 validate 함수를 작성해서 각 입력값을 검증하고, 오류가 있으면 errors 상태에 저장한다.

4) 오류 메시지 처리

  • React Hook Form : formState.errors에서 자동으로 오류 메시지를 관리해 주기 때문에 검증 로직을 따로 작성할 필요가 없다.
  • 제어 컴포넌트 : 검증에 실패한 필드에 대해 오류 메시지를 수동으로 설정하고, 이를 화면에 렌더링해줌. 예시코드에서는 setErrors로 오류 상태를 관리하고, errors.email이나 errors.password로 오류 메시지를 렌더링했다.

요약
React Hook Form은 비제어 컴포넌트 기반으로 상태 관리를 간소화하고 폼 검증을 자동으로 처리해줌.
제어 컴포넌트를 사용하면 입력 필드마다 상태와 검증 로직을 수동으로 관리해야 하기 때문에 코드가 조금 더 복잡해질 수 있다.


❓궁금한 점

1) 렌더링 방식

next.js를 사용해보면서, useState를 사용하는 경우 매번 "use client;"라는 표시를 해주면서 CSR 방식으로 렌더링했었다.
그래서 제어컴포넌트로 작성하면 CSR로만 렌더링 가능한건가라는 의문이 들었다.

=> 로그인 페이지를 만들 때 어떤 렌더링 방식(SSR/CSR)을 사용할지는 Next.js의 설정에 따라 결정되고, 폼 관리 방식(React Hook Form, useState)은 별개의 문제였다.

2) 언제 사용하는지?

주로 폼 데이터 처리에 특화된 라이브러리로,

  • 회원가입/로그인 폼 : 사용자 정보를 입력받아 서버에 제출
  • 프로필 수정 폼 : 사용자의 프로필 정보를 수정하고 저장
  • 다단계 폼 : 여러 단계로 나뉜 폼을 관리하고 처리
  • 필터링 폼 : 검색이나 필터링을 위한 다양한 옵션 입력 폼 구성
  • 비밀번호 변경 폼 : 기존 비밀번호와 새 비밀번호를 입력받아 검증
  • 결제 폼 : 결제 정보 입력 및 검증

다양한 폼 유효성 검사를 손쉽게 처리하고, 상태 관리를 최적화해서 성능도 뛰어나기 때문에 여러 형태의 폼에서 유용하게 사용된다.

profile
24.07.15 프론트엔드 개발 첫 걸음

0개의 댓글