음 저는 클로저라는 단어를 처음 들었을 때 바보같이 Closer라는 단어인 줄 알아서 가까운..? 했던 기억이 나네요!
하지만 클로저의 스펠링은 Closure! 폐쇄라는 의미를 가지고 있습니다. Javascript를 한다면 참 많이 듣는 단어 중에
하나가 아닐까 싶습니다. 이번 글에서는 Closure에 대해서 정리 해보겠습니다.
❓Closure
클로저는 JS에서 중요한 개념 중 하나입니다. 하지만 그래서 JS 고유의 개념이 아니라
함수를 일개 객체로 취급하는 함수형 프로그래밍 언어들에서도 사용되는 중요한 개념입니다.
클로저를 한 번쯤 검색해보신 분들은 아마 공통적으로 아래 문장과 비슷한 말을 보셨을 것 같습니다.
함수와 그 함수가 선언 된 Lexical Environment(어휘적 환경)의 조합이다.
음 한 문장인데 이해하기 어려울 수 있습니다. 어휘적 환경이라는 말 자체가 흔히 사용되는
단어는 아니니까요. 하나하나 알아볼까요?
🗣️ 함수, Lexical Environment (어휘적 환경)
위에서 말하는 함수란 반환된 내부 함수를 의미하고 Lexical Environment란
이 내부 함수가 선언됐을 때의 스코프를 나타냅니다.
이말은 클로저란 내부 함수가 자신이 선언됐을 때의 환경 즉, 스코프를 기억하여
자신이 선언됐을 때의 스코프 밖에서 호출되어도 그 스코프에 접근할 수 있는 함수를 뜻합니다!
스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언했는지에 따라서 결정됩니다.
아를 Lexcial Scoping이라 합니다.
글로는 이해가 안 되실 거 같다면 예시 코드를 살펴봅시다.
function outer() {
const hello = "hello";
console.log(hello);
}
outer(); // ok
console.log(hello); // error
자 위 코드에서 왜 error가 나는지는 대부분 아실 것 같습니다.
hello라는 변수의 스코프는 outer라는 함수 내부이기 때문에 outer 함수 외부에서는 접근할 수 없기에
당연히 에러가 발생합니다.
그렇다면 클로저는 어떨까요?
function outer() {
const hello = "hello";
return function inner() {
console.log(hello);
}
}
const closureValue = outer();
closureValue(); // output: hello
function outer(outerValue) {
return function inner(innerValue) {
return outerValue + innerValue;
}
}
const closureValue = outer(1);
console.log(closureValue(3)); // output: 4
어떤가요? 내부 함수인 inner 함수가 자신의 스코프 바깥에 있는 outer 함수 범위의 변수들에
접근합니다. 놀랍게도 잘 작동하고요. 네? 이게 어떤 장점이 있길래 그렇게 중요하냐고요?
클로저는 되게 다양한 상황에서 유용하게 사용 가능합니다!
✍️ 사용 이유
const increaseBtn = document.getElementById('increase');
increaseBtn.addEventListener('click', handleClick);
let count = 0;:
function handleCilck() {
count++;
return count;
}
increaseBtn이라는 button을 찾아서 클릭됐을 때 전역 변수 count를 올리는 코드입니다.
클로저를 적용시키기 이전 모습인데요. count를 전역 변수로 만들어야만 handleClick함수 내부에서
count에 접근할 수 있습니다. 하지만 count 변수가 handleClick 하나의 함수에서만 필요하다면?
말이 달라지죠. count가 handleClick에서만 접근 가능하도록 바꿀 수 있는 방법이 클로저입니다!
const increaseBtn = document.getElementById('increase');
increaseBtn.addEventListener('click', handleClick);
function handleCilck(){
let count = 0;
return function () {
count++;
return count;
}
}
이렇게 클로저를 적용 시켜주면 handleClick에서만 count에 접근할 수 있고
불필요한 전역 변수를 효과적으로 줄일 수 있게 되었습니다!
또 클로저는 코드를 재사용에도 유용한데요.
const newTag = function(open, close) {
return function(content) {
return open + content + close;
}
}
const bold = newTag('<b>', '</b>');
const italic = newTag('<i>', '</i>');
console.log(bold(italic("Test Content"))); // output: <b><i>This is my content!</i></b>
만약 open, close와 더불어 content 까지 한 번에 받아도 되겠지만 태그 안의 또 태그를 넣는
위와 같은 상황이라면 가독성과 유지보수 비용이 많이 들 수도 있습니다.
클로저를 사용하니 가독성도 늘고 변경이 일어나도 쉽게 대응할 수 있을 것 같습니다.
🚨주의
사실 저도 헷갈린 코드인데요. 다음 코드는 클로저일까요? 라고 말하면 대부분 틀ㄹ..
function outer() {
const hello = "hello";
if (true) {
const city = "seoul";
return function inner() {
console.log(city);
};
}
}
지금까지의 내용을 보면 어! 클로저다! 라고 할 수 있습니다. 하지만 클로저는
단지 내부 함수를 리턴하는 것이 아니라 해당 내부 함수가 외부 함수의 지역 변수를
사용했을 때만 클로저라고 말할 수 있습니다! 즉, 아래 코드는 이제 클로저라고 부를 수 있겠죠?
function outer() {
const hello = "hello";
if (true) {
const city = "seoul";
return function inner() {
console.log(city);
console.log(hello);
};
}
}
📖 마무리
클로저에 대한 블로그를 많이 읽었지만 제대로 정리해본 것은 처음인 것 같습니다!
이제 JS에서 클로저가 무엇이고 왜 쓰냐고 물으면 잘 대답할 수 있을 것 같나요?
사실 완벽히 원리를 파악하려면 더 깊게 들어가야 합니다. 그만큼 JS에서 중요한 개념이니까요.
활용되는 곳도 아주 다양합니다. react의 useState도 클로저를 이용한 것이라고 알고 있어요!
마지막 주의 부분에서 봤던 코드는 저도 클로저 아닌가..? 라고 생각할만큼 헷갈렸네요.
좀 더 분발하겠습니다!
🔗Ref
'Javascript' 카테고리의 다른 글
비동기 공부에 동기를 부여해줄게 - JS에서의 비동기 처리 (1) | 2024.04.27 |
---|---|
this? 이것? (0) | 2024.04.15 |
ES6? 다른 숫자도 있는건가? (0) | 2024.03.31 |
🦴 JS 타입 종류 (원시 타입, 참조 타입?) (0) | 2024.02.05 |
Call Stack..? Event Loop? 🤔 (1) | 2024.01.30 |