Coding Archive

[React Hook Form] Next.js ํ”„๋กœ์ ํŠธ์— React Hook Form ์ ์šฉ๊ธฐ ๋ณธ๋ฌธ

๐Ÿ’ป Programming/Next.js & project

[React Hook Form] Next.js ํ”„๋กœ์ ํŠธ์— React Hook Form ์ ์šฉ๊ธฐ

์ฝ”๋“ฑ์–ด 2024. 10. 15. 02:27

 

1. React Hook Form์ด๋ž€?

react-hook-form์€ ๊ฐ„๋‹จํ•˜๊ณ  ํšจ์œจ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ํผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์™€ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” React ๊ธฐ๋ฐ˜์˜ ํผ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

1.1 ํ”„๋กœ์ ํŠธ์—์„œ React Hook Form์„ ๋„์ž…ํ•œ ์ด์œ 

react-hook-form์„ ํ”„๋กœ์ ํŠธ์— ๋„์ž…ํ•œ ๊ฐ€์žฅ ํฐ ์ด์œ ๋Š” '์„ฑ๋Šฅ'๊ณผ '์ฝ”๋“œ์˜ ๊ฐ„๊ฒฐํ•จ' ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. React์—์„œ ํผ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์€ ๋Œ€๋ถ€๋ถ„ useState๋กœ ๊ฐ ํ•„๋“œ์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š”๋ฐ, ์ด๋Š” ๋ณต์žกํ•œ ํผ์ผ์ˆ˜๋ก ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ง€๊ณ  ์ƒํƒœ ๋ณ€ํ™”๋งˆ๋‹ค ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜์—ฌ ์„ฑ๋Šฅ ์ €ํ•˜๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ ํ”„๋กœ์ ํŠธ์—์„œ useState ๊ธฐ๋ฐ˜์œผ๋กœ ๋ชจ๋“  ํ•„๋“œ ๊ฐ’์„ ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•˜๊ณ , ๊ฐ ํ•„๋“œ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํผ ์ „์ฒด๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋Š” ๋น„ํšจ์œจ์ ์ธ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ ์ด๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด react-hook-form์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

react-hook-form์€ ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ์™€ Ref ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜์—ฌ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ DOM์—์„œ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ƒํƒœ ๊ด€๋ฆฌ์™€ ๋ฆฌ๋ Œ๋”๋ง ๋ถ€๋‹ด์„ ์ค„์ด๋Š” ๋™์‹œ์— ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ์™€ ํผ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. register์™€ validation์„ ํ†ตํ•ด ํ•„๋“œ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‰ฝ๊ฒŒ ์ •์˜ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ œ์ถœ ์‹œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•˜์ง€ ๋ชปํ•œ ํ•„๋“œ์— ๋Œ€ํ•ด ์ž๋™์œผ๋กœ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ธฐ์กด ๋ฐฉ์‹:

  • useState๋กœ ๊ฐ ํ•„๋“œ๋งˆ๋‹ค ์ƒํƒœ ๊ด€๋ฆฌ → ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง
  • ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ ๋ณ„๋„์˜ ๋กœ์ง, ๊ทธ์— ๋”ฐ๋ฅธ ์ƒํƒœ ๊ด€๋ฆฌ์˜ ๋ณต์žกํ•จ

React Hook Form ๋ฐฉ์‹:

  • Ref๋กœ ์ƒํƒœ ๊ด€๋ฆฌ → ๋ฆฌ๋ Œ๋”๋ง ์ตœ์†Œํ™”, ์„ฑ๋Šฅ ์ตœ์ ํ™”
  • ๊ธฐ๋ณธ์ ์ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ถ”๊ฐ€ ์šฉ์ด, ์ปค์Šคํ…€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์œ ์—ฐํ•˜๊ฒŒ ์ฒ˜๋ฆฌ

2. ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๊ธฐ

2.1 ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ์— ๋งž์ถ˜ ์„ค์ •

ํ”„๋กœ์ ํŠธ์—์„œ ํผ์„ ๊ตฌ์กฐํ™”ํ•  ๋•Œ, ๋จผ์ € FormProvider๋ฅผ ํ†ตํ•ด react-hook-form์˜ ํผ ์ƒํƒœ๋ฅผ ์ „์—ญ์—์„œ ๊ด€๋ฆฌํ•˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. react-hook-form์€ FormProvider์™€ useForm ํ›…์„ ์ด์šฉํ•ด ํผ ์ „์ฒด์˜ ์ƒํƒœ๋ฅผ ์บก์Аํ™”ํ•ฉ๋‹ˆ๋‹ค. ํผ์˜ ๋ชจ๋“  ์ƒํƒœ๋Š” methods ๊ฐ์ฒด๋กœ ์ œ๊ณต๋˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๊ฐ ํ•„๋“œ์˜ ์ƒํƒœ์™€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const methods = useForm<ISignUpRequest>({ 
  mode: 'onBlur', defaultValues: { 
    email: '', 
    password: '', 
    accountname: '', 
    username: '', 
    image: '', 
    intro: '', 
  }, 
});

FormProvider๋ž€?

FormProvider๋Š” ํผ ์ƒํƒœ๋ฅผ ์ „์—ญ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ํผ ํ•„๋“œ๋“ค์ด ์—ฌ๋Ÿฌ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ๋‚˜๋‰˜์–ด ์žˆ๋”๋ผ๋„, ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋™์ผํ•œ ํผ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๊ณ  ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ ํผ์ด ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ๋งŒ ๊ด€๋ฆฌ๋œ๋‹ค๋ฉด ํ•„์š”ํ•˜์ง€ ์•Š์ง€๋งŒ, ํผ ํ•„๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์— ๋‚˜๋‰˜์–ด์ ธ ์žˆ์„ ๋•Œ๋Š” FormProvider๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํผ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { FormProvider, useForm } from 'react-hook-form';

export default function Signup() {
  const methods = useForm<ISignUpRequest>();
  
  return (
    <FormProvider {...methods}> {/* FormProvider๋กœ ๊ฐ์‹ธ์„œ ์ƒํƒœ๋ฅผ ๊ณต์œ  */}
      <form>
        <AuthForm />  {/* AuthForm์—์„œ useFormContext๋กœ ํผ ์ƒํƒœ๋ฅผ ์ ‘๊ทผ */}
        <ProfileForm /> {/* ProfileForm์—์„œ๋„ ๋™์ผํ•œ ํผ ์ƒํƒœ ์ ‘๊ทผ */}
      </form>
    </FormProvider>
  );
}

useFormContext๋ž€?

useFormContext๋Š” FormProvider๋กœ ๊ฐ์‹ธ์ง„ ์ปดํฌ๋„ŒํŠธ์—์„œ ํผ ์ƒํƒœ๋ฅผ ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ํ›…์ž…๋‹ˆ๋‹ค. ์ด ํ›…์„ ์‚ฌ์šฉํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ ๊ฐ„์— ๋™์ผํ•œ ํผ ์ƒํƒœ๋ฅผ ์‰ฝ๊ฒŒ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ ํ•„๋“œ์™€ ํ”„๋กœํ•„ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์— ์žˆ๋”๋ผ๋„, useFormContext๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ™์€ ํผ ์ƒํƒœ์— ์ ‘๊ทผํ•˜๊ณ  ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useFormContext } from 'react-hook-form';

export default function AuthForm() {
  const { register, formState: { errors } } = useFormContext(); // ํผ ์ƒํƒœ ์ ‘๊ทผ
  
  return (
    <div>
      <input {...register('email')} placeholder="์ด๋ฉ”์ผ" />
      {errors.email && <span>{errors.email.message}</span>}
      <input {...register('password')} placeholder="๋น„๋ฐ€๋ฒˆํ˜ธ" />
      {errors.password && <span>{errors.password.message}</span>}
    </div>
  );
}
import { useFormContext } from 'react-hook-form';

export default function ProfileForm() {
  const { register, formState: { errors } } = useFormContext(); // ํผ ์ƒํƒœ ์ ‘๊ทผ
  
  return (
    <div>
      <input {...register('username')} placeholder="์‚ฌ์šฉ์ž ์ด๋ฆ„" />
      {errors.username && <span>{errors.username.message}</span>}
    </div>
  );
}

2.2 ๊ธฐ๋ณธ ํผ ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„๊ณผ ๋ฆฌํŒฉํ† ๋ง

์ดˆ๊ธฐ์— ์ž‘์„ฑํ•œ ํผ ์ปดํฌ๋„ŒํŠธ๋Š” AuthForm๊ณผ ProfileForm์œผ๋กœ ๋‚˜๋‰˜์–ด ๊ฐ ํ•„๋“œ๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋™์ผํ•œ ๋ ˆ์ด์•„์›ƒ์„ ๊ณต์œ ํ•˜์ง€๋งŒ ProfileForm์—์„œ๋Š” userimage๋ฅผ ๋ฐ›์•„์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ๊ฐ์˜ Form์œผ๋กœ ๋‚˜๋ˆ„์–ด ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ดˆ๊ธฐ AuthForm ์ปดํฌ๋„ŒํŠธ:

'use client';

import { useFormContext } from 'react-hook-form';
import { UnderlineInput } from '@/components/common/input/UnderlineInput';

interface AuthFormProps {
  type: 'login' | 'signup';
}

const authFields = {
  signup: {
    email: {
      label: '์ด๋ฉ”์ผ',
      placeholder: '์ด๋ฉ”์ผ์„ ์„ค์ •ํ•ด์ฃผ์„ธ์š”.',
    },
    password: {
      label: '๋น„๋ฐ€๋ฒˆํ˜ธ',
      placeholder: '๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์„ค์ •ํ•ด์ฃผ์„ธ์š”.',
    },
  },
  login: {
    email: {
      label: '์ด๋ฉ”์ผ',
      placeholder: '์ด๋ฉ”์ผ์„ ์ž…๋ ฅํ•˜์„ธ์š”.',
    },
    password: {
      label: '๋น„๋ฐ€๋ฒˆํ˜ธ',
      placeholder: '๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.',
    },
  },
};

export default function AuthForm({ type }: AuthFormProps) {
  const {
    register,
    formState: { errors },
  } = useFormContext();

  const fields = authFields[type];

  return (
    <fieldset className="flex-center gap-4">
      {Object.entries(fields).map(([name, field]) => (
        <UnderlineInput
          key={name}
          {...register(name)}
          isClearable
          variant="underlined"
          type={name}
          label={field.label}
          placeholder={field.placeholder}
          isInvalid={!!errors[name]}
          errorMessage={errors[name]?.message?.toString()}
        />
      ))}
    </fieldset>
  );
}

๋™์ผํ•œ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ์ค‘๋ณต ์ฝ”๋“œ๊ฐ€ ๋งŽ์•˜๊ธฐ ๋•Œ๋ฌธ์—, AuthForm๊ณผ ProfileForm์„ ํ•˜๋‚˜์˜ FormTemplate ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜์—ฌ ํผ ํ•„๋“œ๋ฅผ ๋™์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฆฌํŒฉํ† ๋ง ํ›„ FormTemplate ์ปดํฌ๋„ŒํŠธ:

'use client';

import { useFormContext } from 'react-hook-form';

import { UnderlineInput } from '@/components/common/input/UnderlineInput';
import { IFields } from '@/config/authFieldConfig';

interface FormTemplateProps {
  fields: IFields;
  children?: React.ReactNode; // ์ถ”๊ฐ€์ ์ธ ์ปดํฌ๋„ŒํŠธ(ํ”„๋กœํ•„ ์‚ฌ์ง„) ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ
}

export default function FormTemplate({ fields, children }: FormTemplateProps) {
  const {
    register,
    formState: { errors},
  } = useFormContext();

  return (
    <fieldset className="flex-center gap-4">
      {children}
      {Object.entries(fields).map(([name, field]) => {  
        <UnderlineInput
            key={name}
            {...register(name, field.validation)}
            isClearable
            variant="underlined"
            type={
              name === 'email'
                ? 'email'
                : name === 'password'
                  ? 'password'
                  : 'text'
            }
            label={field.label}
            placeholder={field.placeholder}
            isInvalid={!!hasError}
            errorMessage={errors[name]?.message?.toString()}
          />
      })}
    </fieldset>
  );
}

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ๋ถ€ํ„ฐ fields(์˜ˆ: SIGN_UP_FIELDS, PROFILE_FIELDS, LOGIN_FIELDS)์™€ children์„ props๋กœ ๋ฐ›์•„ ํผ์ด ๊ตฌ์„ฑ๋˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋•๋ถ„์— ํผ ํ•„๋“œ์— ๋Œ€ํ•ด ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ์œผ๋ฉฐ, ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


3. ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…: ์ค‘๋ณต๋œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

ํšŒ์›๊ฐ€์ž… ์‹œ ์Šคํ… ๊ธฐ๋ฐ˜ ํผ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ, ๋‹ค์Œ ์Šคํ…์œผ๋กœ ๋„˜์–ด๊ฐˆ ๋•Œ๋งˆ๋‹ค ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ๋˜์–ด ๋ Œ๋”๋ง๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ๋Š” react-hook-form์˜ ๊ธฐ๋ณธ ๋™์ž‘ ๋ฐฉ์‹ ๋•Œ๋ฌธ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ํผ ํ•„๋“œ๊ฐ€ ๋ Œ๋”๋ง๋˜๋Š” ์ˆœ๊ฐ„, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ์ฆ‰์‹œ ์‹คํ–‰๋˜์–ด, ์Šคํ…์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ž…๋ ฅ๊ฐ’์ด ์—†์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋ฐ”๋กœ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

  1. clearErrors() ์‚ฌ์šฉ(ํ•ด๊ฒฐX) : ์Šคํ… ๋ณ€๊ฒฝ ์‹œ ์ด์ „์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์˜ค๋ฅ˜๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด clearErrors()๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ, ์—ฌ์ „ํžˆ ํ”„๋กœํ•„ ํ•„๋“œ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
  2. touchedFields ํ™œ์šฉ(ํ•ด๊ฒฐO) : FormTemplate ์ปดํฌ๋„ŒํŠธ์— touchedFields๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, ํ•„๋“œ๊ฐ€ ํ„ฐ์น˜๋œ ๊ฒฝ์šฐ์—๋งŒ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์˜ค๋ฅ˜๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. touchedFields๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ•„๋“œ๋ฅผ ํ•œ ๋ฒˆ์ด๋ผ๋„ ํด๋ฆญํ•˜๊ฑฐ๋‚˜ ํŽธ์ง‘์„ ์‹œ๋„ํ–ˆ๋Š”์ง€๋ฅผ ์ถ”์ ํ•˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ์Šคํ… ๋ณ€๊ฒฝ ์‹œ ์ž๋™์œผ๋กœ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์ œ์–ดํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

4. ํšŒ๊ณ 

4.1 React Hook Form ์‚ฌ์šฉ์˜ ์žฅ๋‹จ์ 

์žฅ์ :

  • ํผ ์„ฑ๋Šฅ ์ตœ์ ํ™”: ์ด์ „ ํ”„๋กœ์ ํŠธ์—์„œ useState๋กœ ๋ชจ๋“  ํ•„๋“œ์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ–ˆ์„ ๋•Œ, ๋ณต์žกํ•œ ํผ์—์„œ ๋นˆ๋ฒˆํ•œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•ด ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ฒด๊ฐํ•œ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด, react-hook-form์€ Ref ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ์ตœ์†Œํ™”ํ•˜๋ฉด์„œ๋„ ๋น ๋ฅธ ๋ฐ˜์‘์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • API์˜ ์ง๊ด€์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ: ์ฒ˜์Œ ๋„์ž…ํ•  ๋•Œ๋Š” ์ƒ๋Œ€์ ์œผ๋กœ ์ƒ์†Œํ–ˆ์ง€๋งŒ, register, handleSubmit, setError ๊ฐ™์€ ํ•จ์ˆ˜๊ฐ€ ๋งค์šฐ ์ง๊ด€์ ์ด๋ผ ๊ธˆ๋ฐฉ ์ต์ˆ™ํ•ด์งˆ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋กœ์ง์„ ํ•œ ๊ณณ์—์„œ ํ†ตํ•ฉ ๊ด€๋ฆฌํ•˜๋ฉด์„œ, ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ์š”๊ตฌ์‚ฌํ•ญ์ด ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ ํผ์˜ ๊ตฌ์กฐ์™€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์†์‰ฝ๊ฒŒ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์–ด, ๋ฐ˜๋ณต ์ž‘์—…์ด๋‚˜ ์ˆ˜์ •ํ•  ๋ถ€๋ถ„์ด ์ ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์œ ์—ฐํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ: ๊ธฐ๋ณธ์ ์ธ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” register ์˜ต์…˜์„ ํ†ตํ•ด ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ๋ณต์žกํ•œ ๊ฒ€์ฆ ๋กœ์ง์€ validate ์†์„ฑ์„ ํ™œ์šฉํ•ด ์œ ์—ฐํ•˜๊ฒŒ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. 

๋‹จ์ :

  • ์ดˆ๊ธฐ ์ ์‘์˜ ์–ด๋ ค์›€: ์ฒ˜์Œ์—๋Š” FormProvider์™€ useFormContext๋ฅผ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•ด์•ผ ํ• ์ง€, ๊ฐ ํ•„๋“œ์˜ ์ƒํƒœ๋ฅผ ์–ด๋–ป๊ฒŒ ์ตœ์ ํ™”ํ• ์ง€ ๊ณ ๋ฏผ์ด ๋งŽ์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์Šคํ… ๊ธฐ๋ฐ˜ ํผ์—์„œ ๊ฐ ์Šคํ…์— ๋”ฐ๋ผ ํ•„๋“œ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ๋ณต์žกํ•จ์„ ๋А๊ผˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ์ดˆ๋ฐ˜์—๋Š” ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์€ ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ์ง€๋งŒ, ์ต์ˆ™ํ•ด์ง€๊ณ  ๋‚˜๋‹ˆ ์ด ๋ฐฉ์‹์ด ๋” ์ง๊ด€์ ์ด๋ฉด์„œ๋„ ๊ฐ•๋ ฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

4.2 ํ–ฅํ›„ ๊ฐœ์„  ๋ฐฉํ–ฅ

  • ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ตœ์ ํ™”: ์„œ๋ฒ„์™€์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋กœ์ง์„ ๋” ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ปค์Šคํ…€ ํ›…์„ ๊ฐœ์„ ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ํŠนํžˆ ์ด๋ฉ”์ผ์ด๋‚˜ ๊ณ„์ • ์ด๋ฆ„ ๊ฐ™์€ ํ•„๋“œ์— ๋Œ€ํ•œ ์„œ๋ฒ„ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ๊ฐœ์„ ํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ: ๊ฐ ํผ ํ•„๋“œ์— ๋Œ€ํ•œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฐ ํผ ์ œ์ถœ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Jest๋ฅผ ์‚ฌ์šฉํ•ด ์œ ํšจ์„ฑ ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ํ”„๋กœ์ ํŠธ์˜ ์•ˆ์ •์„ฑ์„ ๋”์šฑ ๊ฐ•ํ™”ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

 

 
Comments