๐Ÿ€ React-Hook-Form ์‚ฌ์šฉํ•ด๋ณด๊ธฐ

ํ•ด๋กฑ๊ทธยท2024๋…„ 5์›” 1์ผ

react

๋ชฉ๋ก ๋ณด๊ธฐ
14/14

ํŽ€๋”ฉ ๊ฐœ์„ค ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋„์ค‘ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ๋งŽ์•„์ ธ ์ƒํƒœ๊ด€๋ฆฌ์˜ ํ•„์š”์„ฑ์„ ๋А๊ผˆ๊ณ , ๋ฌด์—‡๋ณด๋‹ค gift item list์— dnd-kit(๋“œ๋ž˜๊ทธ์•ค๋“œ๋กญ)๋ฅผ ๋ถ™์˜€๋”๋‹ˆ ๋”์ฐํ•œ ๋ฆฌ๋ Œ๋”๋ง ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.
02์–ธ๋‹ˆ์™€ ์ƒ๊ฐํ•ด๋ณธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ์ด ๋‘ ๊ฐ€์ง€๋‹ค.

  1. ์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ (Recoil)
  2. React Hook Form ์ ์šฉ

์‚ฌ์‹ค ๋‘ ๋ฐฉ๋ฒ• ๋ชจ๋‘ ์‚ฌ์šฉํ•ด๋ณด์ง€ ์•Š์•„์„œ ๋ญ๋“  ์ข‹์„ ๊ฒƒ ๊ฐ™์•˜๋‹ค. ์–ธ๋‹ˆ๊ฐ€ ํ•ด๋ณด๊ณ  ์‹ถ์€ ๊ฑธ ์„ ํƒํ•ด๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ฒจ์–ธ์„ ํ•ด์ค˜์„œ React Hook Form์„ ํ™œ์šฉํ•ด ํšจ๊ณผ์ ์œผ๋กœ ํผ ๊ด€๋ฆฌ๋ฅผ ํ•ด๋ณด๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.
(๊ฐ์ž์˜ ์žฅ์ ์ด ๋ถ„๋ช…ํžˆ ์žˆ์„ํ…๋ฐ.. ์‹œ๊ฐ„๋‚ด์„œ ์ด ๋ถ€๋ถ„๋„ ์ •๋ฆฌํ•ด๋†“๊ฒ ์Šต๋‹ˆ๋‹ค._)

React-Hook-Form

๋ฆฌ์•กํŠธ์—์„œ ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ฐ’์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„  useState๋‚˜ useRef๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, React Hook Form๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ์ค„์ด๋ฉด์„œ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
React Hook Form์€ ref ๊ธฐ๋ฐ˜์ด๋ฏ€๋กœ, value ์†์„ฑ์ด ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค. (defaultValue๋ฅผ ์‚ฌ์šฉํ•˜์ž!)
๋ฐ”๋กœ API๋กœ ๋„˜์–ด๊ฐ€๋ณด์ž.

useForm

useForm์€ React-Hook-Form์—์„œ ๊ฐ€์žฅ ์ฃผ๊ฐ€ ๋˜๋Š” ์š”์†Œ์ด๋ฉฐ, ํผ์„ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ์ปค์Šคํ…€ ํ›…์ด๋‹ค.

FormProvider / useFormContext

FormProvider๋Š” React-Hook-Form์—์„œ ์ œ๊ณตํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ, React์˜ Context API๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„๋˜์—ˆ๋‹ค. FormProvider๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ ํผ ๋ฐ์ดํ„ฐ์™€ ๊ด€๋ จ๋œ ์ƒํƒœ+๋กœ์ง์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.

useFormContext๋Š” React-Hook-Form์˜ ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•œ ์ปค์Šคํ…€ ํ›…์ด๋‹ค. useFormContext๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปจํ…์ŠคํŠธ๋ฅผ Prop์œผ๋กœ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ณ  ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ธฐ์— nested structures์—์„œ ๋งค์šฐ ์ข‹๋‹ค. ์ด ํ›…์„ ์‚ฌ์šฉํ•˜๋ฉด useForm์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ์™€ ์†์„ฑ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, useForm์˜ ๋ฐ˜ํ™˜๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

const { methods } = useForm();

/* FormProvider ์˜ props๋กœ useForm ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์ ธ์˜จ ํผ ๋ฉ”์„œ๋“œ(methods)๋ฅผ ๋„˜๊ฒจ์คŒ */
<FormProvider {...methods} >
  
  	/* ํผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉํ•˜๋ฉด props๋ฅผ ๋„˜๊ฒจ์•ผ ํ•˜์ง€๋งŒ, 
	   FormProvider๋กœ ๊ฐ์‹ผ๋‹ค๋ฉด ๋”ฐ๋กœ props๋ฅผ ๋„˜๊ธฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค. */
  <์ž์‹ ์ปดํฌ๋„ŒํŠธ />
    ...

useFormContext๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” form์„ FormProvider ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ์‹ธ์ค˜์•ผ ํ•œ๋‹ค. FormProvider ์ปดํฌ๋„ŒํŠธ์— useForm์—์„œ ๋ฐ˜ํ™˜ํ•œ ๋ฉ”์„œ๋“œ์™€ ์†์„ฑ(methods)์„ ์ „๋‹ฌํ•˜๋ฉด ๋œ๋‹ค.
๊ทธ ์ดํ›„, ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ useFormContext๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํ•ด๋‹น ๋ฉ”์„œ๋“œ์™€ ์†์„ฑ์„ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// ์ž์‹ ์ปดํฌ๋„ŒํŠธ
const { register } = useFormContext();
...
return <TextField label="์ œ๋ชฉ" fullWidth {...register("fundTitle")} />

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฑด โญprops drillingโญ ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ !์ด๋‹ค.

register

  • React-Hook-Form์— ์ž…๋ ฅ์š”์†Œ๋ฅผ ๋“ฑ๋กํ•˜๋Š”๋ฐ ์‚ฌ์šฉ. registerํ•˜๋Š” ๊ณผ์ •์„ ํ†ตํ•ด ํ•ด๋‹น input๊ฐ’์„ ์ œ์–ดํ•˜๊ณ , ๊ฐ’์„ ์ˆ˜์ง‘, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์‹คํ–‰ ๊ฐ€๋Šฅ (ํ•จ์ˆ˜ ํ˜•ํƒœ)
  • ์ฒซ ๋ฒˆ์งธ params๋Š” name์œผ๋กœ, ํ•ด๋‹น ํ•„๋“œ์— ๋Œ€ํ•œ key๊ฐ’
  • ๋‘ ๋ฒˆ์งธ๋Š” optional ๊ฐ์ฒด. ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•œ ํ”„๋กœํผํ‹ฐ๋“ค์„ ๋„ฃ์„ ์ˆ˜ ์žˆ์Œ
  • onChange handler๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅ optional ๊ฐ์ฒด์— onChange ํ”„๋กœํผํ‹ฐ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•ด์ฃผ๋ฉด ๋จ!
<input
  type="text"
  {...register("email", {
    pattern: {
      value:
        /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i,
      message: "์˜ฌ๋ฐ”๋ฅธ ์ด๋ฉ”์ผ ํ˜•์‹์ด ์•„๋‹ˆ์—์š” ๐Ÿ˜•",
    },
  })}

์•„๋ž˜๋Š” register ํ•จ์ˆ˜์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜์ด๋‹ค.

register: (name: string, RegisterOptions?) => ({ onChange, onBlur, name, ref });<br>
type RegisterOptions = Partial<{
  required: boolean | string | { value: boolean; message: string; }; // ํ•„์ˆ˜๊ฐ’ ์—ฌ๋ถ€ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค.
  min: number | string | { value: number | string; message: string }; // ์ž…๋ ฅ ๊ฐ’์˜ ์ตœ์†Ÿ๊ฐ’ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค.
  max: number | string | { value: number | string; message: string }; // ์ž…๋ ฅ ๊ฐ’์˜ ์ตœ๋Œ“๊ฐ’ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค.
  maxLength: number | string | { value: number | string; message: string }; // ์ž…๋ ฅ ๊ฐ’์˜ ์ตœ๋Œ€ ๊ธธ์ด ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค.
  minLength: number | string | { value: number | string; message: string }; // ์ž…๋ ฅ ๊ฐ’์˜ ์ตœ์†Œ ๊ธธ์ด ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค.
  pattern: RegExp | { value: RegExp; message: string }; // ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•œ ์ž…๋ ฅ ๊ฐ’ ์ฒดํฌํ•ฉ๋‹ˆ๋‹ค.
  validate: Validate | Record<string, Validate>; // ์ปค์Šคํ…€ ์œ ํšจ์„ฑ ์ฒดํฌ๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  valueAsNumber: boolean // Number๋กœ ํ˜•๋ณ€ํ™˜ ํ•ฉ๋‹ˆ๋‹ค.
  valueAsDate: boolean // Date๋กœ ํ˜•๋ณ€ํ™˜ ํ•ฉ๋‹ˆ๋‹ค.
  setValueAs: (value: any) => any // value๋ฅผ ๊ฐ€๊ณตํ•ฉ๋‹ˆ๋‹ค.
  disabled: boolean // true๋กœ ์„ค์ • ์‹œ, value๊ฐ€ undefined ๋˜๋ฉฐ ์š”์†Œ๊ฐ€ disabled ๋ฉ๋‹ˆ๋‹ค.
  onChange: (event: any) => void // change ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ์‹คํ–‰๋˜๋Š” ์ฝœ๋ฐฑ์ž…๋‹ˆ๋‹ค.
  onBlur: (event: any) => void // blur ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ์‹คํ–‰๋˜๋Š” ์ฝœ๋ฐฑ์ž…๋‹ˆ๋‹ค.
  value: unknown // ์š”์†Œ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค.
  shouldUnregister: boolean // true๋กœ ์ง€์ •ํ•  ๊ฒฝ์šฐ ์š”์†Œ๊ฐ€ ์–ธ๋งˆ์šดํŠธ ๋˜๋ฉด ์ž…๋ ฅ๊ฐ’์ด ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.
  deps: string | string[] // ๋‚˜์—ด๋œ ์š”์†Œ๊ฐ€ ํ•จ๊ป˜ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
}>;

type Validate<TFieldValue, TFormValues> = (value: TFieldValue, formValues: TFormValues) => ValidateResult | Promise<ValidateResult>;
type ValidateResult = Message | Message[] | boolean | undefined;

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

๐Ÿšจ ๋ฒ— deeply nested structures ๋ผ๋ฉด?
๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ FormProvider๋กœ ๊ฐ์‹ธ์ ธ์žˆ๊ณ , ๊ทธ ํ•˜์œ„์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž.
๊ทธ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค ์ค‘์—์„œ useFormContext๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋‹ค๋ฉด, ํ•ด๋‹น useFormContext๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ๋กœ์ปฌ ํผ ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ, ๊ทธ ์ปดํฌ๋„ŒํŠธ์™€ ๊ทธ์˜ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ฆฌ๋ Œ๋”๋ง ๋  ์ˆ˜ ์žˆ๋‹ค.
์ด๋Š” React์˜ ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์—์„œ ์ƒ์œ„๋กœ์˜ ๋ฆฌ๋ Œ๋”๋ง ์ „ํŒŒ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•œ๋‹ค.
๊ณ ๋กœ... ๊นŠ์€ ๊ณ„์ธต๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” useFormContext๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์š”์†Œ๋“ค์„ ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค. ํ•„์š”ํ•œ ๊ฒฝ์šฐ React.memo๋‚˜ shouldComponentUpdate๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฆฌ๋ Œ๋”๋ง์„ ์ตœ์ ํ™”ํ•˜๊ณ , useFormContext๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ์ž์ฒด์˜ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ก ๊ฒฐ๋ก ! Context๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹Œ, ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ฆฌ๋ Œ๋”๋ง ๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ด ๋ถ€๋ถ„์„ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•˜์ž.

profile
์‚ฌ๋ž‘์•„ ์ปดํ“จํ„ฐํ•ด ~

0๊ฐœ์˜ ๋Œ“๊ธ€