React

React Hooks는 클로저를 사용하는가?

milliwonkim 2023. 3. 15. 19:59
반응형
SMALL

리액트 훅은 클로저 개념을 사용합니다.

 

클로저는 함수가 속한 렉시컬 스코프에서 선언된 변수에 대한 참조를 유지하는 함수입니다.

렉시컬 스코프란 변수의 유효범위를 결정하는데 사용되는 스코프의 종류 중 하나입니다.

함수를 어디에 정의했는지에 따라 해당 함수의 변수 스코프가 결정됩니다.

// 렉시컬 스코프 때문에 innerFunction 안에서는 outerVariable에 접근 가능합니다.
// innerFunction 함수가 outerFunction 함수 내부에서 정의됐으므로
// outerFunction의 함수 스코프에 접근 가능합니다.

function outerFunction() {
  const outerVariable = "outer";

  function innerFunction() {
    console.log(outerVariable); // "outer"
  }

  innerFunction();
}

outerFunction();

 

이를 리액트로 가져와봅시다.

import React, { useState } from 'react';

function Counter() {
/*
위 코드에서 useState 훅은 내부적으로 클로저를 사용하고 있습니다. 
useState 훅은 컴포넌트가 처음으로 렌더링될 때 
함수 내부에서 선언한 state 변수와 setState 함수를 반환합니다. 

이후 컴포넌트가 다시 렌더링되어도 같은 state 변수와 setState 함수를 사용하게 되는데, 
이는 클로저에 의해 가능해집니다.

위 코드에서 count 변수와 setCount 함수는 useState(0) 함수가 호출될 때 생성되며, 
이후 increment 함수에서 사용됩니다. 
increment 함수는 외부 변수 count와 setCount에 접근하여 값을 변경하는데, 
이때 클로저에 의해 내부 함수인 increment가 외부 변수를 참조할 수 있게 됩니다. 

따라서 increment 함수는 count 변수의 값을 증가시키는데 사용됩니다.
*/
  const [count, setCount] = useState(0);
  
  //
  const increment = () => {
    setCount(count + 1);
  };
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={increment}>Click me</button>
    </div>
  );
}

export default Counter;

 

이렇게 보니

클로저는 어떻게 내부의 값을 기억하는지 너무 궁금해졌습니다.

 

자바스크립트에서 클로저는 함수가 선언될 때의 렉시컬 스코프에 대한 참조를 유지하면서,

함수가 실행될 때, 해당 스코프의 변수에 접근할 수 있는 함수입니다.

이 때 클로저는 내부의 값을 기억하기 위해 자바스크립트 엔진이 내부적으로 활용하는 두 가지 메커니즘이 있습니다.

 

1. 클로저가 기억해야할 변수, 객체를 자바스크립트 엔진이 더 이상 사용하지 않을때까지 메모리에 유지합니다

=> 이렇게 하면 클로저가 기억하는 변수, 객체의 값이 변하지 않습니다.

 

createCounter 함수는 내부적으로 count 변수를 생성하고, 이 변수에 접근할 수 있는 클로저를 반환합니다.

여기서 클로저는 리턴되는 함수입니다

리턴이 될 때 클로저가 생성됩니다.

return function() {
}

 

count 변수는 클로저에 의해 참조되므로

클로저가 기억합니다.

 

따라서 counter 함수가 실행될 때 마다, counter 함수의 count 변수 값을 유지할 수 있습니다.

function createCounter() {
  let count = 0;
  return function() {
    return ++count;
  }
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

 

2. 자바스크립트 엔진은 클로저가 기억할 변수, 객체를 복사하여 저장합니다.

이 때 복사된 값은 클로저가 생성된 시점의 값으로 유지됩니다.

 

double 함수가 실행될 때마다, number * factor 계산을 수행하는데

이 때 factor 변수의 값은 클로저에 의해 참조됩니다.

이렇게 하면 factor 변수의 값을 변경해도

double 함수가 반환한 클로저는 여전히 기존의 factor 값을 참조하게 됩니다.

function createMultiplier(factor) {
  return function(number) {
    return number * factor;
  }
}

const double = createMultiplier(2);
console.log(double(3)); // 6
console.log(double(4)); // 8

리액트에서는 JSX를 리턴할 때와 useState를 사용할 때 모두 클로저가 생성됩니다.

 

JSX를 반환할 때 클로저가 생성되는 이유는 JSX 코드 내에서 외부 변수나 상태를 참조할 수 있기 때문입니다. 

이렇게 참조된 외부 변수나 상태는 해당 컴포넌트의 렌더링이 완료된 후에도 메모리에 유지됩니다. 

이를 가능하게 하는 것이 클로저입니다.

또한 useState를 사용할 때에도 클로저가 생성됩니다. 

useState는 함수형 컴포넌트에서 상태를 관리하기 위한 Hook이며, 

이 Hook을 호출하면 상태를 저장하기 위한 메모리 공간과 이 상태를 변경하는 함수가 생성됩니다. 

이 때 생성된 함수는 해당 컴포넌트 내에서만 호출되기 때문에, 해당 함수가 참조하는 상태값은 클로저를 통해 유지됩니다.

따라서 JSX를 반환할 때와 useState를 사용할 때 모두 클로저가 생성되며, 이를 통해 외부 변수나 상태를 유지할 수 있습니다.

반응형
LIST