업데이트:

[01] 클로저란 ?

클로저는 자바스크립트에서 난해하기로 유명한 개념 중 하나라고 한다.

자바스크립트의 고유 개념도 아니어서 ECMAScript 사양에는 설명되어있지도 않다.

대신 MDN에서 정의한 개념을 보자.

☝🏻 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.

글만 보면 와닿지 않으므로 개념을 구체화해보겠다.

const x = 1;

function outerFunc() {
	const x = 10;
	function innerFunc(){
		console.log(x);
	}
innerFunc();
}

outerFunc();

위 코드는 외부함수 안에 내부함수가 들어있다.

Untitled

개발자 도구를 이용하여 보면 innerFunc() 함수의 상위 스코프가 outerFunc이고 그 안에 변수 x 의 값도 들어있다는 것을 알 수 있다.

내부 함수 innerFunc() 가 만들어지는 시점에서 그 함수의 외부(부모)함수가 가지고 있는 스코프, 변수, 함수의 정보를 동봉해서 가지고 있고 언제든지 접근할 수 있다 !!

결국 핵심은 내부 함수에서 외부 환경을 접근할 때 클로저로 접근할 수 있다는 것이다.

[02] 클로저와 렉시컬 환경

보통 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하여 이러한 중첩 함수를 클로저라고 한다.

예시를 들어보겠다.

function foo(){
	const x = 1;
	const y = 2;
	
	function bar(){
		console.log(x);
	}
	return bar;
}
const bar = foo();
bar();

위 코드는 bar()함수가 클로저이다.

그 이유는 bar()함수가 상위 스코프의 식별자 x를 참조하고 있고 외부 함수보다 더 오래 유지되기 때문이다.

[03] 클로저의 활용

클로저는 자바스크립트의 강력한 기능이라서 필요하면 적극적으로 활용해야 한다.

클로저가 유용하게 사용되는 상황들을 알아보겠다.

1) 상태 유지

현재 상태를 기억하고 변경된 최신 상태를 유지, 은닉

<!DOCTYPE html>
<html>
<body>
  <button class="toggle">toggle</button>
  <div class="box" style="width: 100px; height: 100px; background: red;"></div>

  <script>
    var box = document.querySelector('.box');
    var toggleBtn = document.querySelector('.toggle');

    var toggle = (function () {
      var isShow = false;

      // ① 클로저를 반환
      return function () {
        box.style.display = isShow ? 'block' : 'none';
        // ③ 상태 변경
        isShow = !isShow;
      };
    })();

    // ② 이벤트 프로퍼티에 클로저를 할당
    toggleBtn.onclick = toggle;
  </script>
</body>
</html>

toggle 변수에 즉시 실행 함수를 할당해주었고 즉시 실행 함수의 반환값으로 함수가 리턴되었다.

함수가 생성되면 클로저도 생성된다.

isShow는 클로저가 기억하는 렉시컬 환경의 변수이기 때문에 소멸하지 않아서 현재 상태를 기억할 수 있다.

만약 클로저가 없다면 isShow를 전역 변수로 만들어주어야 하는데 전역 변수는 누구나 접근 가능하고 부작용을 유발할 수 있다.

그런데 클로저는 전역에서 참조할 수 없기 때문에 함수 내부에서 안전하게 현재 상태를 기억하고 최신 상태를 유지할 수 있다.

2) 캡슐화와 정보 은닉

캡슐화는 객체의 상태를 나타내는 프로퍼티와 동작인 메서드를 하나로 묶는 것을 말한다.

캡슐화는 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 하는데 이것이 정보 은닉이다.

원래 자바스크립트는 public, private, protected 같은 접근 제한자가 없고 모두 기본적으로 public하다.

function Counter() {
  // 카운트를 유지하기 위한 자유 변수
  var counter = 0;

  // 클로저
  this.increase = function () {
    return ++counter;
  };

  // 클로저
  this.decrease = function () {
    return --counter;
  };
}

const counter = new Counter();

console.log(counter.increase()); // 1
console.log(counter.decrease()); // 0

함수 안에 들어있기 때문에 전역에서는 counter 변수에 접근할 수 없다.

그렇지만 Counter() 함수 안에서 increase와 decrease 메서드는 자신의 클로저인 외부함수 Counter의 counter변수를 참조할 수 있다.

이러한 클로저의 특징을 이용하면 자바스크립트에서도 클래스 기반 언어의 private 키워드를 흉내내어 정보 은닉이 가능한 것처럼 보인다.

맺음말

클로저를 알고 나니 스코프에 대한 개념이 확실히 자리잡힌것 같다.

아직은 코드내에서 클로저를 활용해 자유자재로 코드를 짜기에는 어려울 것 같지만 자바스크립트 엔진의 코드 실행 방식이 조금은 더 친숙하게 다가오는 느낌이었다.

댓글남기기