우당탕탕
JavaScript 클로저, 저도 처음엔 헷갈렸던 실전 사례들 본문
프론트엔드 개발 초반에 클로저라는 말을 듣고 “도대체 뭐지?” 싶었어요. 저도 처음엔 이 단어가 뭔지 전혀 몰랐거든요. 검색해보면 어려운 설명과 개념들이 너무 많아서, 막상 실무에서 바로 써먹으려면 혼란스럽고 헷갈리기 일쑤였어요.
그래서 제가 직접 코딩하면서 겪었던 JavaScript 클로저 실전에서 헷갈렸던 사례들을 모아봤습니다. 용어 풀이부터, 어떻게 동작하는지, 그리고 실제로 이런 부분 때문에 삽질했던 경험까지 모두 담았어요. 이 글만 읽으면 클로저에 대해 '아, 이거였구나!' 하고 이해가 될 거예요.
클로저라는 단어, 도대체 뭘까요?
사실 이 부분이 제일 헷갈렸어요. 클로저(closure)란 함수가 생성될 당시의 스코프(scope, 변수들이 유효한 범위)를 기억하는 것이라고 설명하더라고요. 근데 그게 왜 필요하고, 어떻게 쓰는지 감이 잘 안 잡혔죠.
쉽게 말하면, 함수가 자기 자신이 태어났던 환경을 계속 기억해서 그 내부 변수에 계속 접근 가능하다는 뜻이에요. 이게 우리가 자주 쓰는 function이나 () => {} 내부에서 만들어진 작은 함수들 덕분이죠.
제가 처음 배울 때는 이 설명만 듣고는 도통 감이 안 왔는데, 직접 코드 짜면서 경험하니 확 와닿더라고요.
JavaScript 클로저 실전에서 헷갈렸던 사례 관련 정보
실제 코딩하다 만난 클로저 기본 사례
처음 해본 게 바로 함수 내부에 함수를 만들어서 값을 기억하는 거였어요. 예를 들면, 이렇게 말이죠.
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
여기서 makeCounter가 실행되면 내부에 count라는 변수가 선언되고, 그걸 증가시키는 함수를 반환해주잖아요? 중요한 건, 반환된 함수가 count 변수에 접근할 수 있다는 점이에요. 이게 바로 클로저 덕분이죠.
저도 처음에 이걸 보고 ‘잠깐, 함수 호출이 끝났는데 왜 count 변수가 사라지지 않고 유지되는 거지?’ 하면서 엄청 헷갈렸어요. 일반적으로 함수가 끝나면 내부 변수는 메모리에서 제거되는데, 클로저 덕분에 해당 변수가 살아남는 거라고 하더라고요.
이렇게 하면 진짜 헷갈려요, 반복문에서 클로저 쓸 때
그런데 여기서 진짜 삽질을 했던 게 반복문 안에서 클로저를 쓸 때였거든요. 저도 이 부분에서 한참 멈칫했어요.
예를 들어, 이렇게 버튼 여러 개를 만들면서 각각 클릭할 때 인덱스를 alert로 띄우고 싶었는데, 막상 해보니 이상한 값이 나오는 거예요.
for (var i = 0; i < 3; i++) {
document.getElementById('btn' + i).addEventListener('click', function() {
alert(i);
});
}
버튼을 누르면 전부 3이 출력됐어요. 왜냐면, 이벤트가 발생하는 시점에 i가 이미 3으로 증가 끝난 상태였거든요. 저도 이거 한참 헤맸는데, 이때 var가 함수 스코프여서 반복문이 끝난 후에도 i는 하나의 변수로 유지되는 게 문제였던 거예요.
이걸 해결하려고 여러 방법을 써봤는데, 가장 간단한 건 let으로 바꾸는 거였어요.
for (let i = 0; i < 3; i++) {
document.getElementById('btn' + i).addEventListener('click', function() {
alert(i);
});
}
이렇게 하면 반복문이 돌 때마다 i가 블록 스코프를 가지면서 각각의 이벤트에 해당하는 i 값을 클로저가 기억하더라고요. 버튼 클릭 시 0, 1, 2가 제대로 출력됐어요.
JavaScript 클로저 실전에서 헷갈렸던 사례 관련 정보
예전 방식으로도 클로저 활용할 수 있어요
그렇다고 let만 있는 건 아니니까, ES5 스타일의 함수 안에 함수를 만들어 클로저를 강제로 생성하는 방법도 있어요. 바로 즉시 실행 함수, IIFE(Immediately Invoked Function Expression)인데요.
for (var i = 0; i < 3; i++) {
(function(index) {
document.getElementById('btn' + index).addEventListener('click', function() {
alert(index);
});
})(i);
}
이 코드는 즉시 실행되는 함수에 i 값을 인자로 넘기면서, 각 함수를 호출할 때마다 별도의 index 변수를 가진 클로저를 생성해요. 그래서 클릭해도 각각 0, 1, 2가 제대로 나왔습니다.
저도 ES6 전에 이런 식으로 클로저 문제를 피해왔던 기억이 나네요.
클로저 때문에 메모리가 걱정될 때가 있어요
그런데 여기서 또 하나 짚고 넘어갈 게 있는데, 클로저가 변수를 계속 기억하기 때문에 메모리 누수 걱정을 할 수도 있다는 점이에요.
저도 한창 집중해서 클로저를 오래 쓰다 보니, 쓸데없이 오래 살아남는 변수가 많으면 메모리가 계속 차서 앱이 느려질까 걱정했거든요. 실제로 너무 많은 클로저를 모아두거나, DOM 이벤트 리스너를 제대로 해제하지 않으면 문제가 될 수 있답니다.
그래서 클로저 내부에 꼭 필요한 변수만 갖고 있어야 하고, 더 이상 필요 없으면 이벤트 리스너 제거나 변수 초기화를 통해 참조를 끊어주는 습관이 중요해요.
JavaScript 클로저 실전에서 헷갈렸던 사례 관련 정보
제가 겪은 클로저 관련 실제 문제 사례
그냥 기능 구현할 때는 문제가 없었는데, 리팩토링하면서 클로저 때문에 발생한 문제도 있었어요. 예를 하나 들면, 상태값을 클로저로 저장한 상태에서 이벤트가 여러 번 등록되어 의도치 않은 동작이 반복된 경우였죠.
처음에는 어디서 중복 이벤트가 등록됐나 한참 찾느라 고생했는데, 나중에 보니 함수 호출마다 새 클로저가 더해지면서 내부 상태가 꼬였던 거예요. 그래서 결국 이벤트 등록 기준을 명확히 잡고, 필요 없을 땐 꼭 이벤트를 제거하는 코드를 넣었어요.
클로저 활용 팁과 주의점
- 1. 꼭 필요한 변수만 클로저에 담으세요. 너무 많은 데이터를 기억하면 메모리 부담 커집니다.
- 2. 반복문에서 클로저 쓸 땐
let을 적극 활용하세요. ES6부터는 이게 기본 해결책입니다. - 3. 이벤트 리스너나 타이머 등록 시 중복을 주의하세요. 클로저가 중복되면 예상치 못한 동작이 나올 수 있어요.
- 4. 클로저를 너무 복잡하게 쓰지 말고, 필요하다면 상태 관리 라이브러리를 검토하세요.
- 5. 디버깅할 때는 함수 내 변수 출력과 이벤트 흐름을 꼼꼼히 체크하는 게 도움됩니다.
자주 물어보시는 것들
Q. 클로저랑 스코프 차이가 뭔가요?
A. 스코프는 변수가 유효한 범위이고, 클로저는 그 스코프를 기억하는 함수라고 보시면 돼요. 클로저 덕분에 함수 외부에서 이미 끝난 스코프 변수에 접근할 수 있는 거죠.
Q. 클로저가 무조건 좋은 건 아니에요?
A. 네, 무조건 좋은 것만은 아니에요. 잘못 쓰면 메모리 누수나 중복 로직 문제를 일으킬 수 있으니 주의가 필요해요.
Q. 화살표 함수로 만든 클로저는 일반 함수랑 다르나요?
A. 내부 동작은 비슷해요. 다만 화살표 함수는 this 바인딩이 다르고, arguments 객체가 없다는 점을 유의해야 해요.
처음 접하면 넘 어렵고 복잡해 보여도, 클로저는 자주 써 보면 자연스레 감이 옵니다. 저도 처음엔 멀게만 느껴졌는데, 여러 시행착오 끝에 코드가 훨씬 짧아지고 재사용성도 좋아졌어요. 지금도 자주 쓰는 개념이고, 앞으로도 계속 함께할 친구라고 생각합니다.
'언어 > JavaScript' 카테고리의 다른 글
| TypeScript 제네릭 타입 추론, 2026년 바뀐 점까지 제가 겪은 경험 담았어요 (0) | 2026.08.04 |
|---|---|
| Next.js 13 App Router 마이그레이션 하면서 궁금했던 점들 답해봅니다 (0) | 2026.08.03 |
| React useMemo와 useCallback, 진짜 필요할 때만 쓰는 법 알려드려요 (0) | 2026.07.18 |
| React Query 도입하면서 알게 된 캐싱 함정과 체크리스트 (0) | 2026.07.06 |
| React useEffect 의존성 배열 직접 써보고 알게 된 진짜 비용 차이 (0) | 2026.07.04 |
