[자바스크립트] 클로저
카테고리: javascript
태그: deep dive
들어가며
자바스크립트 좀 친다? 하면 당연히 알고 가야할 개념이다. 별거 아닌데, 어려운 용어들이 들어가서 지레 겁을 먹었던 기억이 있다. 친숙한 변수들로 한번 같이 알아보고자 한다.
✅클로저
오늘은 클로저에 대해 알아보겠다.
🚀why?
왜 클로져를 알아야 할까?
프론트엔드 단골 면접 질문이기도 하다. 사실 공부하면 해당 내용에 대한 지식을 쌓을 수 있다. 그냥 공부하면 알게되는건데 왜 물어보는지 궁금해서 조금 생각해 보았다.
내가 내린 결론은 다음과 같다.
- 자바스크립트의 실행과정을 아는지 확인
- 프론트엔드에서 자주 쓰고있음
💡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?
위에서 클로저를 왜 알아야 하는지 알아보았다. 그렇다면 클로저란 무엇일까?
mdn에서는 “함수와 그 함수가 선언된 렉시컬 환경과의 조합이다” 라고 말한다. 당최 무슨소리인지 모르겠고 감도 오질 않는다. 그렇다면 이해되지 않음을 뒤로한 채, 클로저
라는 어원은 어디에서 왔는지 알아보도록 하겠다.
function 손흥민(){
const 공격 = 1;
const 수비 = 0;
function 축구선수(){ console.log(공격) }
return 축구선수;
}
const 축구선수호출 = 손흥민();
축구선수호출();
위의 코드를 보면, 축구선수()
을 실행시키고 console.log(
공격)
가 찍히게 된다. 손흥민()
는 공격
와 y를 들고있으며, 축구선수()
은 공격
를 받아서 사용한다. 정리 해 보겠다.
이처럼, 참조되는공격
변수를 자유변수
라고 한다. 그리고… 축구선수
함수는 자유변수에 묶여있다.
“자유변수에 묶여있다”는 영어로뭘까? closed to free variable
이다.
그렇다. 축구선수()
은 자유변수 공격
에 대해 closed
되어있기 때문에, 즉 묶여있기때문에 🚨축구선수()
은 클로저(Closure)🚨라고 한다.
🚀Where?
MDN에서는 “함수와 그 함수가 선언된 렉시컬 환경과의 조합이다” 라고 말하지만, 나는 그냥 묶여있는 함수로 이해했다.
손흥민
은 이미 본인의 일을 다 하고 실행컨텍스트(콜스택)에서 사라져버렸는데, 축구선수
는 어디에 묶여있는걸까? 아무것도 없는데 묶여있을수는 없다. 이부분이 가장 중요한 부분이다. 손흥민
은 실행컨텍스트(콜스택)에서 사라졌지만, 손흥민
이들고있던 공격
과 수비
는 버젓이 살아있고, 축구선수
는 여기에 묶여있는 곳이다. 어떻게 이럴수가 있을까?
자바스크립트는, 축구선수
가 아직 공격
를 참조함을 알아차리고, 손흥민
이 죽었음에도 공격
를(렉시컬환경을) 살려두는 것이다. 아무나 살려두는게 아니다. 필요로 하니까 살려두는 것이다. 필요없는 변수들(렉시컬환경)은 가비지컬렉터가 먹어치우는 것이다.
따라서 축구선수
는, 축구선수
를 위해 남겨둔 공격
에 묶여서, 본인의 일을 하는 것이다. 공격
은 손흥민 렉시컬환경
에 남아있는것이다.
🚀클로저 작동 한눈에 정리
다음과 같다
그러면, 수비
는 사용하지 않는데 메모리낭비 아니냐? 라고 생각할 수 있다. (브라우저별로 다르지만) 모던 브라우저에서는 안쓰는 변수까지도 잡아먹는다고 한다. 최신크롬이라면 참조하지 않는 수비
는 가비지컬렉터가 잡아먹을 것이다.
어려운 용어로 정리하면…
손흥민
함수의 실행컨텍스트는 실행컨텍스트 스택에서 제거되지만,손흥민
의 렉시컬 환경까지 소멸하는것은 아니다손흥민
함수의 렉시컬환경은축구선수
함수의 [[Environment]] 내부슬롯에 의해 참조되고 있다. `축구선수`함수는 전역변수 `축구선수호출`에 의해 참조되고 있으므로…. 가비지컬렉션의 대상이 되지 않는다
어떤느낌인지는 알겠지만, 용어가 몇개 더 들어가니 어렵게 느껴진다. 자바스크립트의 큰 틀을 잡고 다시보면 그저 슥 보고 지나갈 내용일 것이다.
댓글 남기기