본문 바로가기

Actionscript3.0

[AS3] callback 과 dispatchEvent

Actionscript 3.0 의 Event Model 은 이벤트를 dispatch 하는 방식으로 이루어져 있습니다. 많은 클래스들이 클래스 내부적으로 "어떠한 행동"에 대해 이벤트를 외부로 알려주는 방식을 사용하고 있습니다. 예를 들어 Loader 클래스를 사용할때, 이미지나 파일들을 로드한 후에 Loader 클래스 내부적으로 이벤트 dispatch 가 일어나서 사용자나 개발자는 그 상황에 따른 후 처리를 할 수 있습니다.


import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;

var ld:Loader = new Loader();
ld.addEventListener(Event.COMPLETE, hnLoaded);

function hnLoaded( e:Event ):void{   
	// 로드끝
}

// Loader 내부
public class Loader extends EventDispatcher{
	public function Loader(){}
	private function loaded(){
		dispatchEvent(new Event(Event.COMPLETE));
	}
}

개발자들은 addEventListener 를 이용하여 내부적으로 dispatchEvent 되는걸 청취(addEventListener) 하고 있다가 반응이 오면 다운로드 된 데이터에 접근 가능 하게 됩니다. 이렇게 대부분의 AS3 이벤트 모델은 dispatch event 에 근거 하고 있습니다.


저도 AS3을 공부하고 초기에는 dispatchEvent 의 개념에 익숙하지 않아, 바보 같은 짓을 하곤 했는데요. 예를 들어 Event.ENTER_FRAME 으로 이벤트가 끝날때 까지 기다린다던지, Timer 를 이용하여 비슷한 방법을 사용하곤 했습니다. 

var ld:Loader = new Loader();
ld.addEventListener(Event.ENTER_FRAME, hnLoaded);

function hnLoaded( e:Event ):void{    
   // 로드끝
   if(ld.content) {

        // null 이 아니면 로드끝이라 판단
	ld.removeEventListener(Event.ENTER_FRAME, hnLoaded);
	
        // 로드후 후처리 로직 구현    
   } 
}



dispatch Event 는 그 밖에도 클래스의 재사용성에 대한 강점을 가지고 있습니다. 내부적으로 dispatch 되는 이벤트(행동)들을 addEventListener를 이용하여 청취하기만 하기 때문에 객체 유연성이 높습니다. 그 밖에도 여러 장점이 있지만, 자랑은 여기까지 하겠습니다.



dispatchEvent를 사용하지 않고 대안으로 사용할 수 있는 방법이 해당 행동에 대한 callback 메소드를 이용하는 것 입니다. Adobe에서 제공한 flashplatform optimizing content 문서를 보면 플래시 콘텐츠 최적화 방법중에 이벤트 구현을 dispatchEvent를 사용하기 보단 callback 을 사용하는게 더 효율적이라고 하고 있습니다. 이유는 여러가지가 있겠지만, 제가 생각하는 걸 나열해 보면 다음과 같습니다.


  • dispatchEvent 가 일어났다는걸 알기 위해서 addEventLister 를 등록해야 한다.
  • 너무 많은 addEventListener 사용은 메모리나 퍼포먼스에 영향을 줄수 있다.
  • 너무 많은 addEventListener 사용 코드를 지져분 하게 한다. (개인적 취향입니다.)

그래서 그 문서에서는 dispatchEvent 를 사용하지 말고 callback 메소드를 전달하여 이벤트 후처리를 수행하는게 퍼포먼스에 이점이 있다고 말하고 있습니다. 아래와 같은 방식으로요.


var ld:LoaderCallback = new LoaderCallback(hnLoaded);

public class LoaderCallback extends EventDispatcher{
	private var _callback:Function;   
	private var _ld:Loader;    
	
	public function LoaderCallback() {
		_ld = new Loader();
		_ld.addEventListener(Event.COMPLETE, loaded);
	}    
	public function load(url:String,callback:Function):void {
		_callback = callback;       
		_ld.load(new URLRequest(url));
	}    
	private function loaded(e:Event) {
		callback(e);
	}
}


callback 메소드를 로드를 수행하는 LoaderCallback의 load 메소드에 파라미터로 넘기기 때문에 따로 addEventListener를 등록할 필요도 없고 이벤트 관리를 효율적으로 할 수 있습니다.


프로젝트를 하면서 고민했던 부분이 바로 이 부분이었는데요. 퍼포먼스를 생각하면 (그밖에도 여러이점이 있겠지만) callback 을 사용해야 하지만, dispatchEvent 를 이용하면 나중에 API 로 변경 했을때 추가 작업없이 바로 사용 할 수 있다는 장점 때문에 두가지 선택 사항에서 고민을 많이 하였습니다. 그래서 내린 결론은 addEventLister 등록을 최소한으로 하고 API 로서 기능을 수행할 가능성이 적은 클래스는 callback 를 사용하고 API 의 기능을 수행해야 하는 객체에는 dispatchEvent 를 사용하였습니다. dispatchEvent 도 일일히 각각의 이벤트를 등록하여 청취하는 방법이 아니라, 하나의 이벤트를 수신하여 커스텀 이벤트에 있는 type 으로 이벤트 유형을 판단 하도록 하였습니다.


아래와 같은 방법은 3번의 addEventListener를 해야 되지만


stream.addEventListener(StreamEvent.PLAY,hnStreamPlay);
stream.addEventListener(StreamEvent.STOP,hnStreamStop);
stream.addEventListener(StreamEvent.PAUSE,hnStreamPause);

public class Stream extends EventDispatcher{ 
	public function Stream(){}
        public function play():void { 
		dispatchEvent(new StreamEvent(StreamEvent.PLAY));
	}  
	public function stop():void {
		dispatchEvent(new StreamEvent(StreamEvent.STOP));
	}
	public function pause():void {
		dispatchEvent(new StreamEvent(StreamEvent.PAUSE));
	}
}

function hnStreamPlay( e:StreamEvent ):void{}
function hnStreamStop( e:StreamEvent ):void{}
function hnStreamPause( e:StreamEvent ):void{}


아래 방법은 한개의 등록만으로 처리가 가능 합니다.

stream.addEventListener(StreamEvent.ECHO,hnStreamEcho);

public class Stream extends EventDispatcher{ 
	public function Stream(){}       
	public function play():void {
		dispatchEvent(new StreamEvent(StreamEvent.ECHO,StreamEvent.PLAY));
	}
	public function stop():void {
		dispatchEvent(new StreamEvent(StreamEvent.ECHO,StreamEvent.STOP));
	}
	public function pause():void{
		dispatchEvent(new StreamEvent(StreamEvent.ECHO,StreamEvent.PAUSE));
	}
}
function hnStreamEcho( e:StreamEvent ):void{ 
	// StreamEvent 에 label 을 구현함    
	if(e.label == StreamEvent.PLAY){} 
	else if(e.label == StreamEvent.STOP){}
	else if(e.label == StreamEvent.PAUSE){}
}


개인적으로 두번째 방법을 더 선호 하는데요. 그때 그때 상황에 따라 선택이 중요한것 같습니다.


Flashplatform 개발 작업이 다른 언어에 비해 이벤트를 관리해야 되는 상황이 많은데요. 사용하기 편한것도 좋지만, 메모리나 퍼포먼스, 코드 가독성을 생각하여 판단하는 능력이 필요 하다는 생각이 드네요.


posted by dongkang