본문 바로가기
JavaScript

클로저(Closure) 이해하기

by Vintz 2021. 9. 17.
반응형

클로저(Closure)란?

함수는 중첩이 가능한데, 내부 함수 객체가 생성되면 이 객체는 자신을 생성한 외부 함수에 대한 활성 객체의 참조를 가지게 된다. 이를 클로저(closure)라 한다. 즉, 클로저를 통해 내부 함수에서 외부 함수의 스코프에 접근 할 수 있다.

✅ 자바스크립트는 함수가 호출되면 활성 객체(activation object)가 만들어진다. 이것은 숨겨진 데이터 구조로써 호출된 함수의 반환 주소와 실행에 필요한 정보를 저장하고, 이를 호출된 함수에 바인딩 해준다.
  • 렉시컬 스코핑(Lexical scoping) 이해하기
  • 활성 객체(Activation object) 이해하기
  • 클로저는 함수와 그 함수가 선언된 주변 객체에 대한 참조(렉시컬 환경)와의 조합이다.

렉시컬 스코핑(Lexical scoping)

자바스크립트는 어떻게 변수의 유효 범위를 지정하는지(lexical scoping)를 알아보자. 자바스크립트의 경우 함수를 호출할 때가 아닌 함수를 어디에 선언하였는지에 따라 스코프가 결정된다. 이를 렉시컬 스코핑이라 한다. 함수가 중첩된 상황을 예로 들어보자.

function outerFunc() {
  const name = "Only Dev"; // 외부 함수에 의해 생성된 지역 변수이다.
  function innerFunc() { // 내부 함수이며, 클로저다.
    console.log(name); // 부모 함수에서 선언된 변수를 사용한다.
  }
  innerFunc();
}

outerFunc(); // Only Dev

위 예제에서 함수 innerFunc()는 함수 outerFunc() 내부에 선언되었다.

 

외부 함수인 outerFunc()는 내부 함수 innerFunc()의 상위 스코프이다. 만약 함수 innerFunc()가 전역에 선언되었다면 상위 스코프는 전역 스코프가 된다.

 

결국 내부 함수인 innerFunc()자신이 속한 렉시컬 스코프(전역, 외부 함수, 자기 자신의 스코프)를 참조할 수 있다.

 

 

위 예제의 실행을 통해 중첩된 함수에서 파서가 어떻게 변수를 처리하는지 알 수 있다. 즉, 중첩된 함수는 외부(outerFunc 함수) 스코프에서 선언한 변수에도 접근할 수 있다.

활성 객체(Activation object)

위의 내용을 다시 한번 떠올려 보자. 자바스크립트는 함수가 호출되면 활성 객체(activation object)가 만들어진다. 이것은 숨겨진 데이터 구조로써 호출된 함수의 반환 주소와 실행에 필요한 정보를 저장하고, 이를 호출된 함수에 바인딩 해준다.

 

함수는 중첩이 가능한데, 내부 함수 객체가 생성되면 이 객체는 자신을 생성한 외부 함수에 대한 활성 객체의 참조를 가지게 된다. 이를 클로저(closure)라 한다. 즉, 클로저를 통해 내부 함수에서 외부 함수의 스코프에 접근 할 수 있다.

function outerFunc() {
  const name = "Only Dev"; // 외부 함수에 의해 생성된 지역 변수이다.
  function innerFunc() { // 내부 함수이며, 클로저다.
    console.log(name); // 부모 함수에서 선언된 변수를 사용한다.
  }
  return innerFunc;
}

// 함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
// 그 후 함수 outerFunc는 생명 주기가 끝이 난다.

const myFunc = outerFunc();
// 클로저는 유효한 스코프의 렉시컬 환경(lexical environment)을 유지한다.
myFunc(); // Only Dev

좀 전과 다른 위의 예제는 outerFunc()가 내부 함수인 innerFunc()를 반환하고 실행이 끝이 났다. 그런데 어떻게 innerFunc()가 이미 생명주기(life-cycle)가 종료된 함수 outerFunc의 지역 변수 name을 참조할 수 있었을까?

 

클로저는 이렇게 자신을 포함하고 있는 외부 함수보다 내부 함수가 더 오래 유지되는 경우, 자신을 생성한 외부 함수에 대한 활성 객체의 참조를 갖고 있기(유지하기) 때문에 외부 함수 밖에서 내부 함수가 호출되더라도 외부 함수의 지역 변수 name에 접근할 수 있다.

 

 

결국 쉽게 말해 클로저는 중첩된 함수에서 내부 함수가 자신이 생성될 때의 환경(lexical environment)을 기억(위의 예제에선 전역, 외부 함수, 자기 자신의 스코프를 기억)하는 함수라고 볼 수 있다.

💡 A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
: 클로저는 주변 상태(lexical environment, 렉시컬 환경)에 대한 참조와 함께 조합된 함수이다. - MDN

내 생각엔..

클로저에 의해 참조되는 외부 함수의 변수, 즉 outerFunc()의 지역 변수 name과 엮여 '닫히게(클로저)'된 의미로 클로저라 부르는 것일까? 🤔 그대로 닫혀 해당 환경을 유지하게 되어서 클로저란 이름을 붙힌 것 같다.

참고

Closures - MDN
클로저 - poiemaweb
'자바스크립트는 왜 그 모양일까?' 읽기 - rinae
반응형