본문 바로가기
Javascript

[Javascript] Closure

by 슥짱 2022. 4. 14.

클로저란?

MDN에 따르면, 클로저는 함수와 함수가 선언된 렉시컬 환경의 조합이라고 한다. 이것만 들으면 무슨 말인지 더 모르겠다😅. 이를 쉽게 이해하기 위해선 먼저 변수의 렉시컬 스코핑이 어떻게 이루어지는지 알아야 한다.

렉시컬 스코핑

자바스크립트에서 함수가 호출되면 먼저 필요한 변수를 자신의 렉시컬 환경에서 찾는다. 만약 자신의 내부 렉시컬 환경에 필요한 변수가 없으면, 참조하고 있는 외부 렉시컬 환경으로 검색 범위를 넓히고, 그래도 변수를 찾을 수 없으면 전역 환경까지 계속해서 검색 범위를 넓힌다. 중간에 필요한 변수를 찾았으면, 해당 변수를 사용한다.

 

이렇게 내부 함수가 외부 환경의 변수를 사용할 때, 외부 함수에서 내부 함수를 반환하면 클로져를 형성하게 된다. 외부 함수가 종료되었지만, 내부 함수는 여전히 같은 변수를 참조한다. 모든 함수는 자신이 생성된 렉시컬 환경의 참조를 숨김 프로퍼티 [[Environment]]에 저장하고 있기 때문에 언제 어디에서 호출 되어도 같은 외부 렉시컬 환경을 유지할 수 있다.

예제

이제 코드로 살펴보자. 두 개의 함수, state와 setState를 반환하는 myUseState가 있다.

function myUseState(initialState) {
  let value = initialState;

  function state() {
    return value;
  }

  function setState(cb) {
    value = cb(value);
  }

  return [state, setState];
}

const [state, setState] = myUseState(3);

state와 setState 각자의 내부 렉시컬 환경에는 value라는 변수가 없기 때문에 외부 환경인 myUseState에서 value를 찾는다. 이들은 외부 렉시컬 환경에 대한 참조를 기억하기 때문에 myUseState 함수가 호출된 이후에도 항상 같은 value를 가르킨다.

 

setState 함수에 콜백함수를 전달 해, value를 변경해보자.

console.log(state()); //3
setState((s) => s ** 2);
console.log(state()); //9

state 함수로 value를 받아 확인해보면 변경된 value를 확인할 수 있다.

 

myUseState를 실행할 때마다 매번 다른 렉시컬 환경이 만들어지기 때문에, setState1, setState2, setState3는 state의 값에 영향을 주지 못한다.

const [state, setState] = myUseState(3);

const setState1 = myUseState(3)[1];
const setState2 = myUseState(3)[1];
const setState3 = myUseState(3)[1];

setState1((s) => s + 1);
setState2((s) => s * 100);
setState3((s) => s % 7);

console.log(state()); //3

클로저는 왜 쓰일까?

클로저를 사용하면 클래스를 흉내낼 수 있다. 다시 myUseState 함수로 돌아가면, 외부에서 접근 불가능한 변수인 value는 마치 private 접근 지정자가 붙은 멤버 변수와 같다. 그리고 멤버 변수를 그대로 반환하는 state 함수는 getter, setState 함수는 setter와 대응된다. 클로저를 사용하면 거추장스러운 클래스 문법 없이 상태를 유지하며 동시에 은닉화 할 수 있다.

 

이벤트 기반 코드와 같이, 객체가 하나의 메소드를 갖는 경우는 모두 클로저로 대체할 수 있다. 대표적으로, 가장 많이 사용되는 상태관리 라이브러리인 리덕스도 클로저를 사용해 구현됐다고 한다.

 


참고 문헌

댓글