Saladin’s Blog

AS3 Event handling 매커니즘 2

Posted on: 12월 31, 2007

캡쳐와 버블의 순환순서에 따라 이벤트 호출 순서가 뒤바뀐다는 사실은 이미 아는 사실이다. 여튼 자바 엔진이 기반인 ActionScript3.0은 이벤트 구조의 개편만으로도 상당히 풍부한 디자인패턴을 제공하게되었다.하지만 2.0을 쓰던 나같은 기존의 사용자들이 이에 익숙해지는데는 일말의 노력이 필요함은 물론이다.

개체 포함관계에 따라, 또한 이벤트 적재 테이블을 캡쳐를 사용하느냐, 버블링을 사용하느냐에 따라 이벤트 절차가 다르게 된다는 것을 알았다. 그런데 한가지 문제가 남아있다.뭐냐하면 그러한 개념만으로 이벤트 흐름을 끊거나 풀거나 하는 본격적인 제어를 능숙하게 할수는 없다는 것이다. 요는 이벤트를 제공하는 메쏘드를 익혀야 한다는 것이다.

 

예를 들어 스테이지에 버튼을 하나 만들고 이런 코드를 적었다고 한다. 일반적인 경우다.

this.addEventListener(MouseEvent.CLICK, fnThis); //C
btn.addEventListener(MouseEvent.CLICK, fnBtn); // A
btn.addEventListener(MouseEvent.CLICK, fnBtn2); // B


function fnThis(e:Event):void{

 trace(“C”);
}

function fnBtn(e:Event):void{

 trace(“A”);
}

function fnBtn2(e:Event):void{

 trace(“B”);
}

btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
디스패치를 시켰으니 버튼을 안눌러도 클릭효과가 날것이다. 이를 실행시켰을 경우 아웃풋 창에는

A
B
C

그러니까 A-B는 동레벨이고, C는 상위레벨인 this이므로 물에서 잠수했다 빠져나오는 것을 상상하면 이해가 간다. 이건 여태껏 했던 부분이다.

 

이제 본격적인 문제가 시작된다. 코드를 이렇게 적었다면 어떻게 될까.

this.addEventListener(MouseEvent.CLICK, fnThis); //C
btn.addEventListener(MouseEvent.CLICK, fnBtn); // A
btn.addEventListener(MouseEvent.CLICK, fnBtn2); // B


function fnThis(e:Event):void{
 trace(“C”);
}

function fnBtn(e:Event):void{
 trace(“A”);

 e.stopImmediatePropagation(); 
}

function fnBtn2(e:Event):void{
 trace(“B”);
}

btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));

 

stopImmediatePropagation 이란 메쏘드를 썼다. 이건 뭐하는 것이냐하면 캡쳐 -> 타겟 -> 버블링 의 이벤트 흐름중에서

그 메쏘드를 호출한 현재 이벤트(즉 여기서는 fnBtn)까지만 이벤트를 흐르게 하고 이 다음 부터는 흐르게 하지 않겠다는 것이다.

 

결과는

A

놀라운 결과다. 그런데 this의 이벤트를 캡쳐테이블로 선언 했다고 하자.

 

this.addEventListener(MouseEvent.CLICK, fnThis, true); //C

이렇게 선언하면 캡쳐 -> 타겟 – > 버블링의 공식에 따라

 

출력결과는

C

A

가 된다. 스탑이미디에이트프러포게이션을 써도 C는 캡쳐이므로 (즉, 이벤트 흐름이 중지되기 전이므로)실행이된다. 다시 적지만 A에 할당된 함수에 스탑을 걸었다고 했다. AS3.0이 이전까지의 프레임 코딩이 필요없다는 이유가 바로 이때문이다. 이벤트 제어구조가 풍부하게 바뀌었다는 것이다. 위의 stopImmediatePropagation()대신 stopPropagation()을 써주면 현재 객체에 포함된 노드의 흐름만 실행하고 그 다음으로 (수면위로 올라와서 다시 아래로 들어가는 동작) 넘어가지 않는다.

 

위의 코드에선 btn 이란 객체 아래에 fnBtn 과  fnBtn2가 있으므로

두 함수가 호출되고 this로 넘어가지 않으므로

 

출력결과는

A

B

마지막으로 한가지 헷갈리는 경우가 있다. 이런 코드가 있다고 한다.

 

this.addEventListener(MouseEvent.CLICK, fnThis);
btn.addEventListener(MouseEvent.CLICK, fnBtn);
btn.addEventListener(MouseEvent.CLICK, fnBtn2);


function fnThis(e:Event):void{
 trace(“C”);
}

function fnBtn(e:Event):void{
 trace(“A”);
 
btn.removeEventListener(MouseEvent.CLICK, fnBtn2);

 // fnBtn 다음에 실행되기로 예정된 fnBtn2가 참조하는 이벤트를 삭제해버렸다. 어떻게 될까?

}

function fnBtn2(e:Event):void{
 trace(“B”);
}

btn.dispatchEvent(new MouseEvent(MouseEvent.CLICK));

 

이것의 실행결과는 간단하지 않다. 예상되는 결과는 B의 이벤트를 메모리에서 삭제했으므로

A

C

가 되어야 하거늘,

 

최초이벤트시엔

 

A

B

C

 

로 동작하고, 다시 클릭을 하면

 

A

C

 

가 출력된다.

 

이것은 Flash Player9의 저레벨 동작 메커니즘에 연관되는 문제이다. Flash Player9는 이벤트 리스트를 메모리에 적재할때,

 

0. 가장 먼저 디스플레이 객체들의 리스트를 조사하고.

1. 빈 이벤트 객체에 AS로 부터 받은 이벤트를 복사한다.

2. 그 복사한 객체를 리스트의 길이만큼 테이블에 적재해나가기 시작한다.

3. 각종 옵션(캡쳐 버블링) 설정에 따라 루프문을 break로 빠져나가든지, 아니면 다음 노드로 넘어갈지를 판단한다.

.

,

바로 위의 빨간색 단계에서 이미 메모리에 이벤트 객체를 복사한 후이기 때문에, 전체 런타임보다 순서가 뒤인 removeEventListener는 제 역할이 드러나지 않는다.

재차 이벤트가 날라갈때야 비로소 새로고침 되어있는 전달객체의 참조값을 새로 받게된다. 따라서 B는 두번째 실행때야 지워지는 것 처럼 보여지게 된다.

이것은 아주 중요한 대목이다.

Advertisements
태그: ,

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중

일자별 보기

12월 2007
« 11월   1월 »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

최근 사진

%d 블로거가 이것을 좋아합니다: