클로저(closure)

EUNSUN KIM·2025년 1월 16일

JS

목록 보기
2/6

자바스크립트에서 가장 혼동 되기 쉬운 개념인 클로저(closure)가 무엇인지, 무슨 원리인지 정리해봤다.

클로저를 이해하려면, 먼저 자바스크립트 코드를 실행하는 실행 컨텍스트(execution context)에 대해 알아야 한다.

실행 컨텍스트란?

자바스크립트 코드를 실행하는 JS 엔진의 콜 스택(call stack)에는 코드가 실행되고 종료됨에 따라 코드의 실행 컨텍스트(execution context)가 LIFO(Last In First Out, 후입선출)방식으로 쌓이고 제거된다.

실행 컨텍스트는 간단히 말해 각 코드별로 실행시 주어지는 실행 환경이다. 이 실행 환경은 각 코드를 실행하기 직전 생성되며, 내부에 실행할 코드에 대한 환경 정보를 저장한다. 이렇게 코드 실행 직전 실행 컨텍스트를 세팅하는 단계를 creation phase라고 한다.

실행 컨텍스트에 저장되는 정보는 다음과 같다.

  • 코드가 실행되는 변수 환경

    • 코드가 실행되는 맥락에 속한 모든 변수 정보를 저장한다(const / let / var 선언들, 함수 선언, arguments 객체(화살표 함수의 실행 컨텍스트는 arguments 객체 미포함))
  • 스코프 체인

  • this 키워드

    • 단, 화살표 함수의 실행 컨텍스트는 this 키워드를 가지고 있지 않다. 따라서 화살표 함수 내부에서 this 키워드를 사용하면 스코프 체인을 통해 상위 스코프의 this를 참조한다.

    • 📚 this 키워드 정리


클로저는 실행 컨텍스트 내부의 변수 환경(variable environment)과 관련이 있다. 우선 변수 환경이 무엇인지 알아보자.

const globalVar = 'I am gloabl variable';

function getSum(num1, num2){
	const result = num1 + num2;
	return result;
}

getSum(1,2);

위 코드를 실행한다고 하면, 콜 스택 내부에는 다음 처럼 먼저 전역 실행 컨텍스트가 생성되고, 이 안에 전역에 선언된 변수에 대한 정보가 저장된다 (변수에 할당된 값은 사실 런타임이 되어서야 알 수 있지만 이해를 돕기위해…). 이것을 변수 환경이라고 한다.

💡 참고로 전역 실행 컨텍스트는 애플리케이션 규모에 상관없이 전역에 1개 생성 되며, 웹앱으로 치자면 브라우저 종료 등 애플리케이션을 종료하지 않는 이상 콜 스택에서 제거되지 않는다.

그리고 getSum 함수를 호출할 시점에서 다음과 같이 새로운 실행 컨텍스트가 생성되고, getSum 함수 내부에 선언된 변수 정보가 저장된다.

이 실행 컨텍스트와 내부의 변수 환경 정보 등은 코드 실행이 종료되면 call stack에서 제거된다.

그렇다면, 아래 코드를 실행하면 어떻게 될까? getSum 함수 안에서 전역 변수 logFn에 할당된 함수는 내부에서 getSum 함수의 변수를 사용하는데, 실행 되는 시점에서는 getSum 함수와 그 실행 컨텍스트가 제거된 상태다.

const globalVar = 'I am gloabl variable';
let logFn;

function getSum(num1, num2){
	const result = num1 + num2;
	logFn = function(){
		const messageHeader = '[Message]';
		console.log(`${messageHeader} ${num1} 더하기 ${num2}${result}`);
	}
}

getSum(1,2);
logFn();

logFn 함수가 호출 될 때, num1, num2, result 변수에 대한 정보는 함수 내부에도 없고, 상위 스코프인 전역 스코프에도 없다.

그래서 logFn을 실행하는 과정에서 변수 정보를 찾지 못해 에러가 발생하고, 로그 출력창에는 ‘ ~ is not defined’ 에러 문구가 떠야 할 것 같은데, 결과적으로는 아래처럼 (변수 정보를 어떻게 구했는지는 몰라도) 제대로된 로그가 찍힌다.

이 메세지가 찍히기 위해서는 num1 = 1, num2 = 2, result = 3 이라는 정보가 필요한데, 이 값들은 대체 어디서 나온걸까?

결론을 말하면 이것들은 logFn에 할당된 함수가 선언 당시 속해 있던 실행 컨텍스트, 즉 getSum 함수의 실행 컨텍스트 내부에 존재하던 변수정보다.

실행 컨텍스트의 변수 환경은, 같은 실행 컨텍스트에서 생성된 함수에 한해 영구적으로 접근해 사용할 수 있도록 closed-over 된다. 이것을 closed-over 된 변수 환경이라고 하고, 다른 말로 클로저(closure)라고 한다.

그리고 함수 실행시, 내부에 선언되지 않은 변수를 발견하면 상위 스코프를 탐색하기에 앞서 클로저, 즉 함수가 생성되었을 당시의 변수 환경을 탐색해 변수 정보를 찾는다.

클로저가 스코프 체인보다 탐색 우선순위가 높기 때문에, 설령 상위 스코프에 동일한 변수가 존재해도 클로저의 변수 정보를 사용하게 된다.

profile
현재가 미래를 만든다

0개의 댓글