클로저(Closure)

2022-03-14 · 2 min read

요약

클로저란 자신이 정의된 렉시컬 환경을 기억하는 함수입니다.

클로저는 상태(state)를 유지하고 이것이 외부 요인에 의해 의도치 않게 변경되지 않도록 안전하게 은닉(information hiding)하기 위해 사용합니다.

클로저

클로저(Closure)란 자신이 정의된 렉시컬 환경을 기억하는 함수입니다.

const text = 'global';

function outer() {
  const text = 'local';
  const inner = function () {
    console.log(text);
  };
  return inner;
}

const inner = outer(); // outer를 호출하면 중첩 함수 inner를 반환
inner(); // 출력: local

위 예제에서 볼 수 있듯이, inner 함수가 호출되는 시점에는 outer 함수의 생명주기가 이미 끝나 실행 컨텍스트 스택에서 제거된 상태임에도 불구하고, outer 함수의 렉시컬 환경에 있는 text 변수를 참조할 수 있습니다. 이는 외부 함수보다 중첩 함수가 더 오래 유지될 경우, 중첩 함수가 생명주기가 끝난 외부 함수의 변수를 참조할 수 있음을 의미하며, 이러한 중첩 함수를 클로저라고 부릅니다.

렉시컬 스코프

함수 객체는 생성되는 시점에 현재 실행 중인 실행 컨텍스트의 렉시컬 환경에 대한 참조를 [[Environment]] 슬롯에 저장합니다. 함수를 호출할 때 생성되는 함수 실행 컨텍스트의 OuterLexicalEnvironmentReference에는 이 함수 객체의 [[Environment]] 슬롯에 저장된 렉시컬 환경의 참조가 할당됩니다. 따라서 함수는 호출된 위치와 상관없이 자신이 정의된 위치에 따라 스코프를 결정하게 되며, 이를 렉시컬 스코프(lexical scope) 라 합니다.

클로저의 활용

클로저는 상태(state)를 유지하고 이것이 외부 요인에 의해 의도치 않게 변경되지 않도록 안전하게 은닉(information hiding)하기 위해 사용됩니다.

예를 들어, 다음과 같은 카운트 기능을 만들었다고 합시다.

let count = 0;

const increase = function () {
  count += 1;
  return count;
};

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3

위 코드는 잘 작동하지만 count 변수가 전역 스코프에 노출되어 있습니다. 따라서 다른 코드에 의해 언제든지 변경이 가능하여 오류가 발생할 위험을 내포하고 있습니다.

클로저를 통해 이를 보완하여 다음과 같이 작성할 수 있습니다.

const increase = (function () {
  let count = 0;

  return function () {
    count += 1;
    return count;
  };
})();

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3

이 코드를 실행하면 런타임에 즉시 실행 함수가 실행되고 increase에 그 결과가 할당됩니다. 이때 increase에 할당되는 것은 즉시 실행 함수의 렉시컬 환경(count 변수를 포함한)을 저장한 함수 객체입니다. 즉시 실행 함수의 생명주기는 이미 종료되었으므로 count 변수에는 오직 즉시 실행 함수가 리턴한 익명 함수만 접근할 수 있게 됩니다.

이를 이용하여 타 언어의 private 접근 제한자를 흉내낼 수도 있습니다.

const createPoint = (x, y) => {
  let posX = x;
  let posY = y;

  const get = () => {
    return [posX, posY];
  };

  const moveX = (value) => {
    posX += value;
  };

  const moveY = (value) => {
    posY += value;
  };

  return {
    get,
    moveX,
    moveY,
  };
};

const point = createPoint(0, 0);

console.log(point.get()); // [ 0, 0 ]
point.moveX(5);
point.moveY(8);
console.log(point.get()); // [ 5, 8 ]

위 예시에서는 posX, posY 변수는 createPoint 함수 내에 은닉시키고 get, moveX, moveY 클로저만 외부로 노출시켰습니다. 이를 통해 createPoint 메서드로 생성된 객체는 posX, posY의 상태를 유지하면서도 직접적인 접근은 방지하여 안전하게 사용할 수 있습니다.

참고 자료

  • 이웅모, 『모던 자바스크립트 Deep Dive』
Copyright 2022-2024.hanse-kimAll right reserved.