Actionscript3.02012/01/08 20:05


요즘 OAuth 인증 때문에 고생좀 했습니다. 머리속에 있는 개념을 정리도 할겸 제가 사용해본 OAuth 라이브러리에 대해 소개 합니다. 


OAuth 란?


 OAuth가 사용되기 전에는 인증방식의 표준이 없었기 때문에 기존의 기본인증인 아이디과 비밀번호를 사용하였는데, 이는 보안상 취약한 구조이다.

기본인증이 아닐 경우는 각 애플리케이션들이 각자의 개발한 회사의 방법대로 사용자를 확인하였다. 예를 들면 구글의 AuthSub, AOL의 OpenAuth, 야후의 BBAuth, 아마존의 웹서비스 API 등이 있다.

OAuth는 이렇게 제각각인 인증방식을 표준화한 인증방식이다. OAuth를 이용하면 이 인증을 공유하는 애플리케이션끼리는 별도의 인증이 필요없다. 따라서 여러 애플리케이션을 통합하여 사용하는 것이 가능하게 된다.


(출처 : http://ko.wikipedia.org/wiki/OAuth )


실 서비스 외부에서 API 를 사용할 때 이 OAuth 를 이용하여 인증을 하게 됩니다. 요즘 많이 사용하는 Twitter, facebook, Dropbox에서 제공 하는 API 모두 이 OAuth 를 지원하고 있고 지원하는 버전은 대부분 1.0 입니다. API 를 제공하는 곳에 따라서 2.0 을 제공하는 곳도 있습니다.  저 자세한 내용은 아래 링크에 자세히 설명 되어 있으니 생략하고 Flash 에서 OAuth 를 사용하는 방법에 대해 다뤄 보겠습니다.


 OAuth 인증 방식 이해 하기 : http://dev.springnote.com/pages/1083036



Actionscript3.0 으로 구현 된 OAuth 라이브러리


OAuth인증을 하기 위해서는 서비스 프로바이더와 몇번의 통신을 해야 하고 결과적으로 accessToken 과 accessTokenSecret 을 발급 받게 됩니다. 일종의 로그인을 위한 허락 키를 발급 받는 거지요. 이 키와 몇가지 OAuth 에서 원하는 값들을 조합해서 리퀘스트를 날릴때 Header 나 리퀘스트 URL 에 포함 시켜서 보내게 되면 서비스 프로바이더는 해당 리퀘스트를 분석하여 제대로된 인증을 가지고 있을 경우에만 결과 값을 내려 주는 구조로 되어 있습니다. 


OAuth 인증을 거치는 과정이 accessToken 과 accessTokenSecret을 발급 받는 과정이지만, 인증이 끝나고 나서 이 값들만 포함 시켜서 리퀘스트를 날리는건 아님니다. OAuth 에서 원하는 값들이 많이 있는데요. 이 값들을 http://oauth.net/ 에 구현 된데로 개인적으로 만들어서 사용해도 되지만, 이미 많은 라이브러리가 구현 되어 사용 되고 있기 때문에 기존 라이브러리를 사용 하는 걸 추천 합니다. (OAuth 인증 절차상 인증이 잘못된 경우 어디서 무엇이 잘못된건지 디버깅 하기가 어렵습니다.)


oauth-as3



oauth-as3은 actionscript3.0으로 구현된 oauth 라이브러리 입니다. 기본적인 기능들만 구현 되어 있어서 바로 사용하기 보다는 이 라이브러리를 이용하여 따로 구현해 줘야 할 것들이 많습니다. 그럼 인증을 받는 순서대로 라이브러리를 사용해 보겠습니다.

사용하기 전에 알아 두면 좋은 이론은 라이브러리에 포함 되어 있는 OAuthRequest 는 실제로 서비스프로바이더와 통신을 하기 위해 리퀘스트를 만드는 클래스 이고, 이 클래스에서 OAuth 인증을 할때 필요한 oauth_nonce, oauth_timestamp, oauth_signature 등을 생성하게 됩니다. 서비스 프로바이더와의 통신은(인증할때) URLLoader 를 사용하지만 이 URLLoader의 값을 세팅할때 참조 해야 하는 클래스가 인증에 대한 정보를 가지고 있는 OAuthRequest 클래스 입니다. 그럼 requestToken을 받는걸로 시작해서 코드를 작성해 보겠습니다. Twitter(https://dev.twitter.com/docs/api) 인증을 예를 들겠습니다.


1. 서비스 프로바이더에서 requestToken 을 받는다.


우선 인증을 시작하기에 앞서 인증을 해야 하는 대상 어플리케이션이 존재 합니다. Twitter 등의 서비스 프로바이더에서 어플리케이션을 생성하면 해당 어플리케이션이 갖게 되는 고유의 consumerKey 와 consumerSecretKey 을 알 수 있습니다. 이 값들을 이용해서 OAuthRequest를 생성 합니다. 



var twitter = {
  consumerKey : "",
  consumerSecret : "",
  requestTokenURL : "https://api.twitter.com/oauth/request_token",
  authorizeURL : "https://api.twitter.com/oauth/authorize",
  accessTokenURL : "https://api.twitter.com/oauth/access_token",
  callbackURL : ""
}


var oauthRequest:OAuthRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_POST, twitter.requestTokenURL, nullnew OAuthConsumer(consumerKey, consumerSecret));

var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, oauthLoadHandler);

var request:URLRequest = new URLRequest();

request.method = URLRequestMethod.POST

request.url = oauthRequest.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_URL_STRING);      

loader.load(request);

private function oauthLoadHandler(event:Event):void {
    
  //oauth_token=&oauth_token_secret=&oauth_callback_confirmed=
  //위의 값을 oauthRequest 에 저장
  
  var token:OAuthToken = new OAuthToken();
  token.key = oauth_token값;
  token.secret = oauth_token_secret값;
  oauthRequest.token = token;
}

 



그리고 Twitter의 경우 requestToken 을 발급 받는 api 가 post 를 사용하므로 셋팅을 해주고 oauthRequest에 인증에 필요한 모든 값들이 들어 있기 때문에 buildRequest 메소드를 이용해서 OAuth인증 값들이 포함 되어 있는 url 을 리턴 받아서 서비스프로바이더에서 리퀘스트를 날리게 됩니다.


buildRequest 에 들어가는 첫번째 파라미터는 oauth_signature_method 값이고 두번째 인자는 OAuth 인증 리퀘스트 값을 어떤 방식으로 받을 것이냐 선택 하는 것 입니다. 파라미터에 따라 URLRequestHeader값 또는 String 으로 받을수 있습니다.


2. requestToken 을 이용하여 서비스 프로바이더에게 접근 한다.


1번에 대한 response 값으로 oauth_token을 받게 되는데요. 이 값을 이용해서 또 다시 서비스 프로바이더에게 인증을 요청해야 합니다. 이번에 요청하는 인증은 URLLoader 를 이용한 인증이 아니라, 직접 URL로 이동하여 사용자가 아이디와 비번을 입력하여자신의 권한을 해당 어플리케이션에 수락 시키는 절차 입니다.





 https://api.twitter.com/oauth/authorize?oauth_token=REQUEST_TOKEN입력


위의 URL로 Desktop AIR의 경우 HTMLLoader 를, Mobile AIR의 경우 StageWebView를 이용해서 해당 사이트로 이동하면 위와 같이 인증 화면이 뜨게 됩니다. 


 

웹용 Flash Player에서는 서버를 (Proxy) 사용하지 않고 인증 할 수 없습니다. 웹상에서 클라이언트를 Flash 로 만들경우에는 (javascript 도 마찬가지 입니다) 서버에서 위의 인증 과정을 모두 대리로 수행 해야 합니다. 



사용자가 아이디와 비번을 입력하고 인증을 마무리 하게 되면 Twitter의 경우 어플리케이션을 등록할때 지정 했던 callback URL 로 이용하게 되는데요. 이동할때 URL 뒤로 추가적으로 필요한 정보들을 전달 받게 됩니다. 이 인증 절차를 통해 OAuthRequest가 가지고 있는 token 값은 이제 accessToken 을 접근 할 수 있는 인증권한을 갖게 됩니다.  


 http://www.ddongkang.com?oauth_token=&oauth_verifier=


위의 callback URL 로 전달 되는 oauth_token값은 요청할때 전달했던 값과 같습니다. oauth_verifier값은 이 예제에서는 사용되지 않습니다. 다음 링크를 참조 하세요. 


3. 서비스 프로바이더에서 accessToken 을 받는다.


이제 인증의 마지막 절차 입니다. accessToken 을 받는 방법은 requestToken 을 받을때와 같습니다. 



oauthRequest.requestURL = twitter.accessTokenURL;

oauthRequest.httpMethod = OAuthRequest.HTTP_MEHTOD_POST;
      
request.url = _oauthRequest.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_URL_STRING);

loader.load(request);

private function oauthLoadHandler(event:Event):void {
  
 // request token 리퀘스트일때
 //oauth_token=&oauth_token_secret=&oauth_callback_confirmed=true
 // access token 리퀘스트일때
 //oauth_token=&oauth_token_secret=&user_id=&screen_name=
 //위의 값을 oauthRequest 에 저장
 

  var token:OAuthToken = new OAuthToken();
 token.key = oauth_token값;
 token.secret = oauth_token_secret값;
 oauthRequest.token = token;

}




4. 인증 끝. 


위의 과정을 통해 accessToken 과 accessTokenSecret을 갖게 되어 인증이 끝났습니다. 이제 oauthRequest값에 인증 값들이 모두 포함 되어 있기 때문에 이를 이용해서 api 호출을 하면 됩니다.



oauthRequest.requestURL = Resource URL
oauthRequest.httpMethod = Request Method
    
var api_loader:URLoader = new URLLoader();

var api_request:URLRequest = new URLRequest();    
api_request.url = oauthRequest.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_URL_STRING);

api_loader.load(api_request);

 



OAuth를 이용한 인증은 개념을 파악하면 간단하지만 처음 개념 없이 무작정 사용하기엔 어렵습니다. 서비스 프로바이더에 따라 OAuth 인증 실패 이유를 제대로 반환해 주는 곳도 있지만 한번 꼬이기 시작하면 어디서 잘못된건지 찾기도 힘든게 OAuth 인증 같습니다. 


웹상에서 Flash 로 Twitter 나 Dropbox 같이 OAuth를 제공하는 서비스를 사용하고 싶으면 서버에 Proxy를 만들어서 서버를 통해서 인증을 받아야 합니다. 클라이언트 단에서 인증이 가능한 서비스 제공 업체도 있겠지만, 대부분은 서버를 통해서 인증을 받아야 보안 제약을 받지 않습니다. 그래서 FLash 를 이용한 방법은 주로 AIR(Desktop, Mobile)에서 사용 가능 합니다. 


많이 사용 되는 Twitter API 는 tweetr (http://wiki.swfjunkie.com/tweetr)는 있지만, 인증에 문제가 있는지 잘못 쓴건지 제대로 동작을 하지 않아 따로 만들어 사용하고 있습니다. 검색을 해 보니 github에 Twitter API를 구현한 소스가 올라와 있는데요. 아직 구현중인걸로 보이네요; 


조만간 사용하고 있는 라이브러리도 정리 해서 공유해 보겠습니다. 

잘못된 점이나 궁금한 점이 있으면 댓글로 문의 주세요~



저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02011/05/01 16:29


 ApplicationDomain 에 대한 개념만 잘 이해 하고 있다면, 이 포스팅은 보지 않으셔도 됩니다. 최근까지 ApplicationDomain 에 대한 개념 자체를 다르게 이해 하고 있어서 어렵다고 생각했던 부분들은 정리 합니다.


 Flash/Flex 를 사용하면서 가장 많이 사용하는 클래스중 하나는 Loader 일 것 입니다. Loader 클래스는 SWF 나, 이미지 (JPG, PNG 등등) 을 로드 하는데 사용 합니다. 많은 분들이 이미지를 로드 하는데 자주 사용하고 있을거라 생각 합니다. 외부에서 이미지를 로드 하면 로드된 리소스는 Bitmap 으로 처리가 되어 BitmapData 의 clone 메소드를 통해서 해당 이미지를 재사용 할 수 있습니다. 코드는 아래와 같습니다.


 // 이미지 로드

package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.Sprite; import flash.events.Event; import flash.net.URLRequest; public class ApplicationDomainTest extends Sprite { public function ApplicationDomainTest() { var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadCompleteHandler); loader.load(new URLRequest("TestAsset.png")); } private function loadCompleteHandler(event:Event):void { var image:Bitmap = event.target.content as Bitmap; var imageData:BitmapData = image.bitmapData.clone(); trace(imageData); } } }



이미지는 위와 같이 재사용하면 되고, 이미지가 아닌 SWF 파일은 어떻게 재사용 할까요? 제가 사용한 방법은 ApplicationDomain 을 이용한 것 입니다. ApplicationDoamin 에 대한 자세한 내용은 다음 블로그에 잘 정리 되어 있으니 참고해 주세요.


Flex 모듈 프로그래밍의 기초 - Application domain의 이해

http://blog.jidolstar.com/469

http://blog.jidolstar.com/346 


 Loader 를 통해 외부 리소스를 로드 하면 해당 리소스는 load 메소드의 LoaderContext 의 ApplicationDomain 설정에 따라 새로운 ApplicationDoamin 또는 로드를 객체의 ApplicationDoamin 에 정의가 저장 됩니다.


 load(request:URLRequest, context:LoaderContext = null):void


LoaderContext 에서 applicationDoamin 값을 (new ApplicationDoamin, ApplicationDomain.currentDoamin 등) 어떤 값으로 설정하는가에 상관 없이 로드된 외부 리소스는 자신의 ApplicationDomain 을 알 수 있습니다. 


 // 로드된 리소스 ApplicationDomain 알기

package {    

    import flash.display.Loader;    
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.net.URLRequest;        

    public class ApplicationDomainTest extends Sprite {        

        public function ApplicationDomainTest()    {

            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadCompleteHandler);
            loader.load(new URLRequest("TestAsset.swf"));    
        }

        private function loadCompleteHandler(event:Event):void {

            var swf:* = event.target.content;

            trace(swf.loaderInfo.applicationDomain);
        }
    }
}


이 ApplicationDomain 에는 로드된 자기 자신의 정의 들을 가지고 있기 때문에 해당 정의(클래스)를 불러와서 사용 할 수 있습니다. 예를 들어 아래와 같이 작성 되어 있는 외부 SWF 를 로드 한다고 하면 각각의 객체 정의 들은 불러 오는 방법은 다음과 같습니다. 


// TestAsset

package { import com.ddongkang.AssetLoader; import com.ddongkang.Circle; import com.ddongkang.List; import flash.display.Sprite; public class TestAsset extends Sprite { private var loader:AssetLoader; private var circle:Circle; private var list:List; public function TestAsset(){ }

} 


 // Main

package {    

    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.net.URLRequest;
    import flash.system.ApplicationDomain;

    public class ApplicationDomainTest extends Sprite {
        

        public function ApplicationDomainTest()    {

            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadCompleteHandler);
            loader.load(new URLRequest("TestAsset.swf"));    

        }

        private function loadCompleteHandler(event:Event):void {
            var swf:* = event.target.content;
            trace(ApplicationDomain(swf.loaderInfo.applicationDomain).getDefinition("com.ddongkang.AssetLoader"));
            trace(ApplicationDomain(swf.loaderInfo.applicationDomain).getDefinition("com.ddongkang.Circle"));

            trace(ApplicationDomain(swf.loaderInfo.applicationDomain).getDefinition("com.ddongkang.List"));

        }

    }

}



getDefinition 으로 불러온 정의는 클래스 정의 이기 때문에 객체를 생성 할 수 있습니다. 


// 외부에서 로드한 Circle 객체 생성

var circle:* = new ((swf.loaderInfo.applicationDomain).getDefinition("com.ddongkang.Circle"))();
addChild(circle);            
trace(circle); 


로드된 TestAsset.swf 는 AssetLoader, Circle, List 정의를 가지고 있기 때문에 컴파일 될때 해당 정의도 SWF 에 포함됩니다. 로드한 클래스는 TestAsset 이지만, TestAsset 이 가지고 있는 정의들이 같은 ApplicationDomain 안에 포함되어 있기때문에 이러한 접근이 가능 합니다. 여기서 SWF 자체인 TestAsset.swf 도 다음 코드를 통해 접근 가능 합니다.


// TestAsset 접근 

var asset:* = new ((swf.loaderInfo.applicationDomain).getDefinition("TestAsset"))(); 


주의할 점은 getDefinition 에 사용한 이름이 package 까지 포함된 이름이라는 점 입니다. getDefinition 을 통해 해당 클래스 정의에 접근 할때는 해당 정의가 ApplicationDomain 에 존재 하지 않을수도 있기 때문에 hasDefinition 를 통해 있는지 확인 후 접근해야 런타임 에러를 방지 할 수 있습니다.


// hasDefinition 사용


 private function createAssetByName(loaderInfo:LoaderInfo, name:String):* {

            
     var applicationDomain:ApplicationDomain = loaderInfo.applicationDomain;
     if(applicationDomain.hasDefinition(name)) {
          return new (applicationDomain.getDefinition(name))();                
     }
            
     return null;
            
}


로드할 자원을 만들때, Flash Builder/ Flex Builder 를 사용할 경우 컴파일 된 자원 이름 (위의 경우에는 TestAsset.swf)이 클래스 이름(TestAsset)과 동일 하기 때문에 외부 리소스가 단일 자원으로 컴파일 될 때는 swf 이름으로 클래스를 접근해도 무방 합니다. 해당 swf 이름을 이용하여 Dictionary 를 만들어서 정의를 저장해 놓고 사용할 수도 있습니다. (http://mrdoob.com/blog/post/612


 하지만 Flash IDE 로 외부 리소스를 만들때는 반드시 Document 클래스를 지정하고 지정된 Document 클래스 이름을 통해 클래스를 접근해야 합니다. Flash/Flex Builder 에서 만든 리소스는 swf 자체가 클래스가 되지만 Flash IDE 에서는 Document 클래스로 연결이 안되어 있는 swf는 swf 자체가 클래스가 아니기 때문에(?) 정의를 불러 올 수 없습니다. 그러나Flash IDE 를 통해 Linkage 를 이용하여 클래스를 정의한 경우에는 Builder 에서 만들때와 마찬가지로 컴파일된 SWF 에 Linkage 를 통해 클래스화 된 객체 정보들을 가지고 있기 때문에 접근이 가능 합니다.  정리해 보면 다음과 같습니다.


로드할 자원 만들 때 주의 사항

1. 클래스 이름을 package 까지 포함하여 알고 있어야 한다.

2. Flash IDE 로 자원을 만들때는 Document 클래스를 연결해 주고 Document 클래스의 이름을 알아야 한다.

3. Flash IDE 의 라이브러리 Linkage 를 사용하여 클래스를 만들면 컴파일된 swf 에서 해당 정의를 불러올 수 있다. ( http://goo.gl/xrY8q ) 


로드한 자원 (클래스) 를 재사용하기 위해서는 로드된 클래스 이름을 알아야 한다는게 핵심입니다. 그 클래스정의는 로드된 swf 의 ApplicationDomain 안에서 있고 해당 클래스 이름을 통해서 가져 올 수 있습니다. 그리고 그 정의를 통해 new 를 통해 객체를 생성하여 사용 합니다.


잘못된 내용이나 개발 중 고민하고 있는 부분들은 댓글을 통해 공유 주세요~ 






저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02011/03/05 12:13

 Flash Player 는 다양한 멀티미디어를 불러오고 재생 할 수 있다. 그 중에서 오디오 관련 파일은 기본적으로 MP3 파일을 재생 할 수 있으며, popforge (http://code.google.com/p/popforge/) 와 같은 라이브러리를 사용하면 WAVE 파일도 재생 가능 하다. 하지만 이 문서에서는 Actionscript3 에서 제공하고 있는 기본 API 를 가지고 파일을 불러오고 재생 하기 위한 기본적인 이론에 대해 다룬다.


 Flash 에서 오디오 파일을 재생 하기 위해서는 Sound, SoundChannel 를 사용한다. 디테일한 플레이어를 구현하기 위해서는 다른 클래스도 알아야 하지만 기본 기능만 만든다면 위 두가지 클래스의 레퍼런스만 봐도 충분히 구현 할 수 있다. 


Sound Class


 Sound Class 를 MP3 파일을 로드 하고 재생 하는 기능을 담당한다. 파일을 로드 하기 위한 load, 재생하기 위한 play, 재생을 끈어버리기(재생의 멈춤이 아니다) 위한 close 메소드 등을 가지고 있다. MP3 파일을 재생 하기 위해서는 다음과 같은 절차를 따라야 한다.


1. Sound 객체 생성

2. Sound 객체에 Event.COMPLETE 이벤트 등록

3. load 메소드를 이용하여 파일 로드

4. 로드가 완료 되면 2 에서 등록했던 이벤트가 발생

5. 로드가 완료 된 후에 play 메소드를 이용해서 재생 


 오디오 파일을 로드 하는 방법은 URLLoader 나 Loader 에서 사용하는 방법과 동일하다. 다음은 위의 절차를 코드화 한 것이다.



package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.media.Sound;
	import flash.net.Socket;
	import flash.net.URLRequest;
	
	public class SoundPlayerTest extends Sprite
	{
		private var _player:Sound;
		public function SoundPlayerTest()
		{
			_player = new Sound();
			_player.addEventListener(Event.COMPLETE, playerReadyHandler);			
			_player.addEventListener(IOErrorEvent.IO_ERROR, playerIOErrorHandler);
			_player.addEventListener(SecurityErrorEvent.SECURITY_ERROR, playerSecurityErrorHandler);
			
			_player.load(new URLRequest("remote/file/10cm_01.mp3"));
		}
		private function playerReadyHandler(event:Event):void 
		{		
			_player.play();
		}		
		private function playerIOErrorHandler(event:IOErrorEvent):void 
		{
			trace("로드에러");
		}
		private function playerSecurityErrorHandler(event:SecurityErrorEvent):void 
		{
			trace("보안에러");
		}
		
	}
}


 위와 같은 방법으로 로드를 하면 해당 파일에 대한 length 로 음원의 길이를 알수 있고, MP3 파일은 곡의 정보를 담고 있는 id3 태그를 가질수 있기 때문에 해당 파일에 id3 태그가 있다면 접근 할 수 있다. 기본적으로 접근 할 수 있는 id3 는 다음과 같다.



_player.addEventListener(Event.ID3, id3TagLoadHandler);
private function id3TagLoadHandler(event:Event):void 
{
	trace("album : "+_player.id3.album);
	trace("artist : "+_player.id3.artist);
	trace("comment : "+_player.id3.comment);
	trace("genre : "+_player.id3.genre);
	trace("songName : "+_player.id3.songName);
	trace("track : "+_player.id3.track);
	trace("year : "+_player.id3.year);
}


id3 태그에 대한 더 자세한 내용은 뒷부분에서 다루고 있다.


주의 해야 할 점은 새로운 파일을 로드 할때마다 Sound 객체를 새로 생성해 줘야 한다. 재사용할시 에러가 발생한다.


SoundChannel Class 


Sound Class 가 중요한 기능을 하고 있지만 모든 기능을 제공 하지는 않는다. SoundChannel Class 를 이용하여 사운드를 재생 하면서 디테일한 조작을 할 수 있다. SoundChannel Class 를 보면 다음과 같은 메소드와 속성이 정의 되어 있다.


속성


leftPeak, rightPeak : 왼쪽 오른쪽의 현재 진폭(볼륨)을 나타내는 0(묵음)부터 1(최대 진폭)까지의 값

position : 사운드가 재생 중일 때 position 속성은 사운드 파일에서 재생 중인 현재 위치를 밀리초 단위 나타냄

soundTransform : SoundChannel 이 가지고 있는 사운드를 조작하기 위한 SoundTransform 객체



메소드

stop : 사운드 재생을 중단


위의 Sound 객체에 stop 메소드가 없고, SoundChannel 에 stop 메소드가 정의 되어 있다. 그 이유는 Sound 객체는 play 메소드를 이용하여 로드한 파일을 열어 주는 역할을 하고 SoundChannel 은 열린 파일 스트림에 대해 조작 역할을 한다.

load 메소드를 이용해 파일을 로드 하고 play 를 할때 play 메소드의 반환값으로 SoundChannel 객체가 넘어오므로 사운드를 조작 하기위해서는 이 반환객체를 조작 하여 사운드를 컨트롤 할 수 있다.



var channel:SoundChannel = new SoundChannel();

channel = _player.play();


일시 정지 및 재생 위치 변경


Sound Class 나 SoundChannel Class 나 모두 일시정지하는 메소드가 없다. 일시정지 기능은 SoundChannel 의 position 값과 Sound Class 의 play 메소드를 사용하여 구현 할 수 있다. 먼저 사운드를 일시 정지 할때 SoundChannel 의 position 값을 저장하고, 다시 재생 하면 Sound 의 play 메소드의 파라미터로 해당 position 값을 넘겨 줘서 사운드를 재생 하면 된다. 코드는 다음과 같다.

 
private function play(position:Number = 0):void 

{	
		
	_channel = _player.play(position);

}


private function pause():void 

{

	_channel.stop();

}

// 재생 버튼 클릭

private function playButtonClicked(event:MouseEvent):void

{

	play(_channel.position);

}

// 일시 정지 버튼 클릭		

private function pauseButtonClicked(event:MouseEvent):void

{

	pause();

}


재생 위치를 변경하는것도 위와 같은 방식으로 하면 된다. 단 play 할때 position 의 위치가 로드된 파일 길이보다 길면 재생이 안된다. 그러므로 Sound 객체의 length 보다 긴지 확인이 필요하다.


private function play(position:Number = 0):void 

{		

       if(position > _player.length)	
       { 
                 position  = _player.length; 
       }

       _channel = _player.play(position);

}


볼륨 변경


볼륨은 SoundChannel 의 soundTransform 을 이용한다. soundTransform 속성은 SoundTransform 객체 이다. 이 객체는 사운드 볼륨을 조작 하기 위한 여러 속성을 가지고 있다. 이 속성 중 volume 값을 변경 하면 된다. 값은 0 ~ 1 사이 이다.



var soundTransform:SoundTransform = new SoundTransform();

soundTransform.volume = 변경할 값;

_channel.soundTransform = soundTransform;


주의할 점은 SoundChannel 객체의 soundTransform 값을 직접 변경 하는것이 아니라, 새로운 SoundTransform 을 생성하여 값을 변경한 후에 적용해 주는 방식이다.


id3 태그



 MP3 파일에는 id3 태그가 파일안에 정의 되어 있다. 그 형식들은 여러가지가 있으며, Flash Player 9 이후 버전 및 AIR 에서는 ID3 2.0 태그, 특히 2.3 및 2.4 태그를 지원 한다. 위의 예제에서 Event.ID3 이벤트를 통해 접근한 속성들은 Actionscript3 ID3Info (http://help.adobe.com/ko_KR/Flash/CS5/AS3LR/flash/media/ID3Info.html객체에서 정의하고 있는 값들이고 이외에도 Flash IDE 나 Flash Builder 의 자동완성에는 나오지 않지만 아래의 속성들이 접근 가능하다. 하지만 모든 값들은 해당 파일에 해당 속성이 정의 되어 있을 경우에만 접근 가능하고 정의 되어 있지 않다면 null 을 반환 한다.


저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02011/02/18 14:36

Adobe 에서는 Flash Player 10 부터 TLF( Text Layout Framework ) 를 제공하고 있다. 기존 TextField 를 사용하는 방식은 사용 방법이 편하긴했지만, 텍스트를 다루는데 기능적으로나 퍼포먼스 측면에서 부족한게 많았다.

TLF 에서는 이러한 단점들을 Text 를 다루는 Flash Player 의 low level 에 접근하는 프레임워크를 제공하므로 부분적으로 해결되고 있는 움직 임이다. 더 자세한 내용은 Adobe Labs - TLF 에서 확인 가능 하다.

사용 방법은 Adobe Labs 에 자세히 나와 있고, 한가지 주의할 점은 Flash CS5 에서 작업할때 Actionscript 3.0 Settings 패널에서 Runtime Shared Library Settings 가 Runtime shared library(RSL) 로 되어 있는데 이 설정은 textLayout.swc를 외부에서 불러와서 사용하기 때문에 외부 서버 상태에 따라 로딩 지연이 있을수 있으니 swf의 용량이 증가되더라도 Merged into code 로 바꿔 주는게 좋다.




문제는 TLF 를 사용한 프로젝트에서 loaderInfo 에 있는 FlashVars 를 접근할때 parameters 에 있는 값들이 null 로 인식 되는 버그가 있다. Main.swf 에서 TLF 를 로드하는 과정에서 loaderInfo 가 달라져서 발생하는 문제 인데 다음과 같은 방법으로 해결 가능 하다.


if (stage) {
	init();	
} else {
	addEventListener(Event.ADDED_TO_STAGE, init);	
}		

function init( e:Event = null ):void {

        var flashVars:Object = {};
	removeEventListener(Event.ADDED_TO_STAGE, init);
			
	if (parent != null && parent.parent != null) {
		flashVars = parent.parent.loaderInfo.parameters;
	} else {
		flashVars = this.root.loaderInfo.parameters;
	}
}


TLF flashvars bug : http://forums.adobe.com/message/3259520?tstart=0

저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02010/06/13 20:43

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




저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02010/05/23 14:47

 URLLoader를 사용하여 데이터를 로드 할때 서버에서 데이터가 안내려 오면 2032 스트림 오류가 발생 한다. 하지만 서버도 정상적으로 동작하고 데이터도 정상적으로 내려오고 있을때(해당 파일이 존재 할 때), IE 에서만 2032 오류가 발생할 경우가 있다.


var infoLd:URLLoader = new URLLoader();
var queryUrl:String = "";
infoLd.addEventListener(Event.COMPLETE,hnInfoLoaded);
infoLd.addEventListener(IOErrorEvent.IO_ERROR,hnLoadError);
infoLd.load(new URLRequest(queryUrl));
function hnInfoLoaded( e:Event ):void{}
function hnLoadError( e:IOErrorEvent ):void{}

위의 코드와 같은 상황에서 여러 가지 이유로 인해 2032 오류가 발생할수 있는데, IE 브라우저에서만 발생 한다면 IE 에서 지원하는 최대 URL 길이 제한을 넘었을 가능성이 크다. IE에서 지원하는 URL 길이가 타 브라우저에 비해 상대적으로 짧기 때문이다.

IE 에서는 최대 2083 길이 이하의 URL 만 request 를 보낼수 있으며, POST/GET 방식 모두 적용 된다.


예를 들어 URLLoader로 다음과 같은 URL 을 로드 할때 발생할 수 있다.


 http://dongkang.com/data/list.do?list=1234567/1234567/1234567/1234567/1234567/1234567/........(length:2083)


위의 API 와 같이 URL 로 인자를 확인할때는 데이터를 나눠서 로드를 하거나, URL 길이를 줄일수 있는 다른 방법으로 접근 해야 한다. 위의 내용은 IE의 대부분 버전에서 동일하게 발생하며, 자세한 내용은 아래 URL에서 확인 가능 하다.


http://support.microsoft.com/kb/q208427/

저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02010/01/14 11:03

작년에 썼던 Javascript vs Actionscript 라는 글에서는 비교 관련 자료가 06년도의 Flash player 9 와 그때 당시의 브라우져를 대상으로 테스트한 것인데요. jacksondunstan.com 블로그에서 최근에 AS3 기반의 Flash player 10과 최근 브라우져의 Javascript 와 Performance 테스트를 한 포스팅이 있었네요. 아래는 테스트 한 결과 입니다.



참고 자료 :  AS3 vs Javascript Performance Test

                AS3 vs Javascript Performance Test Followup

                


저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02010/01/12 01:06

외부 데이터를 불러 와서 TextField 에 넣어 줄때, 외부 데이터 문자 길이를 UI 에 맞게 잘러서 나타내는 경우가 있습니다. 기본적으로 String 클래스에서 제공하는 substr 등을 이용하여 문자열을 보기 좋게 자르는데요. 여기서 소개 하는 방법은 substr 과 TextField 의 getLineOffset를 이용하여 한정된 라인에만 글짜를 출력 하는 방법 입니다.

var field:TextField = new TextField();
field.width = 250;
field.wordWrap = true;
field.text = "안녕하세요.이것은 테스트 입니다.안녕하세요.이것은 테스트 입니다.안녕하세요.이것은 테스트 입니다.";
addChild(field);
위의 코드를 출력하면 아래와 같은 결과가 나옵니다.


위와 같이 width 값은 고정되어 있고 wordWrap = true 로 TextField 의 자동 줄바꿈이 설정 되어 있을때 getLineOffset 을 이용하여 원하는 최대 라인수 보다 넘어 갔을때 그 다음 라인의 첫번째 문자 인덱스 값을 가져온 뒤에 그 인덱스의 -2 만큼 String 값을 잘라 버리는 방식 입니다. getLineOffset 의 설명은 아래와 같습니다. 

var field:TextField = new TextField();
field.width = 250;
field.wordWrap = true;
field.text = "안녕하세요.이것은 테스트 입니다.안녕하세요.이것은 테스트 입니다.안녕하세요.이것은 테스트 입니다.";
cutNumLines(field,2);
addChild(field);
function cutNumLines( $field:TextField,$maxLen:int ):void{
	if($field.numLines-1 < $maxLen) return;
	
	var idx:int = $field.getLineOffset($maxLen);
	$field.text = $field.text.substr(0,idx-2)+"..";
}

getLineOffset(lineIndex:int):int
lineIndex 매개 변수로 지정된 행에 있는 첫 문자의 문자 인덱스를 반환합니다.


위의 함수를 실행한 후의 결과는 다음과 같습니다.



저작자 표시 비영리 변경 금지

'Actionscript3.0' 카테고리의 다른 글

AS3 vs Javascript Performance Test  (0) 2010/01/14
[AS3] TextField 에 한정된 라인에만 글짜 넣기  (0) 2010/01/12
Avoid ints in Actionscript  (10) 2009/11/14
Factory Pattern (AS3)  (0) 2009/10/25
Posted by 동강
Actionscript3.02009/11/14 19:56

 오랜만에 블로그를 둘러 보다 흥미로운 포스팅을 보았는데요. Actionscript 로 작업할시 숫자 타입의 int 를 사용을 자제 하라는 내용 입니다. 일반적으로 생각하면 int 가 Number 보다 크기가 작으니 int 로 처리 하면 더 빠르다고 생각하는 분들이 많으실텐데요. 이 포스팅에서는 여러 이유를 통해 부정 하고 있습니다.


이유 1. Number 를 사용하는게 int를 사용하는 것 보다 빠르다.

아래 코드를 실행 시키면 어떤 결과가 나올까요? 


public function timingTest() : void
{
	var intTime : Number;
	var numberTime : Number;

	var i : int;
	var j : int = 0;

	intTime = (new Date()).time;
	for (i=0; i<10000000; i++)
		j = (j + 15) / 7;

	intTime = (new Date()).time - intTime;

	var n : Number;
	var m : Number = 0;

	numberTime = (new Date()).time;
	for (n=0; n<10000000; n++)
		m = (m + 15) / 7;

	numberTime = (new Date()).time - numberTime;

	var message : String =
		"int version: " + intTime + "ms\n" +
		"Number version: " + numberTime + "ms";

	Alert.show(message);
}

int 를 사용한 코드 : 331ms

number 를 사용한 코드 : 291ms

 

  조금 의아해 할수도 있는데요. number 가 조금 빠르게 나왔습니다. 여기서는 조금한 차이지만 더 많은 루프를 돌리면 더 많은 차이를 낼 것 입니다. 일반적으로 number 가 더 많은 대역의 숫자를 커버 하기 때문에 연산할때 더 느릴텐데 왜 number 를 사용한 코드가 더 빠르게 나온걸까요? 


 j = ( j + 15 ) / 7;


 연산 비교에 사용된 코드를 보면 나누기 연산을 하고 있습니다. 이 나누기 연산이 number 가 더 빠른 연산 속도를 낼 수 있었던 이유 입니다. 연산이 이루어 질때 시스템은 내부적으로 int 범위 내에서 처리 할 수 없는 것은 number (double) 로 바꾸어 처리 하기 때문에 int - > number 로 변환 하는 처리 비용이 더 들기 때문에 number 가 더 빠른 결과를 낸 것 입니다. 


이유 2. Number 는 더 많은 bit 를 가지고 있다.


Number를 나타내는 데이터 format 은 아래와 같습니다. int 에 비해 더 많은 데이터 범위를 저장 할 수 있고 ( 12 ~ 63 ) 연산이 손실되는 데이터 없이 이루어 집니다. 그 만큼 오차를 줄여서 계산할 수 있다는 의미 입니다. 


 S EEEEEEEEEEE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

  0 1        11 12                                                63 


실제로 + 연산이나 - 연산에서는 int 를 이용한 연산이 더 빠릅니다. 하지만, number 에 비해서 많이 빠르지 않습니다. 아래 결과를 보면 gskinner 가 "Types in AS3: ints not so fast, uints slow!" 포스팅을 통해 퍼포먼스 테스트를 한 결과 입니다. int 는 number 에 비해 많이 빠르지 않고, 느릴때도 있으니 차라리 데이터 손실 없이 연산이 이루어 지는 number 를 주로 사용하는게 좋다는 내용 입니다.  


Assignment ( var a:TYPE = 0; )
int: 24-45ms
Number: 24-36ms
uint: 25-37ms

Statistically, they look the same, which is to be expected.

Assignment ( var a:TYPE = 0.5; )
int: 56-83ms
Number: 26-43ms
uint: 57-92ms

Predictably, Number is faster for fractional assignments, as the value does not need to be converted to an integer.

Division ( var a:TYPE = i/2; )
int: 60-105ms
Number: 34-64ms
uint: 184-278ms

Number is a much faster type for division, as expected from Sho's post. uint trails badly.

Multiplication ( var a:TYPE = i*2; )
int: 78-129ms
Number: 39-64ms
uint: 207-280ms

Similar results to division. I thought int might perform better as the value would never have to be converted to a float (whereas in division it would).

Addition ( var a:TYPE = i+2; )
int: 31-49ms
Number: 44-55ms
uint: 85-113ms

As expected from the plain iterator test, int is slightly faster for integer addition.

Bitshift ( var a:TYPE = i<<1; )
int: 31-63ms
Number: 61-114ms

uint: 71-130ms 


가장 좋은 방법은 상황에 따라서 Number 와 int 를 골라 쓰는 건데요. 그 판단이 항상 좋을 수는 없으니, 잘 생각해서 써야 할 것 같습니다. 실제로 "Types in AS3: ints not so fast, uints slow!" 의 댓글에서 Number 의 연산 퍼포먼스가 휠씬 떨어 진다는 내용도 있었습니다. 그러면 언제 int 를 사용하면 좋을까요? 


1. 별로 차이가 없겠지만, 메모리를 적게 쓰고 싶을때

2. var i : int = j / 2; 와 같이 int 를 사용할 때

3. 클라이언트와 서버 측의 통신이 이루어 질때 오차를 줄이고 싶을때 


아무리 여러 테스트가 있어도, 정답은 "상황에 따라 맞는 방법을 쓴다." 이네요.




저작자 표시 비영리 변경 금지

'Actionscript3.0' 카테고리의 다른 글

[AS3] TextField 에 한정된 라인에만 글짜 넣기  (0) 2010/01/12
Avoid ints in Actionscript  (10) 2009/11/14
Factory Pattern (AS3)  (0) 2009/10/25
Actionscript3.0의 DisplayList  (2) 2009/09/22
Posted by 동강
Actionscript3.02009/10/25 23:47

목적 : 애플리케이션을 특정 구현으로 부터 분리시킨다. 객체를 만드는 일을 한다.

(크리에이셔널 패턴 : 생성과정과 관련된 패턴)

팩토리 메소드 패턴 - 팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만듭니다. 팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡긴다.


목적 : 두개나 그 이상으로 분리 됐지만 서로 관련이 있는 클래스들의 계층 구조를 엮어주기 위한 것이다.


- 상속을 통해서 객체를 만든다.

- 객체를 생성할 때는 클래스를 확장 하고 팩토리 메소드를 오버라이드 해야 한다.



Factory Pattern에서 중요시 하는 디자인 원칙

추상화된 것에 의존하도록 만들어라. 구상 클래스에 의존하도록 만들지 않도록 한다.


원칙에 도움이 될만한 가이드 라인

어떤 변수에도 구상 클래스에 대한 레퍼런스를 저장하지 않는다.

구상 클래스에서 유도된 클래스를 만들지 않는다

베이스 클래스에 이미 구현되어 있던 메소드를 오버라이드 하지 않는다.


추상 팩토리 패턴 - 추상 팩토리 패턴에서는 인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 생성할 수 있다.

- 객체 구성을 통해 제품을 구현 한다.

- 새로운 제품을 추가 하려면 인터페이스를 바꿔야  한다.

- 팩토리 메소드를 써서 구상 팩토리를 구현하는 경우가 종종 있다.



결론 : 팩토리는 구상 클래스가 아닌 추상 클래스/인터페이스에 맞춰서 코딩할 수 있게 해 주는 기법이다.


저작자 표시 비영리 변경 금지

'Actionscript3.0' 카테고리의 다른 글

Avoid ints in Actionscript  (10) 2009/11/14
Factory Pattern (AS3)  (0) 2009/10/25
Actionscript3.0의 DisplayList  (2) 2009/09/22
What is "Flashplatform" ?  (3) 2009/08/25
Posted by 동강
Actionscript3.02009/09/22 12:41


목차

DiplayList 란?

- DisplayList의 3가지 요소

- DisplayList의 구조

- DisplayList 구조의 장점

- Index 개념

- Shape, Sprite, MovieClip

- DisplayList의 부모 자식 노드 관계 탐색이 간편해짐

DisplayObject 클래스

- DisplayObject를 상속받는 클래스

- 속성 메소드 및 이벤트

- root , parent, stage 에 접근하기

- Loader 를 사용해 불러온 외부 swf 의 root 와 stage

DisplayObjectContainer 클래스

- DisplayObjectContainer 의 상속

- 속성 및 메소드


DisplayList 란?


DisplayList 란 SWF안에 존재 하는 객체들 중에 눈에 보이는 객체들을 총칭한다. 눈에 보이는 객체라고 하니 잘 이해가 안될수도 있으나, 간단하게 MovieClip 과 같이 addChild 를 통해 사용자에 눈에 보일수 있는 객체들을 뜻한다. 반면에 Date 클래스와 같은 객체는 단순히 시간에 대한 데이터를 처리 하는 객체 이므로 DisplayList 에 속하지 않는다. (빈 MovieClip 이나 Sprite, Shape 도 비어 있지만 모두 DisplayList 이다)


DisplayList의 3가지 요소


Flash Actionscript 에서는 이러한 DisplayList 구조가 아래와 기본 골격을 가지고 있다.


가장 상위에 Stage가 존재 하고 그 아래 MainTimeline 그리고 사용자가 만들어 내는 객체들로 눈에 보이게 되는 것이다. 그러면 어떠한 객체들이 "만들어지는 객체" 에 속하는 것인가? 바로 DisplayObject 와 DisplayObjectContainer 클래스를 상속받은 객체 들이다.

- DisplayObject : 화면에 표시 되는 객체 (Shape, Sprite, MovieClip, SimpleButton 등등)

- DisplayObject : 화면에 표시 되고 다른 객체들을 포함 할수 있는 객체 (Sprite, MovieClip, Loader 등등)



결국 SWF 안에 존재 하는 DisplayList 구조는 위와 같은 구조로 만들어 지게 된다.


DisplayList 구조의 장점


AS1.0, AS2.0 때 까지만 해도 위와 같은 DisplayList 구조를 가지고 있지 않았다. Flash CS3 이전 버젼에서는 MovieClip 이 거의 대부분의 객체의 기본이 되었고 그렇기 때문에 단점이 많았다. 하지만 위와 같은 DisplayList 를 가지게 되고 여러 장점을 가질수 있게 되었다.

- Shape, Sprite, MovieClip 등과 같이 객체가 다양해 져서, 효율적인 화면 구성을 할수 있다 되었다.

AS2.0 까지만 해도 MovieClip 만이 화면을 구성 하였으므로, 효율성이 떨어졌었다. 하지만, MovieClip 에서 타임라인을 제거한 Sprite 와 단지, 드로잉 기능만 가지고 있는 Shape 객체로 인해 좀더 효율적인 렌더링이 가능해 졌다.


Depth 개념에서 Index 개념으로 변화


AS2.0 까지는 객체의 위,아래 순서 개념을 Depth 개념이다. Depth 개념을 말 그대로 절대적인 깊이를 적용하여 z-index를 바꾸는 방식이었다. 예를 들어 mc 라는 객체가 depth 10에 존재 하고 mc2 라는 객체가 depth100에 존재 한다면 mc는 mc2 보다 아래 위치하게 되고 만약 영역히 겹쳐 졌을 경우에는 mc가 mc2에 가려 않 보인다. 여기서 중요한 점은 depth10 과 depth100 사이에는 아무것도 존재 하지 않아도 된다는 점이다. 빈 공간으로 둬도 상관없는 상태 이다. 하지만 AS3.0 부터 적용된 Index 개념에서는 조금 다르다. 항상 순서에 맞게 객체에 index 가 적용 된다. addChild 메소드를 통해 객체를 DisplayList 에 등록했다면, 제일 처음으로 등록한 객체는 index 0 을 할당 받고 그 다음은 1 다음은 2. .....7,8,9 등등 순서대로 할당 받게 된다. 순서를 가지고 있다는 점은 여러 가지 특징을 함께 가지고 있다는 뜻과 같다. 일상 생활에서 10개의 박스가 아래에서 부터 순서대로 쌓여 있다는 경우를 예로 들어 보면, 어떠한 사람이 제일 아래 있는 1 index 에 있는 박스를 가지고 간다면, 1 index 위치가 비게 되므로 위에 있는 박스들이 차례로 아래로 다시 쌓여 제 정렬 되게 됩니다. (1<-2 , 2<-3, 3<-4 등등) AS3.0 에서의 Index 도 같은 개념 이다.


Depth방식에 비해 Index방식이 효율적인 점



 첫번째로 Index방식은 객체에 순서를 부여 하므로 해서 부모 객체에서 자식 객체를 탐색할 때, 매우 빠르게 조회 할 수 있게 해준다. 아래 코드는 어떠한 DisplayObjectContainer 에 속해 있는 객체들을 탐색 하는 메소드 이다.



function displayChild(&prt:DisplayObjectContainer):void{

for(var i:int = 0; i < &prt.numChildren; i++){

var child:DisplayObject = &prt.getChildAt(i);

if(child as DisplayObjectContainer)

{

trace(child.name);

displayChild(child as DisplayObjectContainer);

}

else

{

trace(child.name);

}

}

}


 두번째로 또 다시 일상생활의 예를 들어 보면, Depth 방식으로 책을 만드는 사람들과 Index 방식으로 책을 만드는 사람들이 있다고 하자. 두 사람 모두 150쪽 짜리 책을 완성 하였다. 하지만, 중간에 잘못된 부분을 발견하여 50쪽에 100쪽 분량의 내용을 삭제 하려고 한다. Depth 방식의 사람들은 엄청 고생을 해야 할 것이다. 왜냐하면 기존에 있던 100쪽 부터 150쪽까지의 분량을 일일히 앞으로 옮겨야 하기 때문이다. 하지만 Index 방식으로 책을 만드는 사람들은 옮기는 수고를 할 필요가 없다. 50쪽부터 100쪽까지 의 분량을 삭제 하면 알아서 100쪽에서 150쪽까지 있었던 분량이 알아서 정렬하기 때문이다. Actionscript 에서도 이 예제와 똑같은 원리가 적용 된다.


DisplayObject 와 DisplayObjectContainer


아래 구조도에서 볼수 있듯이 AS3.0 의 DisplayList 는 DisplayObject 와 DisplayObjectContainer 를 통해 만들어 진다. 자주 사용하는 MovieClip 이나 Sprite 의 속성중 x, y, visible, alpha 등등의 속성을 가지고 있는 것은 모두 DisplayObject 클래스를 상속 받았기 때문이다. DisplayObject 란 DisplayList (표시목록) 에 배치 할 수 있는 모든 객체의 기본 클래스 이다. 그러면 DisplayObjectContainer 는 무엇인가 아래 구조도를 보면 DisplayObjectContainer 가 DisplayObject의 상속을 받아서 만들어 졌다는 것을 볼 수 있다. 이 말은 DisplayObject 의 기능은 모두 가지고 있으면서 어떠한 것을 포함 할 수 있는 즉, addChild 와 removeChild 와 같은 DisplayObject 의 객체 컨테이너(바구니) 역할을 할 수 있는 객체들의 기본 클래스 이다.



DisplayObject

상속 받는 구상 클래스 : 실제 객체를 생성 할 수 있는 클래스


Bitmap, Loader, Shape, SimpleButton,Sprite, MovieClip, TextField, Video


상속 받는 추상 클래스 : 실제 객체를 생성 할 수 없고, 기능만을 가지고 있는 추상 클래스이다. 이 추상클래스를 통해 객체를 생성하려고 하면 ArgumentError 가 발생한다.

var mc:DisplayObjectContainer = new DisplayObjectContainer() 를 실행 하면 ArgumentError 가 발생한다.


AVM1Movie, DisplayObjectContainer, InteractiveObject, MorphShape, Stage, StaticText


속성 및 메소드

http://help.adobe.com/ko_KR/AS3LCR/Flash_10.0/flash/display/DisplayObject.html


이벤트

Event.ADDED , Event.ADDED_TO_STAGE,


DisplayObject 는 객체에 addChild 될수 있으므로 A 객체가 B 객체에 addChild 되면 Event.ADDED 이벤트가 발생한다.

Event.REMOVED, Event.REMOVE_FROM_STAGE


DisplayObject 는 객체에 removeChild 될수 있으므로 A 객체가 B 객체에 removeChild 되면 Event.REMOVED 이벤트가 발생한다.


root / parent / stage 의 의미 알기



DisplayObjectContainer



속성 및 메소드

http://help.adobe.com/ko_KR/AS3LCR/Flash_10.0/flash/display/DisplayObjectContainer.html

주요 메소드

추가 / 제거 


addChild / removeChild

addChildAt / removeChildAt : 특정 index에 객체를 추가/삭제 한다.


 addChild(child:DisplayObject):DisplayObject

 addChildAt(child:DisplayObject,index:int):DisplayObject

 removeChild(child:DisplayObject):DisplayObject

 removeChildAt(child:DisplayObject,index:int):DisplayObject

자식 객체에 대한 정보


numChildren : DisplayObjectContainer 안에 addChild되어 있는 (포함되어 있는) 객체의 갯수를 반환한다.


Index 값 정보


getChildAt / getChildByName / getChildIndex : DisplayObjectContainer 안에 존재 하는 객체들을 Index 값(getChildAt), 이름(getChildByName)에 따라 찾아 주고, getChildIndex 는 특정 객체의 Index 위치를 반환한다.


getChildAt(index:int):DisplayObject

getChildByName(name:String):DisplayObject

getChildIndex(child:DisplayObject):int


Index 값 교환


setChildIndex / swapChildren / swapChildrenAt : DisplayObjectContainer 안에 존재하는 객체들의 Index 값을 서로 바꿔주는 메소드 들이다.


setChildIndex(child:DisplayObject,index:int):void

swapChildren(child1:DisplayObject,child2:DisplayObject):void

swapChildrenAt(index1:int,index2:int):void





저작자 표시 비영리 변경 금지

'Actionscript3.0' 카테고리의 다른 글

Factory Pattern (AS3)  (0) 2009/10/25
Actionscript3.0의 DisplayList  (2) 2009/09/22
What is "Flashplatform" ?  (3) 2009/08/25
OpenCV in Flash  (2) 2009/08/21
Posted by 동강
Actionscript3.02009/08/25 10:27


목차

  •  Flash platform 에 대해서
  •  Flash 와 Flex 그리고 AIR
  •  Actionscript 버젼벌 정보
  •  Flash player 버젼별 정보


Welcome to "Flash platform"




Flash platform 이란?

Flash 컨텐츠를 개발 하기 위한 방법은 여러 가지가 있다. Flash IDE (CS3, CS4) 를 이용하는 방법이나, Flex Builder 나 Flash Builder 를 이용하여 MXML 이나 Actionscript Project 를 컴파일 하는 방법, 또는 FDT 를 이용하여 컴파일 하여 SWF 를 생성하는 방법이 있다. 이 모든 Flash 컨텐츠의 결과는 SWF 형식으로 되어 있고 이러한 작업들은 Flash 컨텐츠 개발이라 한다. (곧 Flash Catalyst 가 정식 배포 되지만, 논외로 하겠다.)


얼마 전까지만 해도 Flash 와 Flex 의 네이밍은 따로 따로 존재 했다. 애초 부터 Flex Builder 라고 개발툴을 만들고, Flex 개발 방법을 강조해 오던 Adobe의 잘못으로 인해, Flex 를 새로운 툴이라고 생각하는 사람들이 많다. 하지만, Flex Builder 의 차기 버젼으로 준비되고 있던 Flex 4가 Flash Builder로 네이밍을 변경하여 나오게 되고, Flash IDE 와 Flash Bulder  그리고 Flash Catalyst 등을 이용하여 Flash 컨텐츠를 만드는 모든 것들을 Flash platform 이라고 통합 되었다.


Flash 와 Flex 그리고 AIR 는 무엇인가?



Flash 를 처음 시작 하는 사람이 가장 궁금해 하는 것이 "Flash 와 Flex 그리고 AIR가 무엇인가" 하는 것이다. 우선 AIR 에 대해 말하면 Flash player 와 같은 역할을 하는 환경을 말한다. Flash player 는 웹 브우저나, 모바일 디바이스에서 실시간으로 SWF 를 실행할 수 있는 환경을 제공해 주는데, AIR 는 데스크톱에서 Flash platform 환경에서 만들어진 어플리케이션을 설치하여 실행 시키는 환경이다. Flash player 와 같은 Runtime 환경이므로 Flash IDE 를 이용하여 만들든 Flex framework 를 이용하여 만들던 AIR 환경에서 돌아 가게 만들수 있다. 단 AIR 로 만드면 Flash player 에서 제공되지 않고, 제약 사항이 많았던 환경에서 벗어나서 여러 가지 장점을 가질 수 있다.


Flash 와 Flex 는 무엇인가? 많은 사람들이 Flash IDE (Flash 툴)로 만든 것을 Flash 컨텐츠라 하고 Flex Builder 로 만든 것을 Flex 컨텐츠 라고 알고 있다. 일부는 맞고 일부는 틀리다. Flex Builder 란 Flex framework를 구현하여 Flash 컨텐츠를 만들기 위한 툴이지만 Flex Builder 에서 Flex framework 를 사용하지 않고도 컨텐츠를 만들수 있다. 다만 Flex framework 를 사용하기에 편하게 만들어져 있다. Flex 란 단지 Framework 인데, 이 사실을 잘 알지 못하고 Flash 는 디자이너가 하는거고 Flex 는 개발자가 하는거다 라는 잘못된 생각을 가지게 된 것이고, Flash 와 Flex 가 전혀 다른 컨텐츠라고 생각하게 되는 오류를 발생한 것이다. 결론 적으로 Flash IDE와 Flex Builder는 같은 SWF 를 만들어 내는 툴이다. 다만 Flex Builder 는 Flex Framework 를 사용하기 쉽게 되어 있을 뿐이고 Flash platform 개발을 할때 코드를 짤 수 있는 비교적 편한 환경을 제공해 준다. (이클립스 기반으로 만들어져 있다.) 

Flash platform 개발언어인 Actionscript

Actionscript는 초기 Actionscript1.0 버젼을 시작하여, 현재 3.0 버젼까지 나왔다. Flash 버전 별로 나타낸 표는 다음과 같다.

 Flash player

Flash IDE

Actionscript 
 Flash player 4
 Flash 4
 AS1.0
 Flash player 5  Flash 5
 AS1.0
 Flash player 6
 Flash MX
 AS1.0
 Flash player 7
 Flash MX2004
 AS2.0
 Flash player 8
 Flash 8
 AS2.0
 Flash player 9
 Flash CS3
 AS3.0
 Flash player 10
 Flash CS4
 AS3.0

위의 표와 같이 Actionscript 는 Flash player 의 변화에 맞추어 버전업을 하였다. 버전이 2단계 밖에 바뀌지 않았지만, Actionscript1.0 과 3.0 의 차이는 전혀 다른 언어라고 봐도 될 정도로 다르다. 이 때문에 디자이너나 비 전공자가 Actionscript3.0을 시작하기 어려운 이유이기도 하다. 하지만  그리고 AS1.0 , 2.0 과 AS3.0 은 스크립트를 해석하여 컴파일 하는 엔진 자체가 다르다. Actionscript는 AVM(Actionscript Virtual Machine)에서 돌아가는데, 1.0과 2.0 은 AVM1 에서 돌아 가고 3.0은 AVM2에서 돌아 간다. AVM2가 AVM보다 엄청나게 좋은 퍼포먼스를 내므로 AS3.0 은 1.0, 2.0에 비해 몇배에서 몇십배 백배 차이가 나는 퍼포먼스를 낼수 있다.


Actionscript 의 버전별 언어 형식은 다음과 같다.


AS1.0 언어 형식

on(release){

    // 마우스가 클릭했을때 실행 되는 이벤트

}


AS2.0 언어 형식

객체.onRelease  = function(){

   // 마우스가 클릭했을때 실행 되는 이벤트

}


AS3.0 언어 형식

객체.addEventListener(MouseEvent.CLICK, hnClick);

function hnClick(e:MouseEvent):void{

   // 마우스가 클릭했을때 실행 되는 이벤트

}

 



 

AVM

Actionscript Virtual Machine(액션스크립트 가상 머신) Flash player 포함된 Actionscript 만들어진 SWF 재생하는 엔진이다. Java 실행되려면 JVM 있어야 하듯, Actionscript Java 실행되는 원리와 같다. Flash player(AVM) 설치되어 있다면 어느 디바이스라도 SWF 실행할  있다.


Flash player 에 대해

 위와 같이 Actionscript 버전이 바뀌었다고 해서, 상위 버전의 Flash player에서 AS1.0과 AS2.0 을 실행할 수 없는건 아니다. Flash player 들은 하위 호환성에 맞게 제공 되고 있으므로, 버전에 관계 없이 실행 가능 하다. 하지만, Flash player의 하위버전 즉, Flash player 9 이전 버젼에서는 Actionscript3.0 으로 만든 Flash 컨텐츠를 실행 할 수 없다. 실행 했을때는 상위 버전의 Player 를 설치하라는 경고창이 뜬다. 


저작자 표시 비영리 변경 금지

'Actionscript3.0' 카테고리의 다른 글

Actionscript3.0의 DisplayList  (2) 2009/09/22
What is "Flashplatform" ?  (3) 2009/08/25
OpenCV in Flash  (2) 2009/08/21
Flash player 의 보안  (17) 2009/06/24
Posted by 동강
Actionscript3.02009/08/21 16:48

OpenCV란 무엇인가?

OpenCV

OpenCV(영어: Open Computer Vision)은 오픈소스 컴퓨터 비전 C 라이브러리이다. 원래 인텔에서 개발되었다. 현재 버전 1.0이 나와있으며 윈도, 리눅스 플랫폼에서 사용할 수 있다. 실시간 이미지 프로세싱에 중점을 둔 라이브러리이다. 인텔 CPU에서 사용되는 경우 속도의 향상을 볼 수 있는 Intel Performance Primitives (IPP)를 지원한다.


출처 : Daum 백과 사전


OpenCV 라이브러리를 이용하여 할 수 있는것?

OpenCV를 이용하면 영상처리 기술을 비교적 손쉽게 구현 할 수 있다. 입력과 출력 그리고 영상 처리에 대한 기본적인 기능들이 대부분 구현 되어 있다. 예를 들어 다음과 같은 작업 들을 할 수 있다.

- 얼굴 인식

- Corner Detection

- 증강 현실 (AR)

- 바코드 인식

- 문자 인식


OpenCV in Flash

OpenCV 라이브 러리를 Actionscript3.0 으로 컨버팅 하여 Flash 에서도 OpenCV 의 물건 인식 기능을 사용할 수 있게 되었다. Ohtsuka Masakazu 가 만든 이 라이브러리를 Marilenna 라는 프로젝트 명으로 Spark Project 에서 받아 볼 수 있다.


예제)
http://www.quasimondo.com/examples/face_detection/FaceDetector_Camera.html

http://www.francois-tarlier.com/blog/wp-content/uploads/2009/03/Marilena01/


저작자 표시 비영리 변경 금지

'Actionscript3.0' 카테고리의 다른 글

What is "Flashplatform" ?  (3) 2009/08/25
OpenCV in Flash  (2) 2009/08/21
Flash player 의 보안  (17) 2009/06/24
AS3.0의 통신 - 브라우저와의 통신 (Javascript)  (11) 2009/06/02
Posted by 동강
Actionscript3.02009/06/24 13:51
Front - End 개발을 하다 보면 Flash player의 보안 범위를 판단하지 못해 곤란한 일을 겪은 일이 있을것이다.  Flash 개발자의 경우에는 미쳐 알지 못한 내부 보안 설정을 하지 않아서 데이터를 못불러 온다거나, 바뀐 Flash player 보안 정책 때문에 고생을 했을 수도 있고, UI개발자의 경우에는 Flash 개발자가 없는 상태에서 여러 상황을 판단을 해야 하지만 Flash player의 보안 규칙에 대해 알지 못해서 혼란 스러운 경우가 있을 것이다. 이 문서는 모든 Flash 개발자와 UI개발자가 알아야 할 Flash player의 보안에 대해 기초적인 내용을 다룰 것이다.

Flash player 보안 개요

Flash player의 보안은 Flash, Flex 로 만들어진 SWF 파일이 발생 시킬 수 있는 보안 이슈에 대해 정의 및 제한 하고 있다. 텍스트 파일이나 이미지, 오디오 파일들을 로드 하거나 SWF 사이에 크로스 스크립팅을 할때 발생 할 수 있는 이슈들이 모두 여기에 포함 되어 있다. 예를 들어 SWF에서 http://www.ddongkang.com 에 존재 하는 http://www.ddongkang.com/data.xml 을 로드 한다고 할때, http://www.ddongkang.com 도메인에 crossdomain.xml 파일이 없다면 SecurityError를 발생시킨다. Flash player의 보안은 위와 같은 문제에 대해 Flash player 구현상으로 올바른 접근법을 제시 하고 있다. 

기본적으로 다음과 같은 보안 규칙이 적용 된다.

  • 동일한 보안 샌드 박스의 리소스를 항상 서로 접근(Excess) 할 수 있다.
  • 원격 샌드박스의 SWF파일에서는 로컬 파일과 데이터에 접근 할 수 없다.


안 샌드 박스 타입


Actionscript는 Flashplayer가 로드하는(열거나, 불러온 파일 포함) 모든 SWF파일에 보안 샌드박스 타입이라 불리는 보안 상태를 할당한다. Flashplayer는 아래와 같이 4가지 경우의 보안 샌드 박스 타입이 존재 한다.

  • remote 샌드박스 - Flash Player는 인터넷의 SWF 파일을 포함하여 에셋을 해당 웹 사이트의 원래 도메인에 해당하는 별도의 샌드박스에 분류한다. 기본적으로 이러한 파일은 해당 서버의 모든 리소스에 대한 액세스가 허용된다. 원격 SWF 파일은 URL 정책 파일 및 Security.allowDomain() 메서드와 같은 명시적인 웹 사이트 및 제작자 권한을 사용하여 다른 도메인의 데이터에 추가로 액세스할 수 있다.

  • local-with-filesystem 샌드박스 - 보안을 위해 Flash Player는 기본적으로 모든 로컬 SWF 파일을 local-with-file-system 샌드박스를 적용한다. 이 샌드박스에서 SWF 파일은 URLLoader 클래스 등을 사용하여 로컬 파일을 읽을 수 있지만, 네트워크와는 어떤 방식으로도 통신할 수 없다.


  • local-with-networking 샌드박스 - SWF 파일을 컴파일할 때, 로컬 파일로 실행되지만 네트워크 액세스가 가능하도록 지정할 수 있다. 이러한 파일은 local-with-networking 샌드박스가 적용 된다. local-with-networking 샌드박스에 할당된 SWF 파일에서는 해당 로컬 파일에 액세스할 수 없다. 대신, SWF 파일은 네트워크의 데이터에 액세스할 수 있다. 하지만 URL 정책 파일이나 Security.allowDomain() 메서드에 대한 호출을 통해 권한이 부여되지 않는 한 local-with-networking SWF 파일은 여전히 네트워크의 데이터를 읽을 수 없다. 이러한 권한을 부여하려면 URL 정책 파일에서<allow-access-from domain="*"/> 또는 Security.allowDomain("*")을 사용하여 모든 도메인에 권한을 부여해야 한다.


  • local-trusted 샌드박스 - 사용자나 설치 프로그램에 의해 신뢰할 수 있는 파일로 등록된 로컬 SWF 파일은 local-trusted 샌드박스에 배치된다. 시스템 관리자와 사용자는 보안 고려 사항에 따라 local-trusted 샌드박스에 또는 해당 샌드박스로부터 로컬 SWF 파일을 재할당하거나 이동할 수도 있다. local-trusted 샌드박스에 할당된 SWF 파일은 다른 SWF 파일과 상호 작용하고 원격이나 로컬의 모든 위치에서 데이터를 로드할 수 있다.


 각각의 보안 샌드 박스 타입은 종류에 따라 Flashplayer의 보안 수행 범위가 정해 진다. 다음은 모든 보안 샌드 박스 타입에서 금지 할 가능성이 수도 외부 작업 들이다.

  • 데이터 불러오기
  • 데이터에 접근하기 
  • 크로스 스크립팅 
  • 외부 URL에 데이터 보내기
  • 사용자의 카메라와 마이크로폰에 접근하기
  • 로컬 공유 객체에 접근하기
  • 사용자가 선택한 파일 업로드 또는 다운로드
  • .swf 파일에서의 HTML (javascript) 페이지 스크립팅과 그 반대 작업
  • LocalConnection 채널에 연결하기


보안 샌드 박스 타입이 할당 되는 방법

.SWF 파일의 보안 샌드박스 타입을 결정하기 위해 액션스크립트는먼저 .SWF 파일을 불러오거나 연 위치를 고려한다. 원격 샌드 박스(주로 에서 구동되는 SWF)는 remote 샌드박스가 적용되고, 컴파일러 설정, 환경 설정 파일등 로컬에서 구동되는 SWF 컨텐츠는 local-with-filesystem, local-with-networking, local-trusted 샌드박스에서 선택하여 작업 해야 한다.(각각 보안 기준이 다르다)

UI개발시 고려해야 할 Flash player의 보안

주로 UI개발시 고려해야 하는 상황은 웹이기 떄문에 remote 샌드박스를 고려 하면 된다. 아래 4가지 경우는 UI개발과 Flash개발 사이에 발생 할 수 있는 보안 이슈들이다.

1. SWF에서 외부 파일 로드 (이미지,오디오,텍스트파일 등등)

아무런 통신을 안하는 SWF에 대해서는 이러한 보안 이슈에 대해 주의해야 할 점들이 없지만, 대부분은 SWF들은 데이터를 불러오는 작업을 한다. SWF안에서 좀 더 깔끔한 폰트 처리를 위해서 font.swf를 로드 한다거나, 외부 데이터 파일을 불러와서 뿌려줘야 되는 경우가 종종 발생한다. 이때 font.swf 파일이나, data.xml 파일이 메인이 되는 SWF와 같은 도메인에 있다면 따로 보안 설정을 안해줘도 되지만, 다른 도메인에 존재 할 경우에는 설정을 해 줘야 한다. 좀 더 쉬운 상황 설명을 위해 로딩의 주체가 되는 SWF 파일을 main.swf , 로드 되어 지는 파일인 폰트 파일을 font.swf, 로드 되어 지는 데이터 파일을 data.xml 이라고 하자.

상황 1 .3개의 SWF파일들은 모두 http://www.ddongkang.com/flash/ 폴더안에 존재 하고 main.swf 에서 font.swf 와 data.xml 를 로드 하는 경우

모두 같은 폴더에 존재 하기 때문에 따로 보안 설정을 해줄 필요가 없다.

상황 2 .main.swf 파일은 http://www.ddongkang.com/flash/ 폴더에 존재 하고, font.swf 와 data.xml 은 http://www.daum.net/flash/data/ 에 존재 하는 경우

주체가 되는 main.swf 와 로드 되어 지는 데이터가 도메인에 존재 하기 때문에 Flash player의 보안에 막히게 된다. main.swf 에서 font.swf 와 data.xml 을 로드 하기 전에 main.swf 는 데이터가 존재 하는 http://www.daum.net 에 크로스 도메인 파일(정책파일)이 존재 하는지 확인 하고 그 설정이 맞는다면 로드를 하게 된다. (따로 확인 하지 않아도 Flash에서 Load 를 하게 되면 crossdomain.xml 파일을 로드 한다. 크로스 도메인 파일의 이름을 바꿔서 로드 하고 싶은 경우
Security.loadPolicyFile("파일URL"); 을 로드 하기 전에 호출 하면 된다. 주의 해야 할 점은 crossdomain.xml 파일의 위치이다. main.swf 가 data.xml의 로드를 시작 하면 Flashplayer에서는 우선 data.xml 파일이 존재 하는 폴더에서 crossdomain.xml 파일이 존재 하는지 확인 한다. 존재하면 바로 확인이 되어 로드를 하겠지만, 존재 하지 않는다면 상위 폴더에 crossdomain.xml 파일이 존재 하는지 확인하게 된다. 처음에 flash/data/ 폴더를 확인 하고 없으면 flash/ 폴더를 확인한다. crossdomain.xml(정책 파일)이 계속 존재 하지 않는다면 계속 상위 폴더에서 확인하고 결국엔 서버의 루트까지 확인하게 된다. 위의 예제에서는 http://www.daum.net 이 루트이다. 루트에도 crossdomain.xml(정책파일)이 존재 하지 않으면 Flashplayer는 로드를 못하게 되고 보안 에러를 발생 시키게 된다. 그러므로 상황에 따라 crossdomain.xml 파일의 위치를 잘 판단하여 업로드 해야 한다.

상황 3 .이미지 오디오 파일 로드

이미지 파일이나 오디오 파일을 로드 할때도 crossdomain.xml(정책파일)이 필요 하다. 하지만 정책 파일이 있더라도 Actionscript에서 보안 설정을 안해주면 이미지 파일의 크기를 조작하거나, 오디오 파일을 불러와서 조작 할 수 없다. 이미지를 조작할때 이러한 문제가 자주 발생하는데, 보안 설정을 안해주고 이미지의 크기가 변하지 않는 다고 이상하게 생각했던 경험을 가지고 있는 사람들이 많이 있을것이다. Flash 상에서 이미지를 로드 하는 것은 결국에는 Flash player에서 이미지를 BitmapData 로 가지고 있는 것이기 때문에 다음과 같은 설정을 해줘야만 로드된 이미지의 BitmapData에 접근할 수 있어서 이미지 크기 조작과 같은 동작이 가능하다.

LoaderContext 설정 방법 소스
// 정책파일이 기본 위치에 없다면
Security.loadPolicyFile("정책파일의위치");			
var loadcontext:LoaderContext = new LoaderContext();
loadcontext.checkPolicyFile = true;
var loader:Loader = new Loader();
loader.load(new URLRequest("이미지 URL"),loadcontext);			

사운드의 경우도 마찬가지로 다음과 같은 설정을 해줘야 한다.

SoundLoaderContext 설정 방법 소스
// 정책파일이 기본 위치에 없다면
Security.loadPolicyFile("정책파일의위치");			
var soundcontext:SoundLoaderContext = new SoundLoaderContext();
soundcontext.checkPolicyFile = true;
var sloader:Sound = new Sound();
sloader.load(new URLRequest("오디오 URL"),soundcontext);			

crossdomain.xml (정책 파일)



위의 정책파일은 www.ddongkang.com 과 ddongkang.com 에 대해 접근 허락을 나타내고 있다. 더 자세한 정책 파일 옵션은 웹 사이트 컨트롤(정책파일) 에서 확인할 수 있다.



2. SWF에서 HTML 브라우져 객체의 접근

AS3.0의 통신 - 브라우저와의 통신 에서 다뤘듯이 SWF는 자신을 가지고 있는 HTML객체와 통신을 할 수 있다. SWF를 HTML에 Embed 할때 allowScriptAccess매개변수를 설정하는데, 이 매개변수는 SWF파일 내에서 HTML에 있는 객체를 접근하는 것에 대한 기능을 제어한다.


<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
             id="FlashVar" width="500" height="375"
             codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
         <param name="movie" value="FlashVar.swf" />
         <param name="quality" value="high" />
         <param name="bgcolor" value="#869ca7" />
         <param name="allowScriptAccess" value="sameDomain" />
    <param name="FlashVars" value="data0=DongHyuk&data1=동혁&data2=http:www.ddongkang.com" />
         <embed src="FlashVar.swf" quality="high" bgcolor="#869ca7"
             width="500" height="375" name="FlashVar" align="middle"
             play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
             type="application/x-shockwave-flash"
             FlashVars="data0=DongHyuk&data1=동혁&data2=http:www.ddongkang.com"
             pluginspage="http://www.macromedia.com/go/getflashplayer">
         </embed>
 </object>


allowSCriptAccess는 "always","sameDomain","never" 값중 하나를 가질 수 있다.

allowScriptAccess="always" : SWF파일은 HTML 페이지와 다른 도메인에 있는 경우에도 SWF파일이 포함된 HTML페이지와 통신 할 수 있다.

allowScriptAccess="sameDomain" : SWF파일은 HTML 페이지와 동일한 도메인에 속해 있는 경우에만 SWF파일이 포함된 HTML 페이지와 통신할 수 있다.

allowScriptAccess="never" : SWF파일에서 어떠한 HTML페이지와도 통신 할 수 없다.

allowScriptAccess에 따라 ExternalInterface.call 을 이용하여 HTML 에 있는 javascript 함수를 자유롭게 호출 할 수 있다. 하지만, HTML 객체에서 SWF 객체에 접근 할 때는 SWF 내부의 보안 정책이 필요 하다.  javascript에서 아무런 제약 없이 SWF에 있는 모든 객체들에 접근 할 수 있다면, 심각한 보안 문제가 발생할 것이다.

상황 1. 같은 도메인에 HTML과 SWF파일이 존재 하는 경우(html파일이 swf파일을 포함할때)

같은 도메인에 존재 하기 때문에 따로 보안 설정을 안해줘도 된다. 예를 들어 http://www.ddongkang.com/html/ 폴더에 index.html이 있고 http://www.ddongkang/html/flash/ 에 swf파일이 존재할 경우 HTML에서 SWF에 존재 하는 객체에 제한 없이 접근 할 수 있다.

상황 2. 다른 도메인에 HTML과 SWF파일이 존재 하는 경우(html파일이 swf파일을 포함할때)

index.html 파일이 http://www.daum.net/html/ 폴더에 있고, movie.swf파일이 http://www.ddongkang.com/html/flash 폴더에 있을 경우 movie.swf파일에서 반드시 Security.allowDomain("http://www.daum.net/html/"); 해줘야 index.html 파일에서 movie.swf 에 있는 객체로 접근이 가능해 진다. AS3.0의 통신 - 브라우저와의 통신 에서 다뤘듯이 ExternalInterface.addCallback 을 이용하여 html 에서 swf에 있는 메소드를 실행 하는 것이 이 경우에 해당 한다.

3. 크로스 스크립팅

AS3.0 부터는 서로 다른 SWF 간에 크로스 스크립팅이 가능하다. 크로스스크립팅이란, 아래 그림과 같이 swfA.swf 에서 swfB.swf 를 로드 했을 경우 swfA.swf 에서 swfB.swf의 객체에 접근하여 스크립팅 하거나 swfB.swf 에서 swfA.swf로 접근하여 스크립팅 하는 것을 말합니다. 두개의 SWF파일이 같은 도메인에 있으면 따로 보안 설정을 안해줘도 되지만, 다른 도메인에 있으면 설정을 해 줘야 한다



siteA.com 에 있는 swfA.swf가 siteB.com 에 있는 swfB.swf를 로드 하여 크로스 스크립팅 하는 경우 swfB.swf 에서 Security.allowDomain("siteA.com") 를 호출해야만 swfA.swf에서 swfB.swf로의 접근이 가능하다. allowDomain으로 설정된 보안은 비대칭적이기 때문에 swfB.swf 에서는 swfA.swf 로 크로스 스크립팅을 할 수 없다. swfA.swf 에서도 위와 마찬가지로 Security.allowDomain("siteB.com") 이라 해줘야 쌍방향으로 크로스 스크립팅이 가능하다. 더 자세한 내용은
Adobe Actionscript3.0 Document - Security 에서 확인 할 수 있다.


이 문서에서는 Flash player의 보안중 극히 일부분만을 다루었다. 그 만큼 방대한 보안 설정이 존재 하고 때에 따라 보안 규약을 맞춰서 작업해야 발생할 가능성이 있는 보안 취약점들은 미리 파악하여 대처 할 수 있다. 지금 까지 나온 다양한 이슈들에 대해 Adobe Flash Developer Center - Security 에서 다루고 있으니 참고하라.



저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02009/06/02 23:40

 Flash의 컴파일 결과물인 SWF는 그 자체로 UI 나 GAME 그리고 어플리케이션(AIR)이 될 수 있지만, 대부분은 브라우저에 Embeded 되어 사용됩니다. 배너와 같이 단순하게, 보여 주기 위해서 브라우저에 Embeded하는 경우도 있겠지만, UI 나 웹 어플리케이션을 구현할 때는 브라우져나, 서버사이드 언어와의 통신이 필요 하게 됩니다. AS3.0으로 거의 대부분의 Front-End 단의 개발작업을 모두 할 수 있긴 하지만, 퍼포먼스나 구현의 편의성을 위해 Javascript를 사용해야 하는 경우가 많습니다. 이 문서는 앞으로 몇개의 포스팅을 통해 다루게 될 AS3.0의 통신 방법에 대한 첫 문서로서 AS통신의 가장 기본이라 할 수 있는 브라우저와의 통신에 대해 다루고 있습니다.

 SWF와 브라우저와 통신 하는 방법은 크게 두가지로 구분할 수 있습니다.

첫번째, SWF 를 브라우저에 Embeded 시킬때 변수를 전달해 주는 방법
아래와 같이 main.swf 파일을 브라우저에 Embeded할때 "FlashVars" 변수를 SWF에서 접근 하는 방식입니다.

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
             id="FlashVar" width="500" height="375"
             codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
         <param name="movie" value="FlashVar.swf" />
         <param name="quality" value="high" />
         <param name="bgcolor" value="#869ca7" />
         <param name="allowScriptAccess" value="sameDomain" />
    <param name="FlashVars" value="data0=DongHyuk&data1=동혁&data2=http:www.ddongkang.com" />
         <embed src="FlashVar.swf" quality="high" bgcolor="#869ca7"
             width="500" height="375" name="FlashVar" align="middle"
             play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
             type="application/x-shockwave-flash"
             FlashVars="data0=DongHyuk&data1=동혁&data2=http:www.ddongkang.com"
             pluginspage="http://www.macromedia.com/go/getflashplayer">
         </embed>
 </object>


object 와 embed 를 함께 사용해야 하는 이유

마이크로소프트의 Internet Explore(IE) 에서는 특정한 속성과 함께 <object>를 사용해야 하고, IE 외의 다른 브라우져에서는 이와 다른 속성을 설정한 <object>태그를 사용하거나, 아예 <embed>태그를 사용해야 하기 때문입니다. 모든 브라우저에서 크로스 브라우징을 하기 위함입니다.


object 태그를 사용하여 SWF 를 브라우져에 올릴때, FlashVars 값을 지정해서 넘겨 주는 방식 입니다. 위의 코드에서 볼드로 처리해 놓은 부분을 주의 깊게 보면, 두 부분에서 정의 하고 있는 변수들이 (data0,data1,data2)과 그 값들이 똑같아야 정상적으로 SWF에 넘어가게 됩니다.

AS3.0에서 FlashVars 변수를 통해 넘어 오는 값을 받는 방법은 다음과 같습니다.

package
{
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.text.TextField;
   

    public class FlashVarsExample extends Sprite
    {
        private var field:TextField;
        public function FlashVarsExample()
        {
            field = new TextField();
            field.autoSize = "left";
            addChild(field);
           
            var keyStr:String;
              var valueStr:String;
           
            var paramObj:Object = LoaderInfo(this.root.loaderInfo).parameters;
            for (keyStr in paramObj) {
                valueStr = String(paramObj[keyStr]);
                field.appendText("\t" + keyStr + ":\t" + valueStr + "\n");
            }
        }
       
    }
}

LoaderInfo(this.root.loaderInfo).parameters 는 위의 object 태그에서 넘겨 주는 FlashVars를 받아 오고 paramObj를 for..in 구문을 사용하여 그 안에 있는 변수들을 뽑아 오는 방법 입니다. SWF 가 브라우저 또는 다른 SWF에 로드 되었을때, 자신의 로드 정보를 LoaderInfo 객체에 담고 있기 때문에 사용할 수 있는 방법 입니다. 자세한건 LoaderInfo 클래스를 참고 하세요.

플래시에서 로드 하는 xml의 url이 유동적으로 바뀌거나, 상황에 따라 다른변수를 SWF에 전달해 주고 싶을때 간단하게 사용할 수 있는 방법 입니다. 예를들어 브라우저의 현재 위치를 SWF에 전달하여 네비게이션 Depth를 구현 하는 것도 이 방법을 사용하면 됩니다. 하지만, 비교적 작은 변수만 넘길 수 있다는 점과 변수 값이 그대로 외부에 노출 된다는 단점이 있습니다.

두번째, Flash API 의 ExternalInterface 객체를 사용하는 방법

ExternalInterface는 SWF와 Flash Player컨테이너(브라우져나 Flashplayer를 소유할 수 있는 모든 것들)간에 직접 통신 할 수 있도록 도움을 주는 클래스 입니다. Javascript와의 통신을 위한 중요한 기능을 제공하고 있으며 Adobe에서는 Javascript와의 통신에 ExternalInterface를 사용하도록 권장합니다.

통신은 양방향으로 모두 가능 합니다. SWF에서 Javascript의 함수나 변수에 접근할 수 있으며 변경도 가능합니다. 반대로 Javascript에서 SWF 안에 있는 Flash 객체들도 접근 가능 합니다. 하지만 ExternalInterface는 운영체제와 브라우저가 지원 하지 않으면 사용할 수 없습니다. 지원하는 OS와 브라우저들은 다음과 같습니다.

 브라우저  운영체제  운영체제
 Internet Explorer 5.0 이상
 Windows  
 Netscape 8.0 이상
 Windows  MacOS
 Mozilla 1.7.5 이상
 Windows  MacOS
 Firefox 1.0 이상
 Windows  MacOS
 Safari 1.3 이상
   MacOS

위와 같이 사용할 수 있는 환경이 존재 하기 때문에 사용전 체크를 해 줘야 합니다.

ExternalInterface.available :Boolean = 이 값이 true를 반환하면 사용가능하고 false이면 사용 불가능 합니다.
if(ExternalInterface.available){// 통신 실행}

위와 같은 코드를 통해 사용 가능 여부를 체크 하고 사용해야만 합니다. ExternalInterface는 통신을 위한 두가지 메소드를 제공합니다.

ExternalInterface.call(functionName:String,...arguments):*
먼저 ExternalInterface.call은 SWF를 임베디드(소유)하고 있는 컨테이너의 메소드를 호출하는 함수 입니다.그함수를 수를 호출 할 수 있을 뿐만 아니라, 변수를 전달하여 실행 할 수 있습니다. 예를들어 다음과 같이 메소드를실행 하면 브라우져에서 alert()창을 띄웁니다. (여기서는 주로 사용되는 브라우저를 예를들어 설명하겠습니다.)

ExternalInterface.call("alert","alert function is called");

전달할 인수가 없을때는 빈공간으로 호출할 수 있고, 전달한 인수가 많아지면 그 갯수 만큼 arguments 자리에 넣어
주면 됩니다.

ExternalInterface.addCallback(functionName:String, closure:Function):void
addCallback의 첫번째 인수인 functionName은 Javascript에서 SWF안에 있는 Actionscript 함수를 호출할때 사용하는 이름이고, closure는 실제로 SWF 에서 호출 되는 함수명 입니다. 이 두가지 파라미터가 처음 사용할때 많이 헛깔리는 부분인데요.

예를 들어 SWF에서 addCallback 메소드를 아래와 같이 호출 하면

ExternalInterface.addCallback("sendToActionScript", receivedFromJavascript);

sendToActionScript는 Javascript에서 SWF 에 있는 receivedFromJavascript 함수를 호출 한다는 의미 입니다. 결국 sendToActionScript는 Javascript에서 사용하는 함수이고 receivedFromJavascript는 Actionscript에서 호출되는 함수 입니다. 예를 들어 보겠습니다. (Adobe 도움말에 있는 예제)

예제 샘플
if (ExternalInterface.available) {             
                output.appendText("Adding callback...\n");
                ExternalInterface.addCallback("sendToActionScript", receivedFromJavaScript);
                if (checkJavaScriptReady()) {
                    output.appendText("JavaScript is ready.\n");
                } else {
                    output.appendText("JavaScript is not ready, creating timer.\n");            
                }
               
            } else {
                output.appendText("External interface is not available for this container.");
            }
우선 ExternalInterface.available로 ExternalInterface 객체를 사용할 수 있는지 확인한 후에 addCallback 메소드를 실행 시켰습니다. addCallback 메소드를 등록 한 후에 checkJavaScriptReady() 메소드를 호출하여 javascript 호출이 가능한지 판단 합니다. 호출이 가능한지 체크 하는 이유는 브라우져가 모두 로드가 된 후에 Javascript 메소드를 호출 할 수 있기 때문 입니다. 그래서 Javascript안에 있는 isReady 메소드를 호출하여 반환 값이 true가 넘어 오면 준비가 된걸로 생각하고 스크립트가 진행 됩니다.

ExternalInterface.addCallback 을 등록했기 때문에, 해당 SWF를 임베디드한 브라우져의 Javascript에서 sendToActionScript 메소드를 호출하면 SWF 안에 있는 receivedFromJavaScript 메소드가 실행 됩니다.
private function receivedFromJavaScript(value:String):void {
            output.appendText("JavaScript says: " + value + "\n");
        }

ExternalInterface.call 메소드와 같이 Javascript에서도 SWF로 변수를 전달할 수 있기 때문에 전달된 value값을 받아서 Javascript에서 어떤 값이 넘어 왔는지, TextField에 뿌려 주는 메소드 입니다.

주의해야 할 점 브라우저에 SWF 를 object 태그나, embed 태그를 이용하여 삽입할때, name 값과 id 값을 설정해 줘야 해당 SWF에 접근 할 수 있습니다. 페이지 안에는 여러개의 SWF가 존재 할 수 있기 때문에, id 값이나 name 값을 통해 해당 SWF를 찾아 내서 그 안에 있는 메소드에 접근 할 수 있는 것 입니다. Javascript에서 SWF를 찾아 내는 메소드는 다음과 같습니다.
function thisMovie(movieName) {

         if (navigator.appName.indexOf("Microsoft") != -1) {

             return window[movieName];

         } else {

             return document[movieName];

         }

     }




navigator.appName.indexOf("Microsoft") 를 이용하여 브라우져의 종류를 판단하여, 종류에 따라 접근 방법을 다르게 하여 SWF 를 찾아 내는 메소드 입니다.

전체 브라우저 소스는 아래와 같습니다.

<!-- saved from url=(0014)about:internet -->

 <html lang="en">

 <head>

 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

 <title>ExternalInterfaceExample</title>

 <script language="JavaScript">

     var jsReady = false;

     function isReady() {

         return jsReady;

     }

     function pageInit() {

         jsReady = true;

         document.forms["form1"].output.value += "\n" + "JavaScript is ready.\n";

     }

     function thisMovie(movieName) {

         if (navigator.appName.indexOf("Microsoft") != -1) {

             return window[movieName];

         } else {

             return document[movieName];

         }

     }

     function sendToActionScript(value) {

         thisMovie("ExternalInterfaceExample").sendToActionScript(value);

     }

     function sendToJavaScript(value) {

         document.forms["form1"].output.value += "ActionScript says: " + value + "\n";

     }

 </script>

 </head>

 <body onload="pageInit();">

 

     <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"

             id="ExternalInterfaceExample" width="500" height="375"

             codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">

         <param name="movie" value="ExternalInterfaceExample.swf" />

         <param name="quality" value="high" />

         <param name="bgcolor" value="#869ca7" />

         <param name="allowScriptAccess" value="sameDomain" />

         <embed src="ExternalInterfaceExample.swf" quality="high" bgcolor="#869ca7"

             width="500" height="375" name="ExternalInterfaceExample" align="middle"

             play="true" loop="false" quality="high" allowScriptAccess="sameDomain"

             type="application/x-shockwave-flash"

             pluginspage="http://www.macromedia.com/go/getflashplayer">

         </embed>

     </object>

 

     <form name="form1" onsubmit="return false;">

         <input type="text" name="input" value="" />

         <input type="button" value="Send" onclick="sendToActionScript(this.form.input.value);" /><br />

         <textarea cols="60" rows="20" name="output" readonly="true">Initializing...</textarea>

     </form>
 </body>
 </html>


Javascript 소스를 위에서 부터 보면 Flash 에서 ExternalInterface.call 로 호출한 isReady 메소드가 정의 되어 있습니다.
<body onload="pageInit();"> 태그가 있기 때문에 페이지가 모두 로드 되면 jsReady 변수가 true가 되어 Flash 에서 Javascript를 호출할 준비가 완료 되었다는 것을 알게 됩니다. 그리고 sendToActionScript 메소드를 실행 하면 thisMovie 를 이용하여 해당 SWF를 찾아낸 후에 그 안에 있는 receivedFromJavaScript 메소드를 호출하는 것 입니다.

thisMovie("ExternalInterfaceExample").sendToActionScript(value); 를 호출했다고 해서 SWF안에 있는 sendToActionScript메소드를 호출한다고 생각하면 안됩니다. addCallback을 통해 Javascript에서 sendToActionScript를 호출하였을때 SWF에서는 receivedFromJavasript 메소드를 호출한다고 등록 했기 때문에 receivedFromJavascript 메소드가 호출 됩니다.

그리고 SWF 에서 ExternalInterface.call 메소드를 통해 Javascript의 sendToJavaScript를 호출하면 form태그에 있는 textarea에 그 값이 추가 되는 예제 입니다.

위와 같이 ExternalInterface를 이용하면, Javascript에 있는 함수나 SWF 안에 있는 함수나 쉽게 접근 할 수 있습니다. 다만,
ExternalInterface.call 이나, ExternalInterface.addCallback 으로 등록한 함수만 접근 할 수 있는 단점이 있습니다. 그리고 ExternalInterface에는 몇가지 단점이 존재 합니다.

ExternalInterface의 단점

- 액션스크립트와 자바스크립트 모두에서 별도 코드의 라이브러리를 작성하여, 플렉스 어플리케이션의기능을 자바스크립에서 사용하거나 그 반대의 경우에 대해서도, ExternalInterface 클래스가 있어야 한다.


 - ExternalInterface 클래스는 또한 제약이 있다. 기본 데이터 타입, 배열, 심플 오브젝트들은 전달할 수 있지만, 속성과 메소드를 가지는 사용자 정의 클래스를 전달하는 것은 문제가 있다.


그래서 Adobe에서는 Flex Ajax Bridge (FABridge) 라는 ExternalInterface의 단점을 보완한 Flex framework 기반의 오픈 소스 라이브러리를 제공 하고 있습니다. FABridge의 기능은 다음과 같습니다.

 

FABridge

- 리치 플렉스 컴포넌트를 에이젝트 어플리케이션 안에서 사용하고자 하지만 플렉스 코드를 엄청나게새로 쓰는 것은 원하지 않는 경우이다.  브릿지를 사용하는 일부 어플리케이션 내에 컴포넌트를 감싸려고 한다면, 자바스크립트로 전체를 스크립트할 수 있고, 이것은 서버에서 생성된 eval() 함수를 사용하는 형태의 자바스크립트를 이용한다.


 - 우리 팀에 플렉스를 하는 사람이 한 두 사람뿐인 경우이다. FABridge 라이브러리는 팀의 모든 사람이 한 두 명의 플렉스 전문가가 만든 결과물을 사용할 수 있게 해준다.


 - 플렉스와 에이젝스로 만들어진 부분을 모두 가지는 RIA를 만드는 경우이다. 우리는 ExternalInterface만을 이용하여 통합할 수도 있고, 남보다 앞서 FABridge를 사용하여 더 빠른 방법을 찾을 수도 있다.


공개된 라이브러리는 Flex 에 한정되어 만들어 졌지만, Flash platform에 모두 사용할 수 있도록 컨버팅이 가능한 구조로 되어 있습니다. 다음 문서에서는 FABridge가 어떻게 사용되며, FABridge를 기반으로한 새로운 Javascript 통신 객체에 대해 다룰 것 입니다.

관련 문서 : FABridge (Adobe Lab)

위의 예제에 대한 Actionscript 파일 전체 소스
package
{
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.external.ExternalInterface;
	import flash.text.TextField;	

	public class FlashVarsExample extends Sprite
	{
		private var field:TextField;
		public function FlashVarsExample()
		{
			field = new TextField();
			field.autoSize = "left";
			addChild(field);
			
			var keyStr:String;
  			var valueStr:String;
			
			var paramObj:Object = LoaderInfo(this.root.loaderInfo).parameters;
			for (keyStr in paramObj) {
				valueStr = String(paramObj[keyStr]);
				field.appendText("\t" + keyStr + ":\t" + valueStr + "\n");
			}			
		}		
	}
}


저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02009/05/25 00:38

문서 원본 : Adobe 기술문서
작성된 날짜 : 2009-05
저자 : 강동혁(동강)
저자 소개 : Daum communications 에서 UI 개발 업무를 하고 있으며, Flash 커뮤니티에서동강이라는 닉네임으로 활동하고 있다. 뭐든지 사용하기 편해야 한다는 생각을 가지고 개발을 하고 있으며, 최근엔 새로산 자전거를 자주 못타고 다녀 아쉬워 하고 있다.


   <목차>

-Display 객체들의 다양화

-Display List구조의 변화

-객체의 심도 관리 향상(Depth에서 index)

-Depth방식에 비해 Index방식이 효율적인 점

-이벤트 모델의 변화



Flash를 하던 많은 사람들이 AS3.0이 나오고 나서 혼란스러웠던 이유는 두 가지이다.

첫 번째로 Flash에서 핵심이라고 할 수 있는 Display 객체생성을 정의하는 구조가 바뀐 점이고, 두 번째로는 사용자의 반응에 따라 이벤트를 발생 시켜 주는 이벤트 모델이 바뀐 점이다.

또한 AS2.0은 직관적인 문법이지만, AS3.0에서는 간단한 마우스 이벤트를 만들려고 할 때도 코드의 길이가 길어져 버려, AS2.0 사용자들에게 'Flash가 애니메이션 툴의 기능은 사라져 버리고 개발자를 위한 툴로 바뀌었다.' 는 원성을 듣기도 하였다.

하지만, AS3.0에서 코드를 길게 작성해야 이벤트를 만들 수 있다는 말은 AS3.0을 겉으로만 접한 사람들의 오해다. 오히려 AS3.0 은 이벤트 구조를 잘 설계하여 코드를 줄일 수 있고, 덩달아 SWF가 시스템에서 차지하는 메모리의 용량도 줄일 수 있는 장점을 안겨 주었다.

이번 장에서는 AS3.0 AS2.0에 비해 어떤 점이 달라졌고, AS3.0과 하위 언어와의 호환성에 대해 이야기해 보겠다.

 

-Display 객체들의 다양화

Flash 8까지의 AS1.0 2.0에서는 거의 대부분의 객체들은 MovieClip을 통해서 생성됐다.

여러 유형의 객체들이 MovieClip 객체 하나에 모두 포함됐기 때문에 불필요한 메모리와 시스템 리소스를 차지하는 원인이 되었다.

예를 들어 타임 라인이 필요 하지 않는 원을 만들거나, Flash에서 image를 불러와서 사용할 때도 MovieClip을 통해 객체를 만들었기 때문에, 만들어진 객체에는 항상 타임라인이 존재하였다. 이 타임라인이 불필요한 메모리를 차지했던 것이고, MovieClip에 있는 타임라인을 관리하기 위해 사용되었던 시스템 리소스가 증가하게 됐다. 하지만 AS3.0에서는 AS2.0에서 대부분 MovieClip으로 만들어졌던 Display List 객체들을 상황에 맞는 객체들로 만들 수 있도록 객체의 유형이 다양해졌다.

 

대표적인 유형은 MovieClip, Sprite, 그리고 Shape이다. Sprite MovieClip에서 Timeline을 제거한 객체이다. 타임라인을 제거하였기 때문에 MovieClip에 비해 메모리 및 리소스 사용을 줄일 수 있고, Timeline 외에는 MovieClip과 같은 속성 및 메소드를 가지고 있기 때문에 거의 비슷한 용도로 사용할 수 있다.

Shape Sprite와 비슷한 부분이 많다.(Graphics에 포함되어 있는 드로잉API를 이용할 수 있다.)

하지만, 자식 객체를 가질 수 없고 마우스 클릭 이벤트를 지원하지 않는 차이를 가지고 있다. Shape Sprite에 비해 오버헤드가 적고 Sprite가 지원하는 속성에 대한 메모리를 사용하지 않아도 되기 때문에 속도가 향상되고 메모리를 적게 사용한다.

그래서 마우스 이벤트가 필요없는 그래픽 객체를 만들고 싶을 때는 Shape를 사용하는 게 좋다.

각각의 객체를 하나씩 비교 한다면, 많은 차이가 없다. 하지만 객체의 수가 많아진다면, 덩달아 컴퓨터에 부담을 많이 주게 된다. 예를 들어 MovieClip Sprite가 컴퓨터 리소스를 차지하고 있는 양의 차이가 10이라고 하면, 이러한 객체들을 100개를 쓸 일이 생기게 되면 컴퓨터가 감당해야 할 리소스 부담은 MovieClip을 사용했을 때 Sprite보다 1000이 더 나게 된다.

덩치가 큰 프로그램에서 이러한 차이는 곧바로 퍼포먼스의 차이로 이어 지기 때문에, Timeline이 필요 하지 않은 객체는 Sprite로 정의 하는 습관을 가지고 있어야 한다.

 

- Display List구조의 변화

앞 절에서 자식 객체를 포함할 수 있는 객체는 Sprite이고, 포함할 수 없는 객체는 Shape라고 설명했다.

Sprite DisplayObjectContainer(표시객체컨테이너)이기 때문에 자식 객체를 가질 수 있고, Shape DisplayObject(표시객체)이기 때문에 자식 객체를 소유할 수 없다.

 

AS3.0에서 달라진 Display List의 구조는 DisplayObject를 기본으로 하여 구성된다.

SWF에서 사용자에게 보이게 되는 모든 객체들은 모두 DisplayObject 또는 DisplayObjectContainer의 상속을 받아서 만들어진 것이다. Display List의 구조는 아래와 같다.

그림 1 AS3.0 Display List 구조도

 그림에서 볼 수 있듯이 MovieClip, Sprite, Shape 모두 DisplayObject의 상속을 받아서 만들어진다. 하지만, MovieClip Sprite DisplayObjectContainer의 속성을 한 번 더 상속 받기 때문에 자식 객체를 포함하는 속성을 가지고 있다.

자식 객체를 소유할 수 있다는 의미는 바구니가 되어 이것, 저것을 담을 수 있다는 의미이다.

DisplayObjectContainer의 상속을 받은 객체 만이 addChild(담기) removeChild(꺼내기) 메소드를 사용할 수 있다. AS2.0에서는 아래 그림과 같은 구조로 객체들을 정의 하고 있다.


그림 2 AS2.0의 객체 구조도

 객체들을 AS3.0과 같이 상속에 의해 구현한 것이 아니라, Object를 통해 직접적으로 구현하고 있다. 그만큼 직관적으로 구조를 이해 할 수 있다는 장점이 있지만, 객체의 속성을 중복 구현하여, 효율이 떨어진다는 단점이 있다.

새로운 Display List Flash의 가장 핵심이라고 할 수 있다.

Flash에서 사용되는 모든 기능들은 Display List를 통해 구현이 되고 사용되기 때문이다. 그 만큼 중요한 부분을 차지하고 있기 때문에 비교적 이해하기 어려운 내용을 담고 있다.

 

- 객체의 심도 관리 향상(Depth에서 index)

 

Flash Display List에는 심도가 존재한다.

Layer와 별개로 같은 레이어 상에 존재하는 객체들도 위, 아래가 존재하여 위에 있으면 보이고 아래 있으면 위에 있는 객체에 가려서 안보인다. AS1.0 2.0에서는 다음과 같은 메소드를 사용하여 객체의 위, 아래를 조절 하였다.

 

AS2.0에서 객체 심도를 관리하기 위한 메소드

-getDepth() : Number

-getNextHighestDepth() : Number

-getInstanceAtDepth(depth: Number) : MovieClip

-swapDepths(target: Object) : Void


AS3.0
에서는 Display List 구조가 바뀜에 따라, 위와 같은 메소드는 모두 아래와 같이 바뀌었다.


AS3.0에서 객체 심도를 관리 하기 위한 메소드

getDepth() -> getChildIndex()

getNextHighestDepth() -> 직접적으로 대칭 되는 메소드는 없다. 하지만 구현 가능하다.

getInstanceAtDepth() -> getChildAt();

swapDepths() -> addChildAt(), setChildIndex(),swapChildren(), swapChildredAt() 


 

메소드 변경에 대한 더 자세한 내용은 Adobe에서 제공하는 AS2.0마이그레이션을 보면 자세히 기술 되어있다.

AS2.0에서 getNextHighestDepth()를 이용하여, 가장 높은 Depth를 할당하여 다른 객체들 보다 위에 위치하게 함으로서 사용자들에게 보이게 한다거나, swapDepths를 이용하여 이미 존재하고 있는 객체들의 Depth를 바꿔서 객체들끼리 가려지는 정도를 조절하였다.

AS2.0에서 적용되어 있던 심도 관리 개념은 Depth(깊이) 였지만 AS3.0에서는 메소드 이름 뿐만 아니라, 객체의 심도를 관리하는 개념 자체가 바뀌었다.

 

AS2.0에서는 Depth(깊이)개념을 사용하였다.

깊이란 순서가 없이 사용자가 지정한 깊이에 객체를 올려놓을 수 있음을 뜻한다. 사용자가 지정하는 깊이는 음수든 양수든 상관없고, 지정한 값들 사이에 빈 공간이 존재해도 상관없다. 아래 그림이 Depth의 특징에 대해 말해 주고 있다.


 

1) -1 depth부터 2 depth까지 객체들이 놓여 있다.

2) 100 depth에 객체를 추가한다. 100 depth는 추가되어, 존재하지만, 3~99depth까지의 공간은 빈 공간으로 남아 있다.

3) removeMovieClip()을 통해 1 depth에 있는 객체를 제거하면 1 depth는 빈 공간으로 존재하게 된다.

4) 2 depth에 객체가 있음에도 불구하고, 2 depth에 새로운 객체를 올려놓게 되면 기존에 있던 객체는 사라지게 된다.

 이렇게 AS2.0에서는 Depth로 절대적인 위치를 결정 하였다. 하지만 AS3.0에서는 Index라는 개념을 사용하여, 객체의 위치를 상대적으로 결정하도록 바뀌었다.

Index는 순서라는 의미이다.

Display List에 보여지는 객체들은 모두 순서가 부여 되고, 순서가 높을수록(숫자 크기가 클수록) 위에 위치하게 됩니다. 순서는 양수 값만이 가능하고, 0부터 차례대로 부여하게 된다. Depth처럼 2depth 다음에 100depth가 올 수가 없고, 빈 공간이 존재하지 않는다. 아래 그림이 Index의 특징에 대해 말해 주고 있다 



1) 0 index부터 3 index까지 객체들이 놓여 있다.

2) 100 index에 객체 추가를 시도하였지만, 에러가 발생한다.

3) 1 index에 객체를 추가하니, 기존의 index들이 한 칸씩 위로 올라가며 재정렬된다.

4) 3 index에 있는 객체를 삭제하니, 4 index에 있던 객체가 3 index로 내려오며 재정렬된다.

이렇게 AS3.0에서는 index를 이용해 상대적으로 위치를 정하는 방식으로 변경하였다.


 

-Depth방식에 비해 Index방식이 효율적인 점

Index방식은 객체에 순서를 부여해서, 부모 객체에서 자식 객체를 탐색할 때, 매우 빠르게 조회할 수 있게 해 준다. Index들은 단일 배열로 만들어져 getChildIndex() getChildAt()으로 탐색하는 것을 도와준다. 예를 들어 다음과 같은 코드를 사용하면, 파라미터로 넘어온 객체의 자식객체에 대해 빠르고, 쉽게 알 수 있다.



 그에 비해 Depth방식은 깊이의 위, 아래는 있어도, 그 위, 아래 정보가 단일 배열로 정리가 되어 있지 않기 때문에 탐색에 오랜 시간을 소요된다.
예를 들어, 1쪽부터 200쪽 분량의 책이 있다고 가정해보자. 그런데 갑자기 계획이 변경되어 100쪽 부근에 50쪽 정도의 새로운 챕터를 넣어야 한다.

Index 방법에서는 우리가 현실 생활에서 대처하는 방법대로, 새로운 챕터는 101쪽부터 번호가 매겨지고, 뒤에 있던 내용들은 50쪽이 끝나면 이어져서 250쪽 짜리 책이 완성된다.

하지만 Depth 방식으로 하면 문제가 발생한다. 기존의 200쪽 분량의 책에 50쪽 분량의 새로운 챕터를 넣으려면 기존의 100쪽부터 150쪽까지가 새로운 내용으로 덮어 씌어지게 된다. 이런 상황을 막기 위해 미리 swapDepths()를 통해 100쪽부터 200쪽까지를 150쪽부터 250쪽까지로 옮기고 빈 공간에 새로운 챕터를 넣으면 되지만, 시간과 리소스 낭비가 많아지게 된다. 더구나, 여기에서는 50쪽만 추가하는 것이었지만, 50쪽이 100쪽이 될 수도 있고, 1000쪽이 될 수도 있다. 이와 같이 Index방식은 Depth방식에 비해 사용하기 편하고, 휠씬 효율이 높은 심도 관리 방법이라고 말할 수 있다.

 

- 이벤트 모델의 변화

Flash에서 중요한 부분을 차지하고 있는 이벤트 모델 역시 바뀌었다. 이전 버전의 AS에서는 이벤트를 처리하는 방법이 여러 가지가 있었다


AS2.0에서의 이벤트 핸들링 방법

- on() 이벤트 핸들러와 onClipEvent()핸들러 : 객체 안에 코드를 입력하여 해당 객체에 대한 이벤트를 발생시키는 방법이다. 쉽게 사용할 수 있지만, 객체 안에 있는 코드를 찾기가 어려워서 프로젝트 협업 작업에 어려움이 발생한다.

- 콜백 함수 이벤트 핸들러 : 객체. onRelease XML.onload와 같이 객체에 직접 콜백 함수를 등록함으로써 발생시키는 방법이다. 지정된 이벤트에 대해 콜백 함수 하나만 사용할 수 있다. 예를 들어 AS2.0에서 아래와 같은 코드를 실행하면 두 번째 콜백 함수만이 동작 하게 된다.



- 이벤트 리스너 : addListener() addEventListener()를 이용하여 발생시키는 방법으로 리스너 객체와 함수를 만든 후에 리스너를 등록해야 하므로 번거롭지만, 콜백 함수와는 달리 해당 이벤트에 여러 개의 리스너를 만들어서 모두 사용할 수 있다.




여러 개의 이벤트 처리 방식이 상황에 따라 사용되었지만, AS3.0에서는 하나로 통일되었다. 하나로 통일된 새 이벤트 모델은 DOM Level3 이벤트를 기초로 하고 있다.

DOM Level3 이벤트는 기존의 이벤트 모델보다 더 빠르게 이벤트를 발생시킨 객체를 찾아낼 수 있고, 그에 따른 이벤트를 호출해준다. 또한 이전 버전의 이벤트 모델은 이벤트 흐름을 가지고 있지 않았다. 무조건 이벤트를 발생시킨 객체만이 콜백 함수나 이벤트 리스너를 호출 할 수 있었지만, AS3.0에서는 이벤트 흐름에 연관된 객체들은 모두 이벤트 리스너를 호출할 수 있다. 예를 들어 그림 3과 같이 A, B 객체가 있고 B 객체에 마우스 이벤트를 등록했다면, A B 두 객체 모두 이벤트 흐름 안에 있기 때문에 이벤트 리스너를 호출할 수 있다.








AS3.0을 시작하는 분들이 시작부터 어렵다고 느끼는 이유 중 하나가 바로 이벤트 모델 때문이다.

AS2.0에서는 그림4 코드와 같이 단 3줄에 끝나는 것이 AS3.0에서는 이벤트 리스너를 추가하고, 이벤트 핸들러를 만들고, 이벤트 핸들러에 해당 이벤트(MouseEvent)에 대한 파라미터를 넘기는 작업을 해야 하기 때문에 비교적 복잡하게 보이기 때문이다. 하지만 사용해 보면 휠씬 효율적이라는 것을 느낄 수 있다.

AS3.0의 이벤트모델만이 가지고 있는 이벤트 흐름을 이용하여 복잡한 이벤트 발생 구조도 단순화 시킬 수 있으며, 이벤트 리스너의 등록과 제거 과정을 통해, 효율적인 메모리 관리와 객체지향 코드를 구현할 수 있다.

 

글을 마치면서

1부와 2부에 걸쳐서 AS3.0을 왜 사용해야 되고, 어떻게 사용해야 하는가에 대해 다루었다. 많은 사람들이 Flash platform을 도입하면서 Flash 시장도 많이 넓어 지고 있지만, 한간에서는 아직도 “Flash는 배너를 만드는데 사용하는 애니메이션 툴이다.” “Flash를 사용하면 느려진다.” 라고 생각하는 사람들이 있다.

하지만, 이러한 걱정들은 구시대의 산물이 되어 가고 있고, Flash platform이 단지 표현(User Interface)을 위한 수단만이 아니라, 표현과 퍼포먼스를 모두 만족시킬 수 있는 도구로서 나아 가고 있다.

AS3.0은 좀 더 사용자를 만족 시키기 위해 반드시 이용해야 하는 언어이다.


관련 문서: 왜 Actionscript3.0을 사용해야만 하는가?



저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02009/03/30 16:57
package{                

	import flash.display.MovieClip;		
	import flash.display.Sprite;		

	/**	 * @author kang	 */		

	public class ResourceTest extends Sprite {		

		public function ResourceTest() {						

			var mc : MovieClip = new MovieClip();                        

			addChild(mc);			

			mc.addEventListener(Event.ENTER_FRAME, onEnterframeHandler);		

		}	
		private function onEnterframeHandler(event : Event) : void {			

			trace("execute event");		

		}	

	}

}
위와 같은 코드가 있다고 하자. mc는 Event.ENTER_FRAME 이벤트를 가지고 있는 객체 이다. 하지만 어떠한 상황이 발생하여 mc를 메모리 해제를 할 경우가 발생 하였다. 많은 AS개발자들이 아래와 같은 코드를 입력하여 메모리를 해제 할 것이다. 


private function disposeMc(_mc:MovieClip):void{	
	if(_mc.parent != null)		
	_mc.parent.removeChild(_mc);		
	_mc = null;
}
mc 라는 객체의 모든 참조 값이 제거 되었으므로 mc는 Garbage collection의 수집대상이 될 것이다. 그리고 곧 메모리 상에서 사라질 것이다. 하지만 이 코드에는 문제가 있다. 이벤트를 등록했던 객체는 사라졌지만, 등록된 이벤트 자체는 사라지지 않았다. 그래서 output 창에는 execute event 메시지가 계속호출 될 것이다. 많은 Flash 개발자들이 잘못 알고 있는게 해당 객체를 제거 하면 그 객체에 등록 되어 있던 이벤트들도 제거 된다고 생각한다. 이는 아주 심각한 문제를 발생 시킬수 있는 원인을 제공한다. 컴퓨터가 느려 진다던지, 메모리 사용량 증가로 프로그램 자체나, 브라우져가 멈춰 버리는 일이 발생 할 수도 있다. 그러므로 객체를 메모리 상에서 제거 하기 전에 반드시 removeEventListener로 이벤트를 반드시 해제 해야 한다. addEventListener 의 파라미터로 약한참조를 해 주면 객체가 제거 되면 그 객체에 등록되어 있는 이벤트들도 제거 되지만 약한 참조로 했을때 불편한 점이 많기 때문에 기본값은 강한참조로 되어 있다. 
addEventListener()메서드

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

useWeakReference는 false 값이 기본으로 강한 참조이다. 하지만 이 파라미터를 true로 넘겨 주면 약한 참조가 된다. 위에서 언급했듯이 약한 참조로 등록된 이벤트들은 해당 객체가 제거 되었을때 같이 메모리에서 제거 된다.



이와 비슷한 경우가 MovieClip을 사용할때 발생할 수 있다. 예를 들어 다음과 같이 1~40frame까지 계속
 

루프를 도는 MovieClip이 있다고 하자. 이렇게 루프를 돌고 있는 도중 갑자기 이 무비 클립을 메모리 상에서 제거 하면 어떤 일이 발생 할까?? 결과는 위 ENTER_FRAME 이벤트에서와 같이 계속 루프를 도는 현상이 발생한다. 계속 루프를 돈다는 것은 그 만큼 CPU를 사용한다는 말이 되고, 메모리 낭비를 하고 있다는 말이 된다. 하지만, 이걸 인식 하지 못하고 그냥 MovieClip만 제거 하면 해당 무비 클립에 대한 모든 것들이 메모리 상에서 없어 진다고 잘 못 생각하고 있는것이다. 그러므로 위와 같이 MovieClip을 사용한 작업을 할때는 꼭 MovieClip을 stop() 해 준뒤에 메모리를 해제 해 줘야 한다. stop 해주지 않고 메모리를 해제 했다간, 모든 참조 값들이 사라 졌기 때문에 해당 타임라인을 멈출 수 있는 방법이 존재 하지 않는다. 

이러한 리소스 관리 문제는 위의 두가지 경우에만 국한된 것이 아니다. AS에서 제공하는 Sound 나 Loader를 이용한 이미지로드 및 SWF 로드 또는 Video 작업을 할때 모두 위와 같은 메모리 문제가 발생 할 수 있다. 이 사실을 항상 유념하고 Flash 작업을 해야 한다. 더 자세한 AS3에서의 메모리 관리 방법은 다음 링크를 통해 확인 하길 바란다. 




예제다운로드

아무리 여러 테스트가 있어도, 정답은 "상황에 따라 맞는 방법을 쓴다." 이네요.



저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02009/03/30 10:57
Flash에서 MovieClip을 이용한 작업을 하다 보면 MovieClip에서 일어나는 동작을 코드상에서 알아 내서 이벤트를 발생 시켜야 하는 경우가 있다. 예를 들어 다음과 같은 Timeline이 있을때 재생헤드가 30 frame에 도달 할때 이 동작을 알아 내어 코드를 실행 시켜야 하는 경우가 있다.


이럴 경우 30 frame에 dispatchEvent를 사용하여 MovieClip을 가지고 있는 객체에 이벤트를 전달해 주는 방법을 사용할 수 있다.
dispatchEvent(new Event("playheadreached");
MovieClip에서 전달한 이벤트는
this.addEventListener("playheadreached",headreachedHandler);
private function headreachedHandler(e:Event):void{ // 코드 실행 }
를 통해 전달 받을 수 있다. 하지만, 이 방법은 반드시 Timeline에 코드를 입력해야만 가능한 방법이다. 많은 Flash 개발자들이 Timeline상에 코드를 입력하는 방법이 매우 비효율 적이라는 것을 느끼고 있을 것이다. 왜냐 하면 타입라인에 입력된 코드를 찾기 위해 낭비 하는 시간이 굉장히 많기 때문이다. 그래서, AS3.0에서는 addFrameScript 라는 메소드를 제공해 주고 있다.
adobe에서 제공하는 "AS3.0 Document"에는 이 메소드가 나와 있지 않지만 상당히 유용한 메소드 이다. 사용 방법은 다음과 같다.

package {

	import flash.display.MovieClip;

	public class FrameTest extends MovieClip{
		public function FrameTest()
		{
			this.addFrameScript("29",onFrameHandler);
			
		}		
		private function onFrameHandler():void
		{
			trace("reached");
		}
	}
	
}
Document class를 FrameTest라고 지정했을때, MainTimeline에 있는 재생헤드가 30frame에 위치하면 onFrameHandler 함수가 호출 된다. 주의해야 할 점은 30frame에 위치 했을때 함수를 실행 시키기 위해서는 this.addFrameScript("29",onFrameHandler); 게 해야 한다는 점이다. 29frame을 지난 직후에 메소드가 실행 되는 방식이기 때문이다. 또한 해당 타임라인이 존재 하지 않으면 이 메소드는 실행 되지 않는다.
addFrameScript는 상당히 직관적이고, 간편한 타임라인 컨트롤 방법을 제공해 주고 있다. 하지만 좀 더 간편한 타임라인 컨트롤을 지원해 주는 클래스가 있어 소개하려고 한다. 얼마 전에 Adobe Flash professional 커뮤니티에서 흥미 로운 기술 문서를 보았다. 이 기술 문서의 내용이 오늘 소개하려고 하는 클래스 이다.

타임라인에서 일어 나는 동작을 알려주는, TimelineWatcher

TimelineWatcher는 addFrameScript와 같은 타임라인 이벤트를 관리 해 주는 클래스 이다. EventDispatcher를 상속 받고 있기 때문에 내부적으로 dispatchEvent를 통해 타임라인의 위치를 전달해 주고 있다.


예를 들어 위와 같은 타임라인 모션이 있다고 하자. 그리고 Document class는 WatcherTest로 지정 한다.

package {
	
	import flash.display.MovieClip;
	import flash.text.TextField;
	
	import com.refunk.events.TimelineEvent;
	import com.refunk.timeline.TimelineWatcher;
	
	public class WatcherTest extends MovieClip {
		
		public var ball:MovieClip;
		
		private var timelineWatcher:TimelineWatcher;
		private var output:TextField;
		private var loops:uint = 0;
		
		public function WatcherTest() {
			super();
			stop();
			output = new TextField();
			addChild(output);
			timelineWatcher = new TimelineWatcher(this);
			timelineWatcher.addEventListener(TimelineEvent.LABEL_REACHED, handleTimelineEvent);
			timelineWatcher.addEventListener(TimelineEvent.END_REACHED, handleTimelineEvent);
			gotoAndPlay(1);
		}

		private function handleTimelineEvent(e:TimelineEvent):void {
			switch (e.type) {
				case TimelineEvent.LABEL_REACHED:
					output.text = "label: " + e.currentLabel + "\nloops: " + loops;
					break;
				
				case TimelineEvent.END_REACHED:
					loops++;
					if (loops > 10) {
						stop();
						timelineWatcher.removeEventListener(TimelineEvent.LABEL_REACHED, handleTimelineEvent);
						timelineWatcher.removeEventListener(TimelineEvent.END_REACHED, handleTimelineEvent);
						timelineWatcher.dispose();
						timelineWatcher = null;
					}
					break;
			}
		}
	}
}
		
우선 TimelineWatcher를 생성하고, 파라미터로 MovieClip 객체를 넘긴다. 그리고 TimelineWatcher에 이벤트리스너를 등록한다. TimelineWatcher가 캡쳐 할 수 있는 이벤트는 두가지 이다.

TimelineEvent.LABEL_REACHED : 타임라인에 Label 값이 있는 곳에 재생헤드가 위치해 있으면 호출 되는 이벤트 이다.
TimelineEvent.END_REACHED : 재생헤드가 타임라인의 처음부터 끝까지 돌았을때 호출되는 이벤트이다.
 
 위의 코드에서는 LABEL_REACHED END_REACHED 모두 handleTimelineEvent를 호출 메소드로 등록하고 있다. 같은 메소드라도 전달되는 이벤트의 종류가 같기 때문에 e.type을 이용하여 이벤트를 구분하고 각각에 맞는 이벤트를 처리 할 수 있다. 그리고 TimelineEvent.END_REACHED 이벤트를 이용해서 타임라인이 10번 돌았을때 LABEL_REACHED , END_REACHED removeEventListener 해주고 TimelineWatcher의 dispose 메소드를 이용하여 내부적으로 돌아 가고 있던 ENTER_FRAME 이벤트를 해제해 준다. (TimelineWatcher.as 를 참고)  ENTER_FRAME 이벤트 자체가 상당히 CPU 사용량이 크기 때문에 이벤트가 모두 종료된 후에 dispose 메소드를 반드시 호출해 줘야 한다. addFrameScript가 내부적으로 어떻게 구현되어 있는지는 모르겠으나, TimelineWatcher가 보다 더 유용하게 사용될 수 있는 클래스인거 같다.

  Flash는 타임 라인이라는 강력한 기능을 가지고 있다. 타임 라인이 존재 한다는것은 표현을 좀 더 자유롭게 할 수 있는 도구를 가졌다고 할 수 있다. 그러나, 디자이너는 너무 타임라인에 의존하는 프로젝트를 진행하고 있고, 개발자는 Flash를 Flash답게 사용하지 않고, Flex같이 사용하는 경향이 강하다. 타입 라인을 좀 더 자유롭게 사용할 수 있도록 도와주는 addFrameScript나 TimelineWatcher를 통해 좀 더 효율적인 업무 협업을 할 수 있겠다는 생각이 들었다. 익숙하지 않은 상태에서 다루기는 쉽지 않겠지만, 간단한 AS 구조이니 만큼 TimelineWatcher 나 TimelineEvent 클래스 구조도 살펴보는걸 추천한다.



TimelineWatcher에 대한 기술문서

예제 다운 로드
저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02009/03/17 20:38
Javascript나 Actionscript에서 긴 길이의 문자열을 고정된 너비의 TextField에 집어 넣으려면 substr(startIndex,length) 이나 substring(startIndex,endIndex)을 사용합니다. 원하는 문자의 길이 만큼 파라 미터를 넘겨서 처리 하면 손 쉽게 해결 됩니다.
var field:TextField = new TextField();
addChild(field);   
var str:String = "Flash Actionscript3.0 플레시 액션스크립트";
field.text = str.substring(0,20);
하지만, 위와 같은 방법에는 치명적인 단점이 있습니다. 영어와 한글의 너비값이 차이가 있기 때문에, 영문의 10글짜와 한글의 10글짜의 너비는 큰 차이가 나게 됩니다. 하나의 문자로만 사용하는 경우에는 상관없겠지만, 항상 한글만 쓰거나 하는 경우는 극히 드물고, 특수문자도 
하나의 문자로 인식 하기 때문에 substring 이나 substr 만 가지고는 해결 할 수가 없습니다. 그래서 textfield에 문자를 미리 입력 한 후에 textWidth 값을 체크하여 어느 정도의 문자까지 입력 할수 있는지 확인하는 메소드를 만들어 보았습니다. 
function longStringDot(_field : TextField,_str:String, _width : int):int
{
      var  str_len:int;
      for(var i:int =0; i < _str.length; i++ ){
          _field.text = _str.substr(0,i);
          if(_field.textWidth > _width){
	str_len = i;			
	break;
          }				
      }					
      return (str_len == 0)? _str.length : str_len;			
}
보통 substring 을 이용하여 아래와 같이 코딩을 하지만,
var str:String = "";
var maxLen:int = 20;   // 문자열이 들어 가는 공간크기를 일일히 맞춰줘야 하는 번거로움이 있다.
var feild:TextField = new TextField();
if(str.length > maxLen)
	feild.text = str.substring(0,maxLen);
else
	feild.text = str;
var str:String = "";
var maxWidth:int = 100;     
var feild:TextField = new TextField();
feild.text = str;
if(feild.textWidth > maxWidth)
	feild.text = str.substring(0,longStringDot(feild,str,maxWidth));		
메소드의 파라미터로 TextField를 넘겨 주는 이유는 TextField가 가지고 있는 TextFormat에 따라 textWidth 값이 달라지기 때문 입니다. 요즘 들어 String을 처리 해야 하는 일이 많아 지네요. 좀 정리좀 해야 겠습니다.
저작자 표시 비영리 변경 금지
Posted by 동강
Actionscript3.02009/03/15 17:45
var money:Number = 100000000;
trace(numberToDot(money));
function numberToDot(num:Number):String { 
       var str:String= String(num);	// 숫자값을 받아서 String유형으로 변경한다. 
       var str_arr:Array = new Array();       
       for (var i:int = 0; i < str.length; i++) {        
        // str에 있는 문자값을 charAt을 이용하여 하나씩 불러온 뒤, 배열에 넣는다.    
        // i%3 이 0일때 마다 쉼표를 삽입하여 문자들을 3개씩 나눈다.    
              if ( i % 3 == 0 && i != 0) { 
                  str_arr[i]=str.charAt(str.length-1-i)+",";
               } else {        
                  str_arr[i]=str.charAt(str.length-1-i);     
               }
         }        // Array의 reverse 메소드를 이용하여 배열의 순서를 뒤집는다.        str_arr.reverse();        // 배열의 요소를 문자열로 반환 한다.      
         return str_arr.join("");
}

// 결과
100,000,000

 자주 사용하는 코드 이지만, 갑자기 다시 짜려고 하면 귀찮니즘이 몰려 오기 때문에 종종 코드를 찾는데 시간을 소비 하곤 합니다.  AS3.0으로 제작되어서 javascript에서는 쓰일수 없지만, Actionscript나 Javascript에서 공통으로 사용할 수 있는 라이브러리를 만드는 것도 재미 있을것 같네요.


 

저작자 표시 비영리 변경 금지
Posted by 동강
TAG AS3.0