⚡ 한 줄 요약:
클로저(closure)는 "함수가 자기 바깥 변수를 기억하는 것"입니다.
함수를 리턴한 뒤에도 그 함수가 선언될 때의 변수 환경(렉시컬 스코프)이 살아있는 현상이고,
카운터·프라이빗 변수·디바운스가 전부 이 원리로 동작합니다.
2026년 신입 개발자 면접 문제 정리 기사들이 다시 돌고 있습니다.
JavaScript·Node.js 파트에서 매년 빠지지 않는 단골이 바로 클로저(closure) 입니다.
"설명해보세요"는 쉬운데, "그래서 실무에서 어디 쓰냐"에서 막히면 감점입니다.
이 글은 면접관이 실제로 듣고 싶은 답을 코드로 정리합니다.
📑 목차
🧩 클로저가 뭔지 한 문장으로
클로저는 함수 + 그 함수가 선언된 시점의 렉시컬 환경(lexical environment) 의 조합입니다.
쉽게 말해 함수가 자기 몸 밖에 있는 변수를 "붙잡아 두고" 나중에도 쓰는 것입니다.
function makeCounter() {
let count = 0; // 바깥 변수
return function () { // 이 함수가 count를 '기억'한다
count += 1;
return count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2 ← count가 사라지지 않고 유지됨
makeCounter()는 이미 리턴이 끝났는데 count는 왜 안 사라질까요?
리턴된 안쪽 함수가 count를 참조하는 한, 자바스크립트 엔진은 그 변수를 메모리에서 회수하지 않기 때문입니다.
이게 클로저의 핵심입니다.
🔍 왜 이런 일이 일어나나 (스코프 체인)
자바스크립트 함수는 "어디서 호출됐나"가 아니라 "어디서 선언됐나" 를 기준으로 바깥 변수를 찾습니다.
이를 렉시컬 스코프라고 합니다.
함수는 생성될 때 자신을 감싼 스코프에 대한 참조를 내부에 품고 태어나고, 이 참조 사슬(스코프 체인)이 끊기지 않는 한 바깥 변수는 살아있습니다.
면접에서 가장 많이 나오는 함정이 반복문 + var입니다.
// ❌ 흔한 실수 — 전부 3이 찍힌다
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 3, 3, 3
}
// ✅ let은 반복마다 새 바인딩 → 클로저가 각각 다른 i를 캡처
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 0, 1, 2
}
var는 함수 스코프라 i 하나를 셋이 공유하고, 콜백이 실행될 땐 이미 i === 3입니다.let은 블록 스코프라 매 반복마다 새 i를 만들어 각 클로저가 서로 다른 값을 붙잡습니다.
"왜 let으로 바꾸면 해결되나"까지 말해야 완답입니다.
💡 실무에서 클로저가 진짜 쓰이는 곳
개념만 외우면 반쪽입니다.
클로저는 프레임워크 없이도 매일 쓰는 도구입니다.
| 패턴 | 클로저가 하는 일 | 실제 사용처 |
|---|---|---|
| 프라이빗 변수 | 바깥에서 못 건드리는 상태 은닉 | 모듈, 커스텀 훅 |
| 디바운스/스로틀 | 타이머 ID·마지막 호출 시각 기억 | 검색창 입력, 스크롤 |
| 메모이제이션 | 계산 결과 캐시 유지 | 무거운 연산 최적화 |
| 이벤트 핸들러 | 등록 시점의 데이터 캡처 | React 콜백, DOM 이벤트 |
실전에서 바로 복붙 가능한 디바운스 구현입니다.timer를 클로저로 붙잡아 두는 게 핵심입니다.
function debounce(fn, delay = 300) {
let timer; // 클로저로 유지되는 상태
return function (...args) {
clearTimeout(timer); // 이전 예약 취소
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 사용 — 입력이 멈추고 300ms 뒤에 딱 한 번 실행
const onSearch = debounce((q) => console.log("검색:", q), 300);
onSearch("리");
onSearch("리액");
onSearch("리액트"); // → "검색: 리액트" 만 실행됨
timer 변수가 return된 함수 안에 살아있기 때문에, 여러 번 호출돼도 직전 타이머를 정확히 취소할 수 있습니다.
이 let timer 한 줄이 클로저의 전부이자 이 코드의 심장입니다.
⚠️ 면접에서 놓치기 쉬운 포인트
- 메모리 누수: 클로저가 큰 객체나 DOM 노드를 계속 참조하면 GC가 회수를 못 합니다. 안 쓰는 핸들러는
removeEventListener로 끊어주세요. - 캡처는 값이 아니라 변수(참조): 클로저는 그 순간의 "값"을 복사하는 게 아니라 변수 자체를 가리킵니다. 위의
var함정이 바로 이 성질 때문입니다. - Node.js 맥락: 미들웨어·라우트 핸들러가 상위 스코프의
db연결이나 설정을 참조하는 것도 전부 클로저입니다. "서버 코드 어디에 클로저 있냐"고 물으면 이걸 답하세요.
✅ 체크포인트
- 정의: "함수가 선언된 렉시컬 환경을 기억하는 것" — 한 문장으로 말할 수 있게 준비.
- 함정 코드:
varvslet반복문 예제를 손으로 쓰고 출력값과 이유까지 설명. - 실무 예시 1개: 디바운스나 프라이빗 카운터를 직접 짜보기. 개념보다 코드가 설득력 있습니다.
📚 참고(출처)
🔗 함께 보면 좋은 글
📊 오늘의 개발 트렌드 (자동 집계)
GitHub·Hacker News·Hugging Face·App Store·Cloudflare Radar에서 매일 자동으로 모은 개발 트렌드입니다. (2026-07-20 18:11 기준)
🐙 GitHub 급상승
- tirth8205/code-review-graph (+663★/일) — Local-first code intelligence graph for MCP and CLI. Builds a persiste
- kvcache-ai/ktransformers (+360★/일) — A Flexible Framework for Experiencing Heterogeneous LLM Inference/Fine
- rohitg00/ai-engineering-from-scratch (+501★/일) — Learn it. Build it. Ship it for others.
📰 Hacker News 화제
- Show HN: I replaced a $120k bowling center system with $1,600 in ESP32s (2259p·241💬)
- Qwen 3.8 (874p·600💬)
- Claude Code uses Bun written in Rust now (510p·674💬)
🤗 Hugging Face 트렌딩 모델
- thinkingmachines/Inkling (13,462 DL·1175♥)
- prism-ml/Ternary-Bonsai-27B-gguf (338,945 DL·811♥)
💰 App Store 매출 TOP (한국)
- YouTube — Google
- ChatGPT — OpenAI OpCo, LLC
- 카카오톡 KakaoTalk — Daum Corp.
🌐 가장 많이 방문되는 웹 (Cloudflare Radar)
- google.com (#1) · Search Engines
- googleapis.com (#2) · Information Technology
- cloudflare.com (#3) · Technology
'개발 실무' 카테고리의 다른 글
| Git이 뭐고 GitHub·GitLab은 뭐가 다를까? — 한 번에 정리 (0) | 2026.07.26 |
|---|---|
| 어셈블리 대신 C로 아미가 키보드 입력 (0) | 2026.07.24 |
| 아임웹 홈페이지, AI 웹빌더로 만들까 (0) | 2026.07.24 |
| 데이터 파는 법 — 개발자가 데이터로 돈 버는 현실적인 방법 (2026) (0) | 2026.07.16 |
| AI 코딩 도구 코드 유출 막는 법 3가지 (0) | 2026.07.15 |