티스토리 뷰

안녕하세요. 이번 포스트는 JavaScript Closure(클로저) 에 관해 포스팅을 하도록 하겠습니다. 매우 오래전에 배우고 완벽하지는 않지만 대충이라고 개념을 잡아놨었는데 생각하면서 쓰지 않기에 가끔 헷갈리고 까먹어서 매번 다시 검색해서 이해하고 하는 경우가 많아 답답해서 직접 포스팅으로 남기게 되네요. 생각없이 코딩하면 정말 알던 것도 까먹고 또 지금 코딩하고 있는 것이 어떠한 개념을 통해서 이루어지고 있는지 정리가 되지 않게 되네요.. 그래서 하나하나 기억나는 것들에 대해서는 억지로라도 나만이라도 알아 볼 수 있게 정리를 해서 포스팅을 할까 합니다. 아, 지금까지 그래왔기는 하네요; 하지만 정리안된 것이 훨씬 더 많으니 앞으로도 하나하나 정리해 나가보도록 노력해보겠습니다.


우선 아래 소스 코드 및 예제들은 유투브 `생활코딩` 채널에서 본 내용들을 옮겨 적으면서 나름 이해하고 정리한 내용들입니다.

생활코딩 유투브 채널에서는 클로저에 관한 내용을 4편에 걸쳐 다루고 있으며 총 각 10분에 마지막편 6분여 정도로 대충 40 분정도 분량으로 다루고 있습니다.


- 클로저(Closure)


클로저(Closure)란 내부함수가 외부함수의 맥락(context)에 접근 할 수 있는 것을 말하며 클로저를 알기 위해선 내부함수와 외부함수라는 개념에 대해서도 이해하여야 한다.



#1. 내부함수와 외부함수


- 내부함수

함수 안에 함수가 있는 것


- 외부함수

내부 함수를 감싸고 있는 함수


여기서 내부함수는 외부함수의 지역변수에 접근할 수 있다.


function outter() {


var title = 'hello world';

function inner() {

console.log(title);

}

}


내부함수인 inner() 에서는 외부함수인 outter() 의 지역변수 title 로의 접근이 가능하며 콘솔에는 title 의 값인 hello world 가 찍히게 된다.



#2. 클로저


자, 그럼 클로저에 관한 예시를 하나 보자.


function outter() {

var title = 'hello world';

return function() {

console.log(title);

}

}


var inner = outter();

inner();


위와 같을 때도 역시 콘솔에는 hello world 가 찍히게 된다. 

이와 같이 외부함수(outter()) 가 return으로 실행이 끝나서 외부함수가 소멸되어도 내부함수가 (inner) 가 외부함수의 지역변수에 접근을 할 수 있다. 이러한 매카니즘을 클로저(Closure) 라고 한다.



#3. 클로저를 이용한 Private 변수 구현

생활코딩에서는 클로저를 이용한 Private 변수 구현으로 아래와 같은 예제를 다루고 있다.


function factory_movie(title) {

return {

get_title: function() {

return title;

}, 

set_title: function(_title) {

title = _title;

}

}

}


ghost = factory_movie('Ghost in the shell');

matrix = factory_movie('Matrix');

console.log(ghost.get_title()); --> 출력 : Ghost in the shell

console.log(matrix.get_title()); --> 출력 : Matrix


ghost.set_title('공각기동대');

console.log(ghost.get_title()); --> 출력 : 공각기동대

console.log(matrix.get_title()); --> 출력 : Matrix


위의 factory_movie 함수는 #2 의 변수와 생김새만 다를 뿐 거의 흡사하다고 보면 된다.

factory_movie 는 외부함수로 취급하면 되고 get_title, set_title 은 내부함수라고 보고 내부함수들이 객체로 리턴이 되어 사용된다고 생각하면 된다.

get_title 함수 호출 시 리턴되는 title 이라는 변수는 factory_movie 함수를 생성 시 전달되는 인자인 title 을 가르키며

set_title 함수 호출 시 전달되는 인자인 _title 은 title 이라는 값을 변경하게 되는데 이 title 이라는 값은 내부변수를 가르키기 때문에

역시 factory_movie 함수 호출 시 전달되는 인자인 title 을 가르킨다고 보면된다.


* 함수 생성 시 전달되는 인자는 함수 내에서 내부변수로 사용된다는 것을 기억하면 된다.


그래서 결국엔


ghost = factory_movie('Ghost in the shell'); 선언 시 get_title 과 set_title 을 담고 있는 컨테이너인 객체를 return 하게 되며

get_title() 시 전달 된 인자인 'Ghost in the shell' 이라는 값이 출력되며

set_title('공각기동대') 시에는 '공각기동대' 라는 _title 변수로 전달되어 title 이라는 매개변수를 변경하게 되고

get_title() 시 변경 된 '공각기동대' 라는 값이 출력되는 것이다.


* factory_movie 를 통해서 생성된 2개의 객체인 ghost, matrix 는 실행된 그 시점의 외부함수(factory_movie)의 지역변수(title)에 접근할 수 있는 것이기 때문에 각각의 객체에는 어떠한 영향도 미치지 않는 다는 것도 알 수 있다.


그럼 가장 중요한 Private 변수란 어떤 뜻인가 하면,

factory_movie 호출 시 get_title, set_title 을 담고 있는 컨테이너 객체가 return 되는 순간 해당 함수는 소멸이 된다.

이 때 외부함수(factory_movie)의 지역변수(title). 즉, title 이라는 변수는 factory_movie 의 내부 함수인 get_title, set_title 을 통해서만 접근할 수 있는 변수가 되는 것이다. 


이 private 변수가 왜 중요한가 하면 


1. 많은 사람의 손을 거칠 수 있는 프로그램 개발 시에 title 이라는 변수를 다른 사람이 어떤 의미로 외부에서 사용하던 간에 해당 객체내에 전혀 영향을 주지 않는다.

2. set_title 을 통해서 title 의 값을 변경 할 때 


set_title: function(_title) {

if (typeof _title === 'String') {

title = _title;

} else {

console.log('문자열이 아닙니다.');

}

}


와 같이 변경 하면 입력된 값 자체의 유효성 체크 등을 사용자에게 알려 줄 수 있다.


이렇게 title 이라는 함수를 꼭꼭 숨겨놓고 get_title, set_title 을 통해서만 접근이 가능하게 하면 이 변수가 좀 더 안전하게 저장, 수정이 될 수 있다고 한다.


--> 중요한 이유 중 1번의 내용에 대해서는 대충 어떤 의미인지는 알겠는데 2번의 내용은 유효성 체크를 해주는 것이 어떠한 면에서 중요한지 잘 이해하지 못하겠다.



#4. 클로저 구현 시 발생할 수 있는 실수


var arr = [];

 

for (var i=0; i<5; ++i) {

    arr[i] = function() {

      return i;

    }

}


for (var index in arr) {

    console.log(arr[index]());

}


와 같이 코딩하였을 시 언뜻 보기에 0~4 까지의 수가 출력 될 것 같지만 정작 확인해 보면 5 만 5번 출력이 된다.

이유는 첫번째 for 문의 function 은 외부함수 i 를 리턴하는 함수인데 for 문에서 사용되는 i 는 function() { return i; } 의 외부함수가 아니며 function() { return i; } 는 실행이 아니라 정의가 되는 것이다. 

그리고 arr[i] 에는 i 의 값이 아니라 i 를 리턴하는 함수 자체(function(){})가 들어가는 것이다.

또한 첫번째 for 문에서 선언된 변수 i 는 전역변수로 선언이 되어 첫번째 for 문이 끝나고 난 후 i 의 값은 5가 된다.

이렇게 첫번째 for 문이 끝난 후, 두번째 for 문이 실행이 되면 

arr[index]() 로 arr 에 들어간 함수인 functino() { return i; } 를 호출하게 되는데 

이때 값이 5로 변한 전역변수인 i 를 리턴하게 되어 계속 5만 출력이 되게 되는 것이다.

그렇기에 해당 소스는 아래와 같이 수정 할 수 있다.


var arr = [];


for (var i=0; i<5; ++i) {

arr[i] = (function(id) {

return function() {

return id;

}

})(i);

}


for (var index in arr) {

console.log(arr[index]());

}


위와 같이 수정하면 즉시실행함수에서 i 가 매개변수인 id 로 넘어가면서 내부함수인 return function() { return id; } 는 외부함수의 지역변수 값이 id 를 리턴하게 된다.

그렇기 때문에 내부함수, 외부함수의 구조가 만들어지며 정상적으로 클로저의 기능을 할 수 있게 된다.


이러한 이유는 아래와 같은 2가지 이유 때문이라고 한다.


1. JavaScript 에서 함수는 '일급객체'이기 때문이다. 일급객체는 아래 3가지 특징이 있다. (예시는 그냥 제가 이해한 정도에서 써본 것이니 100% 맞는 것은 아닙니다.)


a. 변수로 할당이 가능하다.

var price = function() { console.log(10000); };

price();


--> 출력 : 10000



b. 매개변수로 넘길 수 있다.

function keyword() {

return 'title';

}


function search(args) {

console.log(args());

}


search(keyword);


또는


function search(args) {

console.log(args());

}


search(function() { return 'title'; });



c. 함수의 반환값으로 반환 될 수있다.

function price() { 

return 10000; 

}


function get_price() {

return price();

}


또는 클로저의 예제와 같은 함수.



2. JavaScript 에서는 '함수'의 괄호 안에서만 지역변수로서 할당되고, 그 외 모든 장소에서는 '전역변수'로 할당된다.

function name(first_name, family_name) {

--> first_name, family_name 은 지역변수가 된다.

}


for (var i=0; i<5; ++i) {

--> i 는 전역변수로 할당된다.

}



이와 같이 클로저(Closure)에 대해 알아보면서 함께 내부함수, 외부함수, JavaScript 의 특징에 대해서 포스트를 작성해 보았습니다. 본문 머리말에서도 이야기하였지만 유투브 `생활코딩` 채널의 클로저에 관한 4편의 동영상을 보면서 동영상에 나오는 예제와 함께 제가 이해한 것들에 대해 나름대로 작성한 것이므로 100% 맞는 내용, 해석, 예제는 아니지만 클로저에 대해 이해 하고자하는 분들에게 작게나마 도움이 되기를 바라면서 포스트를 마치도록 하겠습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함