JavaScript / / 2024. 1. 11. 20:02

[자바스크립트 스터디] 클로저(Closure) 그리고 작동 원리

 

function outer() {
  var a = 1;
  
  var inner = function () {
    return a++;
  }

  return inner;
}

var increment = outer();

console.log(increment());
console.log(increment());
console.log(increment());

 

클로저를 살펴보기 전에 위의 코드를 살펴보겠다. 단순한 변수 a의 값을 호출할 때 마다 inner의 함수가 scope체인 상의 변수 a를 가져와서 하나씩 늘린다. 밑의 결과를 보면은 납득이 될 것이다.

 

 

console.dir(increment); 를 찍어보면 위의 사진처럼 Closure라고 되어있고 a = 4라고 되어있다. 이것이 바로 클로저이다. 클로저를 MDN에서 찾아보면 다음과 같이 뜬다.

 

클로저는 주변 상태(어휘적 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합입니다. 즉, 클로저는 내부 함수에서 외부 함수의 범위에 대한 접근을 제공합니다. JavaScript에서 클로저는 함수 생성 시 함수가 생성될 때마다 생성됩니다.

 

위의 코드를 한 번 Environment관점에서 한 번 보겠다. 그러면 아래와 같이 나온다.

 

 

근데 여기서 궁금한 점이 나온다. Execution Context상에서 사라지는 Outer 환경 정보에 a가 있을 텐데 어떻게 inner에서 이 정보를 참조해서 유지하고 있는 것일까? 이를 일단 코드 라인 별로 어떻게 되는지 살펴본 뒤, Execution Context가 콜 스택에 쌓이는 순으로 확인해 보겠다.

 

function outer() {
  var a = 1;
  
  var inner = function () {
    return a++;
  }

  return inner;
}

var increment = outer();

console.log(increment());

 

  1. outer라는 변수와 increment 라는 변수를 전역에 생성하고 Global Execution Context에 모아둔다.
  2. outer에 함수를 할당하고, increment에는 outer()를 호출한 다음 그 리턴 값인 함수 자체인 inner를 increment변수에 할당한다. 이 때 outer라는 함수를 호출했으므로 새로운 Execution Context가 생성된다. 이 때 변수 a와 inner의 정보를 수집한다. 이때 inner의 선언 당시에 환경을 파악하여 스코프 체인을 만든다. 
  3. outer의 실행 컨텍스트가 삭제된다. 하지만 변수 a가 inner의 스코프 체인 상에 있으므로 GC의 대상이 되지 않는다. 그리고 increment인 inner함수가 실행된다. 
  4. 이 때 inner함수의 a값을 찾아 스코프 체인 상을 탐색하고, 여기서 a = 1을 찾아 실행한다.
  5. 그 후 a = 2가 되고, console.log(increment())를 실행한 후 Execution Context가 사라진다.

 

그렇다면 이러한 Closure를 왜 사용할까? 여러 이유가 있지만, 크게 두가지로 정리가 가능할 것 같다. 개인적으로 "폐쇄성"이 키워드인 것 같다.

 

  1. 폐쇄성을 이용한 정보/상태 보존
  2. 폐쇄성을 이용해 정보의 접근을 제한한 캡슐화

1번의 경우 정보/상태 보존이라는 점이 크다. 위에서 말한 것처럼, 외부 함수의 정보가 그대로 유지되기 때문에, 정보가 지속적으로 보존된다는 것이다. 2번도 큰 특징이다. 한마디로 inner함수를 통해서만 접근이 가능하다. 즉 다른 함수 - 자바로 예를 들자면 - class에서 private역할을 해준 다는 것이다. 위의 특징을 이용해서 아래와 같이 특정 함수만을 이용해서 정보/상태 - 위의 코드에서는 변수 a - 에만 접근이 가능하도록 코드를 짤 수 있다.

 

var change = function numberChange() {
  var a = 1;

  return {
    plusOne: function () {
      return ++a;
    },
    minusOne: function () {
      return --a;
    },
    getA: function () {
      return a;
    }
  };
}

var example = change();

console.log(example.plusOne());
console.log(example.getA());

 

위의 코드 처럼 짜면, a의 정보를 은닉화 하면서, 접근도 제어할 수 있다.

 

이렇게 클로저를 알아봤는데, 많이 쓰이는 React에서는 과연 어떻게 쓰일까? 바로 Hooks에서 사용한다. 이 개념은 나중에 시간이 되면 알아보겠다.

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유