개인적으로 JavaScript(이하 JS)를 공부할 때 이해하기 어려웠던 내용을 뽑자면,
단연 이 글의 주제인 Closure와 IIFE라고 할 수 있다.
심지어 Closure와 IIFE, 이 두 가지 개념은 면접 단골 질문이라고 할 수 있다.
(js 개발자 혹은 FE(Front-end) 개발자 면접에서 자주 나온다고 한다.)
위의 사진은 하단 참고 자료 3번에 있는 인터뷰 질문 정리 깃헙이다. (정말 좋다!!)
(https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/master/JavaScript)
이 인터뷰 질문 정리 내용에도 이미 있는 내용이지만,
필자 본인이 Closure와 IIFE를 좀 더 이해해 보자는 마음으로 이 글을 쓰게 되었다.
물론 아래에서 구체적으로 더 설명할 예정이지만,
내가 생각한 결론은 다음과 같다.
Closure와 IIFE는 완전한 객체 지향은 아닐지라도,
그러한 개념의 일부를 구현할 수 있는 발판이 된다.
특히 정보를 숨기고 캡슐화할 수 있게 도와주는 "접근 제한"을 위한 것이라고 생각하면 편하겠다.
(타 언어를 예로 들자면, public / private과 같은 키워드의 역할을 해줄 수 있다는 의미다.)
Closure에 대해 이해하기 위해서는 Scope에 대해 한 번 되짚을 필요가 있다.
물론, 아주아주 당연하게 여기는 부분일 수 있다는 것을 안다. 그럼에도 한 번쯤은 짚고 넘어가자.
Scope는 변수와 상수, 매개변수가 언제 어디서 정의되는지 결정하는 것이다.
예를 들어 간단하게 1을 더해주는 함수가 있다고 하면,
function plus(x) {
return x++;
}
plus(5); // 6
x; // ReferenceError: x is not defined
위와 같이 x는 잠시 존재했으나 함수를 벗어나면 사라진 것처럼 보인다.
이런 경우를 보통 x의 스코프가 함수 "plus"라고 한다.
(js에서는 함수 스코프라고 부르며, 보통 local variable의 스코프라고 하기도 한다.)
우리는 가볍게 스코프의 예시를 하나 봤다.
JS의 스코프는 정적 스코프에 해당하며, 총 3가지의 스코프가 존재한다.
이렇게 3가지가 존재한다.
사실 이름에서 각각의 Scope의 뜻을 유추할 수 있지 않은가?
따라서 각 스코프에 대한 설명은 본 글에서는 생략하도록 하겠다.
참고로 아래의 내용부터 사용된 코드의 실행 결과를 숨겨두었다!
간단하게 말하자면, 선언한 변수에 대한 접근을 막을 수 있다.
정확하게 말하자면, 함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 경우다.
구체적으로 말하자면,
보통 우리는 함수가 끝나고 나면 내부에 있는 지역 변수들이 사라진다고 배웠다.
(엄밀하게 말하자면 사라지는 것은 아니고 사라진 것처럼 보이는 것이며, GC 그러니깐 Garbage Collector에서 자동으로 메모리에 있는 것을 회수해 가는 형식이다.)
그러나, 다음과 같은 케이스가 존재한다.
function counter() {
var count = 0;
return function() {
count++;
console.log("Counter is ", count);
}
}
var cnt = counter();
cnt();
cnt();
실행 화면
counter 함수에서 정의한 count라는 지역 변수는
우리가 통상적으로 알고 있는 수명인 counter 함수의 호출이 끝나고,
count를 증가하고 출력하는 익명함수 "function()"을 cnt라는 변수에 저장하게 된 다음,
이 cnt를 호출 할 때까지 count는 놀랍게도!! 살아 있다!!!
(다잉 메시지도 아니고... 이게 뭐야...)
이처럼,
어떤 함수가 자신을 포함하고 있는 외부의 스코프의
parameter, local variables(보통 자유 변수, free variable라고 부릅니다) 와 같은 것들에 접근할 수 있으며,
해당 스코프가 더 오래 유지되는 상황을
Closure라고 한다.
그냥 외부 스코프의 변수에 참조하는 경우가 있다면,
그 스코프가 날아가는 것이 아니라 좀 더 유지되는 유예기간을 받는 것이다.
즉, 놀랍게도 이런 일도 가능하다.
let f;
{
let isSafe = { description : 'Safe' };
f = function() {
return isSafe;
}
}
let findIsSafe = f();
findIsSafe.description = "Not safe!!";
실행 화면
결론적으로, isSafe는 접근할 수 있는 변수가 되어버린 것이므로 완벽하게 Safe하진 않다.
위의 예제에서는 isSafe라는 객체를 그냥 바로 반환하여 수정이 가능했지만,
만약 객체를 반환하지 않고 isSafe의 description을 출력하는 형식이었으면 어땠을까?
직접 접근은 불가능했을 것이다.
혹은 맨 처음의 예제처럼 count 변수를 직접 조작할 수 있는 방법은 없었을 것이다.
솔직히 말하면, MDN 문서에 너무 깔끔하게 잘 설명되어 있다.
IIFE, Immediately Invoked Function Expression은,
정의되자마자 즉시 실행되는 js 함수 형태를 말한다.
(function () {
// statements
})();
이런 식으로 좀 신기하게 생겼다.
간단히 설명하자면 함수 표현식으로 익명 함수를 만들고 즉시 호출하는 형태이다.
IIFE의 장점은 내부에 있는 것들(지역 변수 등등)이 자기 만의 스코프를 가지지만,
IIFE는 함수기 때문에 스코프 밖으로 무언가를 내보낼 수 있다. (return을 꼭 해야한다!)
const msg = (function() {
const secret = "I'm a secret!";
return `The secret is ${secret.length} chars long.`;
})();
console.log(msg);
실행 화면
대신 return 없이 secret에 접근하려고 하면 접근할 수 없게 된다.
IIFE와 클로저를 연결하여 설명하자면,
클로저에서는 외부 스코프가 필요했는데 IIFE와 같은 형태로 쓰면
클로저가 하나만 필요한 경우에,
함수 내부에는 안전하게 보관할 변수를 쓰고, return 문에 내보낼 것을 쓰기만 하면 된다.
IIFE는 클로저를 만들고 반환 받을 때 아주 유용하게 쓸 수 있는 문법이라고 생각하면 된다.
1. MDN 공식 문서 https://developer.mozilla.org/ko/docs/Glossary/IIFE
2. Learning JavaScript(코뿔소 책), 이선 브라운, O'REILLY
3. https://github.com/JaeYeopHan/Interview_Question_for_Beginner/tree/master/JavaScript
* 최대한 꼼꼼하게 보며 썼지만, 혹시 틀린 부분이 있다면 적극적인 피드백을 부탁드립니다!
[TS] TypeScript에서 Singleton 패턴 적용하기 (0) | 2020.10.06 |
---|---|
[TS / Node] TS + Node.js + Express + Babel(option) + eslint로 개발환경 세팅하기 (0) | 2020.07.23 |
[JavaScript] Arrow function과 this 키워드 (0) | 2020.04.28 |
댓글 영역