JavaScript 클로저 완전 정복: 함수가 기억하는 비밀을 파헤치다 (50-60자)
# 서론 – “함수가 왜 기억을 가지고 있을까?”
프로그래밍을 처음 시작한 초보 개발자라면, 함수는 “입력값을 받아서 결과를 반환한다”는 간단한 개념만 떠올릴 것입니다. 그런데 실제 프로젝트에서 함수가 이전에 만든 변수나 상태를 기억하고 있다는 이야기를 들으면 “그게 어떻게 가능한가?” 하는 의문이 생깁니다. 바로 이때 등장하는 것이 JavaScript 클로저(Closure) 입니다.
클로저는 JavaScript의 함수 스코프와 렉시컬 스코프 개념을 기반으로 동작하는 강력한 메커니즘으로, 코드 재사용성·비동기 처리·데이터 은닉 등 다양한 상황에서 핵심적인 역할을 합니다. 이번 글에서는 Chapter 4: Functions Remember 라는 주제에 맞춰, 클로저가 어떻게 작동하고, 실무에서 어떻게 활용되는지 초보자도 쉽게 이해할 수 있도록 단계별로 풀어보겠습니다.
핵심 키워드:
JavaScript 클로저,함수 스코프,프로그래밍 기초
# 본론
# 1️⃣ 함수 스코프와 렉시컬 스코프 – 기억의 기반을 이해하라
# 1-1. 함수 스코프란?
JavaScript는 함수 단위로 변수를 구분합니다. 함수 내부에서 선언된 변수는 그 함수 안에서만 접근 가능하고, 외부에서는 접근할 수 없습니다. 이를 함수 스코프라고 합니다.
js
function outer() {
let message = 'Hello, world!';
function inner() {
console.log(message); // outer의 변수에 접근 가능
}
inner();
}
outer(); // 'Hello, world!'
위 예시에서 inner 함수는 outer 함수의 message 변수에 접근합니다. 이때 inner가 message를 기억하고 있다는 것이 바로 클로저의 시작점입니다.
# 1-2. 렉시컬 스코프(Lexical Scope)
JavaScript는 코드가 작성된 위치에 따라 스코프 체인을 결정합니다. 즉, 함수가 어디서 정의되었는지가 실행 시점에 중요한 역할을 합니다. 이를 렉시컬 스코프라고 부릅니다.
정의 시점에 스코프가 고정 → 실행 시점에 동적으로 변하지 않음
외부 함수의 변수에 접근할 수 있는 이유는 정적 스코프 체인 덕분
정리: 함수 스코프와 렉시컬 스코프가 결합돼, 내부 함수가 외부 변수들을 “기억”하게 되는 구조가 바로 클로저입니다.
# 2️⃣ 클로저란 무엇인가? – 함수가 기억을 저장하는 메커니즘
# 2-1. 클로저 정의
클로저(Closure)는 함수가 선언될 때의 렉시컬 환경을 함수와 함께 저장한 객체입니다. 이 환경에는 함수가 접근할 수 있는 모든 외부 변수와 매개변수가 포함됩니다.
핵심: 함수가 실행이 끝난 뒤에도 그 함수가 참조하고 있던 외부 변수들은 가비지 컬렉터에 의해 바로 사라지지 않고, 클로저에 의해 살아남습니다.
# 2-2. 클로저가 동작하는 흐름
함수 선언 → 렉시컬 환경(스코프 체인) 생성
함수 호출 → 내부 로직 실행
함수 반환 → 반환값이 내부 함수를 포함하면, 그 내부 함수는 외부 변수에 대한 참조를 유지
외부 변수는 메모리 상에 남아 → 이후 호출 시에도 동일한 값 사용 가능
# 2-3. 간단한 예시 – 카운터 만들기
js
function createCounter() {
let count = 0; // 외부 변수
return function() { // 내부 함수 (클로저)
count += 1;
return count;
};
}
const counter = createCounter(); // 클로저가 반환된 상태
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
createCounter 함수가 끝난 뒤에도 count 변수는 메모리에 남아 있습니다. counter 함수가 호출될 때마다 같은 count를 계속해서 기억하고 업데이트합니다.
# 3️⃣ 클로저 활용 사례 – 실전에서 어떻게 쓰일까?
# 3-1. 데이터 은닉 및 프라이버시
클로저를 이용하면 공개/비공개 속성을 구분할 수 있습니다. 객체 지향 언어에서 private 키워드가 없는 JavaScript에서도 클로저를 활용해 비공개 변수를 만들 수 있습니다.
js
function Person(name) {
let _age = 0; // 비공개 변수
this.getName = function() { return name; };
this.getAge = function() { return _age; };
this.setAge = function(age) {
if (age >= 0) _age = age;
};
}
const tom = new Person('Tom');
tom.setAge(25);
console.log(tom.getAge()); // 25
// console.log(tom._age); // undefined (외부에서 직접 접근 불가)
# 3-2. 비동기 처리와 콜백 함수
setTimeout, fetch와 같은 비동기 함수에서 클로저는 시점에 따른 값 보존에 유용합니다.
js
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(`Timer ${i}`); // 각각 다른 i 값을 기억
}, i * 1000);
}
let을 사용해 블록 스코프를 만들었지만, var를 사용할 경우 클로저를 이용해 현재 루프 인덱스를 기억하도록 해야 합니다.
# 3-3. 함수형 프로그래밍과 부분 적용(Partial Application)
클로저는 함수형 프로그래밍에서 자주 쓰이는 partial 혹은 curry 패턴을 구현하는 데 필수적입니다.
js
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5)); // 10
multiply 함수는 첫 번째 인자를 기억하고, 반환된 내부 함수가 두 번째 인자를 받아 최종 결과를 반환합니다.
# 4️⃣ 클로저 사용 시 주의점 – 메모리 누수와 성능
# 4-1. 메모리 누수 위험
클로저가 외부 변수를 계속 참조하면, 그 변수는 가비지 컬렉션 대상이 되지 않아 메모리 누수가 발생할 수 있습니다. 특히 큰 객체를 클로저 안에 넣고 오래된 참조를 유지하면 애플리케이션 성능에 악영향을 줍니다.
예방 방법
필요 없는 클로저는 null 또는 undefined 로 초기화
전역 변수 대신 지역 변수 사용
클로저 내부에서 큰 객체를 직접 다루지 말고 복사본을 사용
# 4-2. 성능 최적화 팁
한 번만 생성되는 클로저는 재사용하도록 설계
반복문 안에서 클로저를 만들 때는 불필요한 생성을 최소화
최신 브라우저는 클로저 최적화가 잘 되어 있지만, 오래된 환경을 지원해야 한다면 테스트를 반드시 수행
# 5️⃣ 클로저와 최신 JavaScript 문법 – let, const, 화살표 함수
# 5-1. let·const와 클로저
ES6부터 도입된 let·const는 블록 스코프를 제공해 클로저와 함께 사용할 때 변수 캡처가 더 직관적입니다. 위의 for 루프 예시처럼 let을 쓰면 각 반복마다 새로운 스코프가 생성돼 클로저가 올바른 값을 기억합니다.
# 5-2. 화살표 함수와 this 바인딩
화살표 함수는 lexical this를 가지므로, 일반 함수와 달리 this가 클로저에 의해 자동으로 바인딩됩니다. 따라서 객체 메서드 내부에서 콜백을 사용할 때 유용합니다.
js
class Timer {
constructor() {
this.seconds = 0;
}
start() {
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
}
new Timer().start(); // 정상적으로 this가 바인딩됨
# 결론 – 클로저를 활용해 더 스마트한 코드 작성하기
JavaScript 클로저는 함수가 기억을 갖는 비밀이라고 할 수 있습니다. 함수 스코프와 렉시컬 스코프가 결합돼, 외부 변수에 대한 영구적인 접근 권한을 제공함으로써 데이터 은닉, 비동기 처리, 함수형 프로그래밍 등 다양한 상황에서 강력한 도구가 됩니다.
하지만 클로저를 남용하면 메모리 누수와 성능 저하 위험이 있으니, 필요한 경우에만 적절히 사용하고, 사용 후에는 참조를 해제하는 습관을 들이는 것이 중요합니다.
# 📢 행동 유도 (Call to Action)
직접 실습해 보기 – 위에서 소개한 카운터 예제와 데이터 은닉 코드를 코드 에디터에 복붙해 실행해 보세요.
프로젝트에 적용 – 현재 진행 중인 작은 프로젝트(예: Todo 앱)에서 클로저를 활용해 상태 관리 로직을 구현해 보세요.
블로그 구독 & 댓글 – 클로저와 관련해 궁금한 점이나 직접 겪은 에러 사례가 있다면 댓글로 공유해 주세요! 다음 포스트에서는 클로저와 메모리 관리를 심층 탐구합니다.
클로저를 제대로 이해하고 활용한다면, 당신의 JavaScript 실력이 한 단계 성장할 것입니다. 지금 바로 코드를 작성하고, “함수가 기억한다”는 마법을 직접 체험해 보세요! 🚀
Comments
Join the discussionNo comments yet. Be the first to comment.