[JavaScript] Closure 클로저 응용하기(Feat. Daum 지도 Api, Naver 지도 Api 이벤트 등록)
평소 Daum Map api 서비스를 이용하다가 처음으로 Naver Map api 서비스를 사용하게 되었다.
1. 사건의 전말 : Daum Map api 마커 클릭 이벤트 등록 과정
- 여러 개의 마커를 반복문으로 올리면서 동시에 해당 마커에 대한 인포윈도우 등록을 하는데 그 때 마커 클릭 시 해당 인포윈도우창이 열리도록 이벤트를 등록한다.
코드는 다음과 같다
for(var i in markerObj){
kakao.maps.event.addListener(markerObj[i], 'click', function(){
openInfoWindow(this.key); // openInfoWindow는 인포윈도우 창을 열기 위해 만든 함수
});
}
※ markerObj의 구조는 다음과 같이 각 데이터의 seq값을 key로 마커 객체를 value로 갖고 있으며, key라는 속성을 추가하여 각 seq값을 할당해주었다.
// markerObj
{
seq1 : {
kakao 마커 객체 데이터,
key : 'seq1'
},
seq2 : {
kakao 마커 객체 데이터,
key : 'seq2'
}
}
kakao.maps.event.addListener로 등록한 function 내의 this.key는 markerObj[i]의 key값인 'seq1' 혹은 'seq2'를 가져온다.
여기서 this는 markerObj[i]를 가르킨 것이다.
this 게시물을 한 번 더 살펴오자.
2. 사건의 발달 : Naver Map api 마커 클릭 이벤트 등록
- 위와 같은 경우로, 여러 개의 마커를 반복문으로 올리면서 동시에 해당 마커에 대한 인포윈도우 등록을 하는데 그 때 마커 클릭 시 해당 인포윈도우창이 열리도록 이벤트를 등록한다.
나는 주로 daum 서비스를 이용해왔기 때문에 같은 방식으로 구현을 한 상태에서 조금씩 수정하는 방향으로 다음과 같은 코드를 구현했다.
for(var i in markerObj){
naver.maps.Event.addListener(markerObj[i], 'click', function(){
openInfoWindow(this.key);
});
}
그런데 this.key가 내가 원하는 키 값인 'seq1' 혹은 'seq2'가 아닌 'opacity'라는 이상한 문자가 뜨는 것이 아닌가..
뭔가 이상함을 직감했기에 this가 무얼 가리키는지 찍어보았다.
근데 이게 왠걸, this는 window를 가리키고 있었다.
3. 왜?
- 사실 다음이나 네이버에서 이벤트 등록 리스너를 어떻게 구현해놨는지 모르겠지만, 짐작컨대 다음은 addListener 를 call 메서드를 사용해서 해당 객체를 넘겼을 것이라고 추정된다.
반면, 네이버 이벤트는 this가 window를 가르킨 것을 보면 단순 콜백 함수로 구현했을 가능성이 있지 않나 생각했다.
call, apply 그리고 this 객체에 대한 게시글도 참고하면 좋다.
4. 해결 방법
- 먼저 위 코드에서 이벤트 함수 내의 this가 window를 가리킨다는 것은 알았으니 더 자세히 살펴보면,
for(var i in markerObj){
var key = markerObj[i].key;
naver.maps.Event.addListener(markerObj[i], 'click', function(){
console.log(key);
});
}
여기서 콘솔창에 key값이 'seq1', 'seq2'라고 출력된다고 예상한다면 클로저에 대해 더 알아본 후 해결을 하는 것이 맞다.
결론부터 말하자면 콘솔창에는 'seq2', 'seq2'가 나온다.
이미 주기가 끝난 변수(클로저)에 접근을 하려니 마지막 반복문에서 'seq2'값을 할당받은 것이다.
나는 이를 외부 함수인 이벤트 함수로 감싸주어서
원래 함수였던 이벤트 내에 함수를 내부 함수로 만들어
내부 함수를 값으로 반환받는 외부함수를
그자리에서 바로 호출하기 때문(즉시실행함수)에 내부 함수가 값으로서 반한되어 바로 원하는 변수에 할당이 된다.
for(var i in markerObj){
var key = markerObj[i].key;
naver.maps.Event.addListener(markerObj[i], 'click', function(key){ // 외부함수
return function(){ // 내부 함수
console.log(key);
openInfoWindow(key);
}
}(key)); // 외부함수를 바로 호출 (매개변수 key)
}
응용 코드가 잘 이해가 안간다면, 아래 간단한 예제를 보고 이해하자!
// 기존
for(var i=0; i<10; i++){
array1[i] = function(){
return i;
}
}
// array1 : [10,10,10,10,10,10,10,10,10,10]
// 해결
for(var i=0; i<10; i++){
array2[i] = function(i){
return function(){
return i;
}
}(i));
}
// array2 : [1,2,3,4,5,6,7,8,9,10]
댓글