자바스크립트 개발을 하면서 우리는 클로저라는 단어를 많이 듣게된다. 그래서 이번에는 자바스크립트 클로저에 대해서 이야기 해보려한다.

기본


먼저 클로저를 이해하기에 앞서 MDN에서는 클로저를 다음과 같이 정의하고 있다. MDN에서 클로저 정의 의역을 해보자면 클로저는 함수와 선언된 함수가 어휘적 환경으로된 조합이라고한다. 여기서 우리는 자바스크립트 함수의 특징을 몇가지 살펴볼 필요가 있다.

자바스크립트는 일급(first-class) 객체다.
함수는 유효범위를 제공한다.

여기서 일급 객체가 가지고 있는 몇가지 특징을 이야기 해보면 다음과 같다.(여유가 된다면, 일급 객체에 대해서 따로 다뤄보겠다)

  1. 변수 혹은 객체에 할당 할 수 있다.
  2. 다른 함수의 인자로 전달할 수 있다.
  3. 함수의 반환 값이 될 수 있다.

위 특징들은 클로저를 이해하는데 있어서 매우 중요하다.

es6 에서는 let, const를 사용하여 block단위 scope이 정해진다.

자바스크립트의 특징에 대해서 이해를 한다면 클로저를 이해하는데 준비는 되었다라고 생각한다. 다시 본론으로 돌아가서 우리는 어휘적 환경(lexical environment) 에 대해 이해 할 필요가 있다. 먼저 자바스크립트는 함수 실행이 아닌 선언에 영향을 받는다. 즉 함수 선언시 어휘적 환경이 생성된다.

어휘적 환경 -> 환경을 기억하다 -> 자바스크립트의 모든 함수는 자신이 선언된 환경을 기억한다

함수 scope와 scope chain


자바스크립트에서는 함수로 scope으로 정해진다.

function outer() {
  var i = 'outer';

  function inner() {
    var j = 'inner';

    console.log(j, i);
  }
  inner();
}
outer();

위 코드를 inner 입장에서 해석해보자면

  • 변수 i는 inner함수의 자유변수이다.
  • 변수 j는 inner함수의 로컬변수이다.
    1. inner함수는 j를 찍는다.
    2. inner함수는 i를 찍으려해보지만 inner함수에 존재하지 않는다.
    3. inner함수내에 i가 존재하지 않으므로 socpe chain을 통해 outer의 변수 i를 들고온다.(이것이 함수 scope chaing이라고 한다)
    4. outer 함수로부터 얻어온 i를 console.log에 찍게된다.

위 예제는 함수가 내부에서 변수를 선언한다면, 그 변수는 선언된 함수 내부에서만 접근할 수 있다. 즉 outer함수나 전역(global context)에서는 j를 접근 할 방법이 없다.

자신의 선언된 위치를 기억하는 함수


자바스크립트에서 모든 함수는 자신이 선언된 위치(환경)을 기억한다. 이걸 고려하여 다시 예제를 살펴보자.

function outer() {
  var i = 'outer';

  function inner() {
    var j = 'inner';

    console.log(j, i);
  }
  inner();
}
outer();
  1. outer함수는 global에서 선언된 함수이다.
  2. inner함수는 outer 함수에서 선언된 함수이다. 그래서 inner함수는 자신의 선언된 환경은 다음과 같다.

    outer함수의 scope범위에 있다. outer함수의 local변수는 i이며 값은 outer이다.

  3. inner함수가 실행되면 이 조건을 가지고 실행이된다.

클로저(closure)


함수 scope를 이야기 할 때 우리는 함수는 자신의 유효범위 밖에서는 함수 내부에 선언된 변수를 접근 할 수 없다고 하였다. 하지만 다음 예제를 통하여 함수 외부에서도 내부의 변수 값을 접근이 가능하다

function outer() {
  var i = 'outer';

  function inner() {
    var j = 'inner';

    console.log(j, i);
  }
  return inner;
}
var fn = outer();
fn(); // inner, outer

자 우리는 함수 내부에서 중첩되어 선언된 함수는 scope chain을 통해 자신의 유효범위를 넘어 참조가 가능하다고 하였다. 그리고 함수 내의 변수는 함수 바깥에서 접근할 수 없었다. 하지만 클로저를 이용한다면 간단히 해결된다. 여기서 우리는 아까 자바스크립트의 모든 함수는 자신이 선언된 환경을 기억한다 라고도하였다. 이번 예제에서는 inner함수를 일급객체의 특징(변수에 담을 수 있다, 리턴 값으로 사용할 수 있다, 인자로 활용할 수 있다)을 이용하여 함수를 outer의 리턴 값으로 사용하였고 outer함수가 실행시 inner함수는 변수 fn에 담길것이다. 이때 변수 fn는 inner함수가 선언된 환경과 자신의 scope을 그대로 가지고 있다. 그래서 함수외부에서도 함수 구현 방식에 따라 외부에서도 내부 함수를 중첩하여 실행하거나 값으로 활용이 가능하다

결론


많은 개발자들이 클로저를 사용해왔을것이다. 하지만 정확하게 이해하는것은 쉽지 않을것이다. 자바스크립트 동작, 호이스팅, 실행콘텍스트, 실행콘텍스트 스택, scope chain등을 이해해야 위 내용들이 하나둘 이해가 될듯싶다. 즉 사전적으로 이해야할 용어와 특징을 이해해야 클로저에대해서 이해했다라고 생각된다. 하지만 이 글에서 조금이라도 클로저에 이해를 돕기를 기원할 뿐이다.

참조


https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures