React Hooks는 클로저를 사용하는가?
리액트 훅은 클로저 개념을 사용합니다.
클로저는 함수가 속한 렉시컬 스코프에서 선언된 변수에 대한 참조를 유지하는 함수입니다.
렉시컬 스코프란 변수의 유효범위를 결정하는데 사용되는 스코프의 종류 중 하나입니다.
함수를 어디에 정의했는지에 따라 해당 함수의 변수 스코프가 결정됩니다.
// 렉시컬 스코프 때문에 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()); // 1console.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)); // 6console.log(double(4));// 8
리액트에서는 JSX를 리턴할 때와 useState를 사용할 때 모두 클로저가 생성됩니다.
JSX를 반환할 때 클로저가 생성되는 이유는 JSX 코드 내에서 외부 변수나 상태를 참조할 수 있기 때문입니다.
이렇게 참조된 외부 변수나 상태는 해당 컴포넌트의 렌더링이 완료된 후에도 메모리에 유지됩니다.
이를 가능하게 하는 것이 클로저입니다.
또한 useState를 사용할 때에도 클로저가 생성됩니다.
useState는 함수형 컴포넌트에서 상태를 관리하기 위한 Hook이며,
이 Hook을 호출하면 상태를 저장하기 위한 메모리 공간과 이 상태를 변경하는 함수가 생성됩니다.
이 때 생성된 함수는 해당 컴포넌트 내에서만 호출되기 때문에, 해당 함수가 참조하는 상태값은 클로저를 통해 유지됩니다.
따라서 JSX를 반환할 때와 useState를 사용할 때 모두 클로저가 생성되며, 이를 통해 외부 변수나 상태를 유지할 수 있습니다.