[ JavaScript ] Closure 클로저란?

2025. 3. 25. 23:01개발자 블로깅/오늘의 TIL

📝개요

분명 들어봤던 단어! 너는 왜 항상 내 기억에서 사라지는거니..  기술 면접의 시작으로 절대 잊을 수 없는 단어가 되어버린  "클로저", 다시 이해해보기

클로저는 초반에는 쉽지 않아도, 한 번 정확하게 이해하면 각종 상황에서 상태 저장, 정보 은닉, 비동기 처리 등에 유용하게 쓰이면 강력한 무기입니다.

📚 목차

1. 클로저란?
2. 클로저가 왜 필요할까?
3. 클로저 언제 사용하나요?
4. 실전 예제
5. 클로저와 관련된 주의점
6. 정보 정리 & 마무리
7. (부모) 클로저 한눈에 보기 : Venn Diagram

 

✅ 1. 클로저란?  closure 폐쇄

클로저는 함수와 그 함수가 선언되었을 때의 렉시컬 환경과의 조합이다.   

더보기


렉시컬 환경이란?

실행 중인 코드가 사용하는 변수, 상수, 함수 정보를 저장한는 내부 구조 (메모리 구조)
  - 자바스크립트 엔진 내부에서 만들어지는 객체(구조)
  - 함수가 생성되거나 실행될 때 자동으로 생성

렉시컬 스코프란?

(함수가 실행되는 위치가 아니라) 함수가 어디에 정의되었는지에 따라 상위 스코프가 결정되는 규칙

자바스크립트에서 쉽게 말하면?  함수가 생성될 때의 외부 변수 환경(스코프)을 기억하는 함수 를 말합니다.

더 쉽게 설명하면?  내부 함수가 외부 함수의 변수에 접근할 수 있는 상태!
즉, 함수가 끝났다고 외부 변수들이 사라지지 않고, 내부 함수가 계속 기억하고 접근할 수 있다는 것 입니다.

 

클로저 예제 코드

function outer() {
  let secret = "🔐 비밀";

  function inner() {
    console.log(secret); // 외부 변수에 접근!
  }

  return inner;
}

const myFunc = outer(); // outer 실행 → inner 함수 리턴
myFunc(); // 🔐 비밀 (클로저로 외부 변수 접근)
  • outer() 실행 → secret은 outer의 지역 변수
  • inner()는 secret에 접근 가능
  • outer()는 종료됐지만, inner()가 여전히 secret을 기억하고 있음 → 이게 클로저

 

✅ 2. 클로저가 왜 중요한가?

1. 상태 유지 (데이터 숨기기)

function counter() {
  let count = 0;
  return function () {
    count++;
    console.log(count);
  };
}

const clickCounter = counter();
clickCounter(); // 1
clickCounter(); // 2
clickCounter(); // 3
  • count는 외부에서 접근 불가
  • clickCounter만 내부에서 조작 가능 → 은닉 + 상태 유지

2. 비동기 처리와 반복문에서 유용

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 출력: 3 3 3

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 출력: 0 1 2 (클로저로 각각의 i 기억)

 

✅ 3. 클로저 언제 사용하나요?

  • 상태 저장 (counter 만들기)
  • private 변수 숨기기
  • 비동기/반복문 변수 유지

 

✅ 4. 실전 예제

1. 카운터 만들기 (상태 보존 + 은닉화)

function createCounter() {
  let count = 0;

  return {
    increment() {
      count++;
      console.log(count);
    },
    decrement() {
      count--;
      console.log(count);
    },
    getCount() {
      return count;
    }
  };
}

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

특징

  • count는 외부에서 직접 접근 불가 (private)
  • 함수들이 count에 접근하는 클로저 구조
  • 상태 저장 + 은닉화 + 캡슐화 구현

 

2. 함수 팩토리 (값을 기억하는 함수 생성기)

function makeMultiplier(multiplier) {
  return function (value) {
    return value * multiplier;
  };
}

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

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

특징

 

  • multiplier는 외부 함수의 매개변수지만,
  • 내부 함수가 그 값을 계속 기억함 → 클로저
  • 다양한 설정값을 함수로 캡슐화해서 재사용 가능

이런 구조 덕분에 상태를 은닉하거나, 콜백 함수에서도 안전하게 값을 유지하는 로직을 만들 수 있어요.


✅ 5. 클로저와 관련된 주의점

클로저는 매우 강력한 기능이지만, 잘못 사용하면 오히려 메모리 누수, 예상치 못한 동작, 디버깅 어려움 등의 문제를 유발할 수 있습니다. 다음은 클로저 사용 시 주의해야 할 대표적인 사항들입니다.

🔸 1. 불필요한 클로저로 인한 메모리 누수

클로저는 외부 변수를 참조하기 때문에 해당 변수가 GC(Garbage Collection) 대상이 되지 않아 메모리에 오래 남아 있을 수 있습니다.

function createBigClosure() {
  const bigData = new Array(1000000).fill('📦');
  return function () {
    console.log(bigData[0]);
  };
}

→ 위 코드에서 클로저가 bigData에 접근하고 있어, 해당 클로저가 참조되는 한 bigData도 메모리에 유지됩니다.

🔸 2. 반복문 안에서의 클로저 사용 주의

var를 사용할 경우 모든 클로저가 하나의 동일한 변수를 참조하게 되어 예상치 못한 결과가 발생할 수 있어요.

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
} // 출력: 3, 3, 3

 해결 방법은 let을 사용하거나, IIFE(즉시실행함수)를 이용해 스코프를 분리하는 것입니다.

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

🔸 3. 디버깅이 어려울 수 있음

클로저는 숨겨진 상태를 내부적으로 유지하기 때문에, 디버깅 시에 어떤 값이 유지되고 있는지 추적이 어려울 수 있습니다. → 따라서 가독성이 중요한 코드에서는 불필요한 클로저 남용을 피하고 명확하게 함수 범위를 설계하는 것이 중요합니다.


✅ 6. 정보 정리 & 마무리

클로저는 JavaScript의 핵심 개념 중 하나로, 함수가 자신이 선언된 렉시컬 환경(Lexical Environment)을 기억하여 외부 스코프의 변수에 접근할 수 있는 구조를 말합니다

클로저의 핵심 포인트
- 함수가 만들어질 때의 스코프를 기억함
- 외부 함수가 종료되어도 변수에 접근 가능
- 상태 저장, 은닉화, 비동기 로직 등에 유용

주의해야 할 점
- 메모리 누수 주의
- 반복문 내 사용 시 let 또는
IIFE 스코프 분리
- 디버깅 어려움 -> 가독성 고려

=> 클로저를 이해하면 실무에서 다양한 기능을 효율적이고 안전하게 구현할 수 있음


✅ 7. 클로저 한눈에 보기 : Venn Diagram

closure

 

마무리

클로저는 반환된 내부 함수가 자신이 선언됐을 때의 환경(Lexical environment 렉시컬 환경)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말합니다.

클로저는 단순한 이론이 아니라, 실무에서도 굉장히 자주 쓰이는 패턴입니다.
상태 저장, 은닉화, 함수 팩토리 구성 등 다양한 방식으로 활용되니, 꼭 자신의 코드에 적용해보세요!