티스토리 뷰

hacking/web

dojo by example(6) - 이벤트 시스템

Ho Eyo He Hum! iolo 2008.08.10 17:13
자바스크립트로 코딩 좀 했다는 분들도 IE(참고자료)와 불여우, 그리고 W3C의 표준 DOM 이벤트 모델간의 차이에 대해서 제대로 이해하기보다는 까이꺼~ 대충~ 통밥으로~ 처리하는 경우를 많이 본다. 누가 좋으냐 나쁘냐는 논쟁은 끝이 없으니... 넘어가기로 하고... 골치아픈 브라우져간의 차이점을 신경쓰지 않고 개발할 수 있는 dojo의 이벤트 시스템을 알아보자.
  • dojo.connect(이벤트객체, 이벤트이름, 이벤트핸들러함수) : 지정한 객체에서 지정한 이름의 이벤트가 발생하면 이벤트 핸들러 함수를 호출하도록 "연결"한다. 핸들을 리턴한다.
  • dojo.disconnect(핸들) : dojo.connect()함수의 리턴값으로 받은 핸들을 사용하여 이벤트와 이벤트 핸들러 함수의 연결을 끊는다.
뭔 얘긴지 더 헷갈린다. 간다한 예제를 보자:
<html>
<head>
<script type="text/javascript" djConfig="isDebug:true" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js"></script>
<script type="text/javascript">
</script>
</head>
<body>
<button id="helloBtn">Click Me</button>
<script type="text/javascript">
var helloBtn_onclick = function(evt) {
    alert('hello, world!');
    if (helloBtn_onclick_handler) {
       dojo.disconnect(helloBtn_onclick_handler);
       helloBtn_onclick_handler = null;
    }
};
var helloBtn = dojo.byId('helloBtn');
var helloBtn_onclick_handler = dojo.connect(helloBtn, 'onclick', helloBtn_onclick);
</script>
</body>
</html>

보시다시피 단순하다.

기존 방식대로 하면:
helloBtn_onclick = function(evt) {
    alert('hello, world!');
    helloBtn.onclick = null;
};
var helloBtn = document.getElementById('helloBtn');
helloBtn.onclick = helloBtn_onclick;

어랏?! 더 단순하자나! 그러나 이 코드가 항상 동작하는 것이 아니다라는 점이 문제다. 기존의 방식에 익숙하다면 지금 당장 바꿀 필요는 없다. dojo.connect가 있다는 점만 기억해두면 머지않아 필요할때가 올 것이다. 하나 이상의 이벤트 핸들러를 연결해야할 때, 이벤트 핸들러에서 이벤트의 전달을 중단해야 할 때, 또는 기본 동작(폼 submit같은)을 막고 싶을 때... 등등...
dijit(dojo의 위젯 시스템)을 쓴다면 별도로 disconnect를 해줄 필요가 없지만, 그 외의 경우에는 꼼꼼하게 disconnect해주는 것이 좋다. DOM 노드를 제거할때 dojo._destroyElement()를 사용하면 자동으로 연관된 이벤트 핸들러들을 disconnect 해주므로 편리하게 쓸 수 있다.

script태그가 body태그 끄트머리에 들어가있는 것이 이상하다고 생각한 분 계신가? 그렇다. 일부러 그렇게 한거다. 저렇게 안하면 브라우져와 주변 상황에 따라(!) 예상치 못한 결과를 만나게 된다. 그래서 dojo에서 이런 방법을 제공한다:
  • dojo.addOnLoad(핸들러함수) : 페이지를 열고 난 직후에 호출될 핸들러 함수를 추가한다.
  • dojo.addOnUnload(핸들러함수) : 페이지를 닫기 직전에 호출될 핸들러 함수를 추가한다.
백문이불여일견~
<html>
<head>
<script type="text/javascript" djConfig="isDebug:true" src="http://o.aolcdn.com/dojo/1.1.1/dojo/dojo.xd.js"></script>
<script type="text/javascript">
var helloBtn_onclick = function(evt) {
    alert('hello, world!');
};
var byeBtn_onclick = function(evt) {
    alert('bye~ world!');
    location.href = 'http://google.com/';
};
var handlers = [];
var init = function() {
    handlers.push(dojo.connect(dojo.byId('helloBtn'), 'onclick', helloBtn_onclick));
    handlers.push(dojo.connect(dojo.byId('byeBtn'), 'onclick', byeBtn_onclick));
};
var destroy = function() {
  dojo.forEach(handlers).disconnect();
}
dojo.addOnLoad(init);
dojo.addOnUnload(destroy);
</script>
</head>
<body>
<button id="helloBtn">Click Me</button>
<button id="byeBtn">Bye~</button>
</body>
</html>

예제가 좀 길어졌지만, 기존에 자바스크립질에 익숙한 분들은 dojo.addOnLoad/Unload가 body의 onload/onunload핸들러와 크게 다르지 않다는 것을 알아챘을 것이다. 즉, <body onload="init()" onunload="destroy()"> 이라고 하면 된다는 얘기~ 그런데 왜 저렇게 복잡하게 하느냐고? 예전에도 한번 포스팅한 적이 있는데... onload의 타이밍이 미묘해서 브라우져마다 조금씩 다르다.
좀 다른 얘기지만, 위의 예제 코드에서 init()/destroy()에서 사용된 배열에 이벤트 핸들들을 보관해두고 일괄 해제하는 패턴은 자주 사용되므로 익해두면 편하다.

보너스로 한가지 더:
  • dojo.stopEvent(이벤트객체) : 이벤트의 전파와 기본 동작을 모두 차단한다.
예를 들면:
<form id="loginForm" action="login.do" method="post">
    <input type="text" id="usernameText" name="username" value="" />
    <input type="password" id="passwordText" name="password" value="" />
    <button type="submit">Login</button>
</form>
<script>
dojo.connect(dojo.byId('loginForm'), 'onsubmit', function(evt) {
    if (!dojo.byId('usernameText').value || !dojo.byId('passwordText').value) {
      dojo.stopEvent(evt);
    }
});
</script>

자바스크립트에 익숙한 분들은 return false; 이런 짓(!)을 하시는데... 이거 생각처럼 잘 안된다. 이 기회에 링크한 문서들을 참고해서 왜 되고 또 왜 안되는지 좀 더 파 보시는 것도 괜찮을 듯...

다소 뜬금없지만, 이벤트 시스템의 일부이기도 하고, 유용한 녀석들이니 언급은 하고 넘어가야 할 것 같다:
  • dojo.subscribe(토픽이름, 토픽핸들러함수) : 지정한 토픽이 발생하면 호출할 핸들러함수를 추가한다.
  • dojo.publish(토픽이름, 파라메터배열) : 지정한 토픽을 발생시킨다. 그러면, 위의 dojo.subscribe()를 통해 등록한 핸들러 함수들이 모두 호출된다.
AJAX의 특성상 비동기 동작이 많고, 이를 처리하기위해서 코드가 꼬이고, 이렇게 꼬인 코드를 풀기 위해 전역변수를 많이 쓰게된다. dojo.publish/subscribe()를 사용하면 이러한 상황을 많이 줄일 수 있다. C언어를 오랜동안 쓰신 분이라면 setjmp/longjmp와 비슷하다고 생각할 수 있겠다. 제대로된 예제를 만들려면 꽤 긴 코드가 필요하니, 나중에 따로 설명하기로 하자.

다음은 dojo의 객체 시스템에 대해서 알아보기로 하고...
오늘은... 이만...
사용자 삽입 이미지
정말... 덥다...

댓글
댓글쓰기 폼