해당 포스팅은 자바스크립트 클로저에 대해서 다룹니다.
JS를 주언어로 다루는 개발자라면 ‘클로저’라는 단어를 몇 번이고 들어봅니다. 설명을 찾아보면 그 당시에는 이해가 되지만, 만약 다른 사람에게 설명한다고 하면 입이 떨어지지 않는 그 애매한 개념에 대해서 오늘 정리해보려고 합니다.
스코프
클로저를 이해하려면 일단 스코프부터 알아야합니다. 스코프는 식별자가 유효한 범위, 실행 컨텍스트의 렉시컬 환경을 뜻합니다. 이렇게 말하니 뭔지 모르겠죠?
지금 당장 “여름 좋아세요?” 라는 물음을 듣는다면 어떤 대답을 할 것 같나요?
“아니요, 저는 겨울이 좋아요.” “네, 저는 여름이 좋아요.” 같은 답을 하겠죠. 여기서 ‘여름’은 당연하게도 해가 쨍쨍해서 밖에 나가면 땀이 주륵주륵 흐르는 계절을 뜻합니다.
근데 ‘여름’이라는 단어는 사람 이름으로도 종종 쓰이곤 합니다. 예를들면 한국고등학교 1학년 1반에 ‘임여름’이라는 학생이 다닐 수도 있겠죠.
만약 당신도 한국고등학교 1학년 1반 학생이고 교실 의자에 앉아있습니다. 그리고 옆자리에 앉은 짝꿍이 주변을 이리저리 살피다가 ‘여름’ 자리가 빈 걸 확인합니다. 그리고 당신에게 “너 혹시 좋아하는 사람있어?” 라고 질문합니다. 당신이 대답이 없자 조심스럽게 귓속말로 “너 혹시 그… 여름 좋아해?”라 묻습니다.
그럼 여기서 ‘여름’은 뭘 의미할까요? 이때는 당연히 같은 반에 친구인 ‘여름’을 의미합니다.
이렇듯이 어떤 대상을 구분할 수 있는 이름(식별자)은 상황에 따라 뜻이 달라지곤 합니다.
const 여름 = "한 해의 네 철 가운데 둘째 철"
function 한국고등학교() {
const 여름 = "우리 반, 단발 머리에 반장인 여자애"
console.log(여름);
}
console.log(여름); // 한 해의 네 철 가운데 둘째 철
한국고등학교(); // 같은 반 단발 머리에 반장인 여자애
방금 예시든 상황을 자바스크립트 코드로 표현했습니다.
여기서 식별자는 ‘여름’입니다. 첫 번째 선언한 ‘여름’이 유효한 범위는 전역 스코프(Global Scope)이고, 두 번째로 선언한 여름이 유효한 범위는 한국고등학교 함수 내부인 지역 스코프(Local Scope)입니다. 둘은 동일한 식별자(이름)이지만 스코프가 다른 별개의 변수입니다.
코드의 환경과 문맥
만약 당신이 아까 짝꿍의 물음에 당신이 ‘여름’을 계절이 아닌 사람으로 인식한 이유가 무엇인가요?
일단 첫 번째로 조명, 온도, 습도… 바로 주변 환경이죠. 당신이 한국고등학교 1학년 1반에 학생이라는 점, 그리고 교실에 앉아있는 점, 동시에 지금 반에 '여름'이라는 학생이 없다는 점. 여러가지 동시에 영향을 받습니다.
그 두 번째는 문맥입니다. “혹시 좋아하는 사람있어?” 라고 물은 이 질문. 그 다음에 나온 “여름”이라는 단어는 계절이 아니라 사람일 확률이 당연히 높습니다.
환경과 문맥. 문맥은 환경에 영향을 받습니다. 만약 환경이 달라졌다면. 당신이 한국고등학교 학생이 아니라 직장인인 상태로 길가다 잡혀서 “혹시 좋아하는 사람이 있으세요?” “혹시 여름 좋아하세요?” 라는 질문을 들어도 사람인 “여름”을 떠올릴 수 없겠죠.
자바스크립트도 똑같이 환경과 문맥을 고려합니다.
“코드가 어디서 실행되며 주변에 어떤 코드가 있는지”를 렉시컬 환경(lexcial environment)라고 부릅니다. 번역하면 어휘적 환경이라는 뜻입니다.
근데 왜 어휘적일까요? 코드는 3D 공간이 존재하지 않습니다. const function 등의 텍스트로 이루어진, 깊게 들어가면 0과 1의 이진법 언어로 이루어진 세상입니다. 코드 상의 환경이 어휘적이라고 부르는 거 아닐까요? 이건 제 개인적인 생각이니 가볍게 읽고 넘겨주세요.
문맥은 렉시컬 환경으로 이루어집니다. 그리고 코드를 실행할 때, 렉시컬 환경과 다른 정보들(Variable Environment, This Bindig)을 수집합니다. 이걸 실행 컨텍스트(execution context)라고 합니다.
결론적으로 자바스크립트 엔진은 식별자를 검색할 때 사용하는 규칙으로 스코프를 사용합니다. 코드를 실행할 때 스코프에 따라 문맥을 고려해야합니다. 이 문맥은 렉시컬 환경으로 이뤄지고요.
그럼 클로저는 뭘까요?
스코프, 실행 컨텍스트, 렉시컬 환경. 많은 길을 돌아왔습니다. 그래서 클로저가 대체 뭘까요?
일단 클로저는 함수형 프로그래밍 언어에서 나타나는 보편적인 특징입니다. 자바스크립트 고유한 개념이 아니죠. 그래서 그런가 정의도 참 여러가지입니다.
함수와 그 함수가 선언된 렉시컬 환경과의 조합이다. - MDN
자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수 - 자바스크립트 핵심 가이드
함수가 특정 코프에 접근할 수 있도록 의도적으로 그 스코프를 정의하는 것 - 러닝 자바스크립트
함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 것 - 자바스크립트 닌자 비급
이미 생명주기상 끝난 외부 함수의 변수를 참조하는 함수 - 인사이드 자바스크립트
자유변수가 있는 함수와 자유변수를 알 수 있는 환경의 결합 - Heading First Javascript
자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만 기억하여 유지시키는 함수 - 함수형 자바스크립트 프로그래밍
음… 당신이 한국고등학교를 졸업하고 어엿한 사회인이 됐다고 합시다. 그럼 당신은 고등학교라는 환경에서 벗어난거죠.
근데 사실 짝꿍이 물었던 것처럼 당신은 정말 여름이를 좋아했습니다. 심지어 첫사랑입니다!☺
한 30살 쯤 누군가가 당시에게 첫사랑은 누구였어? 라고 물어보면 이름은 정확히 기억나지 않을 확률이 높습니다. 그래도 첫사랑의 흐릿한 이미지는 떠오를 겁니다.
const 여름 = "한 해의 네 철 가운데 둘째 철"
function 한국고등학교() {
const 일학년담임선생님이름 = "나미리"
const 이학년담임선생님이름 = "채송화"
const 여름 = "같은 반 단발 머리에 반장인 여자애"
const 내첫사랑은 = () => {
console.log("내 첫사랑은", 여름);
}
return {내첫사랑은}
}
const 고등학생때기억 = 한국고등학교()
고등학생때기억.내첫사랑은(); // 내 첫사랑은 같은 반 단발 머리에 반장인 여자애
이걸 코드로 표현하면 위처럼 나타납니다. 고등학생인 환경은 인생에서 사라졌고 다시는 겪을 수 없습니다. 전역 스코프에 고등학생때 기억만 남기고 한국고등학교라는 함수는 생명주기가 종료됐죠. 일학년, 이학년때 담임 이름은 기억을 못해도 ‘내첫사랑은’ 함수 속에 있는 ‘여름’이라는 변수는 기억해냅니다.
이제 클로저에 대한 정의를 다시 한 번 확인해볼까요?
이미 생명주기상 끝난 외부 함수의 변수를 참조하는 함수 - 인사이드 자바스크립트
자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만 기억하여 유지시키는 함수 - 함수형 자바스크립트 프로그래밍
가장 와닿는 정의는 이 두 가지입니다. 이미 생명주기가 끝난 고등학교 함수에서 ‘여름’이라는 변수를 다시 참조할 수 있는 함수(내첫사랑은)
‘고등학생때기억’ 변수가 생성될 때 ‘한국고등학교’라는 스코프 내에서 알 수 있던 변수 중에 사용할 변수(여름)만 기억해서 유지시키는 함수(내첫사랑은)겠네요.
마치면서
포스팅에서는 이해를 돕기위해 비유를 들었습니다. 비유는 어떤 개념을 이해할 때 빠르게 학습할 수 있습니다. 하지만 그 원관념을 완벽하게 설명하진 못합니다. 이 글로 간단히 이해했다면 <자바스크립트 deep dive> 나 <코어 자바스크립트> 책으로 비유 없는 설명된 개념도 확인해보시길 추천합니다.
'FrontEnd' 카테고리의 다른 글
[React🍇]Webpack build시 최적화하기 (1) | 2022.10.10 |
---|---|
[FE🐱💻]브라우저 렌더링 과정은? (0) | 2022.09.29 |
[HTTP✨] 캐쉬헤더 (0) | 2022.09.23 |
[JS📜] Undefined와 null의 차이점, check (0) | 2022.09.05 |
[React🍇] TDD용 Eslint 설정하기 (0) | 2022.09.04 |