Skip to main content

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를 사용할 때 모두 클로저가 생성되며, 이를 통해 외부 변수나 상태를 유지할 수 있습니다.