본문 바로가기
Web/CS (ComputerScience)

[Javascript 기본기다지기]_ 클로저(closure) 알아보기

by outwater 2021. 6. 22.

You Don't know JS (카일심슨) 책을 읽으며 정리한 내용을 바탕으로 포스팅을 작성하였습니다.


클로저 들어가기 전 복습
  • 스코프
    : 스코프는 변수의 유효범위로, 해당 변수가 접근할 수 있는 범위이다.
  • 스코프규칙
    : 중첩스코프가 존재할 시 현재 실행시점의 가장 안쪽 스코프로부터 시작하여 전역스코프 까지 차례대로 스코프를 넓히며 변수를 찾는다.
  • 렉시컬스코프
     : 함수가 선언되는 순간에서의 참조할 수 있는 스코프
클로저란 무엇인가?(What)  
함수가 속한 렉시컬스코프를 기억하여, 함수가 렉시컬 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기능

조금 더 구체적으로 설명을 하자면 중첩함수가 존재할 때, 외부함수의 변수에 접근할 수 있는 내부의 함수, 혹은 이러한 동작원리를 뜻한다.

function foo(){
  var a = 2;
  function bar(){
    console.log( a ) ; // 2
  }
  bar();
}

foo();

위의 코드를 보면, 함수bar( ) 안에서 스코프규칙을 통해 바깥 스코프의 변수인 a에 접근할 수 있다. 
이는 외부함수의 변수에 접근할 수 있기에 bar() 는 클로저함수라고 할 수 있다. 하지만 이것은 스코프규칙을 명확히 보여주는 예시이고, 클로저의 일부를 보여줄 뿐이다.


function outer(){
  var a = 2;
  function inner() {
    console.log(a);
  }
  return inner;
}

var closure = outer();

closure(); // 2

위의 두번째 예시코드는 클로저란 무엇인가에 대해 정말로 잘 설명해줄 수 있는 코드이다.  아주 간단해 보이지만, 천천히 음미할수록 클로저에 대해 본질적으로 이해할 수 있다.

코드설명
  : outer함수는  inner함수 자체를 값으로 반환한다.
  : inner함수는 outer함수의 렉시컬 스코프에 접근할 수 있다. 
  : closure 라는 변수는 outer() 함수를 실행하여 반환한 값(inner함수 자체)이 할당된다.
  : closure를 실행(clousure( ))하여  inner( ) 함수를 실행하는 코드이다.

 

여기서 주목해야할 것은 inner함수가 실행되는 곳의 위치이다.

: inner() 함수는 함수가 선언된 렉시컬 스코프 밖에서 실행되고 있다. 일반적으로 outer( )가 실행된 후에는 가비지 콜렉터에 의해 outer()의 내부 스코프가 사라질 것이다. 하지만 여기서 outer()의 내부스코프는 클로저에 의해 사라지지 않는다. 왜냐하면 outer의 내부 스코프는 closure 라는 변수에 할당된 inner() 함수에 의해 여전히 사용 중이기 때문이다. inner함수는 outer함수의 렉시컬 스코프를 가지고 있으므로, inner 함수가 참조할 수 있도록 outer함수의 렉시컬 스코프는 사라지지 않고 남아있는 것이다.  이것은 inner 함수가 클로저라는 변수를 통해 바깥에서 실행되었지만, 그 외부함수에 대한 스코프를 여전히 기억하고 사용할 수 있는 클로저의 본질을 잘 보여준다.

내부함수(inner)를 자신이 속한 스코프 밖에서 호출하여도, 처음 선언되었던 곳의 스코프를 기억하고 참조를 유지하는 것. 그것이 바로 클로저이다.

 

클로저는 왜 사용하는가? ( Why & How)

클로저는 비공개변수를 둠으로써 정보은닉을 할 수 있고, 여러 내부 함수를 가진 하나의 함수를 반환하도록 하는 모듈 패턴을 구현하는데 적합하다. 대표적인 클로저의 활용사례를 살펴보도록 하자.

비공개변수 (정보은닉) & 모듈화

var counter = function() {
  var count = 0;

  return {
    increase: function() {
      count += 1
    },
    decrease: function() {
      count -= 1
    },
    show: function() {
      console.log(count);
    }
  }
};
var counterClosure = counter();
counterClosure.increase();
counterClosure.show(); // 1
counterClosure.decrease();
counterClosure.show(); // 0

우선 couterClousure의 increase, decrease, show등의 메소드를 counter 함수 밖에서 실행하여도, 해당 스코프에 접근하여 count 변수를 다루고 있으므로 클로져의 사례이다.
여기서 count 변수는 공개되어 있는 메소드(increase, decrease, show)로만 접근 할 수 있으며, 외부에서 직접적으로 접근하여 변경할 수 없다. 이것을 비공개 변수라고 하고, 정보은닉을 통해 보안성을 높일 수 있는 클로저의 장점 중 하나이다.

 

커링

function sum(x) {
  return function(y){
    return x + y;
  }
}

sum(5)(10); // 15

let sum100 = sum(100);  // x의 값을 고정해놓고 재사용할 수 있다.
sum100(1); // 101
sum100(10); // 110

let sum7 = sum(7);
sum7(2); // 9

커링은 함수하나가 N개의 인자를 받는 대신, N개의 함수를 만들어 각각의 인자를 받게 하는 방법이다.
커링은 클로저 개념을 활용한 것인데, x의 값을 고정할 때, sum함수의 스코프를 기억하여 해당 스코프의 x값을 기억하고 계속해서 사용하는 패턴이기 때문이다.
커링과 같은 클로저패턴은 하나의 함수를 마치 탬플릿처럼 사용하며 함수의 재사용성을 높일 수 있다는 장점을 가진다.

댓글