클로저는 함수가 선언될 때의 스코프를 기억하여, 함수가 생성된 이후에도 그 스코프에 접근할 수 있는 기능을 말한다.
클로저는 자바스크립트의 함수가 일급 객체라는 특성과 렉시컬 스코프의 조합으로 만들어 진다.
function makeCounter() {
let count = 0; // 🔐 프라이빗 변수
return function () {
return ++count;
};
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
makeCounter() 실행 → 내부 변수 count가 생성count가 있는 렉시컬 환경을 기억counter() 호출 시마다 count를 증가시키며 상태 유지function createCounter() {
let count = 0;
return {
increment() { count++; return count; },
decrement() { count--; return count; },
}
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
이 예시에서는 외부에서 count에 직접 접근하거나 변경할 수 없다. createCounter 내부에서만 접근 가능한 count는 클로저 덕분에 유지된다.
이로 인해 중요한 상태를 외부에서 직접 수정하지 못하도록 보호할 수 있다.
클로저를 이용하면 함수 호출 이후에도 특정 상태를 계속 유지할 수 있다.
이는 UI 컴포넌트, 애니메이션, 이벤트 핸들러 등에 자주 사용된다.
function makeGreeting(name) {
return function () {
console.log(`Hi, ${name}`);
}
}
const greetKim = makeGreeting('김철수');
greetKim(); // Hi, 김철수
함수가 실행된 이후에도 name이라는 상태를 잃지 않고 유지합니다.
모듈화는 특정 기능을 캡슐화하고, 외부에 공개하고자 하는 부분만 선택적으로 노출하여 코드의 응집력을 높이고, 유지보수성을 향상시킬 수 있다. 클로저를 활용하면 필요한 함수와 데이터만 외부로 노출함으로써 모듈 패턴을 쉽게 구현할 수 있다.
useState, useEffect, useCallback 내부
React의 훅들은 클로저를 적극적으로 사용한다.
useCallback 예시:
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log(`현재 count: ${count}`);
}, [count]);
return <button onClick={handleClick}>클릭</button>;
}
이 handleClick 함수는 렌더링 시점의 count 값을 클로저로 캡처합니다. 즉, 해당 시점의 스냅샷을 기억한다.
주의점
클로저로 인해 오래된 값이 기억되는 Stale Closure 문제가 발생할 수 있다. 이를 해결하기 위해 useRef, useEffect의 dependency array를 관리한다.
1. 클로저란 무엇인가요?
클로저는 함수가 선언될 당시의 렉시컬 스코프를 기억하여, 함수가 그 스코프 밖에서 호출되더라도 내부 변수에 접근할 수 있도록 하는 자바스크립트의 기능이다.
2. 클로저는 메모리에 어떤 영향을 주나요?
클로저는 함수가 참조하는 외부 변수들을 메모리에 유지시키기 때문에, 해당 변수가 더 이상 필요하지 않아도 참조가 남아 있다면 가비지 컬렉션(GC) 대상이 되지 않아 메모리 누수를 유발할 수 있습니다. 따라서 이벤트 핸들러, 타이머 등에서는 참조를 해제해주는 것이 중요하다.
3. 클로저를 활용한 데이터 은닉 또는 상태 유지 방법은?
클로저를 사용하면 외부에서 직접 접근할 수 없는 프라이빗 변수를 생성할 수 있다. 예를 들어, 클로저 내부에 let count = 0과 같은 상태를 유지시키고, 이 변수에 접근하는 인터페이스만 반환함으로써 상태를 은닉할 수 있다.
4. React에서 클로저가 문제를 일으키는 경우는? → stale closure
함수형 컴포넌트에서는 렌더링 시점의 상태나 props를 클로저로 캡처하기 때문에, 나중에 호출될 때 이전 값(old snapshot) 을 참조하게 되는 문제, 즉 stale closure가 발생할 수 있다. 이를 방지하기 위해 useEffect의 의존성 배열이나 함수형 업데이트(prev => ...)를 사용한다.