본문 바로가기
Develop/백엔드 & 서버

클로저- 콜백을 구현하는 이상적인 구조

by 구운밤이다 2021. 1. 27.
728x90
반응형

자바스크립트를 공부하다 보면 흔히 접할 수 있는 단어 중 하나인 클로저! 콜백을 공부하다보면 접할 수도 있고, 전반적인 프로그래밍 언어를 공부할 때 등장하는 개념이기도 하다. 이 글은 mdn 클로저 문서를 보고 공부하며 적은 글입니다 ㅎㅎ..

클로저

클로저란 함수와 함수가 선언된 어휘적 환경(Lexical scoping)의 조합이다.

MDN 에서는 이렇게 클로저를 정의하고 있다. 그렇다면 어휘적 환경, Lexical scoping 은 뭘까? 쉽게 말하면 변수들의 범위를 어휘적으로 지정하겠다는 말이다. 즉 지역변수의 스코프(범위)를 어디에 선언했는지에 따라 결정하는 것. 예제를 보자.

function init() {
    var name = "구운밤";
    function displayName() {
        console.log (name);
    }
    displayName();    
}
init();

위에서 name 변수와 displayName 함수는 같은 init 이라는 function의 내부에서 생성되었다. 즉 스코프가 init 함수의 내부인데, 위 예제를 보면 displayName 함수 내부에서 name 변수를 사용하고 있다. 위를 실행하면 제 이름이 잘 출력이 되는 것을 볼 수 있다.

위는 lexical scoping의 대표적인 예시인데, 위 예제에서도 알 수 있듯이 lexical은 변수가 코드 내 어디에 선언되었는지에 따라 사용 가능한 범위가 지정됨을 말한다. 자바스크립트에서는 함수 내부에서 외부(부모) 함수의 변수에 접근할 수 있기 때문에 변수 name에 접근할 수 있는 것이다. 하지만 이때, 만약 displayName 함수 내부에 같은 이름의 변수가 또 있었다면 this.name, 내부의 변수가 사용됩된다.

클로저가 뭔데?

이번엔 다른 예제를 통해 알아보자.

function makeAdder(x) {
  var y = 1;
  return function(z) {
    y = 100;
    return x + y + z;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 107 (x:5 + y:100 + z:2)
console.log(add10(2)); // 112 (x:10 + y:100 + z:2)

위 예제에서는 먼저 makeAdder라는 함수를 정의했다. 파라미터 x를 받아 y, z 값을 더한 값을 리턴하는 함수란 걸 쉽게 알 수 있는데, 위에서 설명한 것처럼, 동일한 변수 y가 외부와 내부에 동시에 있으면 내부의 변수를 사용하게 된다. 최종 계산 시에는 y는 100!

그 밑에 변수 add5, add10 에 위 함수에 5, 10 이라는 값을 담은 함수를 저장한다. 자바스크립트에서는 함수를 변수에 리턴함으로써 저장할 수 있는데, 이 리턴하는 함수가 클로저를 형성하게 된다. 이 둘은 클로저인데, 위의 정의처럼 함수와 함수가 선언된 어휘적 환경을 저장하게 된다. 쉽게 말해, 둘의 함수 내용은 같지만 다른 환경을 저장하는 거죠. 함수가 실행되면 add5이라는 클로저 내부에서는 x가 5라는 값을 갖지만, add10의 맥락적 환경에서 x 는 10이다.

클로저의 사용

1. 모듈 패턴

먼저 클로저를 이용해 프라이빗 메소드를 흉내낼 수 있다. 프라이빗 메소드를 사용하면 변수에 제한적인 접근만을 허용하여 보안적으로나 코드 인터페이스 관리를 쉽게 할 수 있도록 도와준다. 원래 자바스크립트는 메소드를 프라이빗으로 선언할 수 없죠. 아래 예제를 통해 어떻게 흉내내는지 알아보자.

var counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1

위 예제에서는 counter.increment, counter.decrement, counter.value 세 함수에 의해 공유되는 하나의 어휘적 환경을 만든다. 카운터 변수 에 저장된 익명환경 안에서 어휘적 환경이 만들어지고, 정의되는 즉시 이 함수가 실행되는데, 이 환경은 변수 하나와 함수 하나를 가지고 있다. 이는 외부에서 접근할 수 없고 익명 함수에서 반환된 세 개의 퍼블릭 함수를 통해서만 접근가능하므로, 프라이빗 메소드를 흉내내는 것과 같다. 여기서 increment, decrement, value 는 같은 환경을 공유하는 클로저이다. 위를 실행하면 카운터가 잘 작동하는 것을 볼 수 있다. 이처럼 클로저를 잘 활용하면 객체지향 프로그래밍의 캡슐화, 정보 은닉 같은 이점을 얻을 수 있다.


참고 - mdn

728x90
반응형

댓글