์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

hooks๋Š” ์–ด๋–ป๊ฒŒ closure์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์„๊นŒ

tothesea 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ํ•˜๋ผ๊ณ  ์•Œ๋ ค์ฃผ๊ณ  ์žˆ๋‹ค.