Javascript

Closer ❌ Closure ⭕

아가프린 2024. 2. 2. 02:21

음 저는 클로저라는 단어를 처음 들었을 때 바보같이 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

https://adjh54.tistory.com/64

https://poiemaweb.com/js-closure

https://velog.io/@proshy/JS%ED%81%B4%EB%A1%9C%EC%A0%B8closure%EC%99%80-%ED%81%B4%EB%A1%9C%EC%A0%B8%EC%9D%98-%EC%82%AC%EC%9A%A9-%EC%98%88%EC%A0%9C