JavaScript/JS

자바스크립트 클로저란? (JS Closure)

hihiha2 2023. 3. 2. 16:07

 

1. 클로저란?

클로저는 자신이 선언될 당시의 환경을 기억하는 함수

클로저는 함수함수가 선언된 렉시컬 환경과의 조합

 

 

클로저를 이해하기 위해서는 실행컨텍스트와 렉시컬환경이라는 개념을 이해하는것이 우선된다.

이것에 대해서는 이전글에서 따로 정리를 해두었다.

 

자바스크립트 실행컨텍스트/ 렉시컬환경(환경레코드,외부환경참조)

1️⃣ 실행컨텍스트 코드를 실행하는데 필요한 환경을 제공하는 객체 (환경: 코드 실행에 영향을 주는 조건이나 상태) 식별자결정을 더욱 효율적으로 하기 위한 수단 모든 자바스크립트의 모든

hihiha2.tistory.com

 

 

특징: 해당함수의 생명주기가 종료되더라도 참조가능 

원리: 실행컨택스트의 렉시컬환경에 정보가 남음 

 

일반적으로 함수가 실행되고 나면 실행콘텍스트의 콜스택에서 제거가 되지만, 콜스택에서 이미 제거가 되더라도 참조할 수가 있다.

이런 함수들을 클로저라고 부른다.

 

클로저가 가능한 배경에는 렉시컬환경이 있다. 렉시컬환경과 클로저에 대해서 아래에 자세하게 기록해 보겠다.

 

 

2. 클로저의 정의 (MDN)

mdn사이트에서의 클로저에 대한 설명.

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
클로저는 함수와 그 외부를 둘러싸고 있는 렉시컬 환경의 조합이다.
In other words, a closure gives you access to an outer function's scope from an inner function.
다른 말로하면, 클로저란 내부함수에서 외부함수에 접근할 수 있는 권한을 주는 것을 말한다.
In JavaScript, closures are created every time a function is created, at function creation time.
자바스크립트에서 클로저는 함수가 생성되는 매순간 만들어진다.

➡️ 정의에서는 렉시컬환경과의 조합이라는 말이 중요한 것 같다.

일반적으로 함수가 실행되고 나면 생명주기가 끝이 나서 실행컨텍스트 내에 있는 call satck에서도 제거(pop)가 된다. 그러면 그 이후에 그 함수를 호출하면 아무 값도 나오지 않아야 할 것 같은데 이미 call stack에서 제거된 함수라고 하더라도 이 이후에 호출하면 값이 정상적으로 나온다. 이유가 뭘까? 그게 가능하도록 해주는 것이 바로 렉시컬환경이다.

자바스크립트의 모든 함수는 환경레코드와 자신의 상위 스코프를 기억한다. 따라서 함수가 이미 실행되어 스택에서 제거가 된 상태여도 렉시컬환경에는 남아있기때문에 호출을 해 값을 불러올수가 있는 것이다.

(Lexical) Closure. 클로저는 함수가 선언될 당시의 환경(environment)을 기억했다가 나중에 호출되었을때 원래의 환경에 따라 수행되는 함수이다. 이름이 클로저인 이유는 함수 선언 시의 scope(lexical scope)를 포섭(closure)하여 이후 실행될 때 이용하기 때문이다. 자주 '이름 없는 함수(익명함수)'와 혼동되곤 한다. 많은 언어의 익명함수가 closure를 포함하기 때문에 편하게 부를땐 서로 구분없이 부르기도 한다.

 

3. 구체적 예시

코드를 통해 살펴보자.

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

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

outer함수를 호출하면(③) outer함수는 중첩함수 inner을 반환하고 생명주기를 마감한다. (실행컨텍스트에서 제거). 이때 outer함수의 지역변수 x와 변수 값 10을 저장하고 있던 outer함수의 실행컨텍스트가 제거되었으므로 outer함수의 지역변수 x 또한 생명주기를 마감한다.
➡️ x변수에 접근할 수 있는 방법은 없어보인다.

But 위 코드의 실행결과(④)는 outer함수의 지역변수인 10이다. 이미 생명주기가 종료되어 실행컨텍스트 스택에서 제거된 outer함수의 지역변수가 다시 부활이라도 한듯이 동작하고있다

중첩함수 inner가 이미 생명주기를 마감한 outer함수 속 내부함수의 지역변수 x를 참조할 수 있다면 이때의 inner를 클로저라고 한다.

 

4. inner 함수가 클로저가 될 수 있었던 과정

outer함수는 실행컨텍스트에서 완전하게 제거가 되었다. 하지만 outer함수의 렉시컬환경까지 손을 대지는 않는다. ella는 inner함수 객체를 참조하고 있고 inner함수 객체는 본인의 내부슬롯에 저장된 outer함수의 렉시컬환경을 참조하기 때문에 없어지지 않는다.
따라서 ella에 의해 inner함수를 다시 호출하면 outer함수 안에 있는 10을 값으로 가지고 있는 변수x를 다시 참조할 수 있게 된다.
inner함수의 객체기준 내부함수는 생명주기를 마감하고 실행컨텍스트 스택에서 사라졌지만 기억하고 있는 내부슬롯에 저장된 상위스코프에 의존하여 상위스코프내의 식별자를 참조할 수 있게 되는것이다. 이것이 바로 클로저라는 개념이다.

 

5. 클로저와 정보은닉

클로저의 이러한 특징때문에 정보은닉이 가능하다. 따라서 클로저는 상태(state)를 안전하게 변경하고 유지하기 위해 사용한다.

➡️ 상태가 의도치않게 변경되지 않도록 은닉

 

 

🔫  리액트에서 state를 관리하는 훅인 useState도 클로저의 일종이다!!

const [inputs, setInputs] = useState({
    username: '',
    email: ''

inputs를 건드리지 않고 setInputs를 이용해서 상태의 변화를 관리하는 것도 클로저를 이용한것이다.

 

 
 
 
클로저에 대해 알아보았다.
클로저를 이해하기 위해서 이 개념에 대해서 여러번 봤는데, 처음에는 진짜 이해하기 어려웠던 개념이었다.
그래도 확실히 자바스크립트에 대해서 공부를 하면 할수록 클로저에 대해서도 더 이해가 잘된다. 
클로저가 어려웠던 이유는 클로저 이전에 우선적으로 알아야할 개념들이 많아서였던 것 같다.
실행컨텍스트와 렉시컬환경에 대해 공부하고 다시보니, 확실히 이해가 된다.
 
 
 
 
 
✍️강의를 듣고 책을 보면서 직접 공부하고 정리한 내용입니다
이전에 직접 velog에 정리한 글을 바탕으로 새롭게 안 내용을 추가하였습니다📚
 

 

참고자료

모던자바스크립트, mdn, 나무위키

junh0328/prepare_frontend_interview

우아한Tech 엘라의 Scope & Closure

하루의 실행 컨텍스트