[Flex]Module, ModuleManager, ModuleLoader 분석
[공지]이미지나 링크가 깨졌다면 댓글 부탁드립니다.
대형 프로젝트에서 모듈화는 꼭 필요한 작업이다
나는 [Flex, AIR] 프로그램 크기를 줄여보자 3 - 모듈화를 하자. RSL과 Module이라는 제목으로 일전에 글을 썼다. 모듈화를 위해서 RSL(Runtime Shared Library)를 제작하여 사용하는 방법과 Module또는 ModuleBase를 이용하여 만들어진 모듈을 ModuleLoader와 ModuleManager를 통해 원하는 시점에 로드하는 방법이 있다는 것을 간단하게 소개했다.
지금부터 Module이 어떻게 구성되어 있고 어떻게 운영되는지 관련 SDK를 분석하면서 이해해 보도록 하고자 한다. 이러한 분석이 필요한 이유는 Module을 제대로 쓰기 위함이다. 지피지기면 백전백승이라고 하지 않았는가? 제대로된 모듈화 기법을 알아야 제대로된 모듈 프로그램을 제작하고 운영할 수 있는 것이라 생각한다.
1. Flex SDK에서 제공하는 모듈관련 Class들
윈도우 환경에서 기본 경로 변경없이 Flex Builder 3를 설치했다면 아마도 C:Program FilesAdobeFlex Builder 3...mxmodules 에서 Module관련 클래스들을 볼 수 있을 것이다. Module관련 Event는 C:Program FilesAdobeFlex Builder 3...mxevents 에서 ModuleEvent.as를 확인할 수 있겠다.
겉으로만 본다면 아래와 같은 클래스들을 확인할 수 있다.
mx.events.ModuleEvent
mx.modules.ModuleManagerGlobals
mx.modules.ModuleManager
mx.modules.ModuleLoader
mx.modules.ModuleBase
mx.modules.Module
mx.modules.IModuleInfo
대충 구분해 본다면 이벤트(ModuleEvent), 모듈관리자(ModuleManager, ModuleLoader, ModuleManagerGlobals), 모듈(Module, ModuleBase), 모듈정보 인터페이스(IModuleInfo) 정도가 되겠다.
2. Module과 ModuleBase 살펴보기
레퍼런스를 살펴보면 mx.modules.ModuleBase와 mx.modules.Module을 통해 Module을 제작할 수 있다고 한다. 잠깐 두개의 소스를 살펴보자.
mx.modules.Module
mx.modules.ModuleBase
보자마자... 헉이였다.
너무 단순한 것이 아닌가?
Module은 UIComponent로부터 만들어진 LayoutContainer이다. 그리고 ModuleBase는 EventDispatcher를 확장한 것이다. 이로부터 확실히 알 수 있는 것은 Module은 UIComponent 계열의 모든 컴포넌트에 자식으로 추가될 수 있다는 점이다. 하지만 ModuleBase는 그렇지 못하다는 것이다. 그러므로 모듈을 제작할 때 단순한 기능이나 데이타에 관련된 자료만 사용하는 경우라면 ModuleBase를 이용해 모듈을 만들고 시각적으로 화면에 보여져야 하는 경우라면 Module을 이용하여 제작하면 되겠다.
3. ModuleEvent를 살펴보자.
위의 소스는 ModuleEvent이다.
Event 타입은 Error,Progress,Ready,Setup,Unload 가 있고 ProgressEvent를 확장해서 만들어졌다는 것을 알 수 있다. 즉, Main Application에서 Module을 로드하고 그 과정을 살펴보기 위해 ProgressEvent를 확장했다는 것을 알 수 있다. 특별히 생성자 마지막 인수에 module:IModuleInfo가 추가되어 있다. 이것을 통해 이벤트 리스너(청취자)함수에서 IModuleInfo에 관련된 모듈정보를 얻어울 수 있다는 것을 알 수 있겠다.
4. IModuleInfo 는 뭐지?
말그대로 모듈 정보에 관련된 인터페이스이다. ModuleEvent의 마지막 인자였던 건데... 위에서는 주석문을 다 지웠지만 실제 소스의 주석을 보면 대충 어떤 역할을 하는 것인지 알 수 있다. 이 인터페이스를 가지고 모듈의 상태를 알 수 있고 또한 모듈을 로드하고 언로드할 수 있겠다. 이에 대한 쓰임새는 다른 코드를 더 살펴보며 알아낼 수 있을 것이다.
5. ModuleLoader를 보자.
ModuleLoader는 VBox 를 확장해서 만들었다. 그러므로 비주얼 컴포넌트이라는 것을 알 수 있다. ModuleLoader위에 바로 모듈을 로드해서 addChild를 할 것이라 예상할 수 있다.
그 부분을 찾아보니 바로 moduleReadyHandler() 이벤트 핸들러에 있다는 것을 알 수 있었다. 즉, child = module.factory.create() as DisplayObject;로 module로 부터 자식을 만들어 addChild(child) 함수로 Loader에 add하고 있다는 것을 볼 수 있다.
그럼 module.factory.create에서 module은 어디서 만드는 것인가?
바로 loadModule() 함수를 보면 알 수 있다. loadModule() 함수는 모듈을 로드하는 부분으로 module = ModuleManager.getModule(url);로 module을 얻어오고 module.load() 함수를 통해 모듈을 로드한다. 이때 각종 이벤트 리스너가 등록되어 모듈의 로드상황을 알 수 있다.
그런데 loadModule()함수를 자세히 보면 url이 null이고 child또는 module이 이미 있는 경우는 이 함수가 실행되지 않는다. set url() 함수를 통해 자동으로 loadModule()을 호출하는 것을 확인할 수 있는데... set url()에서 같은 url인 경우 무시하고 loadModule()에서 url이 null이면 무시한다. 그리고 이미 생성된 module이 있는 경우 url이 다른 경우 module.release()를 통해 모듈을 풀어놓고, child도 removeChild()한다.
정리하면 ModulerLoader는 중복된 URL은 무시하면서 모듈이 ModuleLoader에 이미 로드되어 있는 경우 loadModule()함수로 다시 로드할 수 없다. 만약 모듈이 로드된 상태에서 다른 URL을 set하면 이 모듈은 release되고 새로운 모듈이 load된다.
한가지 짚고 넘어갈 사항은 set url()에는 module.release()가 있고 unloadModule()에는 module.unload()가 있다는 것을 확인할 수 있다. 추측해보면 release()는 말그대로 module을 놓는다는 말로 메모리상에서 지우지는 않겠다는 것으로 생각된다. module = ModuleManager.getModule(url); 에서 url을 통해 ModuleManager로부터 모듈의 인스턴스를 받아오는 것을 볼 수 있듯이 release를 한 경우라면 다시 getModule()을 통해 모듈을 메모리상에서 읽어올 수 있다.
하지만 unload()를 하면 ModuleManager에 등록된 모듈 자체를 메모리상에서 해제해버린다. 그러므로 다시 set url()을 할 경우 다시 ModuleManager.getModule(url);를 호출할 때 다시 모듈을 로드하게 된다.
ModuleLoader는 결국 ModuleManager의 일부 기능이 추가된 확장판이다.
ModuleLoader는 MXML을 통해 쉽게 모듈을 로드하고 보여주기 위한 것이지만 세부적인 모듈 컨트롤은 힘들다는 것을 알 수 있다. 또한 ModuleManager를 통해 load를 미리 했다면 ModuleLoader 로 로드하는 같은 url의 모듈의 load는 취소된다는 것을 유추해볼 수 있겠다.
6. ModuleManager를 살펴보자.
앞에서 ModuleLoader가 내부적으로는 ModuleManager에서 제어하고 있다는 것을 확인할 수 있었다.
그럼 ModuleManager를 살펴보자.
ModuleManager.as를 보면 ModuleManager 클래스외에 ModuleManagerImpl, ModuleInfo, FactoryInfo, ModuleInfoProxy 클래스들이 정의되어 있다. 이 클래스들은 직접 쓰여지지 않으며 ModuleManager의 보조 역할을 담당한다.
ModuleManager 클래스
ModuleManager 클래스에는 두개의 public 함수가 있다. getModule()과 getAssociatedFactory()이다.
getModule()는 인자로 넘어온 URL을 이용하여 IModuleInfo 형의 인스턴스를 반환받고 getAssociatedFactory()는 인자로 넘오온 object가 이미 등록되어 있는 모듈의 applicationDomain에 정의되어 있는지 확인하여 정의 되어 있다면 해당 IFlexModuleFactory 인스턴스를 반환한다.
ModuleManager는 private 함수인 getSingleton()로 부터 ModuleManagerImpl 클래스를 인스턴스화 해서 사용하는 것을 볼 수 있다. 단 한번만 생성하게 만들었는데, 이는 ModuleManager의 getModule() 또는 getAssociatedFactory()를 호출할 때 처음으로 ModuleManagerImpl의 인스턴스를 생성하여, 사용하지도 않았는데 메모리에 미리 로드하는 것을 방지하도록 만들어졌다.
ModuleManagerImpl 클래스
위 코드에서 유심있게 살펴볼 것은 바로 getModule()함수이다.
moduleList에 같은 url이 없는 경우에만 ModuleInfo 인스턴스를 만들어 추가하는 것을 볼 수 있다. 즉, 1개의 URL에 여러개의 ModuleInfo의 인스턴스가 만들어지는 것을 방지하고 있다.
재미있는 점은 return 형이 IModuleInfo인데... ModuleInfo의 객체를 반환하는 것이 아니라 생성자에서 ModuleInfo의 인스턴스를 받아 생성한 ModuleInfoProxy()의 인스턴스를 반환한다. 이것이 의미하는 바는 무엇일까? 금방언급한 것과 동일한 이유이다. 즉, 1개의 URL에 한개의 Module만 사용하도록 하기 위한 것이다. ModuleInfoProxy에 ModuleInfo의 인스턴스를 넘기는 것은 실제로는 한개의 Module을 가지고 여러개의 Module을 로드해서 사용하는 것처럼 쓰기 위해서이다. 이름처럼 ModuleInfo의 대리자 역할을 담당한다.
그럼 여기서 ModuleInfoProxy가 어떻게 만들어졌는지 살펴보자.
ModuleInfoProxy 클래스
클래스 선언부분을 살펴보면 class ModuleInfoProxy extends EventDispatcher implements IModuleInfo 이다.
초반에 언급한 IModuleInfo를 Implements하고 있다.
생성자를 보면 ModuleInfo를 인자로 받아 this.info = info;로 사용하고 있다.
모든 함수를 살펴보면 IModuleInfo에서 만들어진 함수를 정의하고 있으면서 info를 참고하고 있다는 것을 알 수 있다.
load()함수를 보면, ModuleInfo의 상태 변수인 error, loaded, setup 등의 설정값에 따라 load를 시행하고 있다. 가령, 로드가 된 상태라면 따로 ModuleInfo의 load함수를 호출하지 않고 SETUP, PROGRESS, READY 이벤트 강제로 발생시켜 꼭 로드하고 있는 것과 같은 동일한 상황을 만들어 주고 있다.
ModuleInfo 클래스
이제 마지막으로 ModuleInfo 클래스를 분석해본다.
생성자의 인자는 url이 된다. 그리고 load()함수를 호출하면 이 URL에 해당하는 모듈을 로드하게 되어 있다.
load()함수를 살펴보자.
재미있게도, 우리가 너무나도 잘 알고 있는 Loader()클래스를 이용해서 모듈을 로드하고 있다는 것을 알 수 있다. 즉, 결국 ModuleLoader나 ModuleManager는 이 Loader를 통해 만들어진 것이라 생각하면 되겠다. Loader로도 충분히 Module을 로드하여 사용할 수 있지만 쓰기가 불편하므로 ModuleManager와 ModuleLoader가 만들어진 것이다.
좀더 살펴볼 것은 release()함수와 unload()함수에 관련된 것이다. 여기에 구체적으로 구현되어 있는데, 살펴보면 unload()함수를 호출할 경우 Loader클래스 인스턴스에 관련된 정보를 모두 지워버린다. 즉, 컨텐츠를 메모리상에서 완전히 삭제한다. 반면 release()함수의 경우 더 이상 참조한 컨텐츠가 없을때 삭제한다. ModuleInfoProxy클래스의 load()함수를 다시 보면 info.addReference()가 있다. 또 ModuleInfoProxy의 release()를 보자. 이번에는 info.removeReference()가 있다. 무엇을 의미하는 것인가? 참조한 횟수가 0가 되는 경우에 ModuleLoader에서는 factory만 삭제한다. (이와 관련되서는 더욱 명확한 해석이 필요할 듯 보인다. 아직 이해를 못했음)
load()를 통해 모듈로드가 완료되면 initHandler() 이벤트 핸들러가 호출된다. 여기에 factoryInfo.factory = loader.content as IFlexModuleFactory; 이런 구문이 있다. 즉 로드된 컨텐츠를 IFlexModuleFactory의 인스턴스 형으로 다루겠다는 것을 의미한다. IFlexModuleFactory에는 create()함수와 info()함수가 있는데 create() 함수를 통해 System에 등록된 모듈의 인스턴스를 생성할 수 있다. 이 말은 하나의 모듈로 여러개의 인스턴스를 만들 수 있다는 의미로 가령 말풍선 모듈이 있다면 약간의 설정이 다른 여러개의 모듈 인스턴스를 생성해 내서 보여줄 수 있다는 의미이다. 이 사실을 잘 알고 있다면 가령 비슷한 기능에 레이아웃이 다른 모듈을 만들때 그에 따라 여러개의 SWF 모듈을 만들지 말고 단 1개의 SWF 모듈을 만들고 로드한 뒤에 인스턴스 생성후 레이아웃 정보를 적용하도록 하면 프로그램 로딩 속도와 메모리 관리에 더욱 현명하게 대처할 수 있겠다.
정리하며
이상으로 Flex Module에 관련된 소스를 살펴보았다. 완벽한 분석은 하지 못했지만 어느정도 Module이 어떻게 구성되는지 이해할 수 있는 걔기가 되었다고 생각한다.
한가지 바라는 것은 본인이 잘 이해하지 못하고 넘어간거나 잘못 설명한 부분을 함께 바로 잡아주었으면 하는 것이다.
글쓴이 : 지돌스타(http://blog.jidolstar.com/311)
'Adobe Flash Platform' 카테고리의 다른 글
| [Flex/AIR] Flex Application이 resize될 때 PopUp 창 중심에 놓기 (0) | 2008/04/08 |
|---|---|
| [Flex,AIR]Actionscript 3 Mysql Driver (14) | 2008/04/02 |
| [Flex]Module, ModuleManager, ModuleLoader 분석 (10) | 2008/04/01 |
| [Flex, AIR] 프로그램 크기를 줄여보자 3 - 모듈화를 하자. RSL과 Module (8) | 2008/04/01 |
| [Flex, AIR] 프로그램 크기를 줄여보자 2 - 폰트(font) 동적로드 또는 문자범위지정 (0) | 2008/04/01 |
| [Flex, AIR] 프로그램 크기를 줄여보자 1 - 사용하지 않는 클래스는 사용하지 않도록 하자. (3) | 2008/03/31 |
-
2009/07/16 20:50SWC로 부터 동적으로 클래스를 가져오기 Tracked from dieBuster




지돌스타님 글 너무너무 잘보고 있습니다^^
다름이 아니라 요새 Module에 대해 공부 하고 있는 중인데요
제가 제대로 이해하고 있는지 잘 몰라서요 ㅠ
하나의 URL에 대해서 하나의 모듈만 메모리에 로드 되는 건가요??
그렇다면 동일 URL로 로드 했을 경우 최초에 로드 되었던 모듈을 통해 인스턴스가 생성되는 건가요??
그럼 위 모듈의 unload는 어느 시점에 해줘야 할지 잘 모르겠습니다ㅜㅜ
으 질문이 두서없이 되버렸네요 ^^;;;;
ModuleManager를 보시면 getModule(url:String) 부분을 보실 수 있을겁니다.
var info:ModuleInfo = moduleList[url] as ModuleInfo;
if (!info)
{
info = new ModuleInfo(url);
moduleList[url] = info;
}
return new ModuleInfoProxy(info);
를 보시면 moduleList가 Object로 선언되어 있고 moduleList[url]로 값을 가져오는데 없으면 새로운 ModuleInfo를 만듭니다. 이 과정을 보면 같은 url의 경우 새로운 Module을 로드하지 않습니다. 로드한 모듈은 하나의 Class입니다. 이 모듈을 가지고 여러개의 인스턴스를 생성하게 되는거지요.
ModuleLoader도 ModuleManager로 동작하게 만들어져 있고
ModuleManger를 분석해보시면 Loader를 이용합니다.
만약 ModuleManager를 활용하지 않고 Loader로 한다면 같은 url에 대한 모듈을 두번이상 로드하지 않도록 하는 로직을 직접 만드셔야합니다. ModuleManager가 바로 Url을 가지고 같은 url이면 2번이상 Module을 로드하지 않도록 합니다.
Loader로 읽어온 모듈을 인스턴스화 하는 작업은 ModuleManager.as안에 정의된 ModuleInfoProxy에 load()부분을 살펴보시면 이해하실 수 있을거예요. 기가막히게 잘 만들어져있답니다. ^^
기억하실것!
같은 url인 경우 두번 로드하지 않는다.
한번 로드된 모듈은 여러개의 인스턴스를 생성할 수 있다.
이 모든작업을 ModuleManager이 알아서 해준다!
와~~ 정말 상세한 답변 감사합니다~!! ^^
요새 지돌스타님 블로그 때문에 Flex에 자신감이 조금씩 생기고 있어요 헤헤~
앞으로도 좋은 글 마니마니 부탁드리겠습니다!! ^^
매번 와서 배우고만 가고있습니다..
모듈로더를 써서 로드해올때 처음 불러온 모듈은 팝업이나 그런게 잘 되는데..
두번째 모듈을 불러오면
DragManager
IPopUpManager
IHistoryManager
여기서 에러가 나더군요.. 검색으로 찾아봐서 매니저를 임포트 해주고 선언 해주면 안난다고 해서 그렇게 해서 쓰고는 있는데..
이게 근본적인 해결책은 아닌것 같아서..
어떻게 사용하고 계시는지 궁금합니다.
왜 저런 문제가 일어나는지 알고 계시는지요 ㅠㅠ
모듈 생성시 옵티마이저 체크와 관련되어 있는 것 같습니다. 애플리케이션과 옵티마이징을 하지 마시고 컴파일 해보세요. 물론 그렇게하면 모듈 크기가 커질 수 있습니다.
여기저기 답을 찾다가 유명하신 지돌스타님 블로그까지 오게되었네요. 물론 질문을 드리고자 ㅠㅠ
mdi와 moduleloader를 사용하고 있는데 mdi의 특징은 서로다른내용의 창을 여러개 열어서 한번에 볼수 있잖아요? 그런데 한번 열리면 다른 창에서 오류가 뜹니다. 한개 이상의 창이 안열리고 있습니다. 오류내용은
message : faultCode:Server.ResourceUnavailable
faultString:'Cannot invoke method 'getHardwareList'.'
faultDetail:'The expected argument types are (com.eosoft.common.model.DataModel)
but the supplied types were (flex.messaging.io.amf.ASObject) and converted to (null).' 입니다.
위의 내용르 읽어보고 있지만 이해가 않가는 부분이 많아서요.
remoteObject을 사용 자바를 사용하구요.tomacat , blazeds등을 사용하고 있습니다.
하도 안되서 module이아닌 application으로 바꿔서 해봐도 똑같은 에러가 납니다.
제발 도와주세요..ㅠㅠ
좀더 자세히 얘기하면 메뉴를 동적으로 생성하구요 메뉴의 파일이름을 각각 db에서 불러서 뿌려지는 식이거든요
private function menu_subHnd(event:MenuEvent):void
{
var contentWin:ContentCanvas = new ContentCanvas();
contentWin.setUrl(event.item.@module);
var mdi:MDIWindow = new MDIWindow();
var GenericModuleLoader:ModuleLoader = new ModuleLoader();
mdi.percentWidth = 90;
mdi.percentHeight = 90;
mdi.title = event.label;
mdi.y = 90;
mdiCanvas.windowManager.add(mdi);
staticUrl = "mxml/module/"+ event.item.@module +".swf";//실제 이용
GenericModuleLoader.url = staticUrl
mdi.addChild(GenericModuleLoader);
}
이거 못봐서 죄송합니다.
이 글만 봐서는 정확히 무슨 말인지 ^^;;
리..리..링크가... ㅠㅠ
그렇군요. 블로그 옮기는 과정에서 문제가 발생했네요.
ㅎㅎ 링크를 찾아야겠어요 ^^