-
hooks๋ ์ด๋ป๊ฒ closure์ ์ฌ์ฉํ๊ณ ์์๊น์นดํ ๊ณ ๋ฆฌ ์์ 2023. 6. 9. 18:39
hooks๊ฐ ํด๋ก์ ๋ฅผ ์ฌ์ฉํ ๊ฑฐ๋ผ๋๋ฐ, ๊ทธ๊ฒ ๋ฌด์จ์๋ฏธ์ผ๊น์? ๋ผ๋ ์ง๋ฌธ์ ๋ตํ์ง ๋ชปํ๋ค๋ฉด ๋ค์ ์์์ ๋ณผ ๊ฒ์ ์ถ์ฒํ๋ค.
ํด๋ก์ ๋ถํฐ ๋ฆฌ์กํธ ๋ด๋ถ ๋์์ ํ์ํํ ๋ผ์ด๋ธ์ฝ๋ฉ์ ๋ณด๋ฉฐ ๋จ๊ณ๋ณ๋ก ๋ฆฌ์กํธ ํ ์ ๋ด๋ถ ์๋ฆฌ๋ฅผ ์ดํดํ๊ธฐ๊ฐ ์ข์๋ค. ์๋ฌธ์ ๋ธ๋ก๊ทธ๋ก ๋์ด ์๋๋ฐ, ๊ฒ์ํ๋ฉด ๋ฐ๋ก ๋์ค์ง๋ง ๋ฏธ๋์ ๋๋ฅผ ์ํด ๋ด ์ธ์ด๋ก ๋ค์ ํ ๋ฒ ์ ๋ฆฌํ๊ณ ์ ํ๋ค.
ํด๋ก์ ๋?
๋จผ์ ํด๋ก์ ์ ๋ํด์ ์ง๋ฌธ์ ํตํด์ ์ ๋ฆฌํ๊ณ ๊ฐ๋ณด์.
1. ํด๋ก์ ๋?
ํจ์์ ํจ์๊ฐ ์ ์ธ๋ ์ดํ์ ํ๊ฒฝ์ ์กฐํฉ์ด๋ค.
2. ํด๋ก์ ๋ฅผ ์ธ์ ์ฌ์ฉํ๋๊ฐ?
์ํ๊ฐ ์๋์น ์๊ฒ ๋ณ๊ฒฝ๋์ง ์๋๋ก ์ํ๋ฅผ ์์ ํ๊ฒ ์๋ํ๊ณ , ํน์ ํจ์์๊ฒ๋ง ์ํ ๋ณ๊ฒฝ์ ํ์ฉํ๊ธฐ ์ํด ์ฌ์ฉํ๋ค. ์ถ๊ฐ๋ก, ๋ถ๋ถ์ ์ฉ ๋๋ ์ปค๋งํจ์์ ์ฌ์ฉ๋๋ค.
3. ํด๋ก์ ์์ ํจ์๊ฐ ์คํ ์ข ๋ฃ๋์์์๋ ๋ณ์์ ์ ๊ทผํ ์ ์๋ ์ด์ ๋ ๋ฌด์์ธ๊ฐ?
๊ฐ๋น์ง ์ปฌ๋ ํฐ์ ์๋ ๋ฐฉ์ ๋๋ฌธ์ด๋ค. ๊ฐ๋น์ง ์ปฌ๋ ํฐ๋ ์ด๋ค ๊ฐ์ ์ฐธ์กฐํ๋ ๋ณ์๊ฐ ํ๋๋ผ๋ ์์ผ๋ฉด ๊ทธ ๊ฐ์ ์์ง ๋์์ ํฌํจ์ํค์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ ์์์์๋ ํด๋ก์ ๋ฅผ ์ด๋ ๊ฒ ์ ์ํ๊ณ ์๋ค.
Closure makes it possible for a function to have "private" variables.
ํด๋ก์ ๋ ํจ์๊ฐ ํ๋ผ์ด๋นํ ๋ณ์๋ฅผ ๊ฐ์ง ์ ์๋๋ก ํด์ค๋ค.์ด๋ป๊ฒ ํ๋ผ์ด๋นํ ๋ณ์๋ฅผ ๊ฐ์ง๊ฒ ํด ์ฃผ๋์ง ์ฝ๋๋ก ์ดํด๋ณด์.
๋ง์ฝ ๋ด๊ฐ ๋ํ๋ ๊ธฐ๋ฅ์ ํ๋ ํจ์๋ฅผ ๋ง๋ค๊ณ ์ถ๋ค๊ณ ํด๋ณด์. ์๋์ ๊ฐ์ด ๋ง๋ค ์ ์์ ๊ฒ์ด๋ค.let foo = 1; function add() { foo = foo + 1; return foo; } console.log(add()); // 2 console.log(add()); // 3 console.log(add()); // 4 console.log(add()); // 5 console.log(add()); // 6
๊ทธ๋ฐ๋ฐ ๋๊ตฐ๊ฐ๊ฐ ๊ฐ์๊ธฐ ๋ด ์ฝ๋ ์ค๊ฐ์ foo๋ฅผ ๋ค์ ์ ์ํด๋ฒ๋ฆฐ๋ค๋ฉด, ์ํ๋ ๊ฐ์ ์ป์ง ๋ชปํ ๊ฒ์ด๋ค.
let foo = 1; function add() { foo = foo + 1; return foo; } console.log(add()); // 2 console.log(add()); // 3 foo = 10000; console.log(add()); // 10001 console.log(add()); // 10002 console.log(add()); // 10003
์ด๋ด ๋ ์ฌ์ฉํ ์ ์๋๊ฒ ํด๋ก์ ๋ค!
ํด๋ก์ ๋ฅผ ์ด์ฉํด์ ์ธ๋ถ์์ ๋ด๊ฐ privateํ๊ฒ ๊ด๋ฆฌํ๊ณ ์ถ์ ๋ณ์์ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ํ ์ ์๋ค.
function getAdd(){ let foo = 1; return function (){ foo = foo + 1; return foo; }; } const add = getAdd(); console.log(add()); // 2 console.log(add()); // 3 console.log(add()); // 4 console.log(add()); // 5 console.log(add()); // 6
getAdd๋ผ๋ ํจ์๋ ์คํ๋์ด ์ฌ๋ผ์ก๊ณ , foo๋ผ๋ ๋ณ์๋ ํจ๊ป ์ฌ๋ผ์ก์ง๋ง, getAdd๊ฐ ๋ฐํํ ํจ์๋ foo๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์ ํด๋ก์ ์ธ ๊ฒ์ด๊ณ , ์ธ๋ถ์์ ๋์ด์ foo๋ผ๋ ๋ณ์์ ์ง์ ์ ๊ทผํ ์๋ ์์ง๋ง(์ ๋ณด ์๋) ๋ฆฌํด๋ฐ์ ํจ์๋ฅผ ์ด์ฉํด foo๋ผ๋ ๊ฐ์ ์ฆ๊ฐ์ํฌ ์ ์๋ค!
closure๋ฅผ hook์ ์ ์ฉํ๊ธฐ
useState๋ ์ด๋ป๊ฒ ํด๋ก์ ๋ฅผ ํ์ฉํ๊ณ ์๋๊ฐ?
์ด์ useState๊ฐ ์ด๋ป๊ฒ ์ด ํด๋ก์ ๋ฅผ ํ์ฉํ๊ณ ์๋์ง ์๋ ์ฝ๋๋ก ์ดํด๋ณด์.
function useState(initVal) { let _val = initVal; const state = () =>_val; const setState = (newVal) => { _val = newVal; }; return [state, setState]; } const [count, setCount] = useState(1); console.log(count()); // 1 setCount(2); console.log(count()); // 2
์ด ์ฝ๋๋ฅผ ๋ณด๋ฉด
ํด๋ก์
๋ฅผ ์ฌ์ฉํ๊ณ ์์์ ์ ์ ์๋ค. useState๋ฅผ ์คํ์์ผ_val
์ด๋ ๋ณ์๋ ์ด๋ฏธ ์คํ์ด ์๋ฃ๋์ด ์ฌ๋ผ์ก์ง๋ง, return์ผ๋ก ๋ฐ์ state์ setStateํจ์์์_val
์ ์ ๊ทผํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.(setState์ดํ ์ ๋ฐ์ดํธ๋ state๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด state๋ฅผ getterํจ์๋ก ๋ง๋ค์ด
count()
์ด๋ฐ ์์ผ๋ก state๋ฅผ ์ฌ์ฉํด์ผ ํด์ ๋ญ๊ฐ ์ด์ํ์ง๋ง ์ผ๋จ ์ด ๋ถ๋ถ์ ๋์ด๊ฐ๊ณ ํด๋ก์ ๋ง ์ดํดํด๋ณด์)์ด๋ฐ ์๋ฆฌ๋ฅผ ์ด์ฉํด์ React๋ผ๋ ๋ชจ๋์ ๋ง๋ ๋ค๊ณ ํด๋ณด์. React๋ผ๋ ๋ชจ๋์์ ์ฆ์ ์คํ ํจ์๋ก ์์ ํจ์๋ฅผ ๋ฐํํ๊ฒ ํ๋ฉด ๋ ๊ฒ์ด๋ค. useState๋ผ๋ ๋ชจ๋์ ์ด์ฉํด์ ์ปดํฌ๋ํธ๋ฅผ renderํ๋ ๊ธฐ๋ฅ์ ๊ฐ๋จํ๊ฒ ์๋์ ๊ฐ์ด ๋ํ๋ผ ์ ์๋ค.
const React = (function() { function useState(initVal) { let _val = initVal const state = _val const setState = newVal => { _val = newVal } return [state, setState] } function render(Component){ const C = Component() C.render(); return C; } return {useState, render} })() function Component(){ const [count, setCount] = React.useState(1) return{ render: () => console.log(count), click : () => setCount(count+1), } } const App = React.render(Component) App.click(); var App = React.render(Component)
์ด๋ ๊ฒ ํ๋ฉด count๊ฐ ์ ์์ ์ผ๋ก ์ ๋ฐ์ดํธ ๋ ๊น? ์ ๋๋ค!
์๋ํ๋ฉด useState์
_val
์๋ initVal ๊ฐ์ธ 1์ด ๊ณ์ ํ ๋น ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค!countํ ๋๋ง๋ค ์ฆ๊ฐํ๊ฒ ํ๊ณ ์ถ๋ค๋ฉด!
_val
๋ณ์๋ฅผ ๋ฐ๊นฅ์ผ๋ก ๋นผ์ํด๋ก์ ๋ฅผ ๋ ๋ฒ ํ์ฉํ๋ฉด ๋๋ค
!!const React = (function(){ let _val function useState(initVal){ const state = _val || initVal; // ... } // ... })()
์ด์
_val
์ด๋ ๋ณ์๊ฐ ์ด์์๊ฒ ๋๊ธฐ ๋๋ฌธ์ React.render(Component)๋ฅผ ์ฌ๋ฌ๋ฒ ํธ์ถํด๋_val
๊ฐ์ด ์ ์ง๋์ด click์ดํ์ renderํด๋ณด๋ฉด count๊ฐ ์ ์์ ์ผ๋ก ๋ณ๊ฒฝ๋๊ณ ์๋๊ฑธ ๋ณผ ์ ์๋ค.์ฌ๋ฌ๊ฐ์ ํ ์ ์ฌ์ฉํ๋ ค๋ฉด?
๊ทธ๋ฌ๋ ์ค์ ์ฝ๋ฉํ๋ค๋ณด๋ฉด ํ ์ ์ฌ๋ฌ๊ฐ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์ฌ๋ฌ๊ฐ ์ฌ์ฉํ ๋๋ ์ด๋ป๊ฒ ํ๋๊ฐ? ํ ์ ๋ํ ๋ด์ฉ์ ๋ณด๋ค๋ณด๋ฉด
ํ ์ ์ํ๋ฅผ ๋ฐฐ์ด๋ก ๊ด๋ฆฌํ๋ค
๋ ์งํ ์ ์ต์์์์๋ง ์ฌ์ฉํ ์ ์๊ณ , ์กฐ๊ฑด๋ฌธ ๋ฐ๋ณต๋ฌธ ์์์ ์ฌ์ฉํ ์ ์๋ค
๋ผ๋ ๋ง์ด ๋์จ๋ค.์ด๋ฐ ๋ง์ด ๋์ค๋ ์ด์ ๊ฐ ๋ฐ๋ก ๋ค์์ ์๋ค. ์์ ์์ ์ฝ๋๋ค์์
_val
์ด๋ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ๋์ ์ค์ ๋ก ํ ์ ๋ฐฐ์ด๋ก ์ด ๊ฐ๋ค์ ๊ด๋ฆฌํ๋ฉฐ, renderํจ์๊ฐ ํธ์ถ๋ ๋๋ง๋ค hooks ๋ฐฐ์ด์์ ๊ฐ๋ค์ ๋๊ณ index๊ฐ ๋ค์ 0์ผ๋ก ์ด๊ธฐํ ๋๋ ๊ฒ์ด๋ค.๋ฐ๋ผ์ ๋ง์ฝ ์กฐ๊ฑด๋ฌธ ์์ ํ ์ ์ฌ์ฉํ๋ค๋ฉด ์ด๋จ ๋์๋ ๋ฐฐ์ด์ ์๋ํ ์ธ๋ฑ์ค ์์ ๊ทธ ๊ฐ์ด ์ ์ฅ๋ ๊ฒ์ด๊ณ , ์ด๋จ ๋์๋ ์๋ํ์ง ์์ ์ธ๋ฑ์ค ์์ ์ ์ฅ๋ ๊ฒ์ด๊ธฐ์ ์ํ๊ฐ์ ์ ๋๋ก ๊ด๋ฆฌํ ์ ์์ ๊ฒ์ด๋ค.
๋ค์์ hooks๋ผ๋ ๋ฐฐ์ด๊ณผ currenthook์ด๋ผ๋ ์ธ๋ฑ์ค๋ฅผ ํตํด ๋ฆฌ์กํธ์ ๋์๋ฐฉ์์ ํ๋ด๋ธ ์ฝ๋์ด๋ค.
const MyReact = (function () { let hooks = [], currentHook = 0; // ๋ชจ๋ ์ค์ฝํ ์์ ์ด ๋ณ์๋ฅผ ๋ค๊ณ ์๊ฒ ํ๋ค. return { render(Component) { const Comp = Component(); Comp.render(); console.log("hooks", hooks); currentHook = 0; // reset for next render return Comp; }, useEffect(callback, depArray) { const hasNoDeps = !depArray; const deps = hooks[currentHook]; const hasChangedDeps = deps ? !depArray.every((el, i) => el === deps[i]) : true; if (hasNoDeps || hasChangedDeps) { callback(); hooks[currentHook] = depArray; } }, useState(initialValue) { hooks[currentHook] = hooks[currentHook] || initialValue; const setStatehookIndex = currentHook; const setState = (newState) => (hooks[setStatehookIndex] = newState); return [hooks[currentHook++], setState]; } }; })(); function Counter() { const [count, setCount] = MyReact.useState(1); const [text, setText] = MyReact.useState("text"); MyReact.useEffect(() => { console.log("effect", count); }, [count]); return { click: () => setCount(count + 1), noop: () => setCount(count), render: () => console.log("render", { count, text }), write: () => setText(text + "chage") }; } let App; App = MyReact.render(Counter); App.click(); App = MyReact.render(Counter); App.noop(); App = MyReact.render(Counter); App.write(); App = MyReact.render(Counter);
์์ ์ฝ๋์์๋ ์๋์ผ๋ก render๋ฅผ ๋๋ ค์คฌ์ง๋ง, ๋ฆฌ์กํธ์์ ์ฐ๋ฆฌ๋ setState๋ฅผ ํตํด ๋ฆฌ์กํธ์๊ฒ renderํ๋ผ๊ณ ์๋ ค์ฃผ๊ณ ์๋ค.