클로저(Closure)

ss_kim·2025년 1월 1일

클로저(Closure)

클로저란?
MDN에서는 "A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)."라고 소개한다.
"클로저는 함수의 주변 상태(어휘 환경)에 대한 참조와 함께 묶인 함수의 조합이다."

어떤 함수 A 내부에서 선언한 변수를 참조하는 내부 함수 B를 외부로 전달할 경우, A의 실행 컨텍스트가 종료된 후에도 변수가 사라지지 않는 현상
<< 코어 자바스크립트 >>

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

var outer2 = outer();
outer2(); // 2
outer2(); // 3

outer 함수의 반환값으로 inner 함수의 실행 결과가 아닌 함수 자체를 반환한다. 그러면 outer 함수의 실행 컨텍스트가 종료될 때 outer2 변수는 inner 함수를 참조하게 되고, outer2를 호출하면 inner 함수가 실행된다.

outer2 함수를 실행했을 때, 왜 같은 값을 반환하지 않고 a의 값이 늘어나는 것일까?

outer2 함수를 실행했을 때 흐름을 보면, inner 함수의 environmentRecord에는 a 변수가 없기에 스코프 체이닝에 따라 outer에서 선언한 변수 a에 접근한다. outer 함수는 이미 종료되었지만 inner 함수가 a를 참조하고 있기 때문에 가비지 컬렉터의 수집 대상이 되지 않아 변수 a는 존재할 수 있다.

즉, 어떤 함수 내부에서 선언한 변수가 외부에서 호출될 가능성(참조)이 있다면 가비지 컬렉터의 수집 대상이 되지 않고 함수가 종료된 이후에도 남아 있는 것이다.

참고
실행 컨텍스트
https://velog.io/@ss_kim/execution-context


메모리 관리

클로저를 사용하여 어떤 값의 참조 카운트가 1 이상이라면 메모리에 남아 있게 된다. 그렇기에 필요성이 사라진 시점에서 메모리를 소모하지 않도록 참조 카운트를 0으로 만들어 메모리를 관리해주어야 한다.

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

var outer2 = outer();
outer2();
outer2();
outer2 = null; // inner 함수 참조를 끊음

클로저 활용

클로저를 어떤 상황에서 사용할 수 있을까?

1. 정보 은닉(캡슐화)

outer 함수는 외부로부터 격리된 공간이다. 외부에서 outer 함수를 실행하는 것은 가능하지만 outer 함수의 내부에 접근하는 것은 불가능하다. 위 예시에서 외부에서 변수 a의 값을 재할당하는 것을 불가능하다.

2. 모듈화

클로저를 각각의 변수에 할당하면 함수를 독립적인 부품 형태로 분리할 수 있다.

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

var outer2 = outer();
var outer3 = outer();

outer2(); // 2
outer2(); // 3

outer3(); // 2
outer3(); // 3

이를 활용해 데이터와 메서드를 묶을 수 있다.

const counter = () => {
  let value = 0;
  
  return {
    increase: () => {
      value = value + 1
    },
    decrease: () => {
      value = value - 1
    },
}

const counter1 = counter();
counter1.increase();  // 1
counter1.increase();  // 2
counter1.decrease();  // 1

const counter2 = counter();
counter2.decrease();  // -1
counter2.decrease();  // -2
counter2.increase();  // -1

3. React의 Hooks

함수형 컴포넌트는 렌더링될 때마다 함수를 호출한다. 그렇기에 리렌더링될 때 이전 상태를 기억하고 있어야 하는데, useState의 개념을 클로저를 이용해 비슷하게 표현하면 아래와 같다.

let state
function useState(initialValue){
  if(state === undefined){
    state = initialValue
  }
  const setState = (newValue) => {
    state = newValue
  }
  return [state, setState]
}

최초 렌더링 시 initialValuestate에 할당해서 반환하고, 상태 업데이트 함수 setState를 호출하면 newValuestate에 재할당 후 반환한다.


참고
https://velog.io/@radin/js-closure
https://yeoulcoding.tistory.com/149#recentEntries

profile
프론트엔드 개발자

0개의 댓글