[TECH-QA] ๋ฆฌ์•กํŠธ ๋ฆฌ๋ Œ๋”๋ง

๋ฆฌ์•กํŠธ์—์„œ ๋ฆฌ๋ Œ๋”๋ง(re-rendering)์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ๊ทธ๋ ค์ง€๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์–ด๋–ค ๋ณ€ํ™”๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ, ๋ฆฌ์•กํŠธ๊ฐ€ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ(ํ˜น์€ ๊ทธ ์ž์‹๋“ค ํฌํ•จ)๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ด์„œ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฑธ ๋งํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ๋ Œ๋”๋ง์ด ๋„ˆ๋ฌด ๋งŽ์ด ๋ฐœ์ƒํ•˜๋ฉด ์„ฑ๋Šฅ์— ๋ถ€์ •์ ์ธ ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋‚˜ ์ž์ฃผ ๋ Œ๋”๋ง๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๋ฆฌ์•กํŠธ ๋ฆฌ๋ Œ๋”๋ง์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐ ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์–ธ์ œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ• ๊นŒ์š”?

  • state๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ
  • props๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ
  • context ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ
  • ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉด ์ž์‹๋„ ๋ฆฌ๋ Œ๋”๋ง
์œ„์™€ ๊ฐ™์€ ๊ฒฝ์šฐ์— ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ํ•œ ์ปดํฌ๋„ŒํŠธ์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์Šคํ…Œ์ดํŠธ๋“ค์ด ์กด์žฌํ•˜๊ณ  UI์—์„œ๋Š” ์—ฌ๋Ÿฌ ์ธํ„ฐ๋ ‰์…˜(๋ฒ„ํŠผ ํด๋ฆญ, ์ž…๋ ฅ, ์ฒดํฌ๋ฐ•์Šค ๋“ฑ)์ด ๋ฐœ์ƒํ•˜๋ฉฐ ์Šคํ…Œ์ด๋“œ ๊ฐ’๋“ค์ด ๋ณ€๊ฒฝ๋ ๋•Œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฆฌ๋ Œ๋”๋ง ๋˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
const MyComponent = () => {
  const [count, setCount] = useState(0);

  const handleClick = () => setCount(count + 1);

  return <button onClick={handleClick}>{count}</button>;
};
์œ„ ํ•จ์ˆ˜๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜๋ฉด
  • ๋ฒ„ํŠผ ํด๋ฆญ โ†’ setCount ํ˜ธ์ถœ โ†’ count ๊ฐ’ ๋ณ€๊ฒฝ
  • ๋ณ€๊ฒฝ๋œ state๋กœ ์ธํ•ด MyComponent() ํ•จ์ˆ˜ ์ „์ฒด๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰๋จ
  • count, handleClick, JSX ๋“ฑ์ด ๋‹ค์‹œ ๊ณ„์‚ฐ๋จ
  • Virtual DOM์—์„œ ๋ณ€ํ™” ๋น„๊ต ํ›„, ์‹ค์ œ DOM์€ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์—…๋ฐ์ดํŠธ๋จ
์œ„์™€ ๊ฐ™์€ ์ผ์ด ์ด๋ฃจ์–ด ์ง‘๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ UI๋Š” ์ตœ์†Œํ•œ์œผ๋กœ ๋ถ€๋ถ„ ์—…๋ฐ์ดํŠธ ๋˜๋ฉฐ handleClick ํ•จ์ˆ˜๋Š” ์ƒˆ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ ์ƒ์„ฑ๋œ๋‹ค ํ•จ์€ ํ•จ์ˆ˜ ๊ฐ์ฒด ์ž์ฒด๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
const handleClick = () => setCount(count + 1);
์œ„ ์ฝ”๋“œ๋Š” ํ•จ์ˆ˜ ํ‘œํ˜„์‹(= ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด)์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ์ค„์ด ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ๊ฐ์ฒด๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค.
์ฝ”๋“œ๋กœ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
const MyComponent = () => {
  const [count, setCount] = useState(0);

  const handleClick = () => setCount(count + 1);

  useEffect(() => {
    console.log("handleClick ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์กŒ์–ด์š”!");
  }, [handleClick]);

  return <button onClick={handleClick}>{count}</button>;
};
  • ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด setCount๋กœ ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ๊ณ ,
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง๋จ,
  • ๊ทธ๋Ÿฌ๋ฉด handleClick๋„ ์ƒˆ๋กœ ์ƒ์„ฑ๋จ,
  • ๋”ฐ๋ผ์„œ useEffect๊ฐ€ ์‹คํ–‰๋จ โ†’ ์ฝ˜์†”์— ๋กœ๊ทธ ์ถœ๋ ฅ!

๊ทธ๋Ÿผ ์™œ ๋ฆฌ๋ Œ๋”๋ง ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑ๋ ๊นŒ์š”?

๋ฆฌ์•กํŠธ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋Š” ๊ฒฐ๊ตญ ํ•จ์ˆ˜์ด์ž, ๋งค๋ฒˆ ์‹คํ–‰๋˜๋Š” ์‹คํ–‰ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด, ๊ทธ ๋‚ด๋ถ€์˜ handleClick = () => { ... }๋„ ๋‹ค์‹œ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์กฐ๊ธˆ ๋” ๋“ค์–ด๊ฐ€์„œ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง์ด ๋œ ๋˜๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ƒ๊ฐํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
ํ•œ ์ปดํฌ๋„ŒํŠธ์—์„œ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๊ณ  ์—ฌ๋Ÿฌ ์Šคํ…Œ์ดํŠธ๋“ค์„ ๊ด€๋ฆฌํ•˜๋˜ ๊ฒƒ์„ ํ›…์—์„œ ์Šคํ…Œ์ดํŠธ ๊ฐ’์ด๋‚˜ ์ƒํƒœ ๊ฐ’์„ ๊ด€๋ฆฌํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ๋Š” ์ˆœ์ˆ˜ํ•˜๊ฒŒ ์ธํ„ฐ๋ ‰์…˜๋งŒ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„๋œ ๊ฒฝ์šฐ, ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋Š” ์Šคํ…Œ์ดํŠธ ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ ๋ฆฌ๋ Œ๋”๋ง์„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๊ฒŒ ๋ณด์žฅ๋˜๋ ค๋ฉด ๋ช‡ ๊ฐ€์ง€ ์กฐ๊ฑด๊ณผ ์ฃผ์˜์ ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ ์ด ์ƒํ™ฉ์„ ์ž์„ธํžˆ ๋ถ„์„ํ•˜๊ณ , ์™œ ๋ฆฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ค ๊ฒฝ์šฐ์— ์ฃผ์˜ํ•ด์•ผ ํ•˜๋Š”์ง€ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ [์ปค์Šคํ…€ ํ›…] usePdfController.js

const usePdfController = () => {
  const [file, setFile] = useState(null);
  const pdfInputRef = useRef(null);

  const handlePDFChange = (event) => {
    const selectedFile = event.target.files[0];
    setFile(selectedFile);
  };

  return { file, pdfInputRef, handlePDFChange };
};

๐Ÿ“ [์ž์‹ ์ปดํฌ๋„ŒํŠธ] FileUpload.jsx (์ธํ„ฐ๋ž™์…˜๋งŒ ์ฒ˜๋ฆฌ)

const FileUpload = ({ onChange, InputRef, children }) => {
  return (
    <label>
      {children}
      <input type="file" ref={InputRef} onChange={onChange} style={{ display: 'none' }} />
    </label>
  );
};

๐Ÿ“ [๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ] StampController.jsx

const StampController = () => {
  const { file, pdfInputRef, handlePDFChange } = usePdfController();

  return (
    <div>
      <FileUpload InputRef={pdfInputRef} onChange={handlePDFChange}>
        PDF ์—…๋กœ๋“œ
      </FileUpload>
      {file?.name && <div>{file.name}</div>}
    </div>
  );
};
  • ์Šคํ…Œ์ดํŠธ ๊ด€๋ฆฌ: ์Šคํ…Œ์ดํŠธ๋Š” ์ปค์Šคํ…€ ํ›…(์˜ˆ: usePdfController) ๋˜๋Š” ์Šคํ† ์–ด(์˜ˆ: useCanvasStore)์—์„œ ๊ด€๋ฆฌ.
  • ์ธํ„ฐ๋ž™์…˜ ์ปดํฌ๋„ŒํŠธ: ํŠน์ • ์ปดํฌ๋„ŒํŠธ(์˜ˆ: FileUpload, Button)๋Š” ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜(ํด๋ฆญ, ํŒŒ์ผ ์„ ํƒ ๋“ฑ)๋งŒ ์ฒ˜๋ฆฌํ•˜๊ณ , ์Šคํ…Œ์ดํŠธ๋‚˜ ์ƒํƒœ ๊ฐ’์„ ์ง์ ‘ ์†Œ์œ ํ•˜์ง€ ์•Š์Œ.
  • ์ธํ„ฐ๋ž™์…˜ ์ฒ˜๋ฆฌ: ์ธํ„ฐ๋ž™์…˜์€ ํ›…์ด๋‚˜ ์Šคํ† ์–ด์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜(์˜ˆ: handlePDFChange, handleDownload)๋ฅผ ํ˜ธ์ถœ.

์ž์‹ ์ปดํฌ๋„ŒํŠธ <FileUpload/> ๋ฆฌ๋ Œ๋”๋ง

๐Ÿ“ ์Šคํ…Œ์ดํŠธ ์†Œ์œ  ์—ฌ๋ถ€

  • ์ž์‹ ์ปดํฌ๋„ŒํŠธ FileUpload์€ ์ž์ฒด ์Šคํ…Œ์ดํŠธ๋ฅผ ๊ฐ€์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ FileUpload ๋‚ด๋ถ€์—์„œ useState๋‚˜ useReducer๋กœ ์ธํ•ด ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
  • ์Šคํ…Œ์ดํŠธ๋Š” usePdfController์—์„œ ๊ด€๋ฆฌ๋˜๋ฉฐ, file ๋ณ€๊ฒฝ์€ StampController ๊ฐ™์€ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์— ์˜ํ–ฅ์„ ์ค๋‹ˆ๋‹ค.

๐Ÿ“ Props ์•ˆ์ •์„ฑ

  • ์ž์‹ ์ปดํฌ๋„ŒํŠธ FileUpload๊ฐ€ ๋ฐ›๋Š” props(์˜ˆ: onChange, InputRef)๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํ•œ, React๋Š” ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ๋ Œ๋”๋งํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • FileUpload๊ฐ€ props๋กœ ๋ฐ›๋Š” handlePDFChange๊ฐ€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋˜์ง€ ์•Š์œผ๋ฉด, StampController๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค handlePDFChange๋Š” ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ์ฐธ์กฐ๊ฐ€ ์ƒ์„ฑ๋˜์–ด FileUpload์˜ props๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋˜์–ด ์ž์‹ ์ปดํฌ๋„ŒํŠธ FileUpload๋Š” ๋ฆฌ๋ Œ๋”๋ง ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์ปค์Šคํ…€ ํ›… usePdfController์—์„œ handlePDFChange๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜(useCallback)ํ•˜์—ฌ, ํ•จ์ˆ˜๊ฐ€ ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ๋™์ผํ•œ ์ฐธ์กฐ๋ฅผ ์œ ์ง€ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
// usePdfController.js
// โ˜…โ˜…โ˜…์ปค์Šคํ…€ ํ›… ๋‚ด๋ถ€์—์„œ useCallback ์‚ฌ์šฉ
  const handlePDFChange = useCallback((event) => {
  const selectedFile = event.target.files[0];
  setFile(selectedFile);
}, []);
  • InputRef๋Š” useRef๋กœ ์ƒ์„ฑ๋œ ์ฐธ์กฐ๋กœ, ๋ Œ๋”๋ง ๊ฐ„์— ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๐Ÿ“ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฆฌ๋ Œ๋”๋ง ์˜ํ–ฅ

  • ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ StampController๊ฐ€ ์ž์ฃผ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉด, ์ž์‹ ์ปดํฌ๋„ŒํŠธ FileUpload๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฆฌ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.
  • React๋Š” ๋ถ€๋ชจ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉด ์ž์‹๋„ ๋ Œ๋”๋ง ์‹œ๋„
  • ์ด๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ FileUpload์„ React.memo๋กœ ๊ฐ์‹ธ์ค๋‹ˆ๋‹ค.
const FileUpload = ({ onChange, InputRef, children }) => {
  console.log('FileUpload rendered');
  return (
    <label>
      {children}
      <input type="file" ref={InputRef} onChange={onChange} style={{ display: 'none' }} />
    </label>
  );
};
export default React.memo(FileUpload);
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด onChange, InputRef, children์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํ•œ FileUpload์€ ๋ฆฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ถ€๋ชจ์ปดํฌ๋„ŒํŠธ StampController๊ฐ€ file ์ƒํƒœ ๋ณ€๊ฒฝ์œผ๋กœ ๋ฆฌ๋ Œ๋”๋ง๋˜๋”๋ผ๋„, ์ž์‹ ์ปดํฌ๋„ŒํŠธ FileUpload๊ฐ€ React.memo๋กœ ๊ฐ์‹ธ์ ธ ์žˆ๋‹ค๋ฉด props๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค
  • React.memo๋Š” props์˜ ์–•์€ ๋น„๊ต(shallow comparison)๋ฅผ ์ˆ˜ํ–‰ํ•ด props๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํ•œ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ์ธํ„ฐ๋ž™์…˜์˜ ๊ฒฝ์šฐ (์˜ˆ: ํŒŒ์ผ ์„ ํƒ)์€ handlePDFChange๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์ด๋Š” usePdfController์˜ file ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ณ€๊ฒฝ์€ StampController์˜ ๋ Œ๋”๋ง์—๋งŒ ์ง์ ‘ ์˜ํ–ฅ์„ ์ฃผ๊ณ , FileUpload์€ ์Šคํ…Œ์ดํŠธ๋ฅผ ์†Œ์œ ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฐ„์ ‘์ ์ธ ๋ฆฌ๋ Œ๋”๋ง์„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ <FileUpload/> Children Props ๋ณ€๊ฒฝ

FileUpload์˜ children(์˜ˆ: "PDF ์—…๋กœ๋“œ")์ด ๋™์ ์œผ๋กœ ์ƒ์„ฑ๋˜๊ฑฐ๋‚˜ ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ์ฐธ์กฐ๋ฅผ ๊ฐ€์ง€๋ฉด, React.memo๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ๋ฆฌ๋ Œ๋”๋ง๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. children์ด ์ •์ ์ด๊ฑฐ๋‚˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
<FileUpload InputRef={pdfInputRef} onChange={handlePDFChange}>
  PDF ์—…๋กœ๋“œ
</FileUpload>

๐Ÿ“ <FileUpload/> ๊ฐ™์€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์Šคํ† ์–ด ์ƒํƒœ๋ฅผ ์ง์ ‘ ๊ตฌ๋…ํ•˜์ง€ ์•Š๋„๋ก ํ•˜์—ฌ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ ํ•œ๋‹ค.

// ์ž˜๋ชป๋œ ์˜ˆ
const FileUpload = ({ onChange, InputRef, children }) => {
  const { file } = useCanvasStore(); // ์Šคํ† ์–ด ๊ตฌ๋…
  return <input type="file" ref={InputRef} onChange={onChange} />;
};

// ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ
const FileUpload = ({ onChange, InputRef, children }) => {
  return <input type="file" ref={InputRef} onChange={onChange} />;
};

๋ฆฌ๋ Œ๋”๋ง์ด ๋˜์ง€ ์•Š๋Š” ์กฐ๊ฑด์„ ์ง€์ผœ๋Š” ๋ฐฉ๋ฒ•

  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์ฒด ์Šคํ…Œ์ดํŠธ๋ฅผ ์†Œ์œ ํ•˜์ง€ ์•Š๊ณ  ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๊ด€๋ฆฌ.
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์Šคํ† ์–ด๋‚˜ Context๋ฅผ ์ง์ ‘ ๊ตฌ๋…ํ•˜์ง€ ์•Š๊ณ  ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•จ.
  • ์ „๋‹ฌ๋œ props(ํ•จ์ˆ˜, ์ฐธ์กฐ, ๊ฐ’)๊ฐ€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋˜์–ด ์ „๋‹ฌ ๋ฐ›๋„๋ก ํ•˜๊ธฐ.
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ React.memo๋กœ ๊ฐ์‹ธ์ ธ ์žˆ์–ด ๋ถ€๋ชจ ๋ฆฌ๋ Œ๋”๋ง์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์Œ.