본문 바로가기

클로저 뭐냐 물으면 — 2026 JS 면접 정답

반응형

⚡ 한 줄 요약:
클로저(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 연결이나 설정을 참조하는 것도 전부 클로저입니다. "서버 코드 어디에 클로저 있냐"고 물으면 이걸 답하세요.

 

 

✅ 체크포인트

  • 정의: "함수가 선언된 렉시컬 환경을 기억하는 것" — 한 문장으로 말할 수 있게 준비.
  • 함정 코드: var vs let 반복문 예제를 손으로 쓰고 출력값과 이유까지 설명.
  • 실무 예시 1개: 디바운스나 프라이빗 카운터를 직접 짜보기. 개념보다 코드가 설득력 있습니다.

 

 

📚 참고(출처)

🔗 함께 보면 좋은 글

 

 

📊 오늘의 개발 트렌드 (자동 집계)

GitHub·Hacker News·Hugging Face·App Store·Cloudflare Radar에서 매일 자동으로 모은 개발 트렌드입니다. (2026-07-20 18:11 기준)

🐙 GitHub 급상승

📰 Hacker News 화제

🤗 Hugging Face 트렌딩 모델

💰 App Store 매출 TOP (한국)

  • YouTube — Google
  • ChatGPT — OpenAI OpCo, LLC
  • 카카오톡 KakaoTalk — Daum Corp.

🌐 가장 많이 방문되는 웹 (Cloudflare Radar)

반응형