클로저

sudyn·2023년 8월 24일

JavaScript

목록 보기
5/14

클로저란?

MDN에서는 클로저에 대해 이렇게 정의한다

“A closure is the combination of a function and the lexical environment within which that function was declared.”

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

정의가 난해하니 조금 더 쉽게 예제코드를 보며 알아보자.

function outer() {
  var x = 10;
  function inner() {
    console.log(x); 
  };
  inner();
}

outer(); // 10

위 코드를 보면 outer 함수는 변수 x와 내부함수 inner를 선언하고 inner를 호출한다.
그 결과 10이라는 값이 출력된다.

어떻게 inner가 outer의 지역변수 x에 접근할 수 있을까?
여기서 우리는 스코프라는 개념에 대해 짚고 넘어가야 한다.

스코프는 함수를 호출할때가 아닌 함수를 어디에 선언했는지에 따라 결정된다.
따라서 inner는 outer의 내부에 선언되었기 때문에 outer가 inner의 상위 스코프로 결정된다. 이를 렉시컬 스코핑(Lexical scoping) 이라 한다.

x를 찾아가는 과정의 스코프체인

그렇다면 x값을 찾아가는 과정에 대해 알아보자.

1) 내부함수 inner가 자신의 함수 스코프 내에서 변수 x를 검색한다.
2) 해당 변수가 자신의 스코프내에 없으면 검색이 실패한다.
3) inner는 자신을 포함하는 외부함수 outer의 스코프에서 재검색을 한다.
4) 이때 있다면 x를 참조할 수 있게 된다.(스코프체인)
5) 만약 없다면 전역변수까지 탐색한다.

일단 inner는 outer 내에 속하기 때문에 outer스코프를 외부 스코프(outer lexical environment) 참조로 저장한다. 그리고 inner는 자신의 렉시컬 스코프 체인을 통해 outer의 x를 정확히 참조할 수 있게 된다. 자신이 속한 렉시컬 스코프(전역, 함수 outerFunc, 자신의 스코프)를 참조할 수 있다.

다시 말해, 상위 스코프에 접근할 수 있는 것은 렉시컬 스코프의 레퍼런스를 차례대로 저장하고 있는 실행컨텍스트의 스코프 체인을 자바스크립트 엔진이 검색하기 때문에 가능한 것이다.

그렇다면 inner를 우리가 클로저라고 할 수 있지 않나?
아니다!
inner는 클로저의 개념에서 보기 어렵다. inner는 outer 안에서 선언되고 실행되었을 뿐, outer 함수 외부로 나오지 않았기 때문에 클로저라 하지 않다.

외부함수가 종료된다면?

이번에는 inner 함수를 outer 내에서 호출하는 것이 아닌 반환하는 코드를 살펴보자.

function outer() {
  var x = 10;
  function inner() { 
    console.log(x); 
  };
  return inner;
}

var innerFunc = outer();
innerFunc(); // 10

outer 함수는 내부함수 inner를 반환하고 생명주기를 마감하며 종료된다. 이때 실행컨텍스트의 스택에서 제거되어 outer의 변수 x또한 유효하지 않게 된다.
그렇다면 변수 x에 우리가 접근할 수 있는 방법은 없지 않을까?

그러나 위 코드의 실행결과 x 는 10이 출력된다. 왜?


실행컨텍스트의 관점에서 보자면,

outer 함수가 생명주기를 마감하며 종료되면 실행컨텍스트의 스택에서 제거된다. 하지만 이때 outer의 렉시컬 환경은 남게된다.
내부함수가 유효한 상태에서 외부함수가 종료되어도 외부함수의 실행컨텍스트가 반환되어 외부함수 내의 활성객체(변수 함수 선언 정보)를 내부함수에 의해 참조되는 한 유효하게 된다.
따라서 내부함수가 스코프체인을 통해 참조할 수 있게 된다.

다시 한번 정리하자면, 클로저는
반환된 내부함수가 자신이 선언되었을 때의 렉시컬 환경인 스코프를 기억하여 자신이 선언되었을 때의 스코프 밖에서 호출되어도 그 환경을 접근할 수 있는 함수라 보면된다.
즉, 자신이 생성될때의 렉시컬 환경을 기억하는 함수다.

이때 클로저에 의해 참조되는 외부함수의 변수, 즉 outer의 지역변수 x를 자유변수(free variable)이라 한다. 클로저라는 이름은 이 자유변수에 함수가 닫혀있다(close)는 의미로 자유변수에 엮여있는 함수를 뜻한다.

클로저는 그럼 어떻게 활용될까?

클로저는 렉시컬 스코프를 기억해야하므로 메모리 차원에서 손해를 볼 수 있다. ? 왜?

상태 유지

현재 state를 기억하고 변경된 최신 상태를 유지하는데 활용된다.

function count() {
    'use strict';
    for (let i = 1; i < 10; i += 1) {
        setTimeout(function timer() {
            console.log(i);
        }, i * 100);
    }
}
count();```

### 전역 변수의 사용 억제
버튼이 클릭될때마다 클릭한 횟수가 누적되어 표시되는 카운터를 만들어보자.여기서는 클릭된 횟수가 유지해야할 상태이다.

### 참고
모던 자바스크립트 Deep Dive, 이웅모 저
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
https://www.youtube.com/watch?v=PVYjfrgZhtU
https://meetup.nhncloud.com/posts/86
profile
개발계발하는 프론트엔드 개발자🍠

0개의 댓글