(JS) 클로저(Closure)와 즉시실행함수(IIFE)

호두파파·2021년 3월 29일
1

잊고 넘어가기 쉬운 개념을 한 번씩 리마인드하기 위해 "코어 자바스크립트"에 작성된 내용을 바탕으로 개념을 리마인드해보려고 한다

이번 주제는 클로저와 독특한(?) 함수 실행방식인 즉시실행함수에 대해 정리해보려고 한다.


JavaScript의 정적 스코프

JavaSript의 스코프는 정적 스코프(Lexical Scope)를 따른다. 즉, 프로그램이 실행되기 전에 코드를 읽어내려가며 스코프가 정해지기 때문에 함수 선언 시에 함수의 스코프가 정해지는 것이다.

const x = 3;

function f() {
  console.log(x);
  console.log(y);
}

{// 새로운 블록 스코프를 만들기 위한 블록 
  const y = 2;
  f();
}

변수 y가 블록 안에 선언되어 있기 때문에 해당 블록 안에서만 유효한다. 함수 f()는 블록 안에서 실행되기 때문에 변수 y에 접근해 3과 2가 출력될 것이라고 예상하기 쉽다.
하지만 JavaScript는 정적인 스코프를 베이스로 따르기 떄문에 함수 f()가 선언된 위치를 기반으로 스코프가 결정된다. 따라서 함수 f()선언 당시에 y는 존재하지 않는 변수이므로 에러가 발생한다.

스코프는 함수를 호출할떄가 아니라 함수를 어디에 선언하였는지에 따라 결정된다.
이를 정적 스코프, 혹은 렉시컬 스코프라 한다.

클로저(closure)

MDN에서 밝히고 있는 클로저의 정의는 다음과 같다.

클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다

"그 함수가 선언될 때의 렉시컬 환경(Lexical Enviroment)"란 내부 함수가 선언됐을 떄의 스코프를 의미한다.즉, 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical Enviroment)인 스코프를 기억해 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수를 말한다.

이를 조금 더 축약해서 말하면

클로저는 자신이 생성될 때의 환경(Lexical Envoriment)을 기억하는 함수라고 정의할 수 있다.

클로저에 의해 참조되는 외부함수의 변수는 자유변수(Free Variable)라고 부른다. 클로저라는 이름은 자유변수에 함수가 닫여 있다(closed)라는 의미로 의역하면 자유 변수에 엮여있는 함수라는 뜻이다.

클로저와 활성 객체(Activation object)

클로저의 작동 원리를 실행 컨텍스트의 관점에서 설명하면, 내부함수가 유효한 상태에서 외부함수가 종료하여 외부함수의 실행 컨텍스트가 반환되어도, 외부함수 실행 컨텍스트 내의 활성객체((Activation object)(변수, 함수 선언 등의 정보를 가진)는 내부 함수에 의해 참조되는 한 유효하여 내부함수가 스코프 체인을 통해 참조할 수 있다.

외부함수가 이미 반환되었어도 외부함수 내의 변수는 이를 필요로 하는 내부함수가 하나 이상 존재하는 경우 계속 유지된다. 이때 주의할 점은 내부함수가 외부함수에 있는 변수의 복사본이 아니라 실제 변수에 접근한다는 것에 주의해야 한다.


실행 컨텍스트의 활성 객체(Activation object)와 클로저(출처: powema web)

즉시실행함수(IIFE)와 클로저

<!DOCTYPE html>
<html>
  <body>
  <p>클로저를 사용한 Counting</p>
  <button id="inclease">+</button>
  <p id="count">0</p>
  <script>
    var incleaseBtn = document.getElementById('inclease');
    var count = document.getElementById('count');

    var increase = (function () {
      // 카운트 상태를 유지하기 위한 자유 변수
      var counter = 0;
      // 클로저를 반환
      return function () {
        return ++counter;
      };
    }());

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
  </script>
</body>
</html>

코드 출처 : Powema Web

스크립트가 실행되면 즉시실행함수(immediately-invoked function expression: 줄여서 IIFE)가 호출되고 변수 increase에는 함수 function () { return ++counter; }가 할당된다. 이 함수는 자신이 생성됐을 때의 렉시컬 환경을 기억하는 클로저이다.

즉시실행함수는 호출된 이후 소멸되지만 즉시실행함수가 반환한 함수는 increase에 할당되어 increase 버튼을 클릭하면 클릭 이벤트 핸들러 내부에서 호출된다. 이때 클로저인 이 함수는 자신이 선언됐을 때의 렉시컬 환경인 즉시실행함수의 스코프에 속한 지역변수 counter를 기억한다.

따라서 즉시실행함수의 변수 counter에 접근할 수 있고 변수 counter는 자신을 참조하는 함수가 소멸할때까지 유지된다.

즉시실행함수는 한번만 실행되므로 increase가 호출될때마다 변수 counter가 재차 초기화될 일은 없을 것이다. 변수 counter는 외부에서 직접 접근할 수 없는 private변수이므로 전역 변수를 사용했을때와 같이 의도되지 않은 변경을 걱정할 필요도 없기 때문에 보다 안정적인 프로그래밍이 가능하다.

불변성(Immutability)을 지향하는 함수형 프로그래밍에서 부수효과(side Effect)를 최대한 억제해 오류를 피하고 프로그램의 안정성을 높이기 위해 클로저는 적극적으로 사용된다.


출처

모던 자바스크립트(클로저)

profile
안녕하세요 주니어 프론트엔드 개발자 양윤성입니다.

0개의 댓글