본문 바로가기

Actionscript3.0

[AS3] 외부에서 로드된 객체 복제(재사용)


 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 를 통해 객체를 생성하여 사용 합니다.


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