๐จ 17. Form ๋ ๋๋ง ์ต์ ํ: ์ ์ด(Controlled) vs ๋น์ ์ด(Uncontrolled)
๐ ๊ฐ์
React์์ Form ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋ ํํ ์ ์ง๋ฅด๋ ์ฑ๋ฅ ๋ญ๋น๋ฅผ ๋ง๊ธฐ ์ํด, ์ ์ด ์ปดํฌ๋ํธ์ ๋น์ ์ด ์ปดํฌ๋ํธ์ ๊ฐ๋ ์ ๋ช ํํ ๊ตฌ๋ถํ๊ณ react-hook-form์ด ์ค๋ฌด ํ์ค์ด ๋ ์๋ฆฌ๋ฅผ ๋ถ์ํฉ๋๋ค.
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ฌ์ฉ์๊ฐ ํค๋ณด๋๋ฅผ ์น ๋๋ง๋ค ํ์ด์ง ์ ์ฒด๊ฐ ๋ฆฌ๋ ๋๋ง๋๋(lagging) ์ฑ๋ฅ ๋ฒ๊ทธ์ ๊ทผ๋ณธ ์์ธ์ ํ์ ํ๋ค.
- ์ํ(
useState) ์์ด๋ ํผ(Form) ์์์ ๊ฐ์ ์ถ์ถํด๋ด๋ '๋น์ ์ด(Uncontrolled)' ๊ธฐ๋ฒ์ ์ฒด๋ํ๋ค.- ์ ์ค๋ฌด์์
react-hook-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์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
- ์ ์ด(Controlled) ๋ฐฉ์ (์์ฒ ์ด): ์ ์๋(React)์ด ํ์(Input)์ด ๊ธ์จ ์ฐ๊ธฐ ์ํ์ ๋ณด๋ ๋ด๋ด ์์ ์ฐฐ์น ๋ถ์ด์ ๊ฐ์ํฉ๋๋ค. "ใฑ ์ผ๋ค? ใ ใ . ใ ์ผ๋ค? ใ ใ ." ์จ ์ด ํ ์์ด ํผ๋ก๋๊ฐ ์์ ๋๋ค(๋ฆฌ๋ ๋๋ง ๋ฌดํ ๋ฃจํ).
- ๋น์ ์ด(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) ์ปดํฌ๋ํธ" ์ฒ ํ์
๋๋ค. ์ด๋ฅผ ํตํด ๋ ๋๋ง ํผํฌ๋จผ์ค๋ฅผ ๊ทน๋ํํ์๊ณ , ์๋ฌ ๋ฉ์์ง ํ์ถ ๊ฐ์ ๊ผญ ํ์ํ ๊ณณ์์๋ง '๊ฒฉ๋ฆฌ๋ ๋ ๋๋ง'์ ์ผ์ผํค๋ ์๋์ ์ธ ๊ธฐ์ ๋ ฅ์ ๋ณด์ฌ์ฃผ์๊ธฐ์ ํ๋ ํ๋ก ํธ์๋์ ๋๊ด์์ ์น๋ฅด๊ฒ ๋ ๊ฒ์
๋๋ค.