본문 바로가기
프론트엔드/JS 공부

클로저

by 학습하는 청년 2025. 6. 4.

최종 수정: 2025.06.04

클로저(Closure)

함수와 그 함수가 선언된 렉시컬 환경의 조합이다. 즉, 내부 함수가 외부 함수의 변수에 접근할 수 있는 개념으로, 외부 함수가 실행을 마친 후에도 외부 함수의 변수를 참조할 수 있다.

 

function outerFunction(x) {
  // 외부 함수의 변수
  const outerVariable = x;
  
  // 내부 함수
  function innerFunction(y) {
    // 내부 함수에서 외부 함수의 변수에 접근
    console.log(outerVariable + y);
  }
  
  return innerFunction;
}

const myClosure = outerFunction(10);
myClosure(5); // 15 출력

// outerFunction의 실행이 끝났지만
// innerFunction은 여전히 outerVariable(10)에 접근 가능

 

클로저를 이해하기 위해서는 먼저 렉시컬 스코프(Lexical Scope)에 대해 알아야 한다.

const globalVar = "전역";

function outerFunction() {
  const outerVar = "외부";
  
  function middleFunction() {
    const middleVar = "중간";
    
    function innerFunction() {
      const innerVar = "내부";
      
      // 모든 상위 스코프의 변수에 접근 가능
      console.log(innerVar);   // "내부"
      console.log(middleVar);  // "중간"
      console.log(outerVar);   // "외부"
      console.log(globalVar);  // "전역"
    }
    
    return innerFunction;
  }
  
  return middleFunction;
}

const closure = outerFunction()();
closure(); // 모든 변수에 접근 가능

■ 클로저의 동작 원리

1. 실행 컨텍스트와 스코프 체인

function createCounter() {
  let count = 0; // 외부 함수의 지역 변수
  
  return function() {
    count++; // 클로저로 외부 변수에 접근
    return count;
  };
}

const counter1 = createCounter();
const counter2 = createCounter();

console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1 (독립적인 클로저)
console.log(counter1()); // 3

 

2. 메모리에서 일어나는 일

// 1. createCounter 호출
function createCounter() {
  let count = 0; // 이 변수는 일반적으로 함수 종료 시 사라져야 함
  
  // 2. 익명 함수 반환 (이 함수가 count를 참조)
  return function() {
    count++;
    return count;
  };
} // 3. createCounter 실행 종료, 하지만 count는 사라지지 않음!

// 4. 반환된 함수가 count를 참조하고 있어서 가비지 컬렉션되지 않음
const counter = createCounter();

■ 활용

1. 데이터 은닉

function createBankAccount(initialBalance) {
  let balance = initialBalance; // private 변수
  
  return {
    // public 메서드들
    deposit: function(amount) {
      if (amount > 0) {
        balance += amount;
        return balance;
      }
      throw new Error("입금액은 0보다 커야 합니다.");
    },
    
    withdraw: function(amount) {
      if (amount > 0 && amount <= balance) {
        balance -= amount;
        return balance;
      }
      throw new Error("출금할 수 없습니다.");
    },
    
    getBalance: function() {
      return balance;
    }
    
    // balance에 직접 접근할 방법이 없음
  };
}

const account = createBankAccount(1000);
console.log(account.getBalance()); // 1000
account.deposit(500); // 1500
account.withdraw(200); // 1300

// balance에 직접 접근 불가
console.log(account.balance); // undefined
account.balance = 999999; // 외부에서 조작 불가
console.log(account.getBalance()); // 여전히 1300

 

2. 함수 팩토리

function createMultiplier(multiplier) {
  return function(number) {
    return number * multiplier;
  };
}

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

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

// 각 함수는 독립적인 multiplier 값을 가짐

'프론트엔드 > JS 공부' 카테고리의 다른 글

자바스크립트 this  (0) 2025.06.04
프로토타입 체인  (0) 2025.06.04
동기 / 비동기  (0) 2025.05.28
스토리지(Storage)  (0) 2024.05.01
쿠키(Cookie)  (0) 2024.05.01

댓글