0. 개요
클로저는 매번 나오는 개념임에도 불구하고 매번 정확한 정의와 설명을 하기 어려운 개념입니다. 이번에 상세하게 블로깅을 해보면서 완벽하게 숙지해도록해봅시다.
1. 클로저의 정의
closure : 폐쇄, 종지, 폐점, 폐쇄음
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합으로, 함수가 자신이 생성될 때의 환경을 "기억하는" 특성을 가지고 있습니다. 이러한 성질 덕분에, 외부 함수의 지역 변수에 내부 함수가 계속 접근할 수 있는 것입니다. 클로저는 자바스크립트의 고유한 특성이며, 함수가 자신이 생성될 때 접근할 수 있었던 변수들을 함수가 실행될 때에도 계속해서 접근할 수 있게 합니다.
즉 클로저는 자신이 생성될 때의 렉시컬 환경(Lexical environment)을 기억하는 함수입니다.
잘 이해가 안 된다면 2. 클로저의 작동 원리 예시를 보면서 이해해 봅시다.
렉시컬 환경이란?
렉시컬 환경(Lexical Environment)"은 프로그래밍 어휘에서 중요한 개념으로, 변수와 그것이 어디에서 사용되는지를 포함하는 코드의 구조를 말합니다.
렉시컬 환경은 크게 두 가지 주요 구성 요소로 이루어져 있습니다:
- 환경 레코드(Environment Record): 이것은 실제로 코드에 의해 생성된 변수, 함수 선언, 매개 변수 등의 식별자와 그 값들을 저장합니다.
- 외부 렉시컬 환경 참조(Outer Lexical Environment Reference): 이는 현재 렉시컬 환경 밖의 외부 환경(일반적으로 코드가 작성된 주변 환경)에 대한 참조입니다. 이를 통해 스코프 체인이 생성되며, 현재 블록 또는 함수가 외부의 변수나 함수를 참조할 수 있습니다.
예를 들어, 함수 내부에서 다른 함수를 선언하면 내부 함수는 외부 함수의 변수에 접근할 수 있습니다. 이는 내부 함수의 렉시컬 환경이 외부 함수의 렉시컬 환경을 참조하기 때문입니다. 이러한 메커니즘은 클로저를 가능하게 하고, 자바스크립트에서 매우 강력한 프로그래밍 패턴을 구현할 수 있게 합니다.
2. 클로저의 작동 원리
클로저는 다음 두 가지 주요 구성 요소로 이해할 수 있습니다.
- 외부 함수: 클로저를 생성하는 함수입니다. 외부 함수는 내부 함수를 포함하며, 내부 함수는 외부 함수의 지역 변수에 접근할 수 있습니다. 아래의 예시에서는 createCounter가 외부함수입니다.
- 내부 함수: 외부 함수의 지역 변수에 접근하는 함수입니다. 외부 함수의 실행이 종료된 후에도, 내부 함수는 외부 함수의 변수를 참조할 수 있습니다. 아래의 예시에서는 createCounter내부의 return function() { count++; console.log(count); } 부분입니다.
// 내부에 변수있는 함수
function createCounter() {
let count = 0;
count++;
console.log(count);
}
createCounter() // 출력: 1
createCounter() // 출력: 1
createCounter() // 출력: 1
// 외부에 변수있는 함수
let count = 0;
function createCounter() {
count++;
console.log(count);
}
createCounter() // 출력: 1
createCounter() // 출력: 2
createCounter() // 출력: 3
// 클로저 함수
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 출력: 1
counter(); // 출력: 2
counter(); // 출력: 3
1) 내부에 변수 있는 함수
count가 항상 0으로 초기화된다.
2) 외부에 변수 있는 함수
count가 초기화되지 않아 제대로 작동한다.
3) 클로저 함수
createCounter 내부의 함수를 반환하는데 createCounter함수에 있는 렉시컬환경을 기억함으로 변수를 참조를 해서 제대로 작동한다.
3. 클로저의 실제 사용 사례
1) 전역변수를 줄일 수 있다.
전역변수는 예상치 못한 Side Effect를 일으킬 수 있기에 최대한 줄이는 것이 좋다. 때문에 최대한 전역변수를 줄여서 코딩해야 한다.
하지만 프로그램 구현 시 함수 하나에 사용하는 전역변수가 필요한 순간이 있다. 이럴 때 클로저가 유용하게 사용된다.
// 외부에 변수있는 함수
let count = 0;
function createCounter() {
count++;
console.log(count);
}
createCounter() // 출력: 1
createCounter() // 출력: 2
createCounter() // 출력: 3
// 클로저 함수
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 출력: 1
counter(); // 출력: 2
counter(); // 출력: 3
2) 비슷한 형태의 코드의 재사용률을 높일 수 있다.
const order = (food) => {
console.log(food + "을(를) 주문하셨습니다.");
return function (drink) {
return drink + "을(를) 추가로 주문하셨습니다."
}
}
const orderBurger = order("햄버거");
const orderPizza = order("피자");
console.log(orderBurger("콜라"));
console.log(orderPizza("사이다"));
4. 면접 답변
클로저는 함수와 렉시컬 환경의 조합으로 함수가 생성될 때의 환경을 기억하는 특성을 가지고 있습니다.
외부 함수는 내부 함수를 포함하며, 내부 함수는 외부 함수의 지역 변수에 접근할 수 있습니다.
클로저 함수를 이용해서 전역변수를 줄이거나 비슷한 형태의 코드의 재사용률을 높일 수 있습니다.
// 클로저 함수
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 출력: 1
counter(); // 출력: 2
레퍼런스
댓글