티스토리 뷰
클로저의 개념
특정 함수와 이를 둘러싼 어휘적 환경 (Lexical Environment)간의 조합
MDN에서는 다음과 같이 클로저를 정의하고 있다. 문장은 참 짧은데, 이렇게만 봐서는 이해가 쉽지 않다. 다음 예제를 통해 클로저를 파헤쳐 보자.
클로저 예시
클로저의 예시로 가장 자주 쓰이는 소스 코드를 살펴보자.
var counter = (function { // 외부 함수
let count = 0; // 외부 변수
return function() { // 내부 함수
alert(count);
}
})();
counter(); // alert으로 0 표시됨
위의 소스 코드를 실행해보면 alert을 통해 0이 표시됨을 확인할 수 있다. 즉시 실행 함수를 통해 리턴한 함수 (내부 함수)를 counter 변수에 할당했고, 할당받은 내부 함수 (counter)를 바로 실행했다.
위에 적어둔 클로저의 개념을 다시 한번 읽어보자. 즉시 실행 함수의 리턴 함수를 클로저의 정의에서 '특정 함수'라고 한다면, 이 특정 함수의 내부 스코프 (함수 몸체)와 외부 스코프 (counter 함수 몸체 부분 및 글로벌 스코프)를 어휘적 환경 (Lexical Environment) 이라고 볼 수 있다.
예제 소스에서 호출된 즉시 실행 함수의 경우 호출 이후 더이상 쓰이지 않는다. 그러므로 즉시 실행 함수 내부에 선언된 변수인 count 역시 사용할 수 없어야 하지만, counter 함수를 통해 count 변수에 계속 접근할 수 있다. 이것이 클로저의 개념이다.
그림을 통해 자세히 알아보자.
먼저 1번의 소스 코드가 실행된 후의 실행 컨텍스트를 보자.
위의 이미지는 즉시 실행 함수가 호출되고 난 이후 생성된 실행 컨텍스트의 내부 모습이다. Lexical Environment 내부의 Environment Record 아래의 Declarative Environment Record에 함수 내부의 변수, 함수 등이 바인딩 되어 count 변수가 선언되어 있는 모습을 볼 수 있다.
(Lexical Environment는 함수 선언문의 경우 function 키워드를 만났을 때, 함수 표현식의 경우 함수가 실행될 때 생성되는 일종의 '실행 환경에 대한 상태'로, 정적으로 현재 환경 상태값들을 생성해두는 개념)
이 상태에서 2번의 소스 코드가 마저 실행된 후의 실행 컨텍스트도 살펴보자.
즉시 실행 함수와 counter함수의 실행 컨텍스트이다. counter 함수 내부의 outer는 counter 함수가 선언된 부분의 Scope를 참조하는데, 이는 외부 함수인 즉시 실행 함수의 Lexical Environment이다.
즉 counter 함수가 변수로 선언되어 즉시 실행 함수의 스코프를 참조하고 있으므로, 즉시 실행 함수는 메모리에 남아있게 되고 counter 함수에서 count 변수에 접근할 수 있는 상태가 된다.
사실 클로저 라는 개념이 자바스크립트 엔진이 소스 코드를 처리하는 과정에서 자연스럽게 나타나는 현상이기에, 실행 컨텍스트의 생성 과정을 정확히 파악하고 있다면 굳이 따로 익힐 필요없이 이해할 수 있는 개념이라고 생각한다. (클로저를 따로 이해하지 말고 실행 컨텍스트 및 스코프의 개념을 먼저 이해하면 더 좋을 것 같다!)
클로저를 사용하는 이유
클로저가 무엇인지 어느정도 이해가 됐다면, 클로저를 어떤 이유로 사용하는지 알아보자.
위에서 작성한 예제와 같이 카운터를 객체 형태로 만든다고 가정해보자.
var counter = {
count: 0,
getCount: function() {
return this.count;
},
increase: function() {
this.count ++;
console.log(this.count);
},
decrease: function() {
this.count --;
console.log(this.count);
}
};
console.log(counter.getCount());
counter.increase();
counter.increase();
console.log(counter.getCount());
counter.decrease();
console.log(counter.getCount());
이렇게 만든 counter의 문제점은 무엇일까? 바로 counter.count로의 직접적인 접근이 가능하다는 점이다. 비록 getCounter(), increase(), decrease() 등의 메소드를 통해 count를 얻거나 제어하긴 하지만 직접 접근을 막을 수는 없다. 자바스크립트에는 접근 제한자 (public, private 등)가 없기 때문이다.
그러나 클로저의 개념을 적용한다면 접근 제한자의 기능을 구현할 수 있다.
다음은 클로저를 이용해 카운터를 구현한 예제이다.
var counter = (function() {
var count = 0;
return {
getCount: function() {
return count;
},
increase: function() {
count ++;
console.log(count);
},
decrease: function() {
count --;
console.log(count);
},
};
})();
console.log(counter.getCount());
console.log(counter.increase());
console.log(counter.count); // undefined
counter를 단순히 객체로 선언하지 않고 즉시 실행 함수를 통해 public 함수들이 담긴 객체를 리턴했다. 이 리턴한 객체에 선언된 함수들은 counter.함수 형태로 접근이 가능하고 (public) 즉시 실행 함수 내부의 count에 접근이 가능하다. 반면에, counter.count와 같이 counter 내부의 변수에 직접 접근이 불가능해졌다는 점에서 private과 유사하게 구현되었다는 점을 확인할 수 있다.
접근 제어를 구현한다는 것이 단지 소스 코드로의 직접적인 접근을 막는다는 점에서만 유용한 것은 아니다. 이를 통해 전역 네임스페이스를 관리하는데도 큰 도움을 받을 수 있다.
(위의 예제 소스 코드는 addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript 에서 설명하는 Module Design Pattern을 참고해서 작성했다. 이 패턴에서는 클로저를 이용해 모듈을 작성한다. 다음에 이러한 디자인 패턴들에 대해서도 좀 더 공부해서 포스팅하도록 하겠다.)
참고
developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures
'Javascript' 카테고리의 다른 글
[Javascript] 호이스팅 (hoisting) (0) | 2020.10.06 |
---|---|
[Javascript] var, let, const 차이점 (0) | 2020.10.04 |
- Total
- Today
- Yesterday
- react #cra #create-react-app #baseUrl #절대경로 #상대경로 #webpack #웹팩
- 알고리즘 #다이나믹프로그래밍 #dynamic programming
- 알고리즘 #BinarySearch #Binary Search #이진검색 #이진 검색 #javasript
- 알고리즘
- cra
- 클로저개념
- lexical environment
- create-react-app
- 도커
- 도커의개념
- javascript
- 클로저사용이유
- 실행컨텍스트
- 도커사용하는이유
- 자바스크립트
- 사진사이즈조절
- 도커장점
- 이미지크기조절
- 도커 #docker #mysql #docker desktop
- nodejs
- 호이스팅
- execution contexts
- execution context
- 실행 컨텍스트
- 렉시컬 환경
- Docker
- 이미지크기줄이기
- react
- 도커란
- 도커의의의
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |