클로저란?
어떤 함수에서 선언한 변수를 참조하는 내부함수에서만 발생하는 현상
>>함수를 만들고 그 함수 내부의 코드가 탐색하는 스코프를 함수 생성 당시의 Lexical 스코프로 고정하면 클로저가 된다.
예제를 통해 더 알아봅시다.
function outerFunction() {
let outerVariable = '나는 외부 변수야';
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const myClosure = outerFunction();
myClosure(); // "나는 외부 변수야" 출력
- 'outerFunction'이라는 함수가 있습니다.
- 'outerFunction' 안에는 'outerVariable'이라는 변수가 선언되어 있고, 그 값은 "나는 외부 변수야"입니다.
- 'outerFunction' 안에는 또 다른 함수인 'innerFunction' 이 있습니다. 이 함수는 'outerVariable'을 출력합니다 .
- 'outerFunction'은 'innerFunction'을 반환합니다.
- 'outerFunction'을 호출하면 'innerFunction'이 반환되므로 , 'myClosure'는 'innerFunction'을 가리키게 됩니다
- 마지막으로 'myClosure()'를 호출하면 'innerFunction'이 실행되고, 'outerVariable'의 값인 "나는 외부 변수야"가 출력됩니다.
여기서 중요한 점은 'innerFunction'이 'outerFunction'의 변수인 'outerVariable'을 기억하고 있다는 것입니다.
이렇게 내부 함수가 외부 함수의 변수를 기억하는 것을 클로저라고 합니다.
왜 클로저가 중요할까요?
클로저는 다양한 상황에서 유용하게 쓰일 수 있습니다.
예를 들어, 특정 상태를 유지하거나, 비공개 변수를 만들고 관리하는 데 사용될 수 있습니다.
상태 유지를 위한 클로저
클로저를 이용해서 상태를 유지하는 방법을 봅시다
function createCounter() {
let count = 0;
return function() {
count++;
return count;
}
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
'createCounter' 함수는 'count'라는 변수를 가지고 있고, 이를 증가시키는 내부 함수를 반환합니다.
'counter'는 'createCounter'의 반환값인 내부 함수이고, 'counter()'를 호출할 때마다 'count'가 증가합니다.
이렇게 클로저를 이용하면 함수가 호출될 때마다 상태를 유지할 수 있습니다.
비공개 변수
클로저는 비공개 변수를 만드는 데도 유용합니다.
자바스크립트는 기본적으로 모든 변수가 공개되어 있지만, 클로저를 이용하면 특정 변수를 외부에서 접근할 수 없게 만들 수 있습니다.
function createSecretHolder(secret) {
return {
getSecret: function() {
return secret;
},
setSecret: function(newSecret) {
secret = newSecret;
}
}
}
const secretHolder = createSecretHolder('초기 비밀');
console.log(secretHolder.getSecret()); // "초기 비밀"
secretHolder.setSecret('새로운 비밀');
console.log(secretHolder.getSecret()); // "새로운 비밀"
'createSecretHolder' 함수는 비공개 변수 'secret'을 가지고 있고,
이를 접근하거나 수정할 수 있는 두 개의 메서드를 반환합니다.
'secret' 변수에 직접 접근할 수 없지만, 'getSecret' 과 'setSecret' 메서드를 통해서만 접근할 수 있습니다.
이런 방식으로 클로저를 이용해 비공개 변수를 만들 수 있습니다.
클로저와 메모리 관리
클로저는 객체지향과 함수형 모두를 아우르는 매우 중요한 개념입니다.
메모리 누수의 위험을 이유로 클로저 사용을 조심해야 한다는 사람들도 있지만 메모리 소모는 클로저의 본질적인 특성입니다.
과거에는 의도치 않게 누수가 발생하는 여러가지 상황들이 있었지만 이제는 의도대로 설계한 '메모리 소모'에 대한 관리법만 잘 파악해서 적용한다면 그걸로 충분합니다.
관리방법은 간단합니다.
클로저는 어떤 필요에 의해 의도적으로 함수의 지역변수를 메모리를 소모하도록 함으로써 발생합니다.
그렇다면 그 필요성이 사라진 시점에는 더는 메모리를 소모하지 않게 해주면 됩니다.
참조 카운트를 0으로 만들면 언젠가 GC가 수거해갈 것이고, 이때 소모됐던 메모리가 회수됩니다.
참조 카운트를 0으로 만드는 방법은 식별자에 참조형이 아닌 기본형 데이터를 할당하면 됩니다.
클로저 활용 사례
접근 권한 제어(정보 은닉)
정보 은닉은 어떤 모듈의 내부 로직에 대해 외부로의 노출을 최소화해서 모듈간의 결합도를 낮추고
유연성을 높이고자 하는 현대 프로그래밍 언어의 중요한 개념 중 하나입니다.
흔히 접근 권한에는 public, private, protected의 세 종류가 있습니다.
public은 외부에서 접근 가능한 것이고, private은 내부에서만 사용하며 외부에 노출되지 않는 것을 의미합니다.
자바스크립트는 기본적으로 변수 자체에 이러한 접근 권한을 직접 부여하도록 설계되어있지 않습니다.
클로저를 이용하면 함수 차원에서 public한 값과 private한 값을 구분하는 것이 가능합니다.
closure라는 영어 단어는 사전적으로 '닫혀있음, 폐쇄성, 완결성' 정도의 의미를 가집니다.
outer함수는 외부(전역 스코프)로부터 철저하게 격리된 닫힌 공간입니다.
외부에서는 외부 공간에 노출돼 있는 outer라는 변수를 통해 outer함수를 실행할 수는 있지만, outer 함수 내부에는 어떠한 개입도 할 수 없습니다.
외부에서는 오직 outer 함수가 return한 정보에만 접근할 수 있습니다.
return값이 외부에 정보를 제공하는 유일한 수단인 것입니다.
외부에 제공하고자 하는 정보들을 모아서 return하고, 내부에서만 사용할 정보들은 return하지 않는 것으로 접근 권한 제어가 가능합니다.
return한 변수들은 공개멤버가 되고 그렇지 않은 변수들은 비공개 멤버가 되는 것입니다.
부분 적용 함수
부분 적용 함수란 n개의 인자를 받는 함수에 미리 n 개의 인자만 넘겨 기억시켰다가,
나중에 (n-m)개의 인자를 넘기면 원래 함수의 실행 결과를 얻을 수 있게끔 하는 함수입니다.
커링 함수
커링 함수란 여러 개의 인자를 받는 함수를 하나의 인자만 받는 함수로 나눠서 순차적으로 호출될 수 있게 체인 형태로 구성한 것을 말합니다.
커링은 한 번에 하나의 인자만 전달하는 것을 원칙으로 합니다.
또하나 중간 과정의 함수를 실행한 결과는 그 다음 인자를 받기 위해 대기만 하기 때문에 마지막 인자가 전달되기 전까지는 원본 함수가 실행되지 않습니다.
>>부분 적용 함수는 여러개의 인자를 전달할 수 있고, 실행 결과를 재실행할 때 원본 함수가 무조건 실행됩니다.
원하는 시점까지 지연시켰다가 실행하는 것이 요긴한 상황이라면 커링을 쓰기에 적합할 것입니다.
혹은 프로젝트 내에서 자주 쓰이는 함수의 매개변수가 항상 비슷하고 일부만 바뀌는 경우에도 적절할 것입니다.
클로저는 자바스크립트에서 함수와 함수가 선언된 환경을 기억하는 기능입니다.
상태 유지, 비공개 변수 관리 등 여러 상황에서 유용하게 쓰일 수 있습니다.
클로저를 이해하고 잘 활용하면 더 효과적이고 안전한 코드를 작성할 수 있습니다.