IT & 웹개발

JavaScript 클로저(Closure) 완벽 가이드

kkwako 2025. 4. 2. 19:45

1. 개요

클로저(Closure)는 JavaScript의 핵심 개념 중 하나로, 함수가 자신이 선언된 환경(Lexical Environment)의 변수에 접근할 수 있는 기능을 의미한다. 이를 통해 캡슐화(Encapsulation), 데이터 유지, 함수형 프로그래밍 등의 다양한 활용이 가능하다.

JavaScript 개발자라면 클로저를 깊이 이해해야 메모리 관리, 콜백 함수, 이벤트 핸들링, 비동기 처리 등에서 효과적으로 활용할 수 있다. 이 글에서는 클로저의 개념을 쉽게 설명하고, 실무에서 활용할 수 있는 다양한 예제를 소개한다.

 

JavaScript 클로저(Closure) 완벽 가이드

 


2. 클로저의 개념과 원리

(1) 클로저란?

클로저는 함수가 생성될 때, 자신이 선언된 렉시컬 스코프(Lexical Scope)에 접근할 수 있는 특성을 의미한다.

javascript
복사편집
function outer() { let message = "Hello, Closure!"; function inner() { console.log(message); // outer()의 변수 접근 가능 } return inner; } const myClosure = outer(); myClosure(); // "Hello, Closure!"

클로저의 특징

  • 내부 함수 inner는 외부 함수 outer의 변수 message를 기억하고 있다.
  • outer 함수가 실행이 끝나도, inner 함수는 message 변수에 접근할 수 있다.
  • 클로저는 **변수를 보호(캡슐화)**하고, 특정 데이터의 **유지(메모리에 저장)**가 가능하다.

(2) 클로저의 동작 원리 (Lexical Scope & Execution Context)

1) 렉시컬 스코프(Lexical Scope)

JavaScript에서는 함수가 선언된 위치(렉시컬 환경)에 따라 스코프가 결정된다.

javascript
복사편집
function outer() { let a = 10; function inner() { console.log(a); // outer()의 변수에 접근 가능 } return inner; } const fn = outer(); fn(); // 10 (inner 함수가 outer의 스코프를 기억)

함수 inner는 자신이 생성된 환경(outer 함수의 스코프)을 기억하므로, a 변수에 접근할 수 있다.

2) 실행 컨텍스트(Execution Context)와 클로저

클로저는 외부 함수의 실행이 끝나도 내부 함수가 변수를 유지할 수 있는 이유와 관련이 있다.

javascript
복사편집
function counter() { let count = 0; // counter()의 실행이 끝나도 유지됨 return function () { count++; console.log(count); }; } const increment = counter(); increment(); // 1 increment(); // 2 increment(); // 3

왜 count 값이 유지될까?

  • counter()가 실행되면 count 변수가 생성된다.
  • counter() 실행이 끝나도 내부 함수가 count를 참조하고 있어, count가 사라지지 않는다.
  • increment() 실행 시마다 count가 유지되면서 증가한다.

3. 클로저의 활용 예제

(1) 데이터 은닉(Encapsulation) – 프라이빗 변수 구현

클로저를 활용하면 객체의 private 변수처럼, 외부에서 직접 접근할 수 없는 변수를 만들 수 있다.

javascript
복사편집
function createBankAccount(initialBalance) { let balance = initialBalance; // private 변수 return { deposit(amount) { balance += amount; console.log(`입금 완료: ${amount}원, 현재 잔액: ${balance}원`); }, withdraw(amount) { if (amount > balance) { console.log("잔액이 부족합니다."); return; } balance -= amount; console.log(`출금 완료: ${amount}원, 현재 잔액: ${balance}원`); }, getBalance() { console.log(`현재 잔액: ${balance}원`); }, }; } const myAccount = createBankAccount(10000); myAccount.deposit(5000); // 입금 완료: 5000원, 현재 잔액: 15000원 myAccount.withdraw(3000); // 출금 완료: 3000원, 현재 잔액: 12000원 myAccount.getBalance(); // 현재 잔액: 12000원

클로저를 활용한 데이터 은닉의 장점

  • balance 변수는 외부에서 직접 접근할 수 없어 보안성이 향상된다.
  • deposit, withdraw, getBalance 메서드를 통해서만 balance 값을 변경 가능하다.

(2) 반복문과 클로저 – setTimeout 활용

클로저를 활용하면 반복문에서 setTimeout이 올바르게 작동하도록 설정 가능하다.

javascript
복사편집
for (var i = 1; i <= 3; i++) { setTimeout(function () { console.log(i); // 4, 4, 4 }, i * 1000); }

이슈: setTimeout 내부의 함수는 i의 최종 값을 참조하므로 4가 세 번 출력됨.
해결 방법 (클로저 사용)

javascript
복사편집
for (var i = 1; i <= 3; i++) { (function (j) { setTimeout(function () { console.log(j); // 1, 2, 3 }, j * 1000); })(i); }

즉, 즉시 실행 함수(IIFE)를 사용하면 각각의 i 값이 클로저에 저장되어 올바른 값이 출력된다.


(3) 이벤트 핸들러에서 클로저 사용

javascript
복사편집
function createButton(label) { const button = document.createElement("button"); button.textContent = label; button.addEventListener("click", function () { console.log(`${label} 버튼 클릭됨`); }); document.body.appendChild(button); } createButton("확인"); createButton("취소");

클로저 덕분에 각 버튼이 클릭될 때 해당 label 값을 유지할 수 있다.


4. 클로저의 단점과 주의할 점

메모리 누수 발생 가능

  • 클로저는 불필요한 변수까지 메모리에 유지할 수 있어, 메모리 누수를 초래할 수 있다.
  • 필요하지 않은 클로저는 null로 설정하여 해제해야 한다.
javascript
복사편집
function createClosure() { let largeArray = new Array(1000000).fill("data"); return function () { console.log(largeArray.length); }; } const closureFn = createClosure(); closureFn(); // 메모리 점유 발생 // 필요 없을 때 명시적으로 해제 closureFn = null;

클로저의 남용을 피하기

  • 무조건 클로저를 사용하기보다는, 단순한 변수나 객체를 활용하는 것이 더 나을 수 있다.
  • 불필요한 클로저 사용은 성능을 저하시킬 수 있다.

5. 결론

JavaScript에서 클로저는 데이터 유지, 은닉, 이벤트 핸들링, 비동기 처리 등에서 매우 유용하게 사용된다.

클로저의 핵심 정리

  • 내부 함수가 외부 함수의 변수를 기억하는 개념
  • 데이터 은닉, 메모리 관리, 반복문과 비동기 처리에서 활용 가능
  • 메모리 누수 방지를 위해 필요 없는 클로저는 해제할 것

클로저를 잘 활용하면 코드를 더욱 효율적이고 안전하게 작성할 수 있다.