๐ŸŽจ 17. Form ๋ Œ๋”๋ง ์ตœ์ ํ™”: ์ œ์–ด(Controlled) vs ๋น„์ œ์–ด(Uncontrolled)

๐Ÿ“‹ ๊ฐœ์š”

React์—์„œ Form ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ํ”ํžˆ ์ €์ง€๋ฅด๋Š” ์„ฑ๋Šฅ ๋‚ญ๋น„๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด, ์ œ์–ด ์ปดํฌ๋„ŒํŠธ์™€ ๋น„์ œ์–ด ์ปดํฌ๋„ŒํŠธ์˜ ๊ฐœ๋…์„ ๋ช…ํ™•ํžˆ ๊ตฌ๋ถ„ํ•˜๊ณ  react-hook-form์ด ์‹ค๋ฌด ํ‘œ์ค€์ด ๋œ ์›๋ฆฌ๋ฅผ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ์‚ฌ์šฉ์ž๊ฐ€ ํ‚ค๋ณด๋“œ๋ฅผ ์น  ๋•Œ๋งˆ๋‹ค ํŽ˜์ด์ง€ ์ „์ฒด๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋Š”(lagging) ์„ฑ๋Šฅ ๋ฒ„๊ทธ์˜ ๊ทผ๋ณธ ์›์ธ์„ ํŒŒ์•…ํ•œ๋‹ค.
  • ์ƒํƒœ(useState) ์—†์ด๋„ ํผ(Form) ์š”์†Œ์˜ ๊ฐ’์„ ์ถ”์ถœํ•ด๋‚ด๋Š” '๋น„์ œ์–ด(Uncontrolled)' ๊ธฐ๋ฒ•์„ ์ฒด๋“ํ•œ๋‹ค.
  • ์™œ ์‹ค๋ฌด์—์„œ react-hook-form ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฑฐ์˜ ํ•„์ˆ˜์ ์œผ๋กœ ์ฑ„ํƒํ•˜๋Š”์ง€ ์•„ํ‚คํ…์ฒ˜ ๊ด€์ ์—์„œ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 10๋ถ„ / ํ•ต์‹ฌ ํŒŒํŠธ: 5๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ์˜์ˆ˜(PM): "์˜์ฒ  ๋‹˜, ์ƒˆ๋กœ ๋งŒ๋“  [๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ] ํŽ˜์ด์ง€ ๋ง์ธ๋ฐ์š”. ๊ธ€์„ ๊ธธ๊ฒŒ ์“ฐ๋ฉด ์“ธ์ˆ˜๋ก ํ‚ค๋ณด๋“œ ํƒ€์ดํ•‘์ด ๋ฒ„๋ฒ…๊ฑฐ๋ ค์š”. ํ•œ ๊ธ€์ž ์น  ๋•Œ๋งˆ๋‹ค 0.5์ดˆ์”ฉ ๋ ‰์ด ๊ฑธ๋ฆฌ๋„ค์š”."
  • ์˜์ฒ (์‹ ์ž…): "์–ด๋ผ? ๊ทธ๋ƒฅ ์ฑ…์—์„œ ๋ฐฐ์šด ๋Œ€๋กœ <input> ๊ฐ’๋“ค ์ „๋ถ€ useState์— ๋‹ด์•„์„œ onChange ๋Œ๋ ธ๋Š”๋ฐ... ๊ธ€์ด ๊ธธ์–ด์ ธ์„œ ๋งˆํฌ๋‹ค์šด ํ”„๋ฆฌ๋ทฐ(๋ฏธ๋ฆฌ๋ณด๊ธฐ) ๋ Œ๋”๋ง์ด ๋ฌด๊ฑฐ์›Œ์ง€๋‹ˆ๊นŒ ์ „์ฒด ํ™”๋ฉด ๋ Œ๋”๋ง์ด ๋ฒ„ํ‹ฐ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ใ… ใ… "
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "์˜์ฒ  ๋‹˜, ํ™”๋ฉด์— ๋‹น์žฅ '์‹ค์‹œ๊ฐ„ ๋ฐ˜์‘' ์„ ๋ณด์—ฌ์ค˜์•ผ ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ๋ฉด, ๊ธ€์ž๋ฅผ 10๋งŒ ์ž๋ฅผ ์น˜๋“  100๋งŒ ์ž๋ฅผ ์น˜๋“  ๋ฆฌ๋ Œ๋”๋ง ํšŸ์ˆ˜๋Š” ํ•ญ์ƒ 0๋ฒˆ ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ์‹œ๋Œ€ ์œ ๋ฌผ์ธ ์ œ์–ด(Controlled) ํŒจํ„ด์—์„œ ๋ฒ—์–ด๋‚˜ ๋น„์ œ์–ด(Uncontrolled) ์˜ ์„ธ๊ณ„๋กœ ๋„˜์–ด๊ฐ‘์‹œ๋‹ค."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: '์‹ค์‹œ๊ฐ„ ์—ฐ๊ฒฐ'์˜ ๋Œ“๊ฐ€

๊ฐ€์žฅ ๋„๋ฆฌ ์•Œ๋ ค์ง€๊ณ  ํ”ํ•˜๊ฒŒ ๊ต์œก๋˜๋Š” React Form ์ œ์–ด ๋ฐฉ์‹์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค:

// โŒ ์˜์ฒ ์ด์˜ ๋ฌด๊ฑฐ์šด ๊ฑฐ๋Œ€ Form (์ œ์–ด ์ปดํฌ๋„ŒํŠธ ๋ฐฉ์‹)
function PostWriter() {
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');
 
  // ๐Ÿšจ ํ•œ ๊ธ€์ž ์น  ๋•Œ๋งˆ๋‹ค ์ด ๊ฑฐ๋Œ€ํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ „๋ถ€ ๋ฆฌ๋ Œ๋”๋ง ๋จ!
  console.log('๋ Œ๋”๋ง ๋ฐœ์ƒ ์›ฝ์•Œ์›ฝ์•Œ!'); 
 
  return (
    <form>
      <input 
        value={title} 
        onChange={(e) => setTitle(e.target.value)} 
        placeholder="์ œ๋ชฉ"
      />
      <textarea 
        value={content} 
        onChange={(e) => setContent(e.target.value)} 
        placeholder="๋‚ด์šฉ"
      />
      <HeavyMarkdownPreview markdown={content} /> {/* ๋ฒ„๋ฒ…์ž„์˜ ์›ํ‰ */}
      <button type="submit">์ž‘์„ฑ ์™„๋ฃŒ</button>
    </form>
  );
}

์ด ๋ฐฉ์‹์„ "์ œ์–ด(Controlled) ์ปดํฌ๋„ŒํŠธ" ํŒจํ„ด์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. React์˜ ์ƒํƒœ(useState)๊ฐ€ ์ž…๋ ฅ์ฐฝ(input)์˜ ๋ฐ์ดํ„ฐ ์ฃผ๋„๊ถŒ์„ 100% ์ฅ๊ณ  ์ œ์–ดํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

  • ์žฅ์ : "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ 8์ž๊ฐ€ ๋„˜์œผ๋ฉด ๋ฒ„ํŠผ์„ ๋นจ๊ฐ›๊ฒŒ ๋งŒ๋“ ๋‹ค" ๋ผ๋˜๊ฐ€, "์†Œ๋ฌธ์ž ์ž…๋ ฅ ์‹œ ๊ฐ•์ œ๋กœ ๋Œ€๋ฌธ์ž๋กœ ๋ฐ”๊พผ๋‹ค" ๊ฐ™์€ ์‹ค์‹œ๊ฐ„ 1:1 ๋™๊ธฐํ™” ๊ฒ€์ฆ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ฌด์กฐ๊ฑด ์ œ์–ด ๋ฐฉ์‹์„ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์น˜๋ช…์  ๋‹จ์ : ์‚ฌ์šฉ์ž๊ฐ€ '๊ฐ€๋‚˜๋‹ค๋ผ' 4๊ธ€์ž๋ฅผ ์น˜๋Š” ์ˆœ๊ฐ„, ๋ฆฌ์•กํŠธ ์•ฑ ์ „์ฒด ๋ Œ๋”๋ง ์‚ฌ์ดํด์ด 4๋ฒˆ ๋•๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ € <form> ์•ˆ์— ์—„์ฒญ ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง ์š”์†Œ(HeavyMarkdownPreview ๋“ฑ)๊ฐ€ ์žˆ๋‹ค๋ฉด **ํ‚ค๋ณด๋“œ ์ž…๋ ฅ ์ง€์—ฐ(Input Lagging)**์ด ํ„ฐ์ง‘๋‹ˆ๋‹ค.

๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?

  1. ์ œ์–ด(Controlled) ๋ฐฉ์‹ (์˜์ฒ ์ด): ์„ ์ƒ๋‹˜(React)์ด ํ•™์ƒ(Input)์ด ๊ธ€์”จ ์“ฐ๊ธฐ ์‹œํ—˜์„ ๋ณด๋Š” ๋‚ด๋‚ด ์˜†์— ์ฐฐ์‹น ๋ถ™์–ด์„œ ๊ฐ์‹œํ•ฉ๋‹ˆ๋‹ค. "ใ„ฑ ์ผ๋„ค? ใ…‡ใ…‹. ใ… ์ผ๋„ค? ใ…‡ใ…‹." ์ˆจ ์‰ด ํ‹ˆ ์—†์ด ํ”ผ๋กœ๋„๊ฐ€ ์Œ“์ž…๋‹ˆ๋‹ค(๋ฆฌ๋ Œ๋”๋ง ๋ฌดํ•œ ๋ฃจํ”„).
  2. ๋น„์ œ์–ด(Uncontrolled) ๋ฐฉ์‹ (์˜ํ˜ธ): ์„ ์ƒ๋‹˜์ด ๊ต๋ฌด์‹ค์—์„œ ์œ ํŠœ๋ธŒ๋ฅผ ๋ด…๋‹ˆ๋‹ค. ํ•™์ƒ์ด ํ˜ผ์ž์„œ OMR ์นด๋“œ๋ฅผ ๋‹ค ์ฐ๊ณ , 60๋ถ„ ๋’ค์— "์„ ์ƒ๋‹˜ ๋‹ค ํ’€์—ˆ์–ด์š”!" ํ•˜๊ณ  ์ œ์ถœ(Submit)ํ•  ๋•Œ ๋”ฑ ํ•œ ๋ฒˆ ์ณ๋‹ค๋ณด๊ณ  ๊ฒฐ๊ณผ๋งŒ ์ฑ„์ ํ•ฉ๋‹ˆ๋‹ค. ์„ ์ƒ๋‹˜(React)์€ ์‹œํ—˜ ์น˜๋Š” ์ค‘์— ๋‹จ ํ•œ ๋ฒˆ๋„ ์›€์ง์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๋ฆฌ๋ Œ๋”๋ง 0ํšŒ).

์šฐ๋ฆฌ๊ฐ€ ํšŒ์›๊ฐ€์ž… ํผ์— ์ด๋ฆ„, ์ฃผ์†Œ, ๋‚˜์ด๋ฅผ ์น  ๋•Œ๋งˆ๋‹ค ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™”๋ฉด์˜ ๋ชจ์–‘์ด ๋ฐ”๋€Œ๋‚˜์š”? ๋ณดํ†ต์€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ์ € ๋งˆ์ง€๋ง‰์— [์ œ์ถœ ๋ฒ„ํŠผ]์„ ๋ˆ„๋ฅด๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋งŒ ์“ฑ ๋นผ์˜ค๋ฉด ๊ทธ๋งŒ์ž…๋‹ˆ๋‹ค. ์ƒํƒœ(useState)๋กœ ๊ทธ ๋…€์„๋“ค์„ ์ผ์ผ์ด ๊ฐ์‹œํ•  ์ด์œ ๊ฐ€ ์ „ํ˜€ ์—†๋Š” ๊ฒƒ์ด์ฃ !


๐Ÿงฉ ๊ทน์˜ ์ฒด๋“: ์–ธ์ปจํŠธ๋กค๋“œ(Uncontrolled) ํผ ์งœ๊ธฐ

"์ƒํƒœ ๋ณ€์ˆ˜์— ๋ฌถ์–ด๋‘์ง€ ์•Š์œผ๋ฉด ๋‚˜์ค‘์— ์–ด๋–ป๊ฒŒ ๊ฐ’์„ ๊บผ๋‚ด์˜ค๋‚˜์š”?"

โœ… ๋ฐฉ๋ฒ• 1: useRef๋กœ ๋ฉฑ์‚ด ์žก๊ธฐ

8๊ฐ• useRef ๋น„๋ฐ€ ์ฐฝ๊ณ ์—์„œ ๋ฐฐ์› ๋“ฏ, useRef๋Š” ๋ Œ๋”๋ง์„ ํŠธ๋ฆฌ๊ฑฐํ•˜์ง€ ์•Š๊ณ  DOM ์š”์†Œ์— ์ ‘๊ทผํ•˜๋Š” ๋’ท๊ตฌ๋ฉ์„ ์—ด์–ด์ค๋‹ˆ๋‹ค.

function FastWriter() {
  const titleRef = useRef(null);
  const contentRef = useRef(null);
 
  const handleSubmit = (e) => {
    e.preventDefault(); // ํ™”๋ฉด ์ƒˆ๋กœ๊ณ ์นจ ๋ฐฉ์ง€
    
    // ์ƒํƒœ ๋Œ€์‹ , ์ œ์ถœ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ ๊ทธ ์ˆœ๊ฐ„! DOM์—์„œ ๊ธฐ์Šต์ ์œผ๋กœ ๊ฐ’์„ ๋นผ์˜จ๋‹ค.
    console.log("์ œ์ถœ๋œ ์ œ๋ชฉ:", titleRef.current.value);
    console.log("์ œ์ถœ๋œ ๋‚ด์šฉ:", contentRef.current.value);
  };
 
  // ๐ŸŽฏ ๊ธ€์ž๋ฅผ ์•„๋ฌด๋ฆฌ ์ณ๋„ ๋ฆฌ๋ Œ๋”๋ง ์™„์ „ 0ํšŒ!
  return (
    <form onSubmit={handleSubmit}>
      {/* value์™€ onChange๋ฅผ ์ง€์šฐ๊ณ , ref๋งŒ ๋ฐ•์•„๋‘”๋‹ค */}
      <input ref={titleRef} placeholder="์ œ๋ชฉ" />
      <textarea ref={contentRef} placeholder="๋‚ด์šฉ" />
      <button type="submit">์ž‘์„ฑ ์™„๋ฃŒ</button>
    </form>
  );
}

โœ… ๋ฐฉ๋ฒ• 2: FormData API (์ฐ ๊ณ ์ˆ˜๋“ค์˜ ๋ธŒ๋ผ์šฐ์ € ๋„ค์ดํ‹ฐ๋ธŒ ๋ฐฉ๋ฒ•)

์‚ฌ์‹ค useRef์กฐ์ฐจ๋„ ํผ ์ธํ’‹ ํ•„๋“œ๊ฐ€ 30๊ฐœ๋ผ๋ฉด useRef 30๊ฐœ๋ฅผ ์„ ์–ธํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋”์ฐํ•ฉ๋‹ˆ๋‹ค. ๋” ์›์ดˆ์ ์ด๊ณ  ๊ฐ•๋ ฅํ•œ ๋ธŒ๋ผ์šฐ์ € ํ‘œ์ค€ DOM API์ธ FormData๋ฅผ ์“ฐ๋ฉด ๋งˆ๋ฒ•์ด ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.

function NativeForm() {
  const handleSubmit = (e) => {
    e.preventDefault();
    
    // e.target์€ form ํƒœ๊ทธ ์ž์ฒด. FormData๊ฐ€ ํ•˜์œ„ ์ธํ’‹ ๊ฐ’์„ ์ซ™ ๊ธ์–ด ๋ชจ์•„์ค€๋‹ค.
    const formData = new FormData(e.target);
    const data = Object.fromEntries(formData); 
    
    console.log("ํ•œ ๋ฐฉ์— ์ถ”์ถœ๋œ 30๊ฐœ ๋ฐ์ดํ„ฐ:", data);
    // { title: "์•ˆ๋…•", content: "๋ฐฉ๊ฐ€", age: "25", ... }
  };
 
  return (
    <form onSubmit={handleSubmit}>
      {/* ๐Ÿ’ฅ ref์กฐ์ฐจ ํ•„์š” ์—†๋‹ค! ์˜ค์ง "name" ์†์„ฑ๋งŒ ๋‹ฌ์•„์ฃผ๋ฉด ๋! */}
      <input name="title" placeholder="์ œ๋ชฉ" />
      <textarea name="content" placeholder="๋‚ด์šฉ" />
      <input name="age" type="number" placeholder="๋‚˜์ด" />
      <button type="submit">์ž‘์„ฑ ์™„๋ฃŒ</button>
    </form>
  );
}

์ด ๋ฐฉ๋ฒ•์˜ ์•„๋ฆ„๋‹ค์›€: useState 0๊ฐœ, useRef 0๊ฐœ, ๋ฆฌ๋ Œ๋”๋ง 0ํšŒ. ํผ ์ตœ์ ํ™”์˜ ๊ทน์˜์— ๋„๋‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค.


๐ŸŒŸ React Hook Form: ์‹ค๋ฌด ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋œ ์ด์œ 

๊ทธ๋Ÿฌ๋‚˜ ํ˜„์‹ค ์‹ค๋ฌด๋Š” ๋…น๋ก์ง€ ์•Š์Šต๋‹ˆ๋‹ค. "์ œ์ถœํ•  ๋•Œ๋งŒ ์—๋Ÿฌ ๊ฒ€์‚ฌ(Validation) ์„ ๋„์›Œ์ฃผ์„ธ์š”" ์˜€๋‹ค๊ฐ€, ๋ฉฐ์น  ๋’ค ๊ธฐํš์ž๊ฐ€ "์•„, ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ 8์ž ๋ฏธ๋งŒ์ด๋ฉด ์ฆ‰๊ฐ์ฆ‰๊ฐ ๋นจ๊ฐ„ ๊ธ€์”จ๋กœ ๊ฒฝ๊ณ ํ•ด ์ฃผ์„ธ์š”" ๋ผ๊ณ  ์š”์ฒญ์„ ํ‹€์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค (๋น„์ œ์–ด์˜ ํ•œ๊ณ„).

์ฆ‰, ๋Œ€๋ถ€๋ถ„์€ ๋น„์ œ์–ด(Uncontrolled)๋กœ ๊ฐ€๋ณ๊ฒŒ ์›€์ง์ด๋˜, ํŠน์ • ํ•„๋“œ๋‚˜ ์กฐ๊ฑด์—์„œ๋งŒ ์ฐฐ๋‚˜์˜ ์ˆœ๊ฐ„ ์ œ์–ด(Controlled)๋กœ ๊ฐ์‹œํ•˜๊ณ  ๋ Œ๋”๋งํ•ด์•ผ ํ•˜๋Š” ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์ง€์˜ฅ์— ๋น ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ์•„ํ‚คํ…์ฒ˜ ๊ณ ๋ฏผ, ๋ณต์žกํ•œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์—๋Ÿฌ ์ถ”๋ก , ์ œ์–ด/๋น„์ œ์–ด ๋ Œ๋”๋ง ์ตœ์ ํ™”๋ฅผ ํ•˜๋‚˜์˜ Hook(useForm) ์œผ๋กœ ํ†ต์ผ์‹œ์ผœ๋ฒ„๋ฆฐ ์™ธ๊ณ„์ธ ๊ธฐ์ˆ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋ฐ”๋กœ react-hook-form (RHF) ์ž…๋‹ˆ๋‹ค.

// ์‹ค๋ฌด RHF ๋ง›๋ณด๊ธฐ (๋‚ด๋ถ€์ ์œผ๋กœ ๊ฑฐ์นœ Uncontrolled ๋กœ์ง์„ ์šฐ์•„ํ•˜๊ฒŒ ์ถ”์ƒํ™”ํ•จ)
import { useForm } from "react-hook-form";
 
function RealWorldForm() {
  // register๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ref์™€ onChange๋ฅผ ์ ˆ๋ฌ˜ํ•˜๊ฒŒ ์„ž์–ด ๋ Œ๋”๋ง์„ ์–ต์ œ์‹œ์ผœ์คŒ
  const { register, handleSubmit, formState: { errors } } = useForm();
 
  const onSubmit = data => console.log(data);
 
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input 
        {...register("username", { required: true, maxLength: 20 })} 
      />
      {/* ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ทธ ์ˆœ๊ฐ„์—๋งŒ ์ง€๋Šฅ์ ์œผ๋กœ ๋ถ€๋ถ„ ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ๋™! */}
      {errors.username && <span>์ด๋ฆ„์„ ์ œ๋Œ€๋กœ ์ ์–ด์ฃผ์„ธ์š”!</span>}
      <button type="submit">๊ฐ€์ž…</button>
    </form>
  );
}

RHF ๋‚ด๋ถ€์˜ ๋ผˆ๋Œ€ ๋…ผ๋ฆฌ๊ฐ€ ์™„๋ฒฝํ•œ '๋น„์ œ์–ด ๊ธฐ๋ฐ˜์˜ ์ง€๋Šฅํ˜• ์ œ์–ด ์‹œ์Šคํ…œ'์ด๊ธฐ ๋•Œ๋ฌธ์— ์‹ค๋ฌด ์ ์œ ์œจ ์„ธ๊ณ„ 1์œ„๋ฅผ ์ˆ˜์„ฑํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.


๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

๊ด€์ ์ œ์–ด (Controlled)๋น„์ œ์–ด (Uncontrolled)
๋ฐ์ดํ„ฐ ๋ณด๊ด€์†ŒReact์˜ useState๋ธŒ๋ผ์šฐ์ € ํƒœ๊ทธ ๋ณธ์—ฐ์˜ DOM ์ž์ฒด ๋ฉ”๋ชจ๋ฆฌ
์ถ”์ถœ ์‹œ์ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ์‹ค์‹œ๊ฐ„ ๊ฐฑ์‹ useRef๋‚˜ onSubmit ์ˆœ๊ฐ„ ํ•œ ๋ฒˆ๋งŒ ๊ธ์–ด์˜ด
๋ฆฌ๋ Œ๋”๋ง ํšŸ์ˆ˜๋ฌดํ•œ ํƒ€๊ฒฉ (์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ์•ฑ ์ „์ฒด๊ฐ€ ๋Ž)0ํšŒ (Zero)
์ถ”์ฒœ ์‚ฌ์šฉ ๋„๊ตฌ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ•๋ ฅ๋„ ์‹ค์‹œ๊ฐ„ ์ธก์ •, ์ฆ‰์‹œ ๊ฒ€์ƒ‰ ํ•„ํ„ฐ๊ฑฐ๋Œ€ํ•œ ํšŒ์›๊ฐ€์ž… ํผ, ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ ์—๋””ํ„ฐ

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

"์‚ฌ์šฉ์ž๊ฐ€ ํ‚ค๋ณด๋“œ ์น  ๋•Œ๋งˆ๋‹ค ์™œ ๋ ‰์ด ๊ฑธ๋ฆฌ์ง€?" ํ•˜๋ฉด์„œ ๋งˆํฌ๋‹ค์šด ์—๋””ํ„ฐ ํƒ“๋งŒ ํ–ˆ๋˜ ๋‚ด๊ฐ€ ์›๋ง์Šค๋Ÿฝ๋‹ค. ์‹ค์‹œ๊ฐ„ ๋™๊ธฐํ™”๊ฐ€ ํ•„์š” ์—†๋Š” ํผ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ์–ต์ง€๋กœ useState ๋ชฉ์ค„์„ ์ฑ„์›Œ๋†“๊ณ  ์ œ์–ด(Controlled)ํ•˜๋ ค๊ณ  ํ–ˆ๋˜ ๊ฒŒ ์›ํ‰์ด์—ˆ๋‹ค๋‹ˆ.

๐Ÿ’ก "ํผ ๋ฐ์ดํ„ฐ ๊ฐ์‹œ์—์„œ ์†์„ ๋–ผ์ž. ์ œ์ถœํ•  ๋•Œ ๋”ฑ ํ•œ ๋ฒˆ ๋‚š์•„์ฑ„๋Š” ๋น„์ œ์–ด(Uncontrolled) ๋ฐฉ์‹์„ ์“ฐ๋ฉด ํ‚ค๋ณด๋“œ ๋ฒ„๋ฒ…์ž„(Zero Re-rendering)์„ ์™„๋ฒฝํ•˜๊ฒŒ ์—†์•จ ์ˆ˜ ์žˆ๋‹ค."

์˜ค๋Š˜ ์˜ํ˜ธ ๋‹˜์ด react-hook-form์˜ ํƒ„์ƒ ๋ฐฐ๊ฒฝ๊ณผ ์ฒ ํ•™์„ ์งš์–ด์ฃผ์‹œ๋‹ˆ๊นŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๊ทธ๋ƒฅ ํˆด์ด ์•„๋‹ˆ๋ผ ์•„ํ‚คํ…์ฒ˜์˜ ์ •์ˆ˜๋ผ๋Š” ๊ฒŒ ๋ผˆ์ €๋ฆฌ๊ฒŒ ๋А๊ปด์ง„๋‹ค. ์ด์ œ ์–ด๋””๊ฐ€์„œ RHF ์™œ ์“ฐ๋ƒ๊ณ  ๋ฌผ์–ด๋ณด๋ฉด ๋น„์ œ์–ด ํŒจํ„ด ํผํฌ๋จผ์Šค ๋•Œ๋ฌธ์ด๋ผ๊ณ  ๊ฐ„์ง€๋‚˜๊ฒŒ ๋Œ€๋‹ตํ•  ์ˆ˜ ์žˆ์„ ๋“ฏ! ๋ฐœ๊ฑธ์Œ์ด ๊ฐ€๋ฒผ์šฐ๋‹ˆ ์˜ค๋Š˜ ์ €๋…์€ ์กฑ๋ฐœ์— ๋ง‰๊ตญ์ˆ˜ ๊ฐ„๋‹ค!


๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. form ์ œ์ถœ ๊ด€๋ฆฌ์— ์žˆ์–ด "๋น„์ œ์–ด(Uncontrolled) ์ปดํฌ๋„ŒํŠธ" ํŒจํ„ด์„ ์ ์šฉํ•˜๊ธฐ์— ๊ฐ€์žฅ ์ ์ ˆํ•œ ์ƒํ™ฉ์€ ์–ธ์ œ์ธ๊ฐ€์š”?

  • A) ์‚ฌ์šฉ์ž๊ฐ€ <input>์— ์‹ ์šฉ์นด๋“œ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ๋’ค์˜ 4์ž๋ฆฌ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ * ํ‘œ ์ฒ˜๋ฆฌํ•˜์—ฌ ๊ฐ€๋ ค ๋ณด์—ฌ์ค˜์•ผ ํ•  ๋•Œ.
  • B) ํšŒ์‚ฌ๊ฐ€ ์š”๊ตฌํ•œ 30๊ฐ€์ง€ ์ธ์ ์‚ฌํ•ญ์„ ์„ค๋ฌธ์กฐ์‚ฌ์ง€์— ๊ธฐ์ž…ํ•œ ํ›„, ๋งˆ์ง€๋ง‰ ํ•˜๋‹จ์˜ "์ œ์ถœํ•˜๊ธฐ" ๋ฒ„ํŠผ ํ•˜๋‚˜๋ฅผ ๋ˆ„๋ฅด๋Š” ์ˆœ๊ฐ„์—๋งŒ ๊ฐ’์ด ์™„์„ฑ๋˜์–ด API๋กœ ์ „์†ก๋˜๋ฉด ๋  ๋•Œ.
  • C) ๊ฒ€์ƒ‰์ฐฝ์— ๋‹จ์–ด๋ฅผ ํƒ€์ดํ•‘ํ•  ๋•Œ๋งˆ๋‹ค ์—”ํ„ฐํ‚ค ์—†์ด ์•„๋ž˜์— ์ฆ‰๊ฐ ๊ฒ€์ƒ‰ ์—ฐ๊ด€ ๊ฒฐ๊ณผ ๋ชฉ๋ก์ด ๋– ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ.

โœ… ์ •๋‹ต: B

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: B๋Š” ์ „ํ˜•์ ์ธ ๋น„์ œ์–ด ํŒจํ„ด์˜ ๋ฌด๋Œ€์ž…๋‹ˆ๋‹ค. ์ž…๋ ฅ ์ค‘๊ฐ„์ค‘๊ฐ„ ์•ฑ์ด ์ƒํƒœ ๋ณ€๊ฒฝ ์‚ฌ์‹ค์„ ๋ชฐ๋ผ๋„ ์ง„ํ–‰์— ์•„๋ฌด๋Ÿฐ ์ง€์žฅ์ด ์—†์œผ๋ฉฐ, ๋ฌด๊ฑฐ์šด ๋ฆฌ๋ Œ๋”๋ง์„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด A๋‚˜ C์ฒ˜๋Ÿผ ๊ธ€์ž๋ฅผ ์น˜๋Š” ์กฑ์กฑ ํ™”๋ฉด์˜ ํ”ฝ์…€(๋งˆ์Šคํ‚น UI, ์ž๋™์™„์„ฑ ๋ชฉ๋ก)์ด ๋‹น์žฅ ๋ฐ”๋€Œ์–ด์•ผ ํ•˜๋Š” ์ผ€์ด์Šค๋Š” ๋ฌด์กฐ๊ฑด React๊ฐ€ ๋ฐ์ดํ„ฐ์˜ ๋ชฉ์ค„์„ ์ฅ๊ณ  ํ”๋“œ๋Š” '์ œ์–ด(Controlled)' ํŒจํ„ด์„ ์จ์„œ ์‹ค์‹œ๊ฐ„ ๋ฆฌ๋ Œ๋”๋ง์„ ์ผ์œผ์ผœ์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค.

Q2. ๋‹ค์Œ ์ค‘ ์ตœ์‹  ์‹ค๋ฌด์—์„œ react-hook-form์ด ๊ณผ๊ฑฐ ์œ ํ–‰ํ•˜๋˜ Formik ๊ฐ™์€ ์ค‘์•™ ์ง‘์ค‘ํ˜• State ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์••๋„ํ•˜๊ณ  ๋Œ€์„ธ๊ฐ€ ๋œ ๊ฐ€์žฅ ์น˜๋ช…์ ์ธ ์•„ํ‚คํ…์ฒ˜์  ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

  • A) ์ปดํฌ๋„ŒํŠธ ์ „์ฒด๋ฅผ ๋ Œ๋”๋ง์‹œํ‚ค๋Š” ์ƒํƒœ ์˜์กด๋„๋ฅผ ๋Š์–ด๋‚ด๊ณ , ๋‚ด๋ถ€์ ์œผ๋กœ Uncontrolled ๋ฐฉ์‹(Ref)์„ ์ฒ ์ €ํžˆ ๊ธฐ์ € ์›๋ฆฌ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํ‚ค๋ณด๋“œ ์ž…๋ ฅ ์‹œ ์•ฑ ์ „์ฒด UI๊ฐ€ ์ •์ง€ํ•˜๋Š” ๋ ‰(Lag)์„ ์—†์•ด๊ธฐ ๋•Œ๋ฌธ.
  • B) ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ Fetch ํ•ด์˜ฌ ๋•Œ, Next.js์˜ use client ์„ ์–ธ์„ ๋ฌด์‹œํ•˜๊ณ  ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ.
  • C) ์ปดํฌ๋„ŒํŠธ ์ƒํƒœ(State)๋ฅผ ๊ฐ•์ œ๋กœ ๋ธŒ๋ผ์šฐ์ € LocalStorage์— ์ง๋ ฌํ™”ํ•˜๋Š” ๋ฐฑ์—… ๊ธฐ๋Šฅ์ด ๋‚ด์žฅ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ.

โœ… ์ •๋‹ต: A

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค. react-hook-form์˜ ๊ฐ€์žฅ ํฐ ์ž๋ถ€์‹ฌ์€ ์ด ๋ฌธ์„œ์—์„œ ๋ฐฐ์šด "๋น„์ œ์–ด(Uncontrolled) ์ปดํฌ๋„ŒํŠธ" ์ฒ ํ•™์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ Œ๋”๋ง ํผํฌ๋จผ์Šค๋ฅผ ๊ทน๋Œ€ํ™”ํ•˜์˜€๊ณ , ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ‘œ์ถœ ๊ฐ™์€ ๊ผญ ํ•„์š”ํ•œ ๊ณณ์—์„œ๋งŒ '๊ฒฉ๋ฆฌ๋œ ๋ Œ๋”๋ง'์„ ์ผ์œผํ‚ค๋Š” ์••๋„์ ์ธ ๊ธฐ์ˆ ๋ ฅ์„ ๋ณด์—ฌ์ฃผ์—ˆ๊ธฐ์— ํ˜„๋Œ€ ํ”„๋ก ํŠธ์—”๋“œ์˜ ๋Œ€๊ด€์‹์„ ์น˜๋ฅด๊ฒŒ ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.