안녕하세요. 김용성입니다.
여러분들은 JavaScript의 클로져 개념에 대해 잘 알고 계시나요?
이 개념은 상당히 혼란스럽게 느껴질 수 있는 개념인데요.
오늘은 그 클로져를 스코프체이닝과 더불어 조금 쉽게 그리고 간단하게 설명해보도록 하겠습니다.
이 포스팅을 읽기 전에 제가 이전에 작성한 링크 해당 포스팅을 읽고오시면 더 도움되실 거라고 생각합니다 :)
스코프체인에 대해 알고계시나요?
스코프체인이란 실행중인 컨텍스트 안에서 변수의 유효범위가 있다는 것을 의미합니다. JavaScript도 마찬가지로 그러한 스코프체이닝이 존재하는데요.
사실 용어로 보면 굉장히 어려워보이지만 예제코드를 통해 살펴보면 쉬운 개념이라는 것을 아실 수 있을거예요. 해당 코드를 살펴보겠습니다.
// example 1
function outer(){
const a='a';
console.log(a);
)
outer();
제가 outer이라는 함수를 하나 선언해준 후 호출하였습니다.
해당 코드의 결과값은 어떻게 출력이 될까요?
쉽죠? 정답은 'a'입니다.
당연한 결과죠? a라는 변수 내에 문자 'a'를 할당해준 뒤 console창에 찍도록 하였고, 그러한 outer라는 함수를 호출했으니 말이죠.
이번에는 이렇게 한번 바꿔볼까요?
// examle 2
function outer() {
const a = "a";
function inner() {
const a = "a2";
console.log(a);
}
inner();
}
outer();
a라는 변수가 outer에도 존재하고 inner에도 존재합니다. 이 출력결과는 어떻게 될까요??
정답은 'a2'가 console창에 찍힙니다.
그 이유는 뭘까요? 우리는 outer 내부에서 inner을 호출해주었습니다. 그렇다면 컴퓨터에서는 a에 어떤 값을 적용해줄지에 대해 생각을 해볼겁니다.
어?? a는 'a'였는데 inner에서는 'a2'라고 되어있네?
뭐 이런식의 생각을 하겠죠?? 이럴 때 컴퓨터는 context의 스코프라는 것을 확인합니다.
제가 그림을 하나 첨부할게요.
우리가 특정 함수(컨텍스트)에서 변수를 선언해주면 다음과 같이 해당 함수의 스코프가 만들어집니다. 해당 컨텍스트의 스코프를 확인하고 어떠한 변수가 유효한지에 대해 체크를 해주는 것이죠.
example 2 코드에서는 이러한 스코프의 작용으로 인해 같은 context 내에서 할당된 'a2'라는 값이 console에 찍히게 되는 것입니다. 그렇다면 만약 이렇게 코드가 변경된다면 어떨까요?
// example 3
function outer() {
const a = "a";
const b = "b";
function inner() {
const a = "a2";
console.log(b);
}
inner();
}
outer();
어떤 값이 console에 찍히게 될지 감이 오시나요?
정답은 바로 "b"입니다. 사실 JavaScript 코드를 어느정도 작성해보신 분들은 용어나 정의에 대해 알지 못하셨었더라도 이러한 코드의 결과값은 감으로 알고계셨을 거라 생각해요.
해당 example3에서의 각 context별 스코프는 다음과 같이 될거예요.
어떤식으로 작용하는지 이해되시나요?? inner라는 함수가 outer 내부에 존재하기 때문에 이렇게 outer 스코프를 참조하게 되는 것이죠.
이러한 스코프는 전역적으로도 존재합니다. 이번에는 이렇게 코드를 한번 수정해볼게요.
//example 4
const c = "c";
function outer() {
const a = "a";
const b = "b";
function inner() {
const a = "a2";
console.log(c);
}
inner();
}
outer();
전역 변수로 c라는 변수에 "c"를 할당해주었기 때문에 해당 출력값은 "c"가 될겁니다. 이해되시죠??
자 이번에는 이렇게 코드를 변경해보겠습니다.
// example 5
const c = "c";
function outer2() {
const d = "d";
}
function outer() {
const a = "a";
const b = "b";
function inner() {
const a = "a2";
console.log(d);
}
inner();
}
outer();
console에는 어떠한 것이 찍힐까요?
바로 d is not defined라는 에러가 찍힙니다.
그 이유가 무엇일까요? 바로 outer2라는 함수와 inner가 스코프체이닝 즉 묶여있는 상태가 아니기 때문이예요.
스코프 체이닝이라는 것은 이렇게 특정 context 내부에 context가 들어있고 그런 상황에서 내부의 context 스코프와 외부의 context 스코프가 체인으로 묶여있다는 것을 의미해요.
이렇게 정리해서 살펴보니까 쉽게 느껴지시지 않나요??
이번에는 클로져에 대해 살펴보도록 하겠습니다.
클로져를 어려워하시는 분들이 많을 거예요. 저 역시 클로져가 어렵게 느껴졌고, 도무지 왜 사용하는 것인지, 어떠한 장점이 있는지에 대해 잘 몰랐기에 더 생소하게 느껴졌고, 어렵게 느껴졌었습니다. 그렇지만 정말 간단하게 설명가능해요.
closure라는 것은 한국말로 폐쇄라는 뜻이잖아요? 쉽게 설명해서 한번 함수가 선언되면 그 안에 변수가 못빠져나가게, 변하지 못하게 폐쇄시킨다고 생각하시면 됩니다.
위에 스코프체인을 설명하던 코드에 이어서 설명드리도록 하겠습니다.
다음 코드를 봐주세요.
// example 6
const c = "c";
function outer() {
const a = "a";
const b = "b";
function inner() {
const a = "a2";
console.log(b);
}
return inner;
}
const closureFunction = outer();
closureFunction();
여기서 보시면 우리는 outer라는 함수를 closureFunction이라는 변수에 할당시켜줄 때 한번 호출을 했어요. 그쵸?
우리가 다른 언어를 사용할 때면 outer 내부가 실행되는 순간 즉 outer의 return이 실행되는 순간 b에 할당된 변수가 사라집니다.
하지만 JavaScript에서는요. 이 클로져라는 녀석 덕분에 return 객체를 들고 있는 function이 할당되어 생성되는 시점에도 해당 할당된 함수의 스코프 체인을 그대로 가지고 있을 수 있게 해줘요. 바로 이것이 클로져를 사용하는 이유이자 장점입니다.
그렇기 때문에 위 코드 결과는 "b"가 되는 것이죠.
이게 바로 클로져입니다. 여러분. 너무 어렵게 생각해오셨다면 제 포스팅을 보고 쉽게 이해가 되셨으면 좋겠어요.
그리고 클로저라는 이름은 말이죠. 겉 보기에는 무슨 되게 어려운 개념의 이름같이 보이지만 JavaScript 함수 객체의 형태 중 하나일 뿐이예요. 여러분들이 이 점을 꼭 아셨으면 좋겠어요. :)
오늘은 스코프체이닝과 클로져 JavaScript의 주요 코어 요소들에 대해 알아보았는데요. 여러분들에게 유익한 포스팅이 되었으면 좋겠습니다.👨💻👨💻
감사합니다.✋