[자바스크립트] 클로저

Date:     Updated:

카테고리:

태그:

들어가며

삼대장

자바스크립트 좀 친다? 하면 당연히 알고 가야할 개념이다. 별거 아닌데, 어려운 용어들이 들어가서 지레 겁을 먹었던 기억이 있다. 친숙한 변수들로 한번 같이 알아보고자 한다.

✅클로저

오늘은 클로저에 대해 알아보겠다.

🚀why?

why

왜 클로져를 알아야 할까?

프론트엔드 단골 면접 질문이기도 하다. 사실 공부하면 해당 내용에 대한 지식을 쌓을 수 있다. 그냥 공부하면 알게되는건데 왜 물어보는지 궁금해서 조금 생각해 보았다.

내가 내린 결론은 다음과 같다.

  1. 자바스크립트의 실행과정을 아는지 확인
  2. 프론트엔드에서 자주 쓰고있음

💡1번: “자바스크립트의 실행이 어떻게 되는지 아는지 확인”

일단 클로저를 이해하려고 하다보니, 실행컨텍스트를 이해해야 한다는 것을 알게 되었다. 한편으로는 실행컨텍스트를 이해하는것이 this바인딩, 클로저 등의 자바스크립트의 신묘함을 이해하는 핵심인 것이다. 저 세가지 친구들은 어떻게 시작해도 결국에는 자바스크립트 실행의 전반적인 과정을 이해하지 않으면 설명이 힘들다. 그래서 자바스크립트의 작동을 잘 아는지 확인하기 위해서 질문한다고 생각한다.

한편으로, 클로저는 프로그래밍에서 중요한 개념 중 하나이다. 프론트엔드 개발자를 희망하는 이들에게, 클로저는 자바스크립트에 있는 중요한 개념이기도 하지만, 이는 파이썬, 루비, 쓰칼라 등 많은 프로그래밍 언어에서 지원하는 기능이고, 자바스크립에 국한된것이 아니라 프로그래밍 전반적으로 중요한 개념인 것이다.

그렇다. 사실 그냥 중요한거니까 물어보는 것이다. 언어의 내부적인 작동 원리를 파악하다보면 알수밖에 없는 그럼 개념인 것이다.

💡2번: “프론트엔드 하면 이미 써봤음”

(나를 포함한)프론트엔드 신입 개발자(를 희망하는 취준생)라면 리액트를 한번씩 써봤을 것이다. 사족을 붙이자면 스벨트 이런거 사이드프로젝트로 꼭 써보고싶다.

리액트를 썼다면, 당연히 useState()를 사용해 봤을 터이다.

const [count, setCount] = useState(0);

이라고 하면, count의 값은 setCount로만 변경할 수 있는 것이다. 어떻게 그럴수가 있을까?? 그냥 count = 30 으로 값을 할당할수 없는 것일까?

const counter = function (){
	let num = 0;
	return{
		increase(){ return ++num},
		decrease(){ return --num}
	};
}();

num이라는 변수는 increase()decrease()로만 그 값을 변경할 수 있으며, 직접적인 변경이 불가능한다. 이와같이 클로져를 통해 해당 변수를 숨겨두고 문지기(?)를 통해서면 값을 조작할 수 있는 것이다. 리액트의 useState가 그러하며, 리덕스의 reducer가 그러하다.

(+💡실제 useState는 reconciler가 shared패키지들을 이용하여, 훅 구현체를 주입한다. 따라서 위의 설명내용은 useState의 작동도 맞지만, 더 정확히는 reconciler가 어떻게 작동하는지에 대한 내용이며, 해당 내용은 리액트 분석 글(클릭)에 작성할 예정이다.)

(+💡아울러 리덕스의 middleware는 Currying을 통하여 lazy execution방식으로 인자를 붙잡아두는데, 이 때 클로저 개념이 사용되는 것이다.)

그렇다. 우리는 리액트와 리덕스로 state를 조작할 때 해당 개념을 사용하고 있던 것이다.

🚀what?

what

위에서 클로저를 왜 알아야 하는지 알아보았다. 그렇다면 클로저란 무엇일까?

mdn에서는 “함수와 그 함수가 선언된 렉시컬 환경과의 조합이다” 라고 말한다. 당최 무슨소리인지 모르겠고 감도 오질 않는다. 그렇다면 이해되지 않음을 뒤로한 채, 클로저라는 어원은 어디에서 왔는지 알아보도록 하겠다.


function 손흥민(){

	const 공격 = 1;
	const 수비 = 0;

	function 축구선수(){ console.log(공격) }
	return 축구선수;

}
const 축구선수호출 = 손흥민();
축구선수호출();

위의 코드를 보면, 축구선수() 을 실행시키고 console.log(공격)가 찍히게 된다. 손흥민()공격와 y를 들고있으며, 축구선수()공격를 받아서 사용한다. 정리 해 보겠다.

이처럼, 참조되는공격변수를 자유변수라고 한다. 그리고… 축구선수함수는 자유변수에 묶여있다.

“자유변수에 묶여있다”는 영어로뭘까? closed to free variable이다.

그렇다. 축구선수()은 자유변수 공격에 대해 closed되어있기 때문에, 즉 묶여있기때문에 🚨축구선수()은 클로저(Closure)🚨라고 한다.

도식도

🚀Where?

where

MDN에서는 “함수와 그 함수가 선언된 렉시컬 환경과의 조합이다” 라고 말하지만, 나는 그냥 묶여있는 함수로 이해했다.

손흥민은 이미 본인의 일을 다 하고 실행컨텍스트(콜스택)에서 사라져버렸는데, 축구선수는 어디에 묶여있는걸까? 아무것도 없는데 묶여있을수는 없다. 이부분이 가장 중요한 부분이다. 손흥민은 실행컨텍스트(콜스택)에서 사라졌지만, 손흥민이들고있던 공격수비는 버젓이 살아있고, 축구선수는 여기에 묶여있는 곳이다. 어떻게 이럴수가 있을까?

자바스크립트는, 축구선수가 아직 공격를 참조함을 알아차리고, 손흥민이 죽었음에도 공격를(렉시컬환경을) 살려두는 것이다. 아무나 살려두는게 아니다. 필요로 하니까 살려두는 것이다. 필요없는 변수들(렉시컬환경)은 가비지컬렉터가 먹어치우는 것이다.

따라서 축구선수는, 축구선수를 위해 남겨둔 공격에 묶여서, 본인의 일을 하는 것이다. 공격손흥민 렉시컬환경에 남아있는것이다.

🚀클로저 작동 한눈에 정리

다음과 같다

설명용GIF

그러면, 수비는 사용하지 않는데 메모리낭비 아니냐? 라고 생각할 수 있다. (브라우저별로 다르지만) 모던 브라우저에서는 안쓰는 변수까지도 잡아먹는다고 한다. 최신크롬이라면 참조하지 않는 수비는 가비지컬렉터가 잡아먹을 것이다.

어려운 용어로 정리하면…

  • 손흥민함수의 실행컨텍스트는 실행컨텍스트 스택에서 제거되지만, 손흥민의 렉시컬 환경까지 소멸하는것은 아니다
  • 손흥민함수의 렉시컬환경은 축구선수함수의 [[Environment]] 내부슬롯에 의해 참조되고 있다. `축구선수`함수는 전역변수 `축구선수호출`에 의해 참조되고 있으므로…. 가비지컬렉션의 대상이 되지 않는다

어떤느낌인지는 알겠지만, 용어가 몇개 더 들어가니 어렵게 느껴진다. 자바스크립트의 큰 틀을 잡고 다시보면 그저 슥 보고 지나갈 내용일 것이다.


맨 위로 이동하기

javascript 카테고리 내 다른 글 보러가기

댓글 남기기