늘 겸손하게

JavaScript - 클로져(Closure)란? 본문

Programming/JavaScript

JavaScript - 클로져(Closure)란?

besforyou999 2022. 8. 11. 12:23

1. 클로저 (Closure)

 

중첩 함수에서 내부 함수가 외부 함수의 Lexical Environment에 기록된 식별자를 기억하는 것을 클로저라 부릅니다.

 

함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical Environment)과의 조합

 

function makeCounter() {
  let counter = 0;

  return function() {
    return counter++;
  };
}

let counter = makeCounter();

alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2

 

 

외부 함수인 makeCounter 함수의 지역변수를 내부 함수인 function() { return counter++}가 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 접근할 수 있는것을 볼 수 있습니다.

 

위 예시를 내부 함수가 외부 함수의 context에 접근하는 closure라고 부를 수 있습니다.

 

이렇게 클로저에 의해 참조되는 외부함수의 변수는 자유변수(Free Variable)라고 부른다.

 

 

1.1 클로저 원리

 

함수는 자체적인 스코프를 가지고 함수의 스코프에 정의된 변수는 해당 함수에서만 접근이 가능하다.

 

자바스크립트에선 함수 내부에 다른 함수를 정의할 수 있다. 이러한 내부 함수는 외부 함수의 지역 변수뿐만 아니라 외부 함수의 스코프에도 접근할 수 있다.

 

클로저는 내부 함수가 외부 함수에서 반환되고 외부 함수의 실행 컨텍스트가 종료된 이후에도 내부 함수가 외부 함수의 소코프에 대한 참조를 유지할 때 생성된다.

 

외부 함수의 실행 컨텍스트가 사라져도 내부 함수가 참조하고 있기 때문에 가비지 컬렉션에 의해 제거되지 않고 유지되는 것이 클로저의 원리이다.

 

 

2. 클로저의 활용

 

클로저는 자신이 생성되었을때의 렉시컬 환경 (Lexical Environment)를 기억해야 하기 때문에 메모리 측면에서는 손해이지만 자바스크립트의 강력한 기능 중 하나이므로 사용하도록 권장된다. 왜 그런걸까?

 

 

2.1 상태 유지

 

현재 상태를 기록하고 업데이트가 가능해야 할 경우이다.

 

클로저를 통해 비동기 작업에서 상태를 보존할 수 있다.

 

<!DOCTYPE html>
<html>
  <body>
    <button class="toggle">toggle</button>
    <div class="box" style="width:100px; height: 100px; background-color: red"></div>
    <script>
      const box = document.querySelector(".box");
      const btn = document.querySelector(".toggle");
      
      const toggle = (function() {
        let isShow = false;

        // 1. 클로저를 반환
        return function() {
          box.style.display = isShow ? 'block' : 'none';
          // 2. 상태 변경
          isShow = !isShow;
        };
      })();
      
      btn.onclick = toggle;
    </script>
  </body>
</html>

 

위 코드를 보면 클로저를 사용하여 버튼에 클릭 이벤트 리스너를 등록하고 있습니다.

 

즉시실행함수는 실행된 후 함수를 반환하고 즉시 소멸합니다.

 

즉시실행함수는 자신이 생성되었을때 렉시컬 환경에 속한 변수인 isShow를 기억하는 클로저를 반환합니다.

 

위 코드로 박스가 visible 해야 하는지 말아야 하는지에 대한 상태값을 관리할 수 있습니다.

 

 

2.2 전역 변수 억제

 

클로저는 오류의 발생 가능성이 높은 전역 변수를 대신할 수 있습니다. 

 

클릭한 횟수를 세는 카운터를 만든다고 가정합니다.

 

<!DOCTYPE html>
<html>
  <body>
    <button class="toggle">toggle</button>
    <p id="count">0</p>
    <script>
      const btn = document.querySelector(".toggle");
      const cnt = document.querySelector("#count");

      var counter = 0;

      function increase() {
        return ++counter;
      }
    
      btn.onclick = function() {
        cnt.innerHTML = increase();
      };

    </script>
  </body>
</html>

 

전역변수 counter를 선언하여 버튼을 클릭한 횟수를 업데이트하고 기록할 수 있으나 전역 변수는 어디서든지 참조가 가능하여 예기치 못하게 값이 변경될 수 있는 단점이 있습니다.

 

이 문제는 클로저 활용으로 해결 가능합니다.

 

<!DOCTYPE html>
<html>
  <body>
    <button class="toggle">toggle</button>
    <p id="count">0</p>
    <script>
      const btn = document.querySelector(".toggle");
      const cnt = document.getElementById("count");
      
      let increase = (function() {
        let count = 0;

        return function() {
          return ++count;
        };
      }());
    
      btn.onclick = function () {
        cnt.innerHTML = increase();
      }
    </script>
  </body>
</html>

 

 

위와 같은 방법으로 전역 변수의 사용없이 과거 값을 기록하고 새로운 값을 업데이트 받을 수 있습니다.

 

 

 

출처

https://poiemaweb.com/js-closure

https://www.opentutorials.org/course/743/6544

https://ko.javascript.info/closure

https://www.w3schools.com/js/js_function_closures.asp