자바스크립트를 사용해봤지만 단 한 번도 클로저 개념을 완전히 이해한 적이 없는 이들에게는 클로저가 열반에 드는 것처럼 고된 노력을 들여야 이해할 수 있는 것일지도 모르겠다.
…깨달음의 순간이 이럴 것이다. “아, 클로저는 내 코드 전반에서 이미 일어나고 있었구나! 이제 난 클로저를 볼 수 있어.”
카일 심슨 , 『You Don’t Know JS - 타입과 문법, 스코프와 클로저』, 한빛미디어(2017), p240.
클로저는 프로그래밍 언어의 긴 역사 속에서도 중요한 발견 중 하나입니다. 스킴(Scheme) 언어에서 처음 발견되었죠. 그리고 자바스크립트의 주류에까지 이르렀습니다. 클로저 덕분에 자바스크립트가 더 흥미로운 언어가 되었습니다.
더글러스 크락포드 , 『자바스크립트는 왜 그 모양일까?』, 인사이트(2020), p140-141.
클로저는 주변 상태(렉시컬 환경)에 대한 참조와 함께 묶인(포함된) 함수의 조합입니다.
MDN, 클로저
클로저는 함수가 속한 렉시컬 스코프를 기억하여 함수가 렉시컬 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기능을 뜻한다.
카일 심슨 , 『You Don’t Know JS - 타입과 문법, 스코프와 클로저』, 한빛미디어(2017), p240.
const increase = function() {
let num = 0; // 상태 변수
return ++num; // 1 증가
}
console.log(num); // num is not defined
console.log(increase()); // 1
console.log(increase()); // 1
console.log(increase()); // 1
const increase = (function() {
let num = 0; // 상태 변수
// 클로저
return function() {
return ++num; // 1 증가
};
}());
console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3
// 전역 범위 (global scope)
const e = 10;
function sum(a) {
return function (b) {
return function (c) {
// 외부 함수 범위 (outer functions scope)
return function (d) {
// 지역 범위 (local scope)
return a + b + c + d + e;
};
};
};
}
console.log(sum(1)(2)(3)(4)); // 20
변수 값은 누군가에 의해 언제든지 변경될 수 있어 오류 발생의 근본적 원인이 될 수 있다. 외부 상태 변경이나 가변(mutable) 데이터를 피하고 불변성(immutability)을 지향하는 함수형 프로그래밍에서 부수 효과를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이기 위해 클로저는 적극적으로 사용된다.
이웅모 , 『모던 자바스크립트 Deep Dive』, 위키북스(2020), p405.
클로저는 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.
다음은 MDN의 예시로, 글자 크기를 바꾸는 DOM 관련 예시이다. size가 그대로 글자 크기 px이 되지만 이를 특정 함수만을 사용해서 변경하도록 캡슐화 되어있다.
function makeSizer(size) {
return function () {
document.body.style.fontSize = `${size}px`;
};
}
const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
document.getElementById("size-12").onclick = size12;
document.getElementById("size-14").onclick = size14;
document.getElementById("size-16").onclick = size16;
다음과 같이 싱글톤 패턴을 응용해 간단한 로거를 구현해 볼 수 있다.
const LoggingService = (function () {
const infoMessage = 'Info: ';
const warningMessage = 'Warning: ';
const errorMessage = 'Error: ';
return {
info: function (str) {
console.log(`${infoMessage}${str}`);
},
warning: function (str) {
console.log(`${warningMessage}${str}`);
},
error: function (str) {
console.log(`${errorMessage}${str}`);
},
};
})();
// someOtherFile.js
LoggingService.info('one'); // Info: one
LoggingService.warning('two'); // Warning: two
LoggingService.error('three'); // Error: three
고차함수에도 사용할 수 있다. 예시는 여기를 참고하였다.
const floatingPoint = 3.456789;
const someInt = Math.round(floatingPoint); // 3
const withDecimals = Number(floatingPoint.toFixed(2)); // 3.46
function rounder(places) {
return function (num) {
return Number(num.toFixed(places));
};
}
const rounder2 = rounder(2);
const rounder3 = rounder(3);
rounder2(floatingPoint); // 3.46
rounder3(floatingPoint); // 3.457
References
더글러스 크락포드 , 『자바스크립트는 왜 그 모양일까?』, 인사이트(2020)
카일 심슨 , 『You Don’t Know JS - 타입과 문법, 스코프와 클로저』, 한빛미디어(2017)
이웅모 , 『모던 자바스크립트 Deep Dive』, 위키북스(2020)
MDN
Ilya Meerovich, 3 Use Cases for Closures (in JavaScript)