본문 바로가기

잡다한 기술

[JAVA SCRIPT/자바 스크립트]클로저(closure)를 알아보자




# 클로저 란?



나는 프론트엔드 개발을 하면서,

클로저란 단어을 전혀 모르고 있었다.

개발을 하면서 사용은 해봤지만, 이게 클로저 였단 것을 몰랐다.

그럼 이제부터 알아보자


클로저(closure)는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르킨다. 클로저는 자바스크립트를 이용한 고난이도의 테크닉을 구사하는데 필수적인 개념으로 활용된다. - 생활코딩(클로저) 참고

개념만 보았을때 이해를 했다면, 정말 그 사람은 이해력이 참 좋은 것이다.

하지만 나는 그런 사람이 아니란 것을 알기 때문에, 좀더 자세히 알아보자.




# 클로저 예시


1
2
3
4
5
6
7
8
9
function outter(){        // 외부 함수
    function inner(){    // 내부 함수
        var title = 'coding everybody'
        console.log(title);
    }
    inner();
}
outter();    // 외부 함수 호출
// 출력 : coding everybody
cs


1
2
3
4
5
6
7
8
9
function outter(){        // 외부 함수
    var title = 'coding everybody'
    function inner(){    // 내부 함수
        console.log(title);
    }
    inner();
}
outter();    // 외부 함수 호출
// 출력 : coding everybody
cs



가장 기본적인 예시이다.

클로저는 외부함수 안에 내부함수를 선언하여, 

외부함수에서만 사용할 수 있는 외부함수를 만들고,

외부함수가 종료 되어도 내부함수에서는 외부함수를 접근할 수 있도록 해준다.


아마 이것만 보고는 이걸 왜 사용하는지 모를것이다.

아래 예제를 참고해 보자.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var Closer = {
    TodayMeal : function(meal){    // 외부 함수
        return {
            GetTodayMeal : function(){    // 내부 함수 1
                return meal;    // 외부 함수에 존재하는 meal을 리턴해 준다.
            },
            SetTodayMeal : function(SetMeal){    // 내부 함수 2
                meal = SetMeal;        // 외부 함수에 존재하는 meal의 값을 변경해 준다.
            },
        }
    }   
}
 
const Meal = Closer.TodayMeal("쌀밥");    // 클로저를 Meal이란 변수에 넣어준다.
Meal.GetTodayMeal();                    // Meal을 가져온다
                                        // 출력 : 쌀밥
Meal.SetTodayMeal("콩밥");                // "콩밥"이란 값을 셋팅해준다.
Meal.GetTodayMeal();                    // Meal을 가져온다
                                        // 출력 : 콩밥
 
 
cs



주석과 같이 읽어보면 어느정도 이해는 갈 것이다.

클로저 함수를 사용함으로써 변수를 직접 접근하지 않고

간접적으로 접근이 가능할 수 있도록 한다.


이로써 meal이란 외부함수 변수는 오직 내부함수에서만 접근이 가능한 것이다.

이로써 협업을 하면서 직접적으로 수정되지 말아야할 변수를 은닉화 시키는대에 도움을 준다.




# 클로저를 사용하면서 생길수 있는 착각



클로저를 사용하면서 "이게 맞겠지?" 하면서 짜다가,

"어?! 이게 왜 안되지?" 하는 부분이 존재할 수 있다.

아래 예제를 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*이 예제는 생활코딩에서 참고하였습니다.*/
 
var arr = []        // 배열 선언
for(var i = 0; i < 5; i++){    
    arr[i] = function(){    // 각 배열마다 i 값을 리턴해 주는 무명 함수를 넣어준다.
        return i;            // 이로써 0, 1, 2, 3, 4가 출력되는 것을 기대한다.
    }
}
for(var index in arr) {
    console.log(arr[index]());    // 각 배열에 함수를 호출한다.
}
 
// 출력 : 5
// 출력 : 5
// 출력 : 5
// 출력 : 5
// 출력 : 5
 
cs



위 예제는 흔히하는 실수중 하나이다.

각 배열에 i값이 0, 1, 2, 3, 4 가 리턴되어지도록 세팅해 놓고

그 값을 기대하지만, 출력되는 결과는 5가 5번 나오게된다.

왜 그럴까?


먼저 for문에서의 var i는 정의되어진 내부 함수의 외부 변수가 아닌,

그저 for문이란 스코프 안에 Function-Scope일 뿐이기 때문이다.

즉 클로저가 아니란 말이다.

그렇기 때문에 i가 증가되어질수록 Function-Scope의 특성상,

모든 배열의 i값이 같이 증가하게 된다.

(사실 ES6부터 let과 const가 나와서 var 대신 Block-Scope인 let을 쓰면 정상적으로 입력되어진다.)

(단, const는 안된다. const는 상수의 역활을 하기 때문에 

참조형을 제외한 값으로 변경하면 안된다.)


그럼 어떻게 해야할까?

아래 예제를 보자.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*이 예제는 생활코딩에서 참고하였습니다.*/
 
var arr = []        // 배열 선언
for(var i = 0; i < 5; i++){    
    
    arr[i] = function(id){    // 각 배열마다 id란 매게변수를 가지고 있는 외부 함수를 넣어준다.
        return function(){    // 내부 함수를 선언해 외부함수에 id값을 리턴해준다.
            return id;        
        }
    }(i);                    // 각 배열에 클로저를 넣기 전 바로 호출을 해준다.
}
for(var index in arr) {
    console.log(arr[index]());    // 각 배열에 함수를 호출한다.
}
 
// 출력 : 0
// 출력 : 1
// 출력 : 2
// 출력 : 3
// 출력 : 4
 
cs


위 예제를 보면 잘 이해가 갈거라 생각된다.
클로저의 형식에 맞게 외부함수를 만들어주고, 
내부함수에서 외부함수에 변수를 접근하도록 한다.
그런 동시에 바로 i값을 매게변수로 호출하게되면 정상적으로 기대한 값이 나온다.

이로써 클로저가 무엇인지 알아보았다.
사실 나도 클로저의 개념이 정확히 잡혀있는지는 확실히 대답은 할 수 없지만.
어느정도 알고있는 내용을 정리겸 업로드 해 보았다. 



# 마무리


위 포스트는 제가 직접 제작한 것 입니다.

그렇기 때문에 틀린점이나 설명이 엉성한 점이 존재할 수 있습니다.

만약 틀린점이나 설명이 엉성한 부분이 존재하면 댓글로 알려주세요.

빠른 처리 하도록 하겠습니다.


티스토리 앱으로는 댓글 이용이 불가능 하므로 웹 브라우저로 봐 주세요

(URL : http://junprogramer.tistory.com/)


읽어주셔서 감사합니다.