
본 게시글은 모던 리액트 딥다이브를 보고 작성된 글입니다.
리액트 클래스 컴포넌트는, 자바스크립트 클래스 / 프로토타입 / this와 관계있다면, 리엑트 함수 컴포넌트는 클로저와 크게 관련이 있다.
클로저란?
함수와함수가 선언된 어휘적 환경의 조합
여기에서 선언된 어휘적 환경 이란, 변수나 함수가 코드 내부에서 어디에 선언되었는지를 말한다, 이 기준을 토대로 스코프가 설정된다.
this가 호출방식에 따라 동적으로 결정되는 반면, 선언된 어휘적 환경은 선언된 순간 정적으로 결정된다.// add의 내부 함수로 innerAdd가 중첩, 이때 a의 유효범위는 add전체
// b의 유효범위는 innerAdd, 내부 함수에서 a+b를 할 수 있는건 클로저 덕분
function add() {
const a = 10;
function innerAdd() {
const b = 20;
console.log(a + b);
}
innerAdd(); // 30
}
add();
전역 레벨에 선언된 변수로, 해당 스코프에서 변수 선언시 어디서든 호출할 수 있게 된다. 브라우저 환경에서 전역 객체는 window, Node.js 환경에서는 global이 있는데, 이 객체에 전역 레벨에서 선언한 스코프가 바인딩된다.
자바스크립트는 기본적으로 함수 레벨 스코프를 따른다고 한다. 특히, var 로 선언된 변수의 경우도 그렇데, 블록으로만 감싸줄 경우 그 밖에서도 접근 가능하다.
if(true) {
var global = 'global scope'
}
console.log(global); // 'global scope'
console.log(global === window.global) // true
만일 {} 와 같은, 블록에서가 아니라 함수 레벨에서 var로 변수를 선언했다면 referenceError가 나왔을 것이다. 다만, ES6 이후부터, 블록 레벨 스코프도 지원되면서,let과 const가 사용되고 있다.
function outerFunction() {
var x = ’hello'
function innerFunction() {
console.log(x)
}
return innerFunction
}
const innerFunction = outerFunction()
innerFunction() // "hello"
outerFunction은 innerFunction을 반환하며, 실행이 종료되었는데
이때 반환한 함수에는 x라는 변수가 존재하지 않지만
함수가 선언된 어휘적 환경인, outerFunction에 x변수가 있기에 hello를 출력할 수 있게 되는 것이다.
전역 스코프는 어디에서든지 값을 꺼낼 수도 있지만, 반대로는 누구든 접근 가능하고 수정도 가능하다. 이 경우, 클로저를 이용할 경우, 특정 변수에 접근할 수 있게 바꾸는 것 역시 가능하다.
그래서 리액트에서 클로저는 어떻게 사용될까?
컴포넌트 훅에서 클로거가 사용되기도 하는데, 대표적인 예시가 바로 useState이다
function Component() {
const [state, setstate] = useState()
function handleClick() {
// useState 호출은 위에서 끝남!
// ⭐️ 하지만 클로저 덕분에 setstate는 계속 내부의 최신값(prev)을 알고 있다 !!
setState((prev) => prev + 1)
}
}
외부함수 useState가 반환한, 내부함수 setState는 외부 함수의 호출이 끝났음에도, 자신이 선언된 외부 함수인 선언된 환경(state가 저장된 어딘가)을 기억하기 때문에 state값을 사용할 수 있는 것 이다.
클로저는 생각보다 다루기 쉽지 않다. 그 예제가 바로 for문 내부에서 setTime 돌리는 경우인데 스코프에 따라서 예상치 못한 결과를 불러올 수도 있기 때문!
이다.
JS 클로저, for문 안에 setTime로 변수를 찍어보자 🕖
또한, 클로저가 생성될때마다 선언적 환경을 기억해야 하는 작업은, 많은 비용을 야기하기 때문에 주의해야 한다.
클로저의 기본 원리에 따라, 클로저 선언 순간부터 내부 함수는 외부함수의 선언적인 환경을 기억하고 있어야 하므로, 사용하는 여부와 관계없이 저장해두기 때문이다.