클로저
는 내부함수
가 외부함수
의 context에 접근할 수 있는 것을 가르킨다.
여기서 context는 한국말로 직역하면 문맥이란 뜻으로 쉽게 코드의 실행 환경
이라 이해하면 된다.
클로저
는내부함수
가외부함수
의실행 환경
에 접근할 수 있는 것을 가르킨다라고 생각하면 된다.
먼저 클로저를 이해하고, 사용하기 위해서는
전역변수
와 지역변수
그리고
scope
와 lexical scoping(렉시컬 스코핑)
대한 이해가 필요하다.
필요한 개념 : Scope
전역변수는 해당 컴포넌트 혹은 페이지 안이라면
스크립트 영역 어디서든 사용할 수 있는 변수를 의미한다.
지역변수는 함수 안에서 정의된 변수로써,
해당 함수 안에서만 사용되는 변수를 의미한다.
보통 블록{}
안이나, 함수 안에서만 변수에 접근할 수 있다.
스코프에 대해 어느 정도 알고 있다면 꼭 알아둬야 할 사항이 있다.
스코프는 함수를 호출할 때가 아니라 선언할 때 생긴다는 것을 꼭 알아야한다.
이는 정적 스코프라고도 불린다.
다음 코드의 결과를 예상해보자
let name = 'kingmo'
function log() {
console.log(name)
}
function wrapper() {
name = 'haha'
log()
}
wrapper()
wrapper()
를 호출하면 콘솔에 'haha'가 찍힌다.
이는 간단하다 호출하기 전에 name을 'haha'로 바꿨기 때문이다.
그렇다면 다음 코드를 봐보자.
let name = 'kingmo'
function log() {
console.log(name)
}
function wrapper() {
let name = 'haha'
log()
}
wrapper()
여기서는 wrapper()
의 호출결과는 어떻게 될까?
똑같이 'haha'가 나올까?
아니다 콘솔에는 'kingmo'가 찍힌다.
스코프는 함수를 선언할 때 생기기 때문에
log()
함수 안의 name
은 wrapper()
안의 지역 변수 name이 아니라
전역변수 name을 가리킨다.
이런 것을 lexical scoping(렉시컬 스코핑)
이라 한다.
이를 이해하기 쉽게 다시 설명하자면
함수를 처음 선언하는 순간, 함수 내부의 변수는
스코프 체인
에 의해서 자기 스코프와 가장 가까운 상위 범위의 변수를 참조한다.
따라서 log()
함수 안의 name 변수는 선언 시
가장 가까운 전역변수 name을 참조한다.
그래서 wrapper()
함수 안에서 log를 호출해도
지역변수 name = 'haha'
를 참조하는 것이 아니라
전역변수 name = 'kingmo'
를 참조하는 것이다.
자바스크립트 코드로 협업할 때에 전역변수를 만드는 일은 최대한 지양해야한다.
그 이유는 변수가 섞일 수 있고, 우연의 일치로 같은 변수 이름을 사용해서
기존의 변수를 덮어쓰는 불상사가 발생할 수 있기 때문이다.
ES6에서 var
보다 let
, const
으로 변수를 선언하는 것도
위와 같은 이유 때문이다.
여기서 애초에 함수 내에서 변수를 선언해서
그 함수를 호출하기 전에는 선언조차 되지 않도록
변수를 비공개
하는 방법도 있다.
다음과 같은 코드에서
let another = function () {
let x = 'local';
function y() {
alert(x);
}
return { y: y };
}
let newScope = another();
newScope
는 선언과 동시의 another
의 리턴 값으로 할당 된다.
여기서 return 되는 값은 { y: function() { alert('local') } }
으로
newScope
라는 네임스페이스(변수이름)을 통해
변수 y에 접근할 수 있지만 변수 x에는 접근할 수 없다.
여기서 y를 공개변수
x를 비공개 변수
라 볼 수있다.
다른 언어와는 달리 자바스크립트에는 자바의
private
, protected
과 같이 함수를 비공개하는 별도의 문법이 없기 때문에
위와 같은 방법으로 비공개 변수
기능을 만들어 사용할 수 있다.
이러한 패턴을 모듈 패턴
이라고도 한다.
다음과 같은 코드에서
function aaa() {
let apple = "이건 사과에요"
return function bbb() {
let banana = "이건 바나나에요"
console.log("안녕하세요")
console.log(apple)
console.log(banana)
}
}
aaa()
함수를 호출하면 bbb()
함수를 리턴하는데
여기서 aaa()()
으로 bbb()
함수까지 한방에 리턴할 수 있다.
console.log(apple)
에 주목하자.
여기서 변수apple
은 bbb()
함수의 내부에서 생성한 변수도 아니고,
aaa()
함수의 매개변수도 아니다.
이런 apple
과 같은 변수가 바로 위에서 말한 비공개 변수
이다.
그리고 위와 같은 문이 바로 클로저
이다.
내부 함수 bbb()
가 외부 함수 aaa()
환경에 접근해 이건 사과에요
라는 콘솔을 출력하는 것을 볼 수있다.
클로저
는 비공개 변수
를 만들어서 활용하기 때문에
사용자의 접근을 막아 사용자가 해킹이나 프로그램에 버그를 만드는 것과 같은
예상을 뒤엎는 행동을 하는 것을 막을 수있다.
때문에 클로저는 자바스크립트에서 사용자를 통제하기 위한 기본적인 방법으로 활용된다.