자바스크립트 클로저 완벽하게 이해하기

    반응형

    자바스크립트 클로저

     

    자바스크립트의 어려운 문법 중 하나로 손꼽히는 클로저에 대해 자세하게 알아보려고 한다. 특히나 React 를 공부하는 사람으로써 클로저의 개념이 다양한 부분에서 활용되는 만큼 확실하게 이해하고 넘어가야 한다고 생각한다. 그럼 이제 클로저에 대해 좀 더 상세하게 알아보려고 한다.

     

    클로저


    클로저는 함수와 함수가 선언된 렉시컬 환경의 조합을 의미한다. 좀 더 쉽게 말해 내부 함수가 외부 함수의 변수에 접근할 수 있는 구조를 의미하는데 이러한 클로저는 데이터를 숨기고, 캡슐화하는데 주로 사용된다. 

     

    클로저를 사용하는 이유

    let num = 0;
    
    const increase = function () {
      return ++num;
    };
    
    console.log(increase()); // 1
    num = 100; // num에 다시 100을 할당
    console.log(increase()); // 101
    console.log(increase()); // 102

     

    클로저는 데이터를 숨기고 캡슐화 하는데 주로 사용된다고 했는데 위의 코드를 보면 num이라는 변수와 num을 1씩 증가시키는 increase라는 함수 표현식이 작성되었고 console.log( )를 통해 increase 함수를 호출한 결과를 볼 수 있다. 그러나 num이라는 변수는 전역변수로 작성되어 있기 때문에 중간에 num에 새로운 값 100을 할당하면 1, 2, 3의 형태로 증가하는 것이 아닌 1, 101, 102의 값들이 출력되는 모습을 확인할 수 있다. num이라는 변수에 어디서든 접근할 수 있기 때문에 누구든지 접근해서 수정이 가능하다. 

    function outerFnc() {
      let num = 0;
    
      function innerFnc() {
        console.log(++num);
      }
    
      return innerFnc;
    }
    
    const increase = outerFnc();
    
    increase(); // 1
    increase(); // 2
    increase(); // 3

     

    위의 코드에서는 num이라는 변수가 outerFnc라는 함수의 내부에 선언되어 있고 num에 새로운 값 100을 할당하던 위의 코드와 달리 num이라는 변수에 값을 새롭게 할당할 수 없다. 이처럼 데이터를 보호하고, 캡슐화하기 위해 클로저를 주로 사용한다. 

     

    클로저의 작동 원리

    function outerFnc() {
      let num = 0;
    
      function innerFnc() {
        console.log(++num);
      }
    
      return innerFnc;
    }
    
    const increase = outerFnc();
    
    increase();

     

    위 코드를 다시 살펴보면 전역 실행 컨텍스트가 생성되고 increase( ) 함수가 호출되면서 함수의 실행 컨텍스트가 생성된다. increase 함수는 outerFnc 함수를 호출해서 할당하고 있다. outerFnc 함수는 innerFnc 함수를 리턴하고 있다. 실행 컨텍스트가 생성될 때는 LE(Lexical Environment)가 만들어지고 LE는 식별자들을 저장하는 환경 레코드(Environment Record)와 상위 스코프를 가리키는 외부 렉시컬 환경 참조(Outer Lexical Environment Reference)가 있다고 이전에 정리했다.

     

    const increase = outerFnc( )를 통해 outerFnc의 실행 컨텍스트는 함수 실행이 완료되었기 때문에 컨텍스트가 제거되지만  innerFnc는 outerFnc 내부에 작성되었기 때문에 외부 렉시컬 환경 참조 즉 상위 스코프의 변수를 참조할 수 있게 된다. 또한 변수 num은 innerFnc에서 계속 참조하고 있기 때문에 가비지 컬렉션의 대상이 되지 않는다. 

     

    const increase = (function () {
      let num = 0;
    
      return function () {
        return ++num;
      };
    })();

     

    즉시 실행 함수를 사용한 클로저의 모습이다. 마찬가지로 num이라는 변수에 접근할 수 없고 increase( )를 통해서만 값을 증가시킬 수 있다.

     

    클로저를 사용하면 좋은 이유

    • 데이터 유지/보존 
      클로저를 사용하면 외부 함수가 이미 종료된 시점이라도 내부 함수에서 외부 함수의 변수를 계속 사용할 수 있기 때문에 데이터를 계속 사용할 수 있다는 장점이 있다. 외부 함수라는 특정 스코프 내부에 데이터를 넣어두기 때문에 폐쇄성을 가지고 있다.

    • 캡슐화
      함수에 리턴 값으로 객체를 사용하고 여러 함수를 리턴하도록 만드는 클로저 모듈 패턴을 사용해 데이터의 접근을 제한할 수 있다.
    function createCounter() {
      let count = 0;  
    
      return { // 객체를 리턴 하여 사용
        increment: function() {
          count++;
        },
        decrement: function() {
          count--;
        },
        getCount: function() {
          return count;
        }
      };
    }
    
    const counter = createCounter();
    console.log(counter.getCount());  // 0
    counter.increment();
    console.log(counter.getCount());  // 1
    counter.decrement();
    console.log(counter.getCount());  // 0
    
    // 직접 count에 접근하려고 하면 undefined가 반환됩니다.
    console.log(counter.count);  // undefined

     

    • 모듈화
      클로저를 사용해서 데이터와 메서드를 함께 묶어 사용할 수 있기에 모듈화에 유리하다. 
    반응형

    댓글