클로저(Closure)에 대하여

huurray·2021년 4월 5일
3
post-thumbnail

들어가기

클로저는 자바스크립트 고유의 개념이 아니라 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어들에서 공통으로 사용되는 중요한 특성이다.

ECMAScript 언어 명세에는 클로저(Closure)에 대한 정의는 없다. 클로저는 자바스크립트가 채용하고 있는 기술적 기반 혹은 컨셉이 때문이다. 자바스크립트의 실행 컨텍스트의 개념을 이해하고 공부했다면 클로저에 쉽게 접근할 수 있다.

클로저의 개념

클로저에 대해 MDN에서는 다음과 같이 정의하고 있다.

클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다.

선언된 어휘적 환경은 렉시컬환경(Lexical Environment)을 말한다. 앞서 실행 컨텍스트에 대해 공부할 때 공부했던 개념이 빛을 볼 시간이다.

간단히 다시 정리해보면, 렉시컬 환경은 Environment Record와 Outer Environment Reference로 이루어진다. 그리고 함수가 호출되고 자바스크립트 엔진이 코드 해석을 위해 이 정보들을 열람할 때는 먼저 자신의 렉시컬 환경의 Environment Record에서 변수를 찾아보고, 없다면 바깥 렉시컬 환경을 참조하여 찾아보는 식으로 탐색한다. 이러한 중첩 스코프 탐색은 해당하는 이름을 찾거나 바깥 렉시컬 환경 참조가 null이 될 때 탐색을 멈춘다.

이러한 실행 컨텍스트의 개념과 같이 클로저를 정의해보면, 클로저는 호출된 함수가 바깥 렉시컬 환경을 참조할때는 외부 함수의 실행 컨텍스트 종료 여부와 상관없이 호출된 함수의 참조에 유효하게 작용하여 사라지지 않는 환경을 이용한 함수를 말한다.

이런 개념을 활용하면 아래와 같은 일도 가능하다.

// 예시
function outer(){
  var count = 0;
  function inner(){
    count++
    console.log(count);
  }
  return inner;
}

var func = outer();

func(); // 1
func(); // 2
func(); // 3

outer 함수는 func라는 변수에 할당될 때 호출되고 소멸되지만 func함수가 실행 될때에는 inner함수의 Outer Environment Reference에서 outer함수의 렉시컬 환경을 가지고 있기 때문에 저렇게 state관리를 할 수 있다. 이게 클로저의 기본 개념이다.

즉시 실행 함수 표현(IIFE)

IIFE는 즉시 실행 함수 표현(Immediately Invoked Function Expression)이다.
쉽게 말해 함수 선언과 동시에 즉시 실행되는 함수를 의미한다. 함수는 익명함수여도 상관 없고 기명함수여도 상관 없다.

(function () {
   ...contents
})();

클로저를 가장 단순하게 만들수 있기 때문에 단순히 전역 스코프를 오염시키지 않기 위해 클로저를 사용해야하거나 또는 모듈화에서 잘 쓰이는 표현 방식이다.

캡슐화(Encapsulation)와 은닉화(Hiding)

그럼 클로저는 어떻게 전역 스코프를 오염시키지 않고 모듈화가 가능한 걸까?
바로 객체지향 프로그래밍의 특징인 캡슐화은닉화를 클로저를 사용해서 구현할 수 있기 때문이다.

먼저 예제 코드를 보자.

function Person(name) {
  this._name = name;
}

var me = new Person('jun');

console.log(me._name);  // 'jun'

me._name = 'somebody';

console.log(me._name);  // 'somebody'

this._name은 _를 앞에 붙여줌으로 네이밍 컨벤션을 기준으로 외부 접근 불가(Private)의 의미를 가진다. 그러나 자바스크립트에서는 private, public, protected 같은 ‘접근 수정자’를 제공하지 않기 때문에, 예제에서의 속성 _name은 ‘접근 불가’라는 의미만 가질 뿐 실제론 외부에서 접근이 가능하다. 따라서 me._name으로 콘솔에 출력하면 값을 확인할 수 있고 변경도 할 수 있다.

그럼 IIFE를 이용해 클로저를 만들어 외부에서 접근할 수 없도록 캡슐화와 은닉화를 해보자.

var Person = (function () {
  var _name;

  function _Person(name) {
    _name = name;
  }

  _Person.prototype = {
    getName: function () {
      return _name;
    },
    setName: function (name) {
      _name = name;
    },
  };

  return _Person;
})();

var me = new Person('jun');

console.log(me._name); // undefined
console.log(me.getName());  // 'jun'

me.setName('somebody');

console.log(me._name); // undefined
console.log(me.getName());  // 'somebody'

IIFE 내부에 별도의 객체를 만들고 객체 자체를 반환하는 형태로 Person 생성자를 만들어 주었다.
이제 은닉화된 _name과 name은 외부에서 접근할 수 없다.

참고

poiemaweb - Closure
MDN - Closure
HEROPY Tech - Closure

profile
꾸준히 발전하는 프론트엔드 개발자입니다.

0개의 댓글