javascript 클로저(closure)란(1편)?

클로저(closure)란?

클로저의 사전적 의미를 오렐리의 코뿔소책에서 발췌해왔다.

함수객체와, 함수의 변수가 해석되는 유효범위(변수바인딩의 집합)을 아울러 컴퓨터 과학 문언에서는 클로저(closure)라고 일컫는다. 기술적으로 모든 자바스크립트 함수는 클로저인데, 함수는 객체이고 함수 자신과 관련된 유효범위 체인을 가지고 있기 때문이다.

대충 보니 자바스크립트에서 함수객체 본인과, 그 함수객체가 접근할수 있는 유효범위를 포괄적으로 클로저라고 한다는것 같다. 하지만 위 글로는 야구의 마무리투수와 클로저의 차이를 구별하긴 어렵다. 역시 소스코드로 클로저를 이해해보자.

1번 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
var testVal = '나는 전역변수이다.';

function test(){
var testVal = '나는 지역변수이다.';

function innerF(){
return testVal;
}//innerF

return innerF();
}//test

test();

1번 예제를 보자, 내부 함수를 선언하고 실행해서 리턴하는 간단한 예제이다. 여기서 마지막 test();의 결과를 보자.

전역변수가 아닌 test함수의 지역변수가 선택되어 console.log함수에 의하여 호출되는 것을 확인 할 수 있다. 음 innerF함수가 test함수 내부에서 실행되고 그 값이 리턴된 것이니 당연한 결과라 생각이 된다.

그렇다면 다음 예제를 보도록 하자.

2번 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
var testVal = '나는 전역변수이다.';

function test(){
var testVal = '나는 지역변수이다.';

function innerF(){
return testVal;
}//innerF

return innerF;
}//test

test()();

1번예제와 거의 같지만, 다른점은 innerF 내부함수는 test함수 내부에서 실행되지 않고 그냥 리턴이 되어버린다. 그리고 전역 영역에서 innerF함수를 실행한다. test()();의 결과를 한번 보자.

클로저를 모르는 분들이라면 이 결과에 이상함 또는 충격과 공포를 느낄 것이다. innerF 함수는 전역영역에서 실행이 되었다. 따라서 전역에 있는 testVal을 사용할것이라고 쉽게 예상한다. 하지만 그 결과는 여전이 test함수의 지역변수를 사용한다는 것을 알수 있다.

보통 여기서 1차 멘붕이 온다.(필자가 그랬으니 당신들도 그러해야한 합니다. 나만 바보일순 없으니...) 왜???? test()(); 동작결과가 지역변수를 호출하는 거지???? 분명 innerF함수는 전역영역에서 실행되었는데???? 전역영역의 testVal을 가져와서 "저의 선택은 나는 전역변수이다. 입니다." 이렇게 나와야 되는데????

그리고 C와 같은 저수준 프로그래밍 언어를 공부한 사람들은 곧 2차 멘붕이 온다. (역시 당신들도 그러해야한 합니다....) 부모함수 test함수는 호출이 이미 끝났잖아???? 리턴된 함수는 이미 실행이 종료(close)된 test함수의 내부 변수에 어떻게 접근할수 있는 거지????? test함수가 실행이 끝나지 않은 것인가???? (참고로 test함수는 종료된것이 맞다.)

예제2번의 결과가 나오는 이유는 바로

자바스크립트의 함수가 동작할때 사용하는 유효범위는 함수를 호출하는 시점에서의 변수 유효범위가 아니라, 함수가 정의된 시점의 변수 유효범위 체인를 사용하여 함수가 실행되기 때문이다. 다시 강조를 하면 호출하는 시점이 아니라! 정의되는 시점!!! 이런 특징을 어휘적 유효범위(lexical scoping)이라고 부른다고 한다. 이것이 굉장히 중요하다. 자바스크립트는 이런 어휘적 유효범위 때문에 클로저라는 것을 구현할수 있는 것이다. test함수가 실행이 종료된 뒤에도 여전히 innerF함수가 test함수의 지역변수를 사용할수 있는 이유는 innerF함수는 자신의 정의된 시점에 자신이 접근하여 사용할수 있는 유효범위 체인 정보를 별도의영역(내부적으로...)에 저장하고 관리하고 있기 때문이다.

즉 2번 예제의, innerF함수는 전역영역에서 실행되었지만, innerF함수가 선언된 그 시점의 유효범위를 가지고 동작하기 때문에 선언된 시점의 test()함수 내부의 지역변수를 사용하게 된 것이다.

그리고 이번에 공부하고 있는 클로저라는 녀석은 저 test()함수 객체를 말한다. 2번 예제에서는 리턴된 함수를 변수에 안담아서 보기가 좀 그렇지만 var a = test(); a(); 이 a를 클로저라고 부른다. 글 맨위에서 사전적 의미의 클로저(함수의 유효범위와 함수객체)가 있지만 실질적으로 어떤 함수 내부에서 정의되고 외부로 리턴된 함수를 클로저라고 말한다.

클로저(closure)라는 말은 어원은 함수의 지역변수가 유효범위 체인에 바인딩 되어 있고, 따라서 그 함수는 함수의 변수에 '따라 닫힌다'는 뜻에서 유래한 용어라고 하는데, 이름을 잘못 지은것 같다. 오히려 개인적으로 이 이름때문에 이해하기 힘든 부분이 있었던것 같다.(마무리 투수만 생각 나더라...)

누군가 당신에게 자바스크립트 클로저(closure)를 물어본다면?

귀찮을 때

함수다.

진지할 때

함수객체와 그 함수객체가 선언된 시점의 유효범위 체인을 통틀어서 클로저라고 부를수 있다. 더 구체적으로 클로저를 지칭 하자면 자신(함수)을 정의한 바깥쪽 함수에 바인딩된 지역변수(그리고 전달인자)를 포착할수 있는 함수객체이다.

이 클로저라는 녀석을 이용해서 다양한 코딩 기법(자바의 private 멤버변수 같은)이 있는데, 그건 아직 공부를 안해서 나중에 시간이 난다면 끄적여 보도록 하겠다. 이만 뿅!.