๋ฆฌ์กํธ์์ ๋ฆฌ๋ ๋๋ง(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๋ก ๊ฐ์ธ์ ธ ์์ด ๋ถ๋ชจ ๋ฆฌ๋ ๋๋ง์ ์ํฅ์ ๋ฐ์ง ์์.