JavaScript의 클로저(Closure)

JavaScript에서 클로저(Closure)는 함수와 함수가 선언된 렉시컬 환경(Lexical Environment)의 조합을 의미합니다. 클로저는 함수가 생성될 때의 외부 변수의 상태를 기억하고, 그 함수가 호출될 때마다 그 외부 변수를 참조할 수 있게 해줍니다. 이번 글에서는 클로저의 개념을 설명하고, 다양한 활용 예제를 소개하겠습니다.

클로저의 개념

클로저란 무엇인가?

클로저는 함수와 그 함수가 선언된 환경의 조합입니다. 클로저를 이용하면 함수가 생성될 때의 외부 변수의 상태를 기억하고, 그 변수를 참조하거나 수정할 수 있습니다.

function outerFunction() {
  let outerVariable = 'I am outside!';

  function innerFunction() {
    console.log(outerVariable);
  }

  return innerFunction;
}

const closure = outerFunction();
closure(); // 'I am outside!'

위 예제에서 innerFunctionouterVariable을 참조할 수 있는 클로저를 형성합니다. outerFunction이 반환된 후에도 innerFunction은 여전히 outerVariable에 접근할 수 있습니다.

클로저의 활용 예제

데이터 은닉

클로저는 데이터 은닉(data encapsulation)을 위해 자주 사용됩니다. 이를 통해 객체의 상태를 외부에서 직접 접근할 수 없도록 보호할 수 있습니다.

function createCounter() {
  let count = 0;

  return {
    increment: function() {
      count++;
      return count;
    },
    decrement: function() {
      count--;
      return count;
    },
    getCount: function() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getCount());  // 1

이 예제에서 count 변수는 클로저를 통해 캡슐화되어 외부에서 직접 접근할 수 없습니다. increment, decrement, getCount 메서드만을 통해 count 변수의 값을 읽거나 수정할 수 있습니다.

함수 팩토리

클로저는 함수 팩토리를 생성하는 데 유용하게 사용됩니다. 함수 팩토리는 다른 함수를 생성하고 반환하는 함수입니다.

function createGreeting(message) {
  return function(name) {
    return `${message}, ${name}!`;
  };
}

const sayHello = createGreeting('Hello');
const sayHi = createGreeting('Hi');

console.log(sayHello('Alice')); // 'Hello, Alice!'
console.log(sayHi('Bob')); // 'Hi, Bob!'

이 예제에서 createGreeting 함수는 클로저를 사용하여 message 변수를 기억하고, 생성된 함수에서 이를 사용할 수 있게 합니다.

부분 적용 함수

클로저를 이용하여 부분 적용 함수(partially applied function)를 만들 수 있습니다. 부분 적용 함수는 일부 인자를 고정한 새 함수를 생성합니다.

function multiply(a, b) {
  return a * b;
}

function createMultiplier(multiplier) {
  return function(value) {
    return multiply(multiplier, value);
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

이 예제에서 createMultiplier 함수는 클로저를 사용하여 multiplier 값을 기억하고, 이를 사용하는 부분 적용 함수를 생성합니다.

이벤트 핸들러와 클로저

클로저는 이벤트 핸들러에서도 자주 사용됩니다. 클로저를 이용하면 이벤트가 발생할 때 필요한 데이터를 유지할 수 있습니다.

function setupButton() {
  let clickCount = 0;

  document.getElementById('myButton').addEventListener('click', function() {
    clickCount++;
    console.log(`Button clicked ${clickCount} times`);
  });
}

setupButton();

이 예제에서 이벤트 핸들러 함수는 클로저를 통해 clickCount 변수를 기억하고, 버튼이 클릭될 때마다 이 값을 증가시키고 출력합니다.

클로저의 주의점

클로저는 매우 유용하지만, 잘못 사용하면 메모리 누수(memory leak)를 초래할 수 있습니다. 이는 클로저가 참조하는 외부 변수가 계속 메모리에 남아 있기 때문입니다. 따라서 클로저를 사용할 때는 필요하지 않은 외부 변수 참조를 피하고, 필요할 때 클로저를 해제하는 것이 좋습니다.

결론

JavaScript의 클로저는 함수와 함수가 선언된 렉시컬 환경의 조합으로, 함수가 생성될 때의 외부 변수의 상태를 기억하고 참조할 수 있게 해줍니다. 클로저는 데이터 은닉, 함수 팩토리, 부분 적용 함수, 이벤트 핸들러 등 다양한 상황에서 유용하게 사용됩니다. 클로저를 잘 이해하고 활용하면, 더 깔끔하고 효율적인 코드를 작성할 수 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다