클로저 (Closure)

민겸·2023년 3월 6일
0

JavaScript

목록 보기
18/20

클로저란?

MDN에서 찾아보면 나오는 클로저에 대한 소개이다.

클로저는 자바스크립트가 제공하는 가장 강력한 추상화이며, 동시에 잠재적으로 매우 혼란스러울 수 있는 개념이다.

추상화(Abstraction)란?

MDN에서 찾아보면 추상화의 정의에 대해 이렇게 나와있다.

컴퓨터 프로그래밍에서 추상화란 복잡한 소프트웨어 시스템을 효율적으로 설계하고 구현할 수 있는 방법이다. 추상화는 뒷편 시스템의 기술적 복잡함을 단순한 API 뒤에 숨긴다.

그리고, 위키피디아에는 이렇게 정의되어있다.

컴퓨터 과학에서 추상화는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것을 말한다.

위키피디아에서는 한국어 번역 버전 대신 영어 원문으로 읽기를 추천한다. 내용의 차이가 어마어마하다..

가장 강력한 추상화라는 의미는, 아마도 클로저를 통해 얻을 수 있는 이점들이 추상화의 정의에 가깝다는 의미인 건가...? 클로저와 추상화가 왜 연결되는 건지 아직 잘 모르겠다.


다시 클로저에 대한 설명으로 돌아오자.

MDN에 나오는 클로저의 정의는

  • 하나의 함수,
  • 함수의 외부 스코프(어휘적 환경lexical environment)에 대한 참조

의 조합이다.

클로저를 이해하기 위해선 어휘적 환경을 둘러싼 렉시컬 스코프에 대한 이해가 먼저 필요하다.

렉시컬 스코프

자바스크립트는 함수를 어디에서 정의했는 지에 따라 상위 스코프가 결정되는 렉시컬 스코프(lexical scople)가 적용된다. 정적 스코프(static scope)라고 불리기도 한다.

const a = "1";

function funcA() {
  const a = "2";
  funcB();
}

function funcB() {
  console.log(a);
}

funcA(); // 콘솔 결과 : 1
funcB(); // 콘솔 결과 : 1

위 예제의 funcA, funcB 함수는 둘 다 전역에서 선언된 함수이다. 즉, 두 함수는 전역이 상위 스코프인 것이다. funcB 함수가 funcA 함수 안에서 호출되었지만, 자바스크립트는 렉시컬 스코프를 따르는 언어이기 때문에 funcB의 상위 렉시컬 스코프는 전역 스코프가 되고 console.log(a)는 스코프 체인을 통해 전역 변수 a의 값을 찾아 콘솔에 출력한다.

자바스크립트에서 모든 함수는 바깥의 렉시컬 환경에 대한 참조유지한다.
쉽게 말해서, 자바스크립트에서 모든 함수는 선언될 때 선언된 위치의 상위 스코프를 기억한다.

코드 예시

간단한 코드를 예시로 들어보겠다.

function outer(){

  const variable = "지역_변수_값";
  return function inner(){ 
    console.log(`hi, ${variable}`);
  }
}

const sayHi = outer();

sayHi(); // 콘솔 결과 : ???

코드의 작동 순서를 예상해보자.

  1. 런타임 이전에 전역에 함수outer, 변수sayHi가 선언된다.
  2. 런타임에 변수 sayHiouter함수의 반환값인 inner함수가 할당되고 return으로 함수 호출이 끝나면서 outer함수와 outer함수의 지역변수 variable는 생명 주기를 마감한다.
  3. sayHi가 호출되면서, inner함수가 호출된다.
  4. inner함수가 작동하면서 콘솔이 찍혀야하지만, variable은 이미 생명 주기가 마감된 변수이기 때문에 어디에서도 참조할 수 없으므로 refernceError을 띄운다.

4번의 상황을 코드로 풀어서 설명해보자면 아래와 같다.

const sayHi = outer(); // 호출 결과 inner 리턴
// 그러므로, sayHi에 inner를 할당하는 것과 같아진다.
sayHi = function inner(){
  console.log(`hi, ${variable}`);
}
sayHi();

그런데, 실제로 작동시켜보면 에러는 나오지 않고 콘솔에는 hi, 지역_변수_값이 찍히게 된다. 이미 생명이 다한 variable 변수가 다시 부활한 것처럼 동작하고 있다.

이처럼 외부 함수보다 중첩 함수가 더 오래 생존하는 함수를 클로저라 부른다.

다시 한번 MDN의 정의를 떠올려보자.

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

즉, inner함수는

  1. 자기자신
  2. outer함수의 호출로 인해 선언된 렉시컬 환경의 조합

으로 클로저를 형성한다.

쉽게 말하면, inner함수는 선언되면서 선언된 위치인 outer함수의 렉시컬 환경을 기억한다. outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만 outer함수의 렉시컬 환경까지 소멸하는 것은 아니다. outer함수의 렉시컬 환경은 inner함수가 참조하고 있고 inner함수는 다시 sayHi에 의해 참조되고 있으므로 가비지 컬렉션의 대상이 되지 않기 때문이다.

그리고 살아있는 outer함수의 렉시컬 환경에는 "지역_변수_값"이 값으로 할당된 variable 변수가 있다.

참고 출처 :

https://stackoverflow.com/questions/111102/how-do-javascript-closures-work
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
https://developer.mozilla.org/ko/docs/Web/JavaScript/Language_overview#%ED%81%B4%EB%A1%9C%EC%A0%80_closures
모던 자바스크립트 딥 다이브

profile
기술부채상환중...

0개의 댓글