클로저(Closure)

이한·2023년 5월 16일
0
post-thumbnail

클로저(Closure)란?

함수와 해당 함수가 선언된 렉시컬 환경(Lexical Environment)의 조합으로 이루어진 개념이다.
JavaScript에서 closure는 함수 생성 시간에 함수가 생성될 때마다 만들어지며 함수가 종료된 후에도 함수의 렉시컬 환경에 접근할 수 있도록 한다.

이 말이 어려우니 예제를 살펴보고 알아보자

function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  innerFunc();
}

outerFunc(); // 10

위의 코드에서 함수 outerFunc 내에서 내부함수 innerFunc가 선언되고 호출되었다.
이때 내부함수 innerFunc는 자신을 포함하고 있는 외부함수 outerFunc의 변수 x에 접근할 수 있다.
이는 함수 innerFunc가 함수 outerFunc의 내부에 선언되었기 때문이다. 이처럼 렉시컬 스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정한다.
위 같은 경우에는 함수 innerFunc의 상위 스코프는 함수 outerFunc라고 볼 수 있다.

이 떄 동작을 살펴보면
1.innerFunc 함수 스코프(함수 자신의 스코프를 가리키는 활성 객체) 내에서 변수 x를 검색한다. -> 검색이 실패하였다.
2. innerFunc 함수를 포함하는 외부 함수 outerFunc의 스코프(함수 outerFunc의 스코프를 가리키는 함수 outerFunc의 활성 객체)에서 변수 x를 검색한다. -> 검색이 성공하였다.

이렇게 되는데 이번엔 다른 코드를 살펴보자

function outerFunc() {
  var x = 10;
  var innerFunc = function () { console.log(x); };
  return innerFunc;
}

/**
 *  함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
 *  그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
 */
var inner = outerFunc();
inner();

위 코드같은 경우에는 간단하게 표현하면 함수 outerFunc는 내부함수 innerFunc를 반환하고 생을 마감한다. 그러므로 함수 outerFunc는 실행된 이후 콜스택에서 제거되었으므로 함수 outerFunc의 변수 x 또한 더이상 유효하지 않게 되어 위와 달리 변수 x에 접근할 수 있는 방법은 달리 없어 보이는데 실행해보면 어떨까?
inner(); // 10 이라는 결과가 나오게 된다 어떻게 된 일일까?

위 케이스와 같이자신을 포함하고 있는 외부함수보다 내부함수가 더 오래 유지되는 경우, 외부 함수 밖에서 내부함수가 호출되더라도 외부함수의 지역 변수에 접근할 수 있는데 이러한 함수를 클로저(Closure)라고 부른다.
간단히 말하면 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수라고 할 수 있다.

클로저의 활용

상태 유지

그러면 이러한 클로저를 어떻게 이용할 수 있을까?
클로저가 가장 유용하게 사용되는 상황은 현재 상태를 기억하고 이 상태가 변경되어도 최신 상태를 유지해야 하는 상황에 매우 유용하다.
만약 자바스크립트에 클로저라는 기능이 없다면 상태를 유지하기 위해 전역 변수를 사용할 수 밖에 없다.
스코프에서 간단하게 설명했듯이 전역 변수는 언제든지 누구나 접근할 수 있고 변경할 수 있기 때문에 많은 부작용을 유발해 오류의 원인이 되므로 사용을 억제해야 한다.

정보의 은닉

예를 들어 생성자 함수 Counter는 메소드를 갖는 인스턴스를 생성한다고 했을 때
이 메소드들은 모두 자신이 생성됐을 때의 렉시컬 환경인 생성자 함수 Counter의 스코프에 속한 변수를 기억하는 클로저이며 렉시컬 환경을 공유한다.
이렇게 생성자 함수가 함수가 생성한 객체의 메소드는 객체의 프로퍼티에만 접근할 수 있는 것이 아니며 자신이 기억하는 렉시컬 환경의 변수에도 접근할 수 있다.
이때 생성자 함수 Counter의 변수가 this에 바인딩된 프로퍼티가 아닌 변수이고 메소드는 this에 바인딩된 프로퍼티라면 생성자 함수 Counter가 생성한 인스턴스를 통해 외부에서 접근이 가능한 public 프로퍼티가 되지만
생성자 함수 Counter 내에서 선언된 변수는 생성자 함수 Counter 외부에서 접근할 수 없다. 하지만 생성자 함수 Counter가 생성한 인스턴스의 메소드는 클로저이기 때문에 자신이 생성됐을 때의 렉시컬 환경인 생성자 함수 Counter의 변수에 접근할 수 있다.
이러한 클로저의 특징을 사용해 클래스 기반 언어의 private 키워드를 흉내낼 수 있다.

하지만 단점으로는 잘못 사용했을 시 성능 문제와 메모리 문제가 발생할 수 있기 때문에 주의가 필요하다.

profile
둥실둥실

0개의 댓글