클로저

귤양·2021년 6월 2일
0

Javascript

목록 보기
8/10
post-thumbnail

아래 내용은 학원 수업과 "모던자바스크립트 Deep Dive : 이웅모 저"를 읽고 정리한 내용입니다.

클로저

클로저가 왜 필요한가?

함수가 어떤 값(상태)을 변경해야 하는 경우에 값이 전역 코드에 있다면 해당 값은 모든 사람, 모든 코드가 접근할 수 있기 때문에 정작 필요한 상황에서는 원하는 결과(값)가 나오지 않을수도 있다. 프로그래밍에서 에러란 프로그램이 동작하지 않는 것 뿐만이 아니라, 개발자가 의도하지 않은 상황이 나오게 되는 것도 포함하기 때문에 에러가 발생하게 되는 것이다. 때문에 상태를 안전하게 숨겨두고, 그 상태를 특정 함수만 변경할 수 있도록 하는 것이 필요한데 이런 상황을 만들기 위해서 클로저를 사용한다.

클로저란 무엇인가?

"클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다."

MDN에서는 클로저를 위와 같이 정의하고 있다. 그렇다면 클로저를 제대로 이해하기 위해서는 렉시컬 스코프와 함수에 대해서 제대로 이해하고 있어야 한다는 소리가 된다.

그렇다면 렉시컬 환경이란 무엇일까?

스코프에서 잠깐 언급을 했지만, 자바스크립트의 함수는 함수를 어디에서 정의했는지에 따라서 함수의 상위 스코프를 결정한다고 했고 이런 방식을 렉시컬 스코프라고 한다고 했었다.

또한 함수에 대해서 공부할 때 함수의 유용한 점에 대해서 말했던 적이 있는 것 같은데, 함수는 미리 정의를 해두면, 필요한 시점에 반복 호출하여 사용할 수 있다고 했다. 그렇다면 함수는 정의된 시점과 함수를 실행하는 시점이 다를 수 있다는 것이고, 위에 말했던 렉시컬 스코프가 가능하기 위해서는 함수가 정의된 순간의 상위 스코프를 기억해야 하고, 이를 위해서 함수는 자신의 내부 슬롯에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장하는 것이다.

const x = 1;

function foo() {
  const x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // ?
bar(); // ?

위 코드의 답은 둘 다 1이다. 이 것은 자바스크립트의 함수는 함수가 정의되는 시점에서 이미 상위 스코프가 결정되기 때문에 bar 함수를 어디에서 호출하는지와는 전혀 관계 없이 bar 함수 코드의 내부에서 확인할 수 있는 x의 값은 1이기 때문이다.

정리하자면 렉시컬 스코프란 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값, 즉 상위 스코프의 참조는 함수 정의가 평가되는 시점에 함수가 정의된 위치에 의해서 결정되는 것이고, 이 것이 렉시컬 스코프이다.

그렇다면 클로저는 진짜 무엇인가?

자신을 포함하고 있는 외부 함수보다 중첩(내부)함수가 더 오래 유지되는 경우 외부 함수의 밖에서 중첩 함수를 호출하더라도 외부 함수의 지역 변수에 접근할 수 있는데, 이러한 함수를 클로저라고 부른다.

이게 왜?

일반적으로 스코프가 종료되면 스코프 외부에서는 종료된 스코프 내부에 있는 식별자를 참조할 수 없기 때문에 이런 상황이 특수한 상황이 되는 것이다.

const x = 1;

// ①
function outer() {
  const x = 10;
  const inner = function () { console.log(x); }; // ②
  return inner;
}

// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer(); // ③
innerFunc(); // ④ 10

위의 코드를 잘 살펴보면, innerFunc는 outer 함수를 실행한 값을 할당하고 있기 때문에 inner 함수라고 볼 수 있다. inner 함수를 호출했을 때는 이미 outer 함수의 스코프가 종료되었고, 함수를 호출할 위치가 전역 스코프이기 때문에 inner 함수가 참조해야 할 x 변수는 이미 종료된 outer 함수의 스코프가 아닌 전역 스코프에 있는 x변수의 값인 1이라고 생각할 수도 있다. 하지만 자바스크립트의 함수는 함수를 호출하는 순간이 아닌 함수가 정의되는 순간의 스코프를 상위 스코프로 기억하고 있기 때문에 inner 함수는 스코프 체인에 따라 outer 함수의 스코프에 있는 x변수의 값인 10을 콘솔창에 찍게 되는 것이다.

만약 내부 함수가 외부 함수보다 오래 유지가 되지만, 외부 함수의 식별자를 참조하고 있지 않다면, 외부 함수의 식별자는 가비지 컬렉터에 의해 메모리에서 해제된다. 참조하고 있지 않은 식별자를 메모리에 계속 남겨두고 있는 것은 메모리 공간을 낭비하는 일이기 때문이다.

예제 코드 출처
모던 자바스크립트 Deep Dive : 이웅모 저

profile
디자인하는 Frontend Developer.

0개의 댓글