스타플에서 대학교 동아리 활동을 추첨을 통해 MT 지원품을 드립니다.

이벤트 참여방법은 간단합니다. 스타플에서 동아리 추가하고 맴버 초대하고 함께 활동하면 됩니다.

물론 활동이 높을 수록 당첨확률은 높겠죠?

당첨되면 1등부터 3등까지 푸짐한 MT 지원품을 드립니다. ^^

이벤트 참여 : http://starpl.com/main/event/view/62


스타플에서 KBS에서 8월 30일에 첫 방송한 성균관 스캔들 관련 이벤트를 진행중입니다.

박유천, 송중기, 유아인, 박민영 4인방중 최고의 꽃미남을 투표하고 응원글을 적으시면 성균관 유생들의 나날 도서와 콘피아 바로보기 정액권을 드립니다.

많은 참여 부탁드려요.

이벤트 페이지 : http://starpl.com/main/event/view/61

스타플에서 시크릿의 Madonna 곡이 들어가 있는 싸인 CD를 받아볼 수 있는 이벤트가 준비되어 있습니다. 그외에도 150곡 다운로드가 가능한 음악이용권을 받을 수 있고요.

기간은 9월 2일부터 9월 16일까지 입니다.

간단한 시크릿 응원 메시지로 상품 받아가세요. ^^

 

이벤트 참여 : http://starpl.com/main/event/view/63

스타플에서 이벤트를 진행중입니다.
술자리에 있었던 다양한 에피소드나 사진을 글로 담으면 이벤트 참여 완료합니다.
기간은 8.23~9.6(2주간)이며 9.10에 당첨자 발표합니다.
재미있는 에피소드 많이 공유해주세요. ^^

술자리 이벤트 페이지 : http://starpl.com/main/event/view/60

그외에 다양한 이벤트가 진행중입니다. http://starpl.com/main/event/ing/list


글쓴이 : 지돌스타(http://blog.jidolstar.com/754)

스타플, 서비스 차별화하여 리뉴얼 오픈

스스로 진화하는 ‘실시간 커뮤니티’ 서비스 제공, 서비스적인 차별화 강화


신개념 SNS 서비스업체 스타플(www.starpl.com)이 8월 9일 리뉴얼된 모습을 새롭게 선보였다.


스타플은 밤하늘에 나의 별을 통해 다른 별의 이용자(사용자)와 ‘관심사’을 중심으로커뮤니케이션의 장을 만들어주는 신개념 SNS서비스다. 스타플은 단편적인 소통이 아닌 3차원 이미지를 바탕으로 새로운 인맥 형성 및 정보 교류의 통로로 활용할 수 있어 오픈 이후 7개월 만에 10만 명을 돌파할 정도로 20대를 중심으로 폭발적인인기를 누리고 있다.


↑↑ ▲ 스타플을 최근 리뉴얼한 위콘커뮤니케이션즈 윤경석 대표 ⓒ2010 더리더/최자웅 ⓒ 더리더


이번 리뉴얼을 통해서는 개인 기록 중심의 기존 서비스에서 스스로 진화하는‘실시간 커뮤니티’ 서비스를제공하며 서비스적인 차별화를 강화했다.


기존의 SNS는 최근 급성장하고 있는 모바일 환경의 영향으로 새로운 이슈에 대한 관심이 실시간으로 이루어 지긴 하지만 모든 정보가 혼재하기 때문에 원하는 관심사를 쉽게 접하기 어렵고, 그 안에서의 커뮤니케이션도 쉽지 않다.


그와는 달리 스타플의 ‘관심사’ 실시간 커뮤니티는 자신의 ‘관심사’에 대해 쉽고 빠르게 정보를 교류하게 만들며, 시시각각변화하는 관심사에 따라 또 다른 커뮤니티의 생성을 유도하여 스스로 진화하게 된다.


스타플을 서비스하는 위콘커뮤니케이션즈 윤경석 대표는 "최근 스마트폰의 확산으로인해 정보의 확산 속도는 가히 상상을 초월하고 있다" 며 "스타플이 제시하는커뮤니케이션의 방법론은 점점 가속화되고 있는 모바일 환경에 딱 맞아떨어져 사용자에게 새로운 경험을 제공할 것이다"고 서비스에 자신감을 드러냈다.


이런 차별화된 커뮤니티로 리뉴얼된 스타플에서 다양한 오픈 이벤트가 진행 중이다.


오픈일부터 20일 동안 회원 가입을 한 이용자를 대상으로 매일 1명씩 사파 영어학습기 매직스터디를, 회원 가입 이벤트를 커뮤니티에 퍼가기만 해도 매일 1명씩 사파 마시멜로 MP3 플레이어를 제공한다.



↑↑ ▲ 위콘커뮤니케이션즈 스타플 리뉴얼관련 이벤트 ⓒ2010 더리더/최자웅 ⓒ 더리더


또, 여름휴가철을 맞아 여행 사진이나 에피소드를 여름휴가 관심사에 올리면 추첨을 통해 음악이용권과 MP3플레이어 등의 다양한 상품을 제공한다


이 외에 현재 인기몰이 중인 KBS 드라마 ‘제빵왕 김탁구’와 ‘구미호 여우누이뎐’ 을 테마로한 재미있는 이벤트도 진행 중에 있다. 제빵왕 김탁구처럼 나는 무슨왕 인지 적는 재치왕 선발대회와 구미호와 같이 나에게 가장 무서운 것을 적는 이벤트가 진행된다. 이벤트 참여자 전원에게는 스타플 별스킨을 제공하며, 추첨을 통해 KBS 다시 보기보기도 볼 수 있는 콘피아 1개월 이용권을 제공한다.


이벤트에 대한 자세한 내용은 http://starpl.com/main/event/ing/list 을 참고하면 된다.


최자웅 기자 pshbear@empal.com


이전에 "10만개 입자를 이용한 유체 시뮬레이션 실험"이라는 제목으로 글을 적었다. 이 글로 10만개의 1x1 픽셀의 입자를 되도록 빠르게 렌더링하기 위한 기술을 습득할 수 있었다.

이번에는 조금 더 실용적으로 접근한다. 이 실험은 입자 유체 시뮬레이션의 연장전으로 입자대신 화살표를 이용한다. 소개하고자 하는 것은 원래 일본의 Yasu 아이디를 가진 개발자가  Wonderfl에 단순한 학습자료 용도로 올린 소스였다. 이 소스는 계속 Fork(원본소스를 퍼가서 수정하는 작업)가 거듭됨에 따라 매우 흥미롭고 재미있는 실험으로 탈바꿈하기 시작했다. 수십번의 Fork와 수백번의 favorite로 지정된 이 실험은 필자로 하여금 Flash 속도 개선에 대한 아이디어를 얻을 수 있다는 기대를 가지게 하기에 충분했다.


최종 실험 결과 화면



이 글은 Yasu님의 블로그에 소개된 글을 다시 한번 검토해보고 분석하는 정도이다.
(참고로 Yasu님은 Wonderfl과 블로그에서 clockmaker 아이디로 활동중이며 꽤 재미있는 컨텐츠를 많이 생산하고 있다.)

앞으로 소개할 내용은 아래 첨부파일내 ActionScript 3.0 코드를  참고하길 바란다.  Flash Builder 또는 Flash CS4에서 Flash Player 10기반으로  테스트하면 되겠다. 





BitmapData 배열을 활용한 속도 증가

Yasu님은 그의 블로그를 통해 Flash 속도 개선을 위한 매우 좋은 아이디어를 알려주었다.

[다음 Flash의 속도를 체험] BitmapData를 배열에 저장하면 2 ~ 3 배 속도 - 일본어 번역기를 통해 보세요.


이 실험은 2가지 경우의 예를 들고 있다. (첨부파일의 bitmap_arrow_01.as, bitmap_arrow_02.as를 참고)

  1. 화살표를 정해진 수만큼 Sprite로 생성하고 회전/이동 처리 
  2. 화살표의 모양을 회전값에 따라 BitmapData로 만들어 배열에 저장한 뒤, 정해진 수만큼 화살표를 렌더링할 Bitmap을 만든다. 1번과 같이 Bitmap의 위치를 지정하되 화살표의 방향은 이미 만든 BitmapData를 이용하여 회전된 모양 처리

(Wonderfl이 아닌 첨부파일 소스에서)
1번 예제는 화살표 500개로 FPS(frame/seconds)가 50정도의 속도가 나온다. 2번예제는 화살표 3000개로 FPS 50정도 나온다. 같은 FPS이지만 화살표를 더 렌더링할 수 있는 2번 예제의 경우가 훨씬 빠르다는 것을 알 수 있다. (물론 FPS는 개인 PC사양에 따라 다르게 나온다.)

일반적으로 많은 Flash 입문자가 처음 접하는 예제는 1번 일것이다. 하지만 화살표 모양의 Vector 데이터가 500개나 되기 때문에 회전, 이동때 마다 많은 수의 Vector정보를 렌더링하기 위해 계산이 많아질 수 밖에 없다. 2번 예제의 경우에는 이 문제를 해결하기 위해 화살표 회전값에 따라 미리 BitmapData를 만들어 놓고 실제 렌더링시에는 회전각도에 따라 선별적으로 미리 만든 해당 화살표 BitmapData를 가져와 사용할 수 있도록 만들었다. 일단 이렇게 하면 렌더링을 위한 계산량이 급격하게 떨어지므로 FPS 증가를 도모할 수 있게 된다.

이것만 보더라도 1번 예제처럼 만들고 Flash는 너무 느려요 라고 말하시는 분들이 있다면 반성해야한다. 어떤 언어로 만들던지 1번처럼 만들면 당연히 느려진다. 이것은 필요하다면 속도 개선을 위한 노력이 필요하다는 것을 말해주는 단적인 아주 좋은 예제이다. ^^


Fork! Fork!

Wonderfl 사이트의 강점 중에 하나가 바로 Fork 기능이다. 이를 이용해 다른 사람이 만들어 놓은 ActionScript 코드를 더욱 개선하거나 발전시킨 코드로 만들 수 있고 공유할 수 있다. 실제로 이 기능을 통해 Yasu님이 올려놓은 코드는 바로 다른 개발자들의 입맛에 맛게 다음과 같은 암묵적인 약속에 의해 변경되기 시작했다. 

  1. 속도에 따라 화살표 색이 달라진다.(빠를수록 빨강, 느릴수록 파랑)
  2. 속도가 빠른 화살표가 가장 화면 상단에 배치된다. 
  3. 위 조건으로 했을 때 최대 속도를 내도록 코드를 갱신한다.

즉, 위 조건은 보는 이로 하여금 더욱 예쁘고 멋진 효과를 보여주도록 하는 구체적인 목표인 것이다. 

이 시발점은 Wonderfl 아이디 keno42 님으로부터 시작한다.

속도개선 전, 색깔을 입힌 화살표 모습


- 화살표의 속도에 따라 색과 레이어를 달리 지정하도록 수정 (첨부파일의 bitmap_arrow_04.as 참고)

확실히 밋밋했던 화살표가 색이 들어가니 보기가 좋아졌다. 하지만 이에 따라 FPS가 급격하게 떨어지게 되었다. 가장 큰 이유는 2번 조건때문에 화살표의 레이어 위치를 수시로 변동시켜 주는 아래와 같은 코드가 추가되었기 때문이다.

arrow.parent.removeChild(arrow);
worldAlphaChildren[speed].addChild(arrow);	

이 때문에 FPS가 급격히 줄어들어 앞선 실험에서 사용된 화살표 3000개를 1000개로 줄일 수 밖에 없었다. 이렇게 해서야 FPS 57정도 나왔다. 


(참고)Fork된 코드는 diff기능으로 비교하자.


Wonderfl의 강력한 기능중에 하나는 Fork된 원본코드와 Fork를 통해 수정된 코드를 비교할 수 있다는 것이다. 위 Wonderl의 캡쳐 화면에서 볼 수 있듯이 Fork한 코드는 forked from과 원래 제작자 및 코드의 제목이 나와 있고 그 옆에 diff(104)가 표시되어 있다. 이 말은 이 소스가 원본 소스와 104개 줄이 추가/삭제/수정 되었다는 것을 의미한다. 이것을 클릭하면 다음과 같은 화면이 나온다. 


이것을 사용하면 어느부분이 수정되었는지 바로 알 수 있기 때문에 매우 유용하다.


속도개선 1 (bitmap_arrow_05.as 참고)

첫번째로 생각한 것은 마우스 이벤트 전파부분이였다. 빠른 속도를 가진 화살표가 위로 올라오게 하는 조건을 만들기 위해 앞선 코드에서는 단계별 레이어를 만들었다. 이 레이어는  Sprite로 만들어졌다. Sprite는 DisplayObjectContainer를 확장한 클래스로 자식을 가질 수 있는 시각객체를 렌더링하는데 기본이 되는 클래스중 하나이다. 시각객체들은 자식과 부모관계(addChild()에 의해)를 가지게 되면 이벤트 전파 메커니즘에 따라 이벤트가 전파된다. 그중에 마우스 이벤트가 대표적이다. 하지만 이 이벤트 메커니즘은 Flash 속도 저하에 큰 요소가 되기도 한다. 그러므로 이벤트 전파가 필요 없는 곳에는 사용하지 않도록 강제로 설정해줄 필요가 있다. 그 역할을 하는 것은 Sprite속성중에 mouseChildren, mouseEnabled 이다. 화살표가 올라갈 부모로서의 Sprite 레이어들은 어떠한 마우스 이벤트를 받을 필요가 없기 때문에 이들 속성을 모두 false로 지정하도록 한다. 이 설정으로 약간의 FPS개선이 있었다. 

두번째로 arrow.parent.removeChild(arrow); 부분을 삭제하는 것이였다. 시각객체는 2개 이상의 부모가 존재할 수 없다. 그러므로 otherParent.addChild(arrow); 하는 것만으로 arrow.parent.removeChild(arrow)가 이미 실행된 것이다. 이는 비싼 실행 비용을 지불하므로 제거하면 확실히 속도 개선이 된다. 실제로 FPS가 70이상으로 개선되며 거의 20이상 증가하게 되었다. 


속도개선 2 (bitmap_arrow_06.as 참고)
아직도 비싼 실행 비용을 지불하는 것이 있다. 바로 addChild()이다. 속도에 따라 레이어를 변경하더라도 굳이 변경할 필요가 없는 경우도 있다. 이러한 경우에는 선별적으로 변경되도록 해야한다. 아래 코드처럼 단순한 조건문 하나 넣어 쓸데없이 addChild를 실행하지 않게 하는 것만으로 속도 개선이 된다. 

if (arrow.parent != world.getChildAt(speed)) {
	Sprite(world.getChildAt(speed)).addChild(arrow);
}
이 작업으로 무려 FPS가 90 이상으로 상승했다. 속도개선 1 보다 약 20정도가 증가했다. 


속도개선 3 (bitmap_arrow_07.as 참고) 
속도개선 2에서 getChildAt()하는 것은 그리 빠른 방법이 아니다. 그래서 Array를 이용해 아래와 같이 바꾸게 된다.
if( arrow.parent != childrenArr[speed] )
	childrenArr[speed].addChild(arrow);	
원래 실험자는 개선이 있었다고 하나 필자는 그리 큰 개선은 못보았다.

속도개선 4 (bitmap_arrow_08.as 참고)
화살표는 정사각형 영역이 아닌 직사각형 영역에 그려진다. 무슨말인가? 회전된 화살표가 그려지는 영역은 항상 작아졌다가 커졌다가 한다. 이는 화살표의 영역을 담은 BitmapData가 실제 화살표 크기보다 클 수 있다는 것을 의미한다. 실제 화살표 크기보다 큰 BitmapData에서 쓸데 없는 부분을 제거하는 작업을 트리밍(trimming)이라고 한다. 이 작업으로 실제로 렌더링하는 화살표의 적당한 크기의 영역만을 가진 BitmapData를 만들기 때문에 물리적인 렌더링 시간 향상에 도움을 줄 수 있다.

while (i--) { //각도에 따라 화살표 비트맵 생성 
	matrix=new Matrix();
	matrix.translate(-11, -11);
	matrix.rotate((360 / ROT_STEPS * i) * Math.PI / 180);
	matrix.translate(11, 11);
	
	temp = new BitmapData(22,22,true,0x0);
	temp.draw(dummyHolder, matrix);

	//트리밍 처리(중심점의 조정은 하지 않는다)
	rect = temp.getColorBoundsRect(0xff000000, 0x00000000); //알파채널이 0이 아닌 사각형 이미지 경계 
	rotArr[i + k]=new BitmapData(rect.width, rect.height, true, 0x0);
	rotArr[i + k].copyPixels(temp, rect, new Point(0,0));
}

위 코드에서 보는데로 BitmapData의 getColorBoundsRect() 메소드를 이용해 트리밍 처리를 하고 있다. 이 작업으로 FPS를 거의 100까지 올릴 수 있었다.


속도개선 5 (bitmap_arrow_09.as 참고)
지금까지의 방식은 화살표 하나를 표현하는 도구로 Bitmap을 사용했다. 이것은 시각객체(DisplayObject)의 하나이다. 이 방식은 화살표 1000개를 그릴려면 시각객체 1000개가 필요하다는 말과 같다. 속도개선 1~4까지 하면서 사실 거의 최대한으로 속도개선했다. 이제 속도개선에 기대할 수 있는 마지막 단계는 바로 화살표를 렌더링하는 방법 조차도 BitmapData 를 이용하는 것이다. 이때는 1000개의 시각객체가 아니라 1000개의 위치/방향 정보를 담는 객체가 필요하므로 일단 상대적인 메모리 부담이 줄어든다. 그리고 기존 방식과 거의 동일하나 그려지는 Canvas가 이제는 Sprite를 이용한 레이어가 아니라 BitmapData 하나가 된다. 그러므로 화살표는 BitmapData의 copyPixels()메소드를 이용해 그리게 된다. 또한 화살표의 속도에 따른 위치를 정렬하기 위해서 Array.sortOn()함수를 이용한다. 

결과적으로 FPS가 120까지 상승했으며 초반에 60미만이였던 것에 비해서 거의 2배이상 빨라졌다. 그뿐인가? ColorTransform적용으로 화살표 궤적도 보여줄 수 있게 되었다. 

속도개선 최종화면



실행 : http://wonderfl.net/code/834201fba6c3562ca6fd7a0f1f229138b263b446 


조금더 예쁘게!!!!
지금까지 속도개선은 모두 Wonderfl의 Fork기능을 통해 각각 다른 사람들에 의해 구현된 것들이다. 최초에 화살표를 이용한 예제를 만든 Yasu님은 자신의 블로그에 지금까지 필자가 다루웠던 내용을 요약하면서 마지막으로 한번더 Fork를해 더 예쁘게 보여주기 위한 코드로 수정했다. 

마지막으로 더 멋진 화면을 만들기 위한 작업 결과




분명히 이전 코드와 속도상에 차이는 없으면서 더욱 아름다운(?) 결과물을 도출해냈다. 

그리고 1000개의 화살표가 아닌 3000개로 바꾸어도 FPS가 40이상 나왔다. 


정리하며
1픽셀짜리 데이터를 다룰때는 10만개나 다루었다가 그와는 상대적으로 너무 적은 3000개 화살표 렌더링하는 것으로 전환되면서 너무 숫자가 급감하는 것처럼 보일 수 있다. 하지만 실제로 렌더링하는 데이터 요소는 10만픽셀 이상으로 많은 데이터를 다루는 것이다.(조금만 생각하면 감이 올거다.)  렌더링 자원 숫자를 줄이는 것도 중요하지만, 그 자원이 어떤 조건의 것인가와 어떻게 렌더링 할 것인가도 역시 중요하다. 결국, 화면 렌더링을 어떻게 하느냐에 따라 Flash 애플리케이션의 속도와 밀접한 관계가 있는 것이다. 실무에서 제약된 환경의 디바이스나 서비스의 경우라면 이러한 노력은 필수이다. 

이 글에 소개된 아이디어는 정답은 아니다. 더 분석하고 더 최적화할 여지가 있다. 

Wonderfl은 아주 고급의 스킬을 다루지는 않지만 적어도 중고급 실력에 도달하기 위한 예제를 찾는데는 이만한 곳이 없다고 생각한다. 필자는 이곳에 올라온 코드들을 보면서 수없이 감격하고 있다. 

마지막으로 라이브 코딩을  SNS 영역으로까지 승격한 Wonderfl을 이용해 다양한 실험들을 하는 일본의 개발 문화를 보면서 항상 감탄한다. 아무쪼록 이러한 일본의 개발 문화가 한국에도 정착되길 희망한다. 


글쓴이 : 지돌스타(http://blog.jidolstar.com/671)

이 문서는 ActionScript 3.0에서 Custom Event를 만들어 사용할 시 Event 클래스의 clone() 메소드의 용법을 이해하는 것을 목적으로 한다. 자주 Event를 확장해서 만드는 사람이라면 꼭 읽어볼 내용이라고 생각한다.

 

1. Event 클래스의 clone() 메소드

 

flash.events.Event 클래스에는 clone() 함수가 정의되어 있다. 이 clone() 함수 내부에는 아마도 아래처럼 정의되어 있을 것이다. (Event클래스는 Native코드이므로 내부를 볼 수 없다. 다만 유추할 뿐이다.)

 

package flash.events
{
	public class Event
	{
		public var type:String;
		public var bubbles:Boolean;
		public var cancelable:Boolean;
		public var target:Object;
		public var currentTarget:Object;

		public function Event( type:String, bubbles:Boolean = false, cancelable:Boolean = false )
		{
			this.type = type;
			this.bubbles = bubbles;
			this.cancelable = cancelable;
		}
		public function clone():Event
		{
			return new Event( type, bubbles, cancelable );
		}
		...(다른 함수 생략)
	}
}

 

 

Event의 clone() 함수는 자신과 똑같은 객체를 새로 만들고 반환하는 역할을 한다.

아래 clone() 함수에 대해서 설명하고 있다.

clone() method

public function clone():Event

Language Version:

ActionScript 3.0

Runtime Versions:

AIR 1.0, Flash Player 9

Duplicates an instance of an Event subclass.

Returns a new Event object that is a copy of the original instance of the Event object. You do not normally call clone(); the EventDispatcher class calls it automatically when you redispatch an event—that is, when you call dispatchEvent(event) from a handler that is handling event.

The new Event object includes all the properties of the original.

When creating your own custom Event class, you must override the inherited Event.clone() method in order for it to duplicate the properties of your custom class. If you do not set all the properties that you add in your event subclass, those properties will not have the correct values when listeners handle the redispatched event.

In this example, PingEvent is a subclass of Event and therefore implements its own version of clone().

 

설명을 간단히 요약해 보면…

  1. Event의 clone() 함수는 Event 서브클래스의 객체를 복제한다.
  2. clone() 객체는 EventDispatcher 클래스의 dispatchEvent(event) 함수에 의해 재송출(redispatch)될 때 자동으로 호출된다.
  3. Event를 확장해 Custom Event를 만드는 경우 반드시 override 해야한다. override하지 않으면 clone()은 그 부모 Event의 clone()을 사용하므로 clone()을 호출 하더라도 Custom Event에 새로 추가된 속성(properties)을 설정할 수 없게된다.

 

여기에서 EventDispatcher의 dispatchEvent()와 Event의 clone()과는 어떤 관계가 있어보인다. 그리고 clone()을 override해야하는 이유와도 엮여 있다는 것을 생각해볼 수 있다.

 

사실 이런 설명만 가지고는 제대로 이해하기 어렵기 때문에 여기서는 EventDispatcher에서 dispatchEvent()가 동작하는 형태와 Event의 clone() 함수의 관계를 하나의 예제로 이해해 보도록 한다.

 

2. EventDispatcher 클래스의 dispatchEvent() 함수의 이해

Event 클래스의 clone()함수에 대한 용도에 대해 알기전에 먼저 아래 내용을 알아야한다.

 

Event 객체를 송출(Dispatch)하기 위해서 ActionScript 3.0에서는 IEventDispather 인터페이스와 EventDispatcher 클래스를 지원한다. IEventDispather를 구현하여 내부적으로 EventDispatcher 객체를 만들어 사용하는 클래스를 정의하거나 EventDispatcher 클래스 자체를 상속하여 만든 클래스라면 Event객체를 송출할 수 있다. 이벤트를 송출할 때 사용하는 것이 EventDispatcher에 정의된 dispatchEvent( event:Event ) 함수이다. dispatchEvent는 인자로 Event의 객체를 인자로 받아 이벤트를 송출한다.

 

그럼 dispatchEvent()는 내부적으로 어떻게 구현되는 것일까? 다음 내용을 보자.

 

dispatchEvent () method
public function dispatchEvent(event:Event):Boolean Language Version : ActionScript 3.0
Runtime Versions : AIR 1.0, Flash Player 9
Dispatches an event into the event flow. The event target is the EventDispatcher object upon which the dispatchEvent() method is called.
Parameters

event:Event — The Event object that is dispatched into the event flow. If the event is being redispatched, a clone of the event is created automatically. After an event is dispatched, its target property cannot be changed, so you must create a new copy of the event for redispatching to work.Returns

Boolean — A value of true if the event was successfully dispatched. A value of false indicates failure or that preventDefault() was called on the event.

Throws

Error — The event dispatch recursion limit has been reached.

 

위 dispatchEvent() 함수에 대한 설명이 담겨 있다. 인자값으로 event:Event가 있는데 이에 대한 설명(빨간부분)을 살펴보면 다음 내용으로 설명된다.

 

  1. Event 객체의 clone() 함수를 사용하는 조건은 해당 Event 객체를 다시 송출할 때이다.
  2. 다시 송출하게 되면 clone() 함수를 이용해 같은 형태의 Event 객체를 새로 생성하고 그 Event 객체의 target 속성을 이벤트를 송출하는 객체로 바꾼다.

 

이 설명으로 우리는 Event가 송출되는 과정에 사용되는 dispatchEvent() 메소드의 동작을 유추할 수 있다.

 

  1. 객체 A, 객체 B 가 있고 A는 이벤트를 송출하는 쪽, B는 이벤트를 받는 쪽이다라고 가정하자.- 이 객체들은 당연히 EventDispatcher를 확장했다.- A,B가 같은 객체일 수도 있고 다를 수도 있다. 다른 경우는 Visual 객체의 경우 이벤트 전파에 의해 가능해진다.

  2. 객체 A에서 이벤트 송출
    처음 A에서 생성된 Event에는 target속성이 정의되지 않는다. A의 dispatch() 함수로 이 Event 객체를 인자로 넘겨주면 clone()함수가 호출되지 않고 Event의 target은 A로 설정된다.
  3. 객체 B에서 이벤트 받음A에서 송출한 Event를 B에서 받는다. 이것은 addEventListener()로 가능하다는 것은 이미 알고 있다. addEventLisener()에 등록된 Event 처리 함수는 인자로 A에서 발생한 Event 객체를 받는다.받은 Event 객체를 다시 B에서 dispatchEvent()로 넘겨주게 되면 Event객체의 target속성이 B가 아닌 A이기 때문에 clone() 함수를 호출해서 새로운 Event 객체를 만들고 target을 B로 설정한다.

 

 

아하! 이쯤되면 EventDispatcher 클래스의 dispatchEvent() 함수가 어찌 생겼는지 알 수 있겠다. Event와 같이 EventDispatcher도 Native코드이므로 볼 수 없지만 어떻게 동작할지 유추만 하는 것이다!

 

 

package flash.events
{
	public var type:String;
	public var bubbles:Boolean;
	public var cancelable:Boolean;
	public class EventDispatcher implements IEventDispatcher
	{
		private target:IEventDispatcher;

		public function EventDispatcher(target:IEventDispatcher = null)
		{
			if( target )
				this.target = target;
			else
				this.target = this;
		}

		public function dispatchEvent(event:Event):Boolean
		{
			if( event == null ) return false;
			if( event.target == null )
			{
				event.target = target;
				event.currentTarget = target;
			}
			else
			{
				var newEvent:Event = event.clone();
				newEvent.target = target;
				newEvent.currentTarget = target;
			}
			dispatchEventFunction( event );
		}

		private function dispatchEventFunction( event:Event ):void
		{
			...(이벤트를 송출하는 알고리즘 구현)
		}

		public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
		{
			...(생략)
		}

		public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
		{
			...(생략)
		}

		public function willTrigger(type:String):Boolean
		{
			...(생략)
		}

		public function hasEventListener(type:String):Boolean
		{
			...(생략)
		}
	}
}

 

 

 

위 코드는 dispatchEvent() 메소드가 어떻게 동작할지 쉽게 알려주는 코드이다. 해석은 앞서 설명했으니 따로 하지 않겠다. (다시 언급하지만 이 클래스는 실제 EventDispatcher 클래스가 아니다. 어떻게 동작하는지 예를 보여주기 위한 코드일 뿐이다. )

 

 

이로써 Event 클래스에 정의된 clone()함수를 언제 쓰냐에 대한 답을 얻은 것이다.

 

 

3. Custom Event를 만들때 clone() 함수를 override 해야하는 이유

지금까지는 clone()함수를 어디서 사용하는가 안 것 뿐이다. 이 문서의 제목을 풀어서 말하면 “Event 클래스를 확장할 때 clone() 함수를 override 해야하는 이유”이다. 글의 목적은 Custom Event를 만들 때 clone() 함수를 override 해서 사용해야하는 이유를 아는 것이다.

 

clone() 함수를 override해야하는 상황을 만든 예를 들어보겠다. 예제는 Flex SDK 3.2 환경에서 작업했다.

 

아래는 Event를 확장한 CustomEvent이다.

 

package
{
	import flash.events.Event;
	public class CustomEvent extends Event
	{
		public static const TEST:String = "test";

		public function CustomEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}

		override public function clone():Event
		{
			return new CustomEvent( type, bubbles, cancelable );
		}
	}
}

 

아래는 Button을 확장해서 만들었다. 이 버튼을 누르면 CustomEvent 객체를 생성해서 test라는 이벤트 type으로 송출해준다.

 

package
{
	import flash.events.Event;
	import flash.events.MouseEvent;

	import mx.controls.Button;

	[Event(name="test", type="CustumEvent")]

	public class AComponent extends Button
	{
		public function AComponent()
		{
			super();
			this.label = "날 눌러줘";
			this.addEventListener( MouseEvent.CLICK, onClick );
		}

		private function onClick( event:MouseEvent ):void
		{
			var customEvent:CustomEvent = new CustomEvent( CustomEvent.TEST, false, false );
			trace( "AComponent 1:",customEvent.target,customEvent.currentTarget );
			this.dispatchEvent( customEvent );
			trace( "AComponent 2:",customEvent.target,customEvent.currentTarget );
		}
	}
}

 

 

아래는 Panel을 확장해 만들었고 자식으로 위에서 만든 AComponent 객체를 등록했다. (이 예제에서 자식으로 등록된 사실은 중요하지 않다.) AComponent에 발생된 CustomEvent 이벤트를 받는다. 받자마자 그 이벤트를 다시 송출하고 있다.

 

package
{
	import flash.events.Event;
	import mx.containers.Panel;

	[Event(name="test", type="CustomEvent")]

	public class BComponent extends Panel
	{
		private var aComp:AComponent;

		public function BComponent()
		{
			super();
		}

		override protected function createChildren():void
		{
			super.createChildren();

			if( !aComp )
			{
				aComp = new AComponent;
				aComp.addEventListener( "test", customEventHandler );
				aComp.width = 200;
				aComp.height = 200;
				addChild( aComp );
			}
		}

		private function customEventHandler( event:Event ):void
		{
			trace( "BComponent 1: ",event.target,event.currentTarget );
			this.dispatchEvent( event ); //re-dispatch
			trace( "BComponent 2: ",event.target,event.currentTarget );
		}
	}
}

 

 마지막으로 Application이다.

 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*">
	<local:BComponent test="trace(’Application: ‘,event.target,event.currentTarget )"/>
</mx:Application>

 

 

 

실행한뒤 버튼을 누르면 콘솔창에 아래와 같은 메시지가 나온다.

 

AComponent 1: null null

BComponent 1 : EventTest0.BComponent4.AComponent12 EventTest0.BComponent4.AComponent12

Application: EventTest0.BComponent4 EventTest0.BComponent4

BComponent 2 : EventTest0.BComponent4.AComponent12

EventTest0.BComponent4.AComponent12

AComponent 2: EventTest0.BComponent4.AComponent12

EventTest0.BComponent4.AComponent12

 

아래는 위 결과에 대한 설명이다.

 

  1. [AComponent 1] AComponent에서 Event 객체를 만들어 송출하는데 dispatchEvent를 호출하기 전에 target과 currentTarget 속성은 모두 null이다. 예상한 대로다.
  2. [BComponent 1]이 이벤트가 송출된 다음 BComponent에서 받게 되면 이 이벤트의 target과 currentTarget은 AComponent의 객체를 참조하고 있다.
  3. [Application] BComponent에서 AComponent에서 생성한 Event 객체를 인자로 dispatchEvent()를 호출하면 Application에서는 Event의 target, currentTarget 속성이 BComponent의 객체를 참고하고 있다.
  4. [BComponent 2] BComponent에서 AComponent의 Event 객체를 dispatchEvent()를 재송출한 다음에 Event객체의 target과 currentTarget속성을 보여주고 있다. 단지 clone()메소드를 이용해 생성했기 때문에 Event객체 자신의 속성은 바뀌지 않는다. 단지 Application으로 전달되는 Event는 clone()으로 새로 생성되었고 BComponent의 객체를 참조하고 있다는 것을 알 수 있다.
  5. [AComponent 2] 마지막 줄은 AComponent에서 Event객체를 만들어 dispatchEvent()함수로 만든 Event객체를 송출한 후에 Event객체의 target과 currentTarget 속성을 보여준다. 4번째와 같은 결과지만 dispatchEvent()내부에서 clone() 메소드가 호출된 것이 아니라 단지 AComponent 객체의 참조값을 target과 currentTarget에 설정했을 뿐이라는 것을 알 수 있다.

 

위에서 한가지 기억할 사항은 이벤트가 재송출(redispatch)하는 경우 target과 currentTarget이 변경된다는 것이다.

 

만약 CustomEvent에서 Event의 clone()를 override한 clone() 함수 정의를 빼보고 실행해보자.

 

AComponent 1: null null

TypeError: Error #1034: 유형 강제 변환에 실패했습니다. flash.events::Event@5115971을(를) CustomEvent(으)로 변환할 수 없습니다.
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298]
at BComponent/customEventHandler()[D:\EventTest\src\BComponent.as:34]

at flash.events::EventDispatcher/dispatchEventFunction()at flash.events::EventDispatcher/dispatchEvent()

at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298]

at AComponent/onClick()[D:\STARPL_DEV_02\Timeline\src03\EventTest\src\AComponent.as:23]

BComponent 1 : EventTest0.BComponent4.AComponent12 EventTest0.BComponent4.AComponent12

BComponent 2 : EventTest0.BComponent4.AComponent12 EventTest0.BComponent4.AComponent12

AComponent 2: EventTest0.BComponent4.AComponent12 EventTest0.BComponent4.AComponent12

 

위처럼 CustomEvent에 clone() 함수를 override하지 않으면 이벤트를 다시 송출할 때 유형 강제 변환 에러(TypeError)가 발생한다.

 

결과적으로 clone() 함수를 override를 해야하는 이유는 받은 Event 객체를 다시 재송출하는 경우에 clone() 함수를 호출하기 때문이다.

 

그럼 어찌 유형 강제 변환 에러(TypeError)가 나는 것일까? 사실 위에서 예를 든 EventDispatcher 클래스의 dispatchEvent() 메소드는 이런 에러가 날 수 없다. CustomEvent의 객체가 Event로 형변환이 일어나지 않을 이유가 없지 않은가? 본인 생각에 이 형변환 에러가 발생시키는 이유는 clone() 메소드에서 엉뚱한 이벤트의 객체를 생성하지 않고 자신 이벤트만을 clone으로 만들어 사용하게 하기 위해 강제성을 부여한 것이 아닌듯 싶다. 가령 다음과 같이 clone()을 만들면 안되게 한다는 것이다.

 

package
{
	import flash.events.Event;
	public class CustomEvent extends Event
	{
		public static const TEST:String = "test";

		public function CustomEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}

		override public function clone():Event
		{
			return new OtherCustomEvent( type, bubbles, cancelable );
		}
	}
}

 

 

위처럼 자기는 CustomEvent인데 clone으로 OtherCustomEvent를 clone으로 삼아 만들면 안되지 않은가? EventDispather의 dispatchEvent는 개발자가 엉뚱하게 프로그래밍하는 것을 막기위해 컴파일 단계에서는 확인할 수 없다. 하지만 런타임시에 전혀 엉뚱하게 clone()함수를 override하는 것을 방지하기 위해 다른 Event Class로 clone함수를 이용하는 경우 TypeError가 나도록 배려(?)한 것이다.

 

이런 TypeError가 날 수 있게 EventDispatcher의 dispatchEvent() 함수를 아래와 같이 만들어봤다.

 

public function dispatchEvent(event:Event):Boolean
{
	if( event == null ) return false;
	if( event.target == null )
	{
		event.target = target;
		event.currentTarget = target;
	}
	else
	{
		var newEvent:Event = event.clone();
		if( getQualifiedClassName(event) != getQualifiedClassName(newEvent) )
		{
			var msg:String = "TypeError: Error #1034: 유형 강제 변환에 실패했습니다. ";
			msg += getQualifiedClassName(event) + "를(을)";
			msg += getQualifiedClassName(event) + "(으)로 변경할 수 없습니다."
			throw new TypeError( msg );
		}
		newEvent.target = target;
		newEvent.currentTarget = target;
	}
	dispatchEventFunction( event );
}

 

 

getQualifiedClassName()은 객체의 클래스 이름을 반환해준다. 그러므로 원래 event객체와 clone()으로 만든 객체가 다르다면 TypeError를 발생시킨다. 이러한 이유로 clone()을 override 하지 않고 다시 Event를 송출하는 경우에는 TypeError가 발생하는 것이다.

 

 

정리하자

Event의 clone() 함수를 override 하는 이유는

  1. Custom Event가 복제될 때 복제된 Event의 속성이 원본 Event 속성과 같은 속성을 가질 수 있도록 하기 위해
  2. 이벤트를 재송출(redispatch)시에 TypeError 직면하지 않기 위해

 

4. clone()함수와 Event 전파와의 관계

많은 분들이 Event전파시 전파가 이뤄질 때마다 clone() 함수가 호출되는 것으로 생각하고 있는 듯하다. 실제로 Flex관련 서적이나 블로그에 보면 그런 내용이 담겨있는 것을 알 수 있다. 여기서는 clone()함수와 Event 전파에 어떤 관계가 있는가 간단한 예제로 알아보고자 한다. 결론을 먼저 말한다면 이들간에는 관계가 없다.

먼저 다음을 살펴보자.

 

ActionScript 3.0 부터 Event를 송출하기 위해 다음과 같은 과정을 거친다.

 

  • 이벤트 생성
    flash.events.Event 클래스를 사용하거나 그것을 확장해서 만든 Custom Event 클래스를 만든다.
    예) var event:MyEvent = new MyEvent( MyEvent.MYTYPE );
  • 이벤트 송출
    Event 클래스나 Custom Event 클래스를 가지고 객체를 생성하여 EventDispatcher를 확장한 클래스의 dispatchEvent()메소드를 이용해 이벤트를 송출한다.
    예) eventdispatcher.dispatchEvent( event );
  • 이벤트 전파
    이벤트 전파는 Visual 객체 즉, DisplayObject를 상속한 모든 객체에서만 가능하다. DisplayObject는 Eventdispatcher 클래스를 확장해서 만들어졌기 때문에 “이벤트 송출”을 할 수 있다.여기서 전파란 부모-자식 관계에 있는 객체들간에 이벤트 전달방법이다. 가령, A-B-C-D 의 순으로 부모-자식 관계를 형성했다면 C 객체에서 이벤트를 송출하는 경우 A->B->C->B->A 형태로 이벤트가 전파된다. 이벤트 전파방식을 세부적으로 나누면 capture, target, bubble 로 나뉠 수 있는데 앞에 A->B(부모->자식들)가 capture 과정, C가 target과정, B->A(자식들->부모)는 bubble 과정을 의미한다. C에서 발생했기 때문에 그의 자식인 D로는 이벤트 전파가 이뤄지지 않는다.이벤트 전파는 반드시 DisplayObject를 확장한 Visual 객체에서만 가능하다. 그게 아니라면 이벤트 전파 단계는 이뤄지지 않으며 앞서 설명한 capture, target, bubble과정중 target 과정만 이루어진다.
  • 이벤트 청취
    송출된 이벤트를 받는 것은 EventDispatcher를 확장한 클래스여야 한다. EventDispatcher에 있는 addEventListener() 메소드를 이용해 송출된 이벤트를 청취하며 더이상 청취하지 않으려면 removeEventListener() 메소드를 호출하면 된다. 이벤트를 송출한 객체와 청취하는 객체가 다른 것은 이벤트 전파단계를 거치는 Visutal 객체만 가능하며 Visual객체가 아니라면 이벤트를 송출한 객체와 청취한 객체가 같다.
    예) eventdispatcher.addEventListener( MyEvent.MYTYPE, myEventHandler );

 

 

지금까지 이벤트를 “생성-송출(재송출 포함)-청취” 하는 단계만 언급했지, “이벤트 전파”는 언급하지 않았다. 이벤트 전파는 Visual 객체만 가능하다. 그럼 위 설명대로 A-B-C-D순으로 부모-자식관계가 형성이 되어 있다면 이벤트 전파시 이벤트가 A->B->C->B->A 형태로 나아갈 때, clone() 메소드가 호출되는지 예제를 들어 알아보겠다.

 

먼저 Custom Event를 아래와 같이 만든다. 주목할 점은 clone()을 override하는데 trace(”clone 호출됨”)을 실행하고 있다는 것이다. 이것을 넣은 이유는 이벤트 전파시 clone() 메소드가 호출되는가 확인하기 위함이다.

 

 

package
{
	import flash.events.Event;

	public class CustomEvent extends Event
	{
		public static const TEST:String = "test";

		public function CustomEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super( type, bubbles, cancelable );
		}

		override public function clone():Event
		{
			trace("clone 호출됨");
			return new CustomEvent( type, bubbles, cancelable );
		}
	}
}

 

아래 클래스는 Button을 확장해서 만들었다. 중요하게 볼 것은 위에서 만든 CustomEvent 클래스를 송출한다는 점이다. CustomEvent의 2번째 인자는 bubbles이다. 즉, 이벤트 전파가 가능하게 하기 위해서는 이 인자를 true로 설정해야한다. (마우스 이벤트가 송출되는게 아니다.)

 

package
{
	import flash.events.MouseEvent;

	import mx.controls.Button;

	public class MyButton extends Button
	{
		public function MyButton()
		{
			this.label = "날 눌러줘";
			this.addEventListener( MouseEvent.CLICK, onClick );
		}

		private function onClick( event:MouseEvent ):void
		{
			var customEvent:CustomEvent = new CustomEvent( CustomEvent.TEST, true, false );
			this.dispatchEvent( customEvent );
		}
	}
}

 

 

아래 예제는 위에서 만든 Button을 HBox로 감싸서 자식으로 추가하고 Button에서 발생하는 CustomEvent를 Application과 HBox, 그리고 Button등 모든 전파단계(capture,target,bubble)에서 청취할 수 있도록 하고 있다. 참고로 addEventListener()의 3번째 인자는 cature이다. 이것을 true로 하면 capture단계에서 이벤트 청취를 할 수 있다. false이면 target-bubble과정만 청취가 가능하다.

 

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="*"
layout="absolute"
creationComplete="init()">

	<mx:HBox id="hbox">
		<local:MyButton id="button"/>
	</mx:HBox>

	<mx:Script>
	<![CDATA[
		import flash.events.EventPhase;

		private function init():void
		{
			this.addEventListener( "test", eventHandler );
			hbox.addEventListener( "test", eventHandler );
			button.addEventListener( "test", eventHandler );
			this.addEventListener( "test", eventHandler,true );
			hbox.addEventListener( "test", eventHandler,true );
			button.addEventListener( "test", eventHandler,true );
		}

		private function eventHandler( event:CustomEvent ):void
		{
			var eventPhase:String;
			switch( event.eventPhase )
			{
				case EventPhase.CAPTURING_PHASE:
					eventPhase = "capture";
					break;
				case EventPhase.AT_TARGET:
					eventPhase = "target";
					break;
				case EventPhase.BUBBLING_PHASE:
					eventPhase = "bubble";
					break;
			}
			trace( "target:", event.target, ", currentTarget", event.currentTarget, ", phase:", eventPhase );
		}
	]]>
	</mx:Script>
</mx:Application>

 

위 애플리케이션을 실행하고 버튼을 클릭해보자. 그럼 콘솔창에 다음과 같은 메시지가 보일 것이다.

 

target: EventTest0.hbox.button , currentTarget EventTest0 , phase: capture

target: EventTest0.hbox.button , currentTarget EventTest0.hbox , phase: capture

target: EventTest0.hbox.button , currentTarget EventTest0.hbox.button , phase: target

target: EventTest0.hbox.button , currentTarget EventTest0.hbox , phase: bubble

target: EventTest0.hbox.button , currentTarget EventTest0 , phase: bubble

 

위 메세지에서 target은 변하지 않는다. 왜냐하면 target은 이벤트가 최초 발생한 곳을 참조하기 때문이다. 이 작업은 dispatchEvent()에서 한다는 것을 앞서 설명했다.

 

두번째 currentTarget은 각각 이벤트 전파때마다 다르다. 즉, currentTarget이 지칭하는 것은 현재 이벤트가 지나가고 있는 객체를 참조한다. dispatchEvent()함수에서 최초로 설정되지만 이벤트 전파가 진행됨에 따라 계속 변한다.

 

phase는 위상으로 이벤트 전파가 어떻게 이뤄지고 있는지 보여준다.

 

만약 이벤트 전파를 통해 Event의 clone()이 호출된다면 여기 결과에 “clone 호출됨”메시지가 포함되어야 하지만 보여지지 않고 있다.

 

결론적으로 Event의 clone()은 Event를 재송출(redispatch)와 밀접한 관계가 있다. 하지만 Event 전파와 clone()과는 아무 상관 없으며 각각의 전파 단계(위상)가 변함에 따라 currentTarget과 phase 속성은 변경된다.

 

참고내용

Adobe Flex를 접할때 어려워하는 부분중에 하나가 Event입니다.
깊숙히 이해하려고 할때 잘 이해가 안되는 부분이 많은게 이벤트죠.

기본에 충실해야한다!


프로젝트(http://starpl.com)를 하면서 많이 느꼈습니다.


처음 Flex 접했을때 가장 어려움을 겪었던 것은 기본이 탄탄하지 못한 것으로부터 였습니다.


그래서 Flex와 함께 ActionScript 3.0의 기본 문법 및 대략적 내용을 이해해야한다고 생각합니다.


단순한 문법뿐 아니라 분석/설계에도 관심을 가지고 공부해서 명확하고 깨끗한 프로그램을 만들 수 있도록 노력해야겠구요.

 

아래는 Event에 대한 질문을 걸어놓고 스스로 답변까지 해두었습니다.
이것으로 Event 전부를 설명할 수 없지만 몇가지 궁금한 사항을 이해하는데는 도움이 될겁니다.


그냥 읽고 지식만 얻어가지 마시고 없는 질문이 있다면 스스로 질문/답변을 댓글로 달아주시면 더욱 고맙겠습니다. 잘못된 내용이나 추가해야한다는 것도 포함합니다.

 

1. ActionScript 3.0 이벤트(Event)

1.1 ActionScript 2.0과 3.0에서 이벤트가 바뀐점은?

  • ActionScript 2.0에서는 이벤트 리스너를 추가하는 경우 상황에 따라 addListener() 또는 addEventListener()를 사용하지만 ActionScript 3.0에서는 항상 addEventListener()를 사용합니다.
  • ActionScript 2.0에는 이벤트 흐름이 없으므로 이벤트를 브로드캐스팅하는 객체에서만 addListener() 메서드를 호출할 수 있지만 ActionScript 3.0에서는 이벤트 흐름에 포함된 모든 객체에서 addEventListener() 메서드를 호출할 수 있습니다.
  • ActionScript 2.0에서는 함수, 메서드 또는 객체를 이벤트 리스너로 사용할 수 있지만 ActionScript 3.0에서는 함수 또는 메서드만 이벤트 리스너로 사용할 수 있습니다.
  • on(event) 구문은 이제 ActionScript 3.0에서 지원되지 않으므로 ActionScript 이벤트 코드를 무비 클립에 첨부할 수 없습니다. 이벤트 리스너를 추가할 때는 addEventListener()만 사용할 수 있습니다.

1.2 Event를 사용해야하는 이유? 2가지 이상

  • 객체간 강한 결합 해결 : 애플리케이션 설계시 Callback함수를 사용하는 등의 강한결합을 이용하는 경우 유지보수 및 확장이 어려워지는 경우가 크다. 이벤트는 객체간 느슨한 결합을 해주는데 큰 힘을 발휘할 수 있다.
  • 작업에 대한 일관성 없는 응답 : 동기적 방식과 비동기적 방식에 대한 응답에 대한 처리를 이벤트를 사용하지 않는 경우 서로 다른 방법을 선택해서 사용해야한다. 이벤트는 1가지 방법으로 응답처리를 가능하게 해준다.


2. Event의 구성요소 3가지는 무엇이고 의미를 설명하라.

  • 이벤트 객체(Event Object)
    flash.net.Event의 객체나 이를 확장한 이벤트 객체를 의미한다. 이벤트 객체에는 이벤트에 대한 정보가 들어가있다. 이벤트의 종류(Type)과 이벤트를 발생한 객체(target)에 대한 정보가 포함된다.
  • 이벤트 발생자(Event Dispatcher)
    이벤트를 발생(broadcast)하는 객체이다. 이벤트 객체에 target으로 이벤트 발생자 정보가 담긴다. flash.net.EventDispatcher를 확장한 모든 객체는 이벤트 발생자가 될 수 있다. EventDispatcher의 dispatchEvent() 메소드에 이벤트 객체를 담아 실행하면 이벤트가 발생하게 된다.
  • 이벤트 청취자(Event Listener)
    일종의 메소드 또는 함수이다. 발생한 이벤트를 듣는 역할을 한다. EventDispatcher클래스를 확장한 클래스의 객체에서 addEventListener()을 이용해 이벤트 청취자를 등록할 수 있다. addEventListener()을 이용하면 청취할 이벤트의 종류(type)을 결정할 수 있으며 청취할 이벤트 흐름(capture,target,bubbling) 및 우선순위등을 선택할 수 있다.

3. Event Listener를 등록/삭제하는 방법을 기술하라.

  • 등록
    EventDispatcher의 addEventListener() 메소드를 이용한다.
    예)
    var button:Button = new Button();
    button.addEventListener( MouseEvent.CLICK, onClick, false );
    public function onClick( event:MouseEvent )
    {
      trace( event.type, event.target  );
    }
  • 삭제
    EventDispathcer의 removeEventListener() 메소드를 이용한다.
    예)
    button.removeEventListener( MouseEvent.CLICK, onClick, false );
    3개의 인자가 addEventListener()를 사용할때와 같아야 정상적으로 이벤트 청취자 등록이 삭제된다.
    참고로 맨 마지막 인자는 useCapture이다.

4. Event 전파단계의 종류 3가지가 무엇이며 각각에 대한 설명

  • 이벤트 전파단계의 종류 : 캡쳐(capture) 단계, 타겟(target) 단계, 버블(bubble)단계
  • 이벤트 전파 순서 : 캡쳐->타겟->버블
  • 캡쳐 단계
    루트(root) 디스플레이 객체서부터 이벤트를 발생한 객체(target)까지 디스플레이 객체 리스트(display Object List)를 따라 검색해나가는 단계이다. 즉, 부모->자식으로 이벤트를 전파한다. 이 캡쳐 단계에 이벤트를 청취하기 위해서는 addEventListener()의 3번째 인자인 useCapture가 반드시 true로 되어야 한다. false로 되어 있다면 캡쳐단계가 아닌 타겟과 버블 단계에서 이벤트 청취를 한다.
    캡쳐단계 청취하는 목적은 다양할 수 있다. 가령 중간에 이벤트 전파를 중지시킬때 필요할 수 있다. 이해가 안될 수 있지만 실제 프로젝트를 하면 이런 경우가 다분하다.
    사용 예 )
    button.addEventListener( MouseEvent.CLICK, onClick, true );
  • 타겟 단계
    갭쳐 단계 이후 타겟단계에 도달한다. 타겟 단계는 이벤트를 발생시킨 객체까지 도달했을때 이뤄진다.
  • 버블 단계
    타겟 단계 이후 다시 루트(root) 디스플레이 객체로 이벤트가 전파되는 단계이다. 버블버블 물망울이 수면위로 다시 올라가는 것을 연상하면 되겠다. 캡쳐와 반대로 자식->부모로 이벤트가 전파되는 단계이다.
    이벤트가 버블 단계로 전파되기 위한 조건은 이벤트 송출시 Event의 생성자 인자값중 bubble값을 true로 설정되어 있어야 한다. MouseEvent같은 경우 이 bubble값이 true로 설정되어 있다.
    타겟단계와 버블단계에 이벤트 청취자를 등록하기 위해 addEventListener()의 3번째 인자 useCapture가 false로 되어야 한다. useCapture를 설정하지 않으면 기본값이 false이다.
  • 이벤트 객체가 전파되는 기능이 굳이 필요한 이유
    부모-자식1-자식2….-자식100 의 형태로 디스플레이 객체 리스트가 형성되어있다고 가정하자 자식50에서 자식 100을 마우스 클릭했을때 이벤트를 받고 싶다고 하자. 자식50은 자식100의 존재유무를 모른다(알 수 있지만 알게되면 강한결합이 일어난다.) 이런 경우 이벤트 객체가 전파되는 기능은 매우 유용하게 쓰일 수 있다. 캡쳐나 버블 단계에서 자식50은 자식100에서 발생된 이벤트를 받을 수 있다.
  • flash.display.DisplayObject 클래스는 EventDispatcher를 확장해서 만들어졌다는 것에 주목하자.

5. 4번의 Event 전파는 어떤 환경에서 가능한가?

  • 반드시 디스플레이 객체 리스트(Display Object List) 상에 존재하는 디스플레이 객체(Display Object)여야 이벤트 전파가 가능하다. 그 외의 이벤트 전파는 타겟 단계밖에 없다. 가령, HTTPService나 URLLoader에서 이벤트가 발생하는 경우 이들은 디스플레이 객체가 아니다.
  • 이벤트 전파의 조건
    1. 디스플레이 객체여야 한다.(flash.display.DisplayObject를 확장한 클래스의 객체)
    2. 디스플레이 객체는 디스플레이 객체 리스트에 존재해야 한다. 즉, stage로 부터 addChild되어 있어야 한다.

6. 이벤트 전파시 target과 currentTarget의 차이점은 무엇인가? 예를 들어 설명하라.

  • target은 이벤트를 발생한 객체이고 currentTarget은 이벤트를 청취하는 객체이다.
  • 예시)
    var p:Sprite = new Sprite(); //parent
    var c:Sprite = new Sprite(); //child
    addChild( p );
    p.addChild( c ); //c가 p의 자식인 것에 주목
    p.addEventListener( MouseEvent.CLICK, onClick ); //이벤트 청취를 p가 하는 것에 주목!
    public function onClick( event:MouseEvent )
    {
    trace( event.target, event.currentTarget ); //c를 클릭했을 경우 c, p가 나오고 p를 클릭했을 경우 p,p가 나온다.
    }

7. Event 우선순위에 대해서 간단한 예제를 들어 설명하라.

  • 이벤트 우선순위는 구체적으로 이벤트 청취 우선순위라고 할 수 있다.
  • 이벤트 청취 우선순위는 addEventListener()의 4번째 인자인 priority를 이용한다. 이 값이 클 수록 청취 우선순위가 높아지며 addEventListener()의 등록 순서에 상관없이 우선순위에 따라 이벤트 리스너가 호출된다.
  • 이벤트 청취 우선순위가 유효한 조건
    1. 같은 객체에서 청취한다.
    2. 같은 이벤트 종류(type)을 청취한다.
    3. 같은 useCapure 값을 가진다.
  • ex)
    a.addEventListener(MouseEvent.CLICK, aHandler, false, 3);
    a.addEventListener(MouseEvent.CLICK, bHandler, false, 1);
    a.addEventListener(MouseEvent.CLICK, cHandler, false, 100);

    a객체에 MouseEvent.CLICK 이벤트가 발생시 등록순서에 상관없이 priority값이 가장큰 cHandler가 먼저 호출되고 다음으로 aHandler, bHandler가 호출된다.

8. Event 전파를 중단하는 메소드 2개가 무엇이며 간단한 예제를 들어 각각의 차이점을 설명하라.

  • Event 클래스의 stopPropogation()와 stopImmediatePropogation() 메소드이다.
  • 차이점
    - stopPropogation()
      이벤트 전파를 중단하되 같은 노드의 이벤트를 청취까지는 허락한다.
    - stopImmediatePropogation()
      이벤트 전파를 즉시 중단한다.
  • 예제
    back이 배경 canvas이고 button1이 back에 포함된 button일때
    back.addEventListener(MouseEvent.CLICK, event1Handler);
    back.button.addEventListener(MouseEvent.CLICK, event2Handler);
    back.button.addEventListener(MouseEvent.CLICK, event3Handler);
    back.button.addEventListener(MouseEvent.MOUSE_OUT, outHandler);private function event1Handler(e:MouseEvent):void
    {
           trace(’back’);
    }
    private function event2Handler(e:MouseEvent):void
    {
           trace(’button’);
           e.stopPropagation();
           e.stopImmediatePropagation();
    }
    private function event3Handler(e:MouseEvent):void
    {
           trace(’button2′);
    }
    private function event1Handler(e:MouseEvent):void
    {
           trace(’out’);
    }

    e.stopPropagation(); e.stopImmediatePropagation(); 두가지 다 없을경우
            button
            button2
            back
            out
    e.stopPropagation(); 만 실행
            button
            button2
            out
    e.stopImmediatePropagation(); 만 실행
            button
            out

9. addEventListener의  useCapture 인자는 무엇을 의미하는가? 간단한 예제를 들어 설명하라.

  • 이벤트 전파를 청취할때 캡처단계를 청취할지 타겟 및 버블 단계를 청취할지 결정하는 인자이다.
  • 모든 전파 단계에서 청취하기 위해서는 useCapture가 다른 2개의 addEventListener()를 사용해야한다.
  • 예제 생략

10. Sprite, Shape와 같은 클래스에서 어떻게 하면 이벤트를 송출할 수 있는가?

  • dispatchEvent() 메소드를 호출하면 된다.

11. Sprite, Shape와 같이 이벤트 송출할 수 있는 클래스는 근본적으로 어떤 클래스를 상속받았기 때문에 가능한가?

  • Sprite와 Shape는 궁극적으로 DisplayObject를 확장해서 만들어졌으며 DisplayObject는 EventDispatcher를 확장했다. 그러므로 Sprite와 Shape와 같은 클래스는 EventDispatcher의 메소드를 전부 사용할 수있다. 이중에 dispatchEvent()도 사용할 수 있다.

12. IEventDispatcher는 무엇이며 어떤 경우에 이것을 사용해야할까?

  • EventDispatcher의 인터페이스이다.
  • IEventDispatcher를 활용하는 것은 다중상속이 불가능한 AS3에서 유용하다.
    가령, 어떤 클래스가 ProxyObject를 확장해서 만들어져야한다. 그런데 이 클래스는 이벤트도 발생시켜야 하므로 EventDispatcher클래스도 확장해야한다. ActionScript 3.0은 다중상속을 지원하지 않는다.(다중상속은 이점도 있지만 단점도 있다.) 그러므로 이러한 경우에는 주가 되는 클래스인 ProxyObject를 is-a 확장을 하고 EventDispatcher를 has-a 확장을 감행한다음 만들어진 클래스가 EventDispatcher의 기능을 수행한다는 것을 명시적으로 외부에 알려주고 내부적으로 구현시켜주기 위해 IEventDispatcher 인터페이스를 implements 해준다. 이렇게 하면 원하는 기능인 ProxyObject와 EventDispatcher의 기능을 전부 수행하면서도 다중상속의 문제를 극복할 수 있다.
  • ex)
    public class MyProxyObject extends ProxyObject implements IEventDispatcher
    {
      var evnetDispatcher:EventDispatcher = new EventDispatcher();
     
      //내부적으로 IEventDispatcher에 선언한 메소드를 구현한다.  가령…
      public function dispatchEvent( event:Event ):Boolean
      {
         return  eventDispatcher.dispatchEvent( event );
      }
    }

13. preventDefault()와 stopPropagation(), stopImmediatePropagation()의 근본적 차이점은 무엇인가?


  • preventDefault()는 기본 동작(dafault behaviors)를 취소하는데 쓰이는 메소드인 반면 stopPropagation(), stopImmediatePropagation()는 이벤트 전파에만 영향을 준다.

 

  • preventDefault()가 사용 가능한지는 isDefaultPrevented() 메소드로 알 수 있다.

14. preventDefault()를 호출해서 실행을 가능하게 하기 위한 조건은 무엇인가?

  • 이벤트 객체를 생성시 cancelable속성이 true로 설정되어 있어야 한다.

15. 커스텀 이벤트를 만들어야하는 어떤 경우이며 어떻게 만드는가?

  • 커스텀 이벤트는 기본 이벤트 클래스(flash.events.Event) 또는 그 상위 이벤트 클래스를 확장하는 경우를 의미한다.
  • 이벤트를 확장해야하는 경우는 이벤트 송출시에 필요한 정보를 담아내기 위함이다.
  • 만드는 방법
    class MyEvent extends Event
    {
      public static const MY_EVENT:String = “myevent”;
      public var value:String;
      public function MyEvent( type:String, value:String ):void
      {
         super( type, false, false );
         this.value = value;
      }
      overide public function clone():Event
      {
        return new MyEvent( type, value );
      }
    }

16. 커스텀 이벤트가 bubbling이 가능하기 위해 어떤 조건이 필요한가?

  • Event의 생성자의 2번째 인자인 bubble값을 true로 설정한다.

17. 커스텀 컴포넌트 만들때 clone()함수를 override해야하는 이유는 무엇인가?

  • clone()메소드는 기존과 신규 properties들을 clone 내로 설정해 이벤트 객체의 복사본을 돌려준다.
    clone() 메소드를 Event 클래스 확장시 오버라이드 해야하는 이유는 이벤트를 다시 재전달해야하는 경우 묵시적으로 복사해서 재전달할 필요가 있을때 필요하기 때문이다.

 

  • 참고로 toString()도 확장해서 사용하는 것이 좋다. 이벤트에 대한 명세서가 될 수 있기 때문이다.

18. 약참조에 대해서 GC(가비지 콜렉션)과 관련되어 설명하라.

  • addEventListener()함수로 이벤트 청취자를 등록할때 5번째 인자로 useWeakReference 인자가 있다. 이것을 true로 설정하면 이벤트 청취자가 이벤트 청취를 약참조를 하겠다는 의미이다. 반대로 false(기본)이면 강참조이다.
  • 약참조, 강참조를 설명하기 전에 가비지 콜렉션(GC, Garbage Collection)에 대해서 먼저 알아야한다.
    만들어졌던 객체가 더이상 쓸모가 없어졌을때 GC라고 한다. 쓸모가 없어졌다는 것은 참조의 고리가 끊어졌다는 것을 의미한다. 메모리에 상주해 있지만 조만간 메모리에서 없애야할 대상인 것이다. Flash Player는 GC로 선정된 객체들을 메모리에서 삭제하는 시점을 내부적으로 구현해놓고 있다.(많은 분들이 Flash Player GC처리를 못한다고 하지만 실제로는 문제없이 잘하고 있다. 단지 효율적으로 GC처리를 하는지는 의문이지만…)
    Flash Player GC 대상을 찾아내기 위한 방법은 2가지가 있다. 그것은 레퍼런스 카운팅(reference counting)과 마크 앤 스윕(Mark and Sweep)이다. 레퍼런스 카운팅은 생성된 객체가 참조할때마다 참조 카운트를 1씩 더해준다. 또 참조에서 끊어지는 경우 1씩 감소시켜준다. 만약 레퍼런스 카운팅 값이 0이되는 경우 바로 GC 대상이 되는 것이다. 마크 앤 스윕은 레퍼런스 카운팅 만으로 찾을 수 없는 GC대상을 찾아준다. 가령, 두개 이상의 객체가 서로 다른 객체를 참조하고 있다고 가정한다.(순환참조) 그럼 이들 레퍼런스 카운팅 값은 1 이상이다. 그러므로 레퍼런스 카운팅 방법으로는 GC대상을 만들 수 없다. 마크 앤 스윕은  이들 객체가 최상위 부모와 연결되어 있는지 검사하는 방법이다. 부모로 부터 꼬리에 꼬리를 물고 객체가 참조되어 있는 것인지 조사해나가는 방법으로 GC대상을 검색한다.
    레퍼런스 카운팅과 마크앤 스윕 방법으로 찾아낸 GC대상은 메모리에 상주되지만 Flash Player의 GC 삭제방법 로직에 의해 어느 순간 삭제된다.(이 시점은 프로그래머가 어찌할 수 없다. AIR 또는 Flash Player Debug버전의 경우에만 System.gc()를 이용해 강제적으로 GC를 메모리에서 삭제할 수 있긴 하다.)
  • addEventListener()의 useWeakReference를 true로 설정하면 약참조가 되어 레퍼런스 카운팅을 증가시키지 않는다. 강참조의 경우 이벤트 대상(target)의 참조를 지웠다고 해도 addEventListener로 등록한 청취자 메소드가 GC대상이 아니라면 이벤트 대상의 레퍼런스 카운팅 값은 0이 될 수 없다. 결국 프로그래머의 뜻과 다르게 메모리를 낭비하는 일을 초래할 수 있다. 물론 이벤트 대상이 사라지는 시점에 removeEventListener() 사용하면 되겠지만 때로는 그 시점을 모르는 경우도 발생한다. 이러한 경우 useWeakReference를 true로 설정해서 메모리 낭비를 없앨 수 있겠다.
  • Flex에서 List 계열의 컴포넌트를 살펴보면 dataProvider()에 ArrayCollection 객체를 받을때 ArrayCollection의 변화를 살펴보기 위해 addEventListener()를 사용한다. 그런데 ArrayCollection 객체는 언제 어떻게 될지 List 계열 컴포넌트는 알 수 없을 것이다. 이때 유용하게 사용하는 것이 바로 useWeakReference=true로 설정해서 필요없어진 ArrayCollection이 메모리 남는 것을 방지할 수 있다. 참고 : http://blog.jidolstar.com/375


19. 이벤트가 전파되는 중간에 전파를 멈추게 하기 위해 stopPropagation()을 사용하지 않고removeEventListener() 를 사용하면 안되는 이유를 설명하라. (힌트, ActionScript 3.0의 Native 메커니즘에 입각한다. http://bixworld.egloos.com/2149717)

 

  • 이벤트 송출을 시작하면 flash player는 이벤트가 송출될 디스플레이 객체 리스트를 전부 조사해 미리 이벤트 객체를 복사해두기 때문이다. 이미 전파할 대상이 모두 정해져있고 전파가 시행중인 경우에는 중간에 removeEventLisener를 사용하더라도 일단 송출된 이벤트는 끝까지 전파된다. 다음 시점 이벤트 송출시에는 removeEventLisener()가 적용되어 있는 상태가 된다.
 

 

 

20. willTrigger()와 hasEventListener()의 의미를 설명하고와 두 메소드의 차이점을 보여줄 수 있는 대한 예를 들어보자.

 

 

 

  • 공통점
    hasEventListener()이나 willTrigger() 모두 이벤트 청취자가 있는지 검사한다.
  • 차이점
    hasEventListener() : 지정한 객체에(만) 이벤트 청취자가 있는지 조사한다. 이벤트 흐름과는 무관하다.
    willTrigger() : 지정한 객체를 포함하는 디스플레이 객체 리스트에 해당 이벤트가 청취자가 있는지 조사한다. 그러므로 이벤트 흐름과 관련 있다.
  • 예제
    var button:Sprite = new Sprite();
    addChild( button );
    this.addEventListener( MouseEvent.CLICK, onClick );
    trace( button.willTrigger( MouseEvent.CLICK ) ); //true
    trace( button.hasEventListener( MouseEvent.CLICK ) ); //false ->왜 false가 되는가 아는게 중요!
    trace( this.willTrigger( MouseEvent.CLICK ) ); //true
    trace( this.hasEventListener( MouseEvent.CLICK ) ); //true
  • 이 두개의 함수를 잘 활용하면 쓸데없이 이벤트를 송출하는 경우를 방지할 수 있다.

21. ActionScript 3.0 이벤트에 대해 참고할 만한 문헌 및 자료를 조사하자.

Adobe Flex를 접할때 어려워하는 부분중에 하나가 Event입니다.
깊숙히 이해하려고 할때 잘 이해가 안되는 부분이 많은게 이벤트죠.

기본에 충실해야한다!
프로젝트(http://starpl.com)를 하면서 많이 느꼈습니다.
처음 Flex 접했을때 가장 어려움을 겪었던 것은 기본이 탄탄하지 못한 것으로부터 였습니다.
그래서 Flex와 함께 ActionScript 3.0의 기본 문법 및 대략적 내용을 이해해야한다고 생각합니다.
단순한 문법뿐 아니라 분석/설계에도 관심을 가지고 공부해서 명확하고 깨끗한 프로그램을 만들 수 있도록 노력해야겠구요.

아래는 Event에 대한 질문을 걸어놓고 스스로 답변까지 해두었습니다.
이것으로 Event 전부를 설명할 수 없지만 몇가지 궁금한 사항을 이해하는데는 도움이 될겁니다.
그냥 읽고 지식만 얻어가지 마시고 없는 질문이 있다면 스스로 질문/답변을 댓글로 달아주시면 더욱 고맙겠습니다. 잘못된 내용이나 추가해야한다는 것도 포함합니다.


1. ActionScript 3.0 이벤트(Event)

1.1 ActionScript 2.0과 3.0에서 이벤트가 바뀐점은?

  • ActionScript 2.0에서는 이벤트 리스너를 추가하는 경우 상황에 따라 addListener() 또는 addEventListener()를 사용하지만 ActionScript 3.0에서는 항상 addEventListener()를 사용합니다.
  • ActionScript 2.0에는 이벤트 흐름이 없으므로 이벤트를 브로드캐스팅하는 객체에서만 addListener() 메서드를 호출할 수 있지만 ActionScript 3.0에서는 이벤트 흐름에 포함된 모든 객체에서 addEventListener() 메서드를 호출할 수 있습니다.
  • ActionScript 2.0에서는 함수, 메서드 또는 객체를 이벤트 리스너로 사용할 수 있지만 ActionScript 3.0에서는 함수 또는 메서드만 이벤트 리스너로 사용할 수 있습니다.
  • on(event) 구문은 이제 ActionScript 3.0에서 지원되지 않으므로 ActionScript 이벤트 코드를 무비 클립에 첨부할 수 없습니다. 이벤트 리스너를 추가할 때는 addEventListener()만 사용할 수 있습니다.


1.2 Event를 사용해야하는 이유? 2가지 이상

  • 객체간 강한 결합 해결 : 애플리케이션 설계시 Callback함수를 사용하는 등의 강한결합을 이용하는 경우 유지보수 및 확장이 어려워지는 경우가 크다. 이벤트는 객체간 느슨한 결합을 해주는데 큰 힘을 발휘할 수 있다.
  • 작업에 대한 일관성 없는 응답 : 동기적 방식과 비동기적 방식에 대한 응답에 대한 처리를 이벤트를 사용하지 않는 경우 서로 다른 방법을 선택해서 사용해야한다. 이벤트는 1가지 방법으로 응답처리를 가능하게 해준다.


2. Event의 구성요소 3가지는 무엇이고 의미를 설명하라.

  • 이벤트 객체(Event Object)
    flash.net.Event의 객체나 이를 확장한 이벤트 객체를 의미한다. 이벤트 객체에는 이벤트에 대한 정보가 들어가있다. 이벤트의 종류(Type)과 이벤트를 발생한 객체(target)에 대한 정보가 포함된다.
  • 이벤트 발생자(Event Dispatcher)
    이벤트를 발생(broadcast)하는 객체이다. 이벤트 객체에 target으로 이벤트 발생자 정보가 담긴다. flash.net.EventDispatcher를 확장한 모든 객체는 이벤트 발생자가 될 수 있다. EventDispatcher의 dispatchEvent() 메소드에 이벤트 객체를 담아 실행하면 이벤트가 발생하게 된다.
  • 이벤트 청취자(Event Listener)
    일종의 메소드 또는 함수이다. 발생한 이벤트를 듣는 역할을 한다. EventDispatcher클래스를 확장한 클래스의 객체에서 addEventListener()을 이용해 이벤트 청취자를 등록할 수 있다. addEventListener()을 이용하면 청취할 이벤트의 종류(type)을 결정할 수 있으며 청취할 이벤트 흐름(capture,target,bubbling) 및 우선순위등을 선택할 수 있다.


3. Event Listener를 등록/삭제하는 방법을 기술하라.

  • 등록
    EventDispatcher의 addEventListener() 메소드를 이용한다.
    예)
    var button:Button = new Button();
    button.addEventListener( MouseEvent.CLICK, onClick, false );
    public function onClick( event:MouseEvent )
    {
      trace( event.type, event.target  );
    }
  • 삭제
    EventDispathcer의 removeEventListener() 메소드를 이용한다.
    예)
    button.removeEventListener( MouseEvent.CLICK, onClick, false );
    3개의 인자가 addEventListener()를 사용할때와 같아야 정상적으로 이벤트 청취자 등록이 삭제된다.
    참고로 맨 마지막 인자는 useCapture이다.


4. Event 전파단계의 종류 3가지가 무엇이며 각각에 대한 설명

  • 이벤트 전파단계의 종류 : 캡쳐(capture) 단계, 타겟(target) 단계, 버블(bubble)단계
  • 이벤트 전파 순서 : 캡쳐->타겟->버블
  • 캡쳐 단계
    루트(root) 디스플레이 객체서부터 이벤트를 발생한 객체(target)까지 디스플레이 객체 리스트(display Object List)를 따라 검색해나가는 단계이다. 즉, 부모->자식으로 이벤트를 전파한다. 이 캡쳐 단계에 이벤트를 청취하기 위해서는 addEventListener()의 3번째 인자인 useCapture가 반드시 true로 되어야 한다. false로 되어 있다면 캡쳐단계가 아닌 타겟과 버블 단계에서 이벤트 청취를 한다.
    캡쳐단계 청취하는 목적은 다양할 수 있다. 가령 중간에 이벤트 전파를 중지시킬때 필요할 수 있다. 이해가 안될 수 있지만 실제 프로젝트를 하면 이런 경우가 다분하다.
    사용 예 )
    button.addEventListener( MouseEvent.CLICK, onClick, true );
  • 타겟 단계
    갭쳐 단계 이후 타겟단계에 도달한다. 타겟 단계는 이벤트를 발생시킨 객체까지 도달했을때 이뤄진다.
  • 버블 단계
    타겟 단계 이후 다시 루트(root) 디스플레이 객체로 이벤트가 전파되는 단계이다. 버블버블 물망울이 수면위로 다시 올라가는 것을 연상하면 되겠다. 캡쳐와 반대로 자식->부모로 이벤트가 전파되는 단계이다.
    이벤트가 버블 단계로 전파되기 위한 조건은 이벤트 송출시 Event의 생성자 인자값중 bubble값을 true로 설정되어 있어야 한다. MouseEvent같은 경우 이 bubble값이 true로 설정되어 있다.
    타겟단계와 버블단계에 이벤트 청취자를 등록하기 위해 addEventListener()의 3번째 인자 useCapture가 false로 되어야 한다. useCapture를 설정하지 않으면 기본값이 false이다.
  • 이벤트 객체가 전파되는 기능이 굳이 필요한 이유
    부모-자식1-자식2....-자식100 의 형태로 디스플레이 객체 리스트가 형성되어있다고 가정하자 자식50에서 자식 100을 마우스 클릭했을때 이벤트를 받고 싶다고 하자. 자식50은 자식100의 존재유무를 모른다(알 수 있지만 알게되면 강한결합이 일어난다.) 이런 경우 이벤트 객체가 전파되는 기능은 매우 유용하게 쓰일 수 있다. 캡쳐나 버블 단계에서 자식50은 자식100에서 발생된 이벤트를 받을 수 있다.
  • flash.display.DisplayObject 클래스는 EventDispatcher를 확장해서 만들어졌다는 것에 주목하자.


5. 4번의 Event 전파는 어떤 환경에서 가능한가?

  • 반드시 디스플레이 객체 리스트(Display Object List) 상에 존재하는 디스플레이 객체(Display Object)여야 이벤트 전파가 가능하다. 그 외의 이벤트 전파는 타겟 단계밖에 없다. 가령, HTTPService나 URLLoader에서 이벤트가 발생하는 경우 이들은 디스플레이 객체가 아니다.
  • 이벤트 전파의 조건
    1. 디스플레이 객체여야 한다.(flash.display.DisplayObject를 확장한 클래스의 객체)
    2. 디스플레이 객체는 디스플레이 객체 리스트에 존재해야 한다. 즉, stage로 부터 addChild되어 있어야 한다.

6. 이벤트 전파시 target과 currentTarget의 차이점은 무엇인가? 예를 들어 설명하라.

  • target은 이벤트를 발생한 객체이고 currentTarget은 이벤트를 청취하는 객체이다.
  • 예시)
    var p:Sprite = new Sprite(); //parent
    var c:Sprite = new Sprite(); //child
    addChild( p );
    p.addChild( c ); //c가 p의 자식인 것에 주목
    p.addEventListener( MouseEvent.CLICK, onClick ); //이벤트 청취를 p가 하는 것에 주목!
    public function onClick( event:MouseEvent )
    {
    trace( event.target, event.currentTarget ); //c를 클릭했을 경우 c, p가 나오고 p를 클릭했을 경우 p,p가 나온다.
    }


7. Event 우선순위에 대해서 간단한 예제를 들어 설명하라.

  • 이벤트 우선순위는 구체적으로 이벤트 청취 우선순위라고 할 수 있다.
  • 이벤트 청취 우선순위는 addEventListener()의 4번째 인자인 priority를 이용한다. 이 값이 클 수록 청취 우선순위가 높아지며 addEventListener()의 등록 순서에 상관없이 우선순위에 따라 이벤트 리스너가 호출된다.
  • 이벤트 청취 우선순위가 유효한 조건
    1. 같은 객체에서 청취한다.
    2. 같은 이벤트 종류(type)을 청취한다.
    3. 같은 useCapure 값을 가진다.
  • ex)
    a.addEventListener(MouseEvent.CLICK, aHandler, false, 3);
    a.addEventListener(MouseEvent.CLICK, bHandler, false, 1);
    a.addEventListener(MouseEvent.CLICK, cHandler, false, 100);

    a객체에 MouseEvent.CLICK 이벤트가 발생시 등록순서에 상관없이 priority값이 가장큰 cHandler가 먼저 호출되고 다음으로 aHandler, bHandler가 호출된다.

8. Event 전파를 중단하는 메소드 2개가 무엇이며 간단한 예제를 들어 각각의 차이점을 설명하라.

  • Event 클래스의 stopPropogation()와 stopImmediatePropogation() 메소드이다.
  • 차이점
    - stopPropogation()
      이벤트 전파를 중단하되 같은 노드의 이벤트를 청취까지는 허락한다.
    - stopImmediatePropogation()
      이벤트 전파를 즉시 중단한다.
  • 예제
    back이 배경 canvas이고 button1이 back에 포함된 button일때
    back.addEventListener(MouseEvent.CLICK, event1Handler);
    back.button.addEventListener(MouseEvent.CLICK, event2Handler);
    back.button.addEventListener(MouseEvent.CLICK, event3Handler);
    back.button.addEventListener(MouseEvent.MOUSE_OUT, outHandler);

    private function event1Handler(e:MouseEvent):void
    {
           trace('back');
    }
    private function event2Handler(e:MouseEvent):void
    {
           trace('button');
           e.stopPropagation();
           e.stopImmediatePropagation();
    }
    private function event3Handler(e:MouseEvent):void
    {
           trace('button2');
    }
    private function event1Handler(e:MouseEvent):void
    {
           trace('out');
    }

    e.stopPropagation(); e.stopImmediatePropagation(); 두가지 다 없을경우
            button
            button2
            back
            out
    e.stopPropagation(); 만 실행
            button
            button2
            out
    e.stopImmediatePropagation(); 만 실행
            button
            out
     


9. addEventListener의  useCapture 인자는 무엇을 의미하는가? 간단한 예제를 들어 설명하라.

  • 이벤트 전파를 청취할때 캡처단계를 청취할지 타겟 및 버블 단계를 청취할지 결정하는 인자이다.
  • 모든 전파 단계에서 청취하기 위해서는 useCapture가 다른 2개의 addEventListener()를 사용해야한다.
  • 예제 생략


10. Sprite, Shape와 같은 클래스에서 어떻게 하면 이벤트를 송출할 수 있는가?

  • dispatchEvent() 메소드를 호출하면 된다.


11. Sprite, Shape와 같이 이벤트 송출할 수 있는 클래스는 근본적으로 어떤 클래스를 상속받았기 때문에 가능한가?

  • Sprite와 Shape는 궁극적으로 DisplayObject를 확장해서 만들어졌으며 DisplayObject는 EventDispatcher를 확장했다. 그러므로 Sprite와 Shape와 같은 클래스는 EventDispatcher의 메소드를 전부 사용할 수있다. 이중에 dispatchEvent()도 사용할 수 있다.


12. IEventDispatcher는 무엇이며 어떤 경우에 이것을 사용해야할까?

  • EventDispatcher의 인터페이스이다.
  • IEventDispatcher를 활용하는 것은 다중상속이 불가능한 AS3에서 유용하다.
    가령, 어떤 클래스가 ProxyObject를 확장해서 만들어져야한다. 그런데 이 클래스는 이벤트도 발생시켜야 하므로 EventDispatcher클래스도 확장해야한다. ActionScript 3.0은 다중상속을 지원하지 않는다.(다중상속은 이점도 있지만 단점도 있다.) 그러므로 이러한 경우에는 주가 되는 클래스인 ProxyObject를 is-a 확장을 하고 EventDispatcher를 has-a 확장을 감행한다음 만들어진 클래스가 EventDispatcher의 기능을 수행한다는 것을 명시적으로 외부에 알려주고 내부적으로 구현시켜주기 위해 IEventDispatcher 인터페이스를 implements 해준다. 이렇게 하면 원하는 기능인 ProxyObject와 EventDispatcher의 기능을 전부 수행하면서도 다중상속의 문제를 극복할 수 있다.
  • ex)
    public class MyProxyObject extends ProxyObject implements IEventDispatcher
    {
      var evnetDispatcher:EventDispatcher = new EventDispatcher();
     
      //내부적으로 IEventDispatcher에 선언한 메소드를 구현한다.  가령...
      public function dispatchEvent( event:Event ):Boolean
      {
         return  eventDispatcher.dispatchEvent( event );
      }
    }


13. preventDefault()와 stopPropagation(), stopImmediatePropagation()의 근본적 차이점은 무엇인가?

  • preventDefault()는 기본 동작(dafault behaviors)를 취소하는데 쓰이는 메소드인 반면 stopPropagation(), stopImmediatePropagation()는 이벤트 전파에만 영향을 준다.
  • preventDefault()가 사용 가능한지는 isDefaultPrevented() 메소드로 알 수 있다.

14. preventDefault()를 호출해서 실행을 가능하게 하기 위한 조건은 무엇인가?

  • 이벤트 객체를 생성시 cancelable속성이 true로 설정되어 있어야 한다.


15. 커스텀 이벤트를 만들어야하는 어떤 경우이며 어떻게 만드는가?

  • 커스텀 이벤트는 기본 이벤트 클래스(flash.events.Event) 또는 그 상위 이벤트 클래스를 확장하는 경우를 의미한다.
  • 이벤트를 확장해야하는 경우는 이벤트 송출시에 필요한 정보를 담아내기 위함이다.
  • 만드는 방법
    class MyEvent extends Event
    {
      public static const MY_EVENT:String = "myevent";
      public var value:String;
      public function MyEvent( type:String, value:String ):void
      {
         super( type, false, false );
         this.value = value;
      }
      overide public function clone():Event
      {
        return new MyEvent( type, value );
      }
    }



16. 커스텀 이벤트가 bubbling이 가능하기 위해 어떤 조건이 필요한가?

  • Event의 생성자의 2번째 인자인 bubble값을 true로 설정한다.

17. 커스텀 컴포넌트 만들때 clone()함수를 override해야하는 이유는 무엇인가?

  • clone()메소드는 기존과 신규 properties들을 clone 내로 설정해 이벤트 객체의 복사본을 돌려준다.
    clone() 메소드를 Event 클래스 확장시 오버라이드 해야하는 이유는 이벤트를 다시 재전달해야하는 경우 묵시적으로 복사해서 재전달할 필요가 있을때 필요하기 때문이다.
  • 참고로 toString()도 확장해서 사용하는 것이 좋다. 이벤트에 대한 명세서가 될 수 있기 때문이다.

18. 약참조에 대해서 GC(가비지 콜렉션)과 관련되어 설명하라.

  • addEventListener()함수로 이벤트 청취자를 등록할때 5번째 인자로 useWeakReference 인자가 있다. 이것을 true로 설정하면 이벤트 청취자가 이벤트 청취를 약참조를 하겠다는 의미이다. 반대로 false(기본)이면 강참조이다.
  • 약참조, 강참조를 설명하기 전에 가비지 콜렉션(GC, Garbage Collection)에 대해서 먼저 알아야한다.
    만들어졌던 객체가 더이상 쓸모가 없어졌을때 GC라고 한다. 쓸모가 없어졌다는 것은 참조의 고리가 끊어졌다는 것을 의미한다. 메모리에 상주해 있지만 조만간 메모리에서 없애야할 대상인 것이다. Flash Player는 GC로 선정된 객체들을 메모리에서 삭제하는 시점을 내부적으로 구현해놓고 있다.(많은 분들이 Flash Player GC처리를 못한다고 하지만 실제로는 문제없이 잘하고 있다. 단지 효율적으로 GC처리를 하는지는 의문이지만...)
    Flash Player GC 대상을 찾아내기 위한 방법은 2가지가 있다. 그것은 레퍼런스 카운팅(reference counting)과 마크 앤 스윕(Mark and Sweep)이다. 레퍼런스 카운팅은 생성된 객체가 참조할때마다 참조 카운트를 1씩 더해준다. 또 참조에서 끊어지는 경우 1씩 감소시켜준다. 만약 레퍼런스 카운팅 값이 0이되는 경우 바로 GC 대상이 되는 것이다. 마크 앤 스윕은 레퍼런스 카운팅 만으로 찾을 수 없는 GC대상을 찾아준다. 가령, 두개 이상의 객체가 서로 다른 객체를 참조하고 있다고 가정한다.(순환참조) 그럼 이들 레퍼런스 카운팅 값은 1 이상이다. 그러므로 레퍼런스 카운팅 방법으로는 GC대상을 만들 수 없다. 마크 앤 스윕은  이들 객체가 최상위 부모와 연결되어 있는지 검사하는 방법이다. 부모로 부터 꼬리에 꼬리를 물고 객체가 참조되어 있는 것인지 조사해나가는 방법으로 GC대상을 검색한다.
    레퍼런스 카운팅과 마크앤 스윕 방법으로 찾아낸 GC대상은 메모리에 상주되지만 Flash Player의 GC 삭제방법 로직에 의해 어느 순간 삭제된다.(이 시점은 프로그래머가 어찌할 수 없다. AIR 또는 Flash Player Debug버전의 경우에만 System.gc()를 이용해 강제적으로 GC를 메모리에서 삭제할 수 있긴 하다.)
  • addEventListener()의 useWeakReference를 true로 설정하면 약참조가 되어 레퍼런스 카운팅을 증가시키지 않는다. 강참조의 경우 이벤트 대상(target)의 참조를 지웠다고 해도 addEventListener로 등록한 청취자 메소드가 GC대상이 아니라면 이벤트 대상의 레퍼런스 카운팅 값은 0이 될 수 없다. 결국 프로그래머의 뜻과 다르게 메모리를 낭비하는 일을 초래할 수 있다. 물론 이벤트 대상이 사라지는 시점에 removeEventListener() 사용하면 되겠지만 때로는 그 시점을 모르는 경우도 발생한다. 이러한 경우 useWeakReference를 true로 설정해서 메모리 낭비를 없앨 수 있겠다.
  • Flex에서 List 계열의 컴포넌트를 살펴보면 dataProvider()에 ArrayCollection 객체를 받을때 ArrayCollection의 변화를 살펴보기 위해 addEventListener()를 사용한다. 그런데 ArrayCollection 객체는 언제 어떻게 될지 List 계열 컴포넌트는 알 수 없을 것이다. 이때 유용하게 사용하는 것이 바로 useWeakReference=true로 설정해서 필요없어진 ArrayCollection이 메모리 남는 것을 방지할 수 있다. 참고 : http://blog.jidolstar.com/375



19. 이벤트가 전파되는 중간에 전파를 멈추게 하기 위해 stopPropagation()을 사용하지 않고removeEventListener() 를 사용하면 안되는 이유를 설명하라. (힌트, ActionScript 3.0의 Native 메커니즘에 입각한다. http://bixworld.egloos.com/2149717)

  • 이벤트 송출을 시작하면 flash player는 이벤트가 송출될 디스플레이 객체 리스트를 전부 조사해 미리 이벤트 객체를 복사해두기 때문이다. 이미 전파할 대상이 모두 정해져있고 전파가 시행중인 경우에는 중간에 removeEventLisener를 사용하더라도 일단 송출된 이벤트는 끝까지 전파된다. 다음 시점 이벤트 송출시에는 removeEventLisener()가 적용되어 있는 상태가 된다.


20. willTrigger()와 hasEventListener()의 의미를 설명하고와 두 메소드의 차이점을 보여줄 수 있는 대한 예를 들어보자.

  • 공통점
    hasEventListener()이나 willTrigger() 모두 이벤트 청취자가 있는지 검사한다.
  • 차이점
    hasEventListener() : 지정한 객체에(만) 이벤트 청취자가 있는지 조사한다. 이벤트 흐름과는 무관하다.
    willTrigger() : 지정한 객체를 포함하는 디스플레이 객체 리스트에 해당 이벤트가 청취자가 있는지 조사한다. 그러므로 이벤트 흐름과 관련 있다.
  • 예제
    var button:Sprite = new Sprite();
    addChild( button );
    this.addEventListener( MouseEvent.CLICK, onClick );
    trace( button.willTrigger( MouseEvent.CLICK ) ); //true
    trace( button.hasEventListener( MouseEvent.CLICK ) ); //false ->왜 false가 되는가 아는게 중요!
    trace( this.willTrigger( MouseEvent.CLICK ) ); //true
    trace( this.hasEventListener( MouseEvent.CLICK ) ); //true
  • 이 두개의 함수를 잘 활용하면 쓸데없이 이벤트를 송출하는 경우를 방지할 수 있다.

21. ActionScript 3.0 이벤트에 대해 참고할 만한 문헌 및 자료를 조사하자.
Introduction to event handling in ActionScript 3.0
Jasu님의 ActionScript 3.0 이벤트 오브젝트
Event 한글 문서
EventDispatcher 한글 문서


글쓴이 : 지돌스타(http://blog.jidolstar.com/415)

+ Recent posts