JavaScript 클로저 완전 정복 – 함수가 기억하는 비밀을 파헤쳐 보자 (58자)
# 서론 – “함수가 왜 기억을 할까?”
프로그래밍을 처음 시작한 사람들은 함수를 “입력값을 받아 결과를 반환하는 작은 기계”라고만 생각합니다. 그런데 JavaScript에서는 함수가 자신이 선언될 때의 환경을 기억하고, 그 기억을 바탕으로 동작하는 클로저(Closure) 라는 특별한 메커니즘이 존재합니다.
이 글에서는 Chapter 4: Functions Remember 라는 주제에 맞춰, JavaScript 클로저가 무엇인지, 왜 필요한지, 그리고 실제 예제로 어떻게 활용할 수 있는지를 초보자도 쉽게 이해할 수 있도록 단계별로 설명합니다.
SEO 키워드 :
JavaScript 클로저,함수 기억,스코프
# 1️⃣ 스코프와 클로저 – 기본 개념 잡기
| 용어 | 의미 |
|------|------|
| 스코프(Scope) | 변수가 접근 가능한 영역. 전역·함수·블록 스코프 등 |
| 렉시컬 스코프(Lexical Scope) | 함수는 정의된 위치의 스코프를 기억한다. |
| 클로저(Closure) | 함수가 외부 스코프에 있는 변수들을 함께 기억하고, 나중에 실행될 때도 그 변수에 접근할 수 있는 현상 |
간단한 예시
js function makeCounter() { let count = 0; // 외부 스코프 변수 return function() { // 내부 함수가 외부 변수에 접근 count += 1; return count; }; } const counter = makeCounter(); // 클로저가 생성 console.log(counter()); // 1 console.log(counter()); // 2
makeCounter가 실행된 뒤에도count변수는 사라지지 않고, 반환된 함수가 그 값을 기억합니다. 이것이 바로 함수가 기억하는 메커니즘, 즉 클로저입니다.
# 2️⃣ 클로저가 필요한 이유 – 데이터 은닉과 상태 유지
# 2-1. 비공개 변수 만들기
클로저를 이용하면 외부에서 직접 접근할 수 없는 변수를 만들 수 있습니다. 이는 객체 지향 언어의 private 속성과 비슷한 효과를 줍니다.
js
function createSecret() {
let secret = '🔒 비밀';
return {
reveal() { return secret; },
change(newVal) { secret = newVal; }
};
}
const box = createSecret();
console.log(box.reveal()); // 🔒 비밀
box.change('새 비밀');
console.log(box.reveal()); // 새 비밀
# 2-2. 비동기 로직에서 상태 유지
버튼 클릭, 타이머, API 콜 등 비동기 상황에서도 클로저는 변수를 살아 있게 합니다.
js
function startTimer() {
let seconds = 0;
setInterval(() => {
seconds++;
console.log(`경과 시간: ${seconds}s`);
}, 1000);
}
startTimer(); // 매초 증가하는 seconds를 클로저가 기억
# 3️⃣ 클로저 사용 시 주의할 점 – 메모리 누수 방지
클로저가 불필요하게 큰 객체를 참조하면 가비지 컬렉터가 메모리를 회수하지 못해 메모리 누수가 발생할 수 있습니다.
| 방지 방법 | 설명 |
|----------|------|
| 필요 없는 레퍼런스 제거 | 사용이 끝난 변수는 null 혹은 undefined 로 초기화 |
| 스코프 최소화 | 클로저를 만들 때 가능한 한 작은 범위 안에서 선언 |
| WeakMap 활용 | 객체를 키로 사용할 때 메모리 회수를 돕는다 |
js
function heavyTask() {
const largeObj = { /* 수백 MB 데이터 */ };
return function() {
// 작업 수행 후 레퍼런스 해제
console.log('작업 완료');
// largeObj = null; // ← 이렇게 해주면 메모리 회수 가능
};
}
# 4️⃣ 실전 활용 사례 – 모듈 패턴 & 커링
# 4‑1. 모듈 패턴 (Module Pattern)
클로저를 이용해 비공개 데이터와 공개 API를 한 파일에 깔끔하게 구성할 수 있습니다.
js
const TodoModule = (function() {
let todos = []; // 비공개
return {
add(item) { todos.push(item); },
list() { console.log(todos); }
};
})();
TodoModule.add('JavaScript 공부');
TodoModule.list(); // ['JavaScript 공부']
# 4‑2. 커링 (Currying)
함수 호출 시 일부 인자를 미리 채워 두고, 나중에 남은 인자를 받아 실행하는 기법도 클로저 덕분에 가능합니다.
js
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5)); // 10
# 5️⃣ 클로저 디버깅 팁 – 초보자를 위한 체크리스트
변수 선언 위치 확인 – 클로저가 참조하는 변수가 어디서 선언됐는지 파악한다.
콘솔 로그 활용 – 클로저 내부와 외부에서 변수 값을 출력해 흐름을 눈으로 확인한다.
메모리 탭 확인 – 브라우저 개발자 도구 → Memory → Heap Snapshot을 통해 누수 여부를 검사한다.
테스트 코드 작성 – Jest·Mocha 등으로 클로저가 기대한 대로 동작하는지 자동 검증한다.
js
test('counter increments correctly', () => {
const counter = makeCounter();
expect(counter()).toBe(1);
expect(counter()).toBe(2);
});
# 결론 – 오늘부터 클로저를 활용해 보세요!
클로저는 함수가 외부 스코프를 기억하고, 이를 통해 데이터 은닉, 상태 유지, 코드 재사용을 가능하게 하는 강력한 도구입니다.
스코프와 렉시컬 환경을 정확히 이해하고, 우선순위와 메모리 관리를 염두에 두면 안전하게 사용할 수 있습니다.
실습 과제
위 예시
makeCounter를 직접 구현하고, 콘솔에 5번 호출해 보세요.
TodoModule을 변형해 완료된 아이템만 필터링하는 메서드를 추가해 보세요.
클로저를 제대로 활용하면 코드가 더 깔끔해지고, 버그 발생 가능성이 낮아집니다. 오늘 배운 내용을 바탕으로 직접 코드를 짜보고, 질문이 있으면 댓글에 남겨 주세요. 함께 성장하는 개발 커뮤니티가 되길 바랍니다!
SEO 키워드 : JavaScript 클로저, 함수 기억, 스코프
참고 원문 : The Secret Life of JavaScript: Understanding Closures
Comments
Join the discussionNo comments yet. Be the first to comment.