ABOUT ME

โš“ A smooth sea never made a skillful mariner

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

    ๋Œ“๊ธ€

Designed by Tistory.