🖥️개발/🐥JavaScript

[JavaScript] Closure 클로저

주_Y 2025. 7. 13. 22:32

 

JS를 공부하다 보면 클로저는 항상 중요하게 다뤄지기도 하고 단골처럼 어렵다고 느껴지는 개념이라 늘 좀 어렵게 접근 하게 되는 것 같습니다..!
그리고 면접에서도 자주 물어보는 주제이기도 하구요ㅎㅎ

하지만,, 클로저를 약간 감으로는 알아도 말로 설명해보라고 하면 말이 잘 안 나오는 것 같아서 한 번 정리해보고자 합니다!

 

 


1️⃣ 클로저란?

MDN에서의 정의는 이렇습니다

클로저는 함수와 그 함수가 선언된 어휘적 환경의 조합이다.

…?
말이 너무 어렵죠...?


그래서 더 쉽게 말해보면!

“외부 함수보다 오래 살아남는 내부 함수”가 클로저다!

즉, 함수 안에 함수를 만들고 그 내부 함수가 외부 함수의 변수기억해서 나중에 사용할 수 있다면 클로저라고 할 수 있습니다!


2️⃣ 예제로 이해해보기

function outer() {
  let count = 0;

  function inner() {
    count++;
    console.log(count);
  }

  return inner;
}

const myFunc = outer();
myFunc(); // 1
myFunc(); // 2
myFunc(); // 3

이 코드에서 outer()는 호출되자마자 inner 함수를 반환하고 끝납니다. 그런데 myFunc()를 호출할 때마다 count 값이 계속 증가합니다.

 

왜 그럴까요?

outer()는 이미 실행이 끝났는데, 그 안의 count는 계속 살아있죠. 바로 이때 count를 기억하고 있는 inner() 함수가 클로저인 거입니다!


3️⃣ 클로저가 유용한 이유?

✅ 데이터를 보호하면서 상태를 유지할 수 있다!

 

예를 들어, 간단한 카운터 모듈을 만들어서 설명드려보겠습니다!

function createCounter() {
  let count = 0;

  return {
    increase() {
      count++;
      return count;
    },
    decrease() {
      count--;
      return count;
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increase()); // 1
console.log(counter.increase()); // 2
console.log(counter.getCount()); // 2
console.log(counter.decrease()); // 1

 

위 코드에서 count는 외부에서 직접 접근할 수 없습니다.

 

increase, decrease, getCount를 통해서만 값을 변경하거나 읽을 수 있죠. 이런 식으로 클로저를 이용하면 전역 변수 없이도 상태를 안전하게 관리할 수 있고 이런 부분 때문에 많이 사용하게 되는 것 이죠!


4️⃣ 주의 사항

다만 헷갈리지 마셔야할 부분이 있다면..! for문 안에서 클로저를 만들면…

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

위 코드를 실행하면 어떻게 될까요?

답은 3, 3, 3이 출력됩니다.

 

왜냐면 var는 함수 스코프이기 때문에 i는 하나의 값만 계속 참조하게 돼요. 이럴 땐 let을 쓰거나, IIFE(즉시 실행 함수)를 사용해서 클로저를 잘라줘야 합니다!

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 0, 1, 2
  }, 1000);
}

 

 

 

 

✂️ 자르기 예시 -> IIFE (즉시 실행 함수 표현식)

 
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(() => {
      console.log(j);
    }, 1000);
  })(i);
}
 
0
1
2

 

설명

  • (function(j) { ... })(i)는 i의 현재 값을 j에 복사한 후, 바로 실행됩니다.
  • 그 안의 클로저는 더 이상 i가 아닌 j를 기억하므로, 값이 고정됩니다.
  • 즉, 반복마다 새로운 j가 만들어지기 때문에 클로저가 잘렸다고 볼 수 있습니다!

 

 

 

✂️ 자르기 -> let 사용 (블록 스코프)

 
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}
 
0
1
2

 

설명

  • let은 블록 스코프이기 때문에 매 반복마다 새로운 i가 생성됩니다.
  • 즉, 반복마다 클로저가 참조하는 i가 다른 메모리 공간에 존재하는 값이 되는 것이죠.
  • 그래서 클로저가 각기 다른 i를 기억하게 되어 의도한 대로 작동합니다!

 

 

 

 

 


  정리


클로저(Closure) 함수가 자신이 선언된 외부 범위의 변수기억하고 계속 접근할 수 있는 기능
주요 사용처 ✅ 상태 유지 (counter)
✅ 캡슐화
✅ 비동기 처리 중 값 유지
주의할 점 var로 만든 변수는 클로저에서 모두 같은 값을 참조하게 됨 (ES6 let 사용 추천!)
 

 

 

 

 

 

 

 

 

썸네일