SWC로 부터 동적으로 클래스를 가져오기
어느 정도는 전에 쓴 글인 ApplicationDomain 과 연관이 있는 글이라 할 수 있다. 따라서 먼저 기존의 http://www.diebuster.com/?p=660 를 읽어보길 권한다.
클래스의 정의를 담는 SWF를 생성하기
ApplictionDomain에서 swf 를 통해 클래스를 로딩하는 경우 해당 swf 안에 그 클래스의 정의가 들어가 있어야 한다. 대체 클래스의 정의가 들어가 있는 swf의 모양은 무엇일까?
예상되는 형태로 Main 클래스에 private var test:Class=TestClass; 라는 식으로 쭉 필요한 만큼 클래스를 속성으로 선언하여 mxmlc가 강제로 클래스 정의를 로딩하게 하는 방법이 있다. 하지만 몇 가지 문제점을 내포하고 있다.
- Main 클래스 즉 Application Class는 반드시 Sprite 또는 MovieClip을 상속받는데 단순 클래스 로딩만 필요한 경우 이는 쓸데 없다.
- internal 이나 private, namespace 등이 관련된 클래스의 경우 Main에 선언 하는 것 만으로는 로딩할 수 없다.
- 결국 개발자가 var 를 선언하여 클래스를 로딩하기 때문에 이 과정에서 실수할 가능성이 높고, 클래스를 업데이트 할 때마다 이 위험을 항상 같이 가져간다.
그럼 어떻게 하는 게 가장 좋은 방법일까?
표준적인 라이브러리 생성방법인 compc컴파일러를 통한 swc 로 출력하는 게 가장 좋은 방법이다. swc 를 생성하는 경우 정확하게 해당 클래스의 정의만 로딩하므로 용량이 최소로 될 뿐만 아니라 복잡한 제한자에 상관없이 클래스 단위로 포함, 불포함을 정할 수 있다. 또한 빌더의 플렉스 라이브러리 프로젝트를 통해 관리하면 manifest와 catalog를 매우 쉽게 관리할 수 있다. 그래서 라이브러리 프로젝트를 생성하여 사용하는 방법을 설명할 줄 아는가 ^^; 아니다 그건 알아서들 해결하길 바란다. 지금부터 말하려는 게 진짜 이 글의 주제다.
RSL에 대한 이해
RSL은 Runtime Shared Library 의 약자로 플렉스 프레임웍이 표준적으로 실행 시 클래스의 정의를 별도의 파일에서 로딩하여 얻는 방법이다. 우리 간단히 생각해보자.
- 개발하고 있을 때를 생각해보면 어짜피 라이브러리 파일은 독립된 swc에 있는데 IDE가 이 파일로부터 정의를 읽어와서 개발 시 에디터와 연동시켜준다.
- mxmlc 컴파일러의 경우도 소스코드에 있는 클래스의 정의를 라이브러리와 global.swc 등에서 찾아서 컴파일 하게 된다.
무엇을 알아차렸는가? 컴파일러가 컴파일 할 때, 빌더가 개발할 때 어짜피 외부 파일에 있는 정의를 읽어서 연동해줬다는 점이다. 여기서 외부에 있던 파일을 안으로 가져와서 포함하지 않고 여전히 외부파일에 둔 상태로 실행 시점에 외부 파일을 읽어서 사용하게 할 수도 있다. 그렇게 하면 컴파일러는 컴파일 하는 당시에 해당 정의를 읽어서 swf에 포함시키지 않고 실행시점에 다른 swc로 부터 읽어서 가져온 후에 swf를 실행하도록 처리한다. 이러한 셋팅 및 적용 방법은 공식문서에 매우 자세히 기술되어있다.
http://livedocs.adobe.com/flex/3/html/rsl_06.html
이 훌륭한 RSL의 문제점은 무엇일까?
사실 RSL에는 작은 비밀이 있는데 절반은 컴파일러가 하고 나머지 절반은 Flex Framework 이 한다는 점이다. RSL이 하는 일을 크게 두 부분으로 나눠보자.
- 컴파일 할 때 RSL로 지정된 클래스는 swf에 포함하지 않도록 컴파일러에게 옵션을 준다. 이 옵션은 external-library-path 로 주는 경우 swc단위로 제외시킬 수 있고, externs 옵션을 줄 경우는 클래스 단위로 줄 수 있다. 빌더는 이를 대화창에 숨겨두지만 실제로는 컴파일 시 저 옵션으로 반영된다. 대표적으로 playerglobal.swc 의 경우 플레이어에게 내장되어있으므로 기본값이 external 되게 설정되어있다(http://livedocs.adobe.com/flex/3/html/compilers_14.html 를 참조하자) 사실 external 시리즈 옵션은 swf엔 포함하지 않지만 컴파일 시에는 그 파일에서 정의를 읽어 들여 검사를 하도록 해주는 옵션이다.
- RSL을 정말로 반영하는 옵션은 runtime-shared-library 옵션이다. 정확하게는 libraries 와 library-path 옵션 두 개로 구성한다. 여기에 swf를 지정하면 해당 swf는 flex Framework가 구동되는 시점에 미리 preloader에서 로딩한 후 어플리케이션을 실행하게 한다. 즉 flexframework 를 위한 옵션이다. 실제 이 옵션이 주어지면 mxmlc는 mxml을 as로 번역하는 과정에서 Preloader 클래스가 로딩해야하는 목록에 이 옵션에 지정된 swf를 추가하도록 한다. 즉 as를 만들어낼 때 텍스트를 추가하도록 반영하는 것이다.
위의 2단계를 정확하게 알아들었다면 당연히 flex framework을 사용하지 않는 순수 actionscript project에서는 RSL 옵션이 나오지 않는게 자동으로 이해된다. 아래 그림을 보자.
순수 액션스크립트 프로젝트에서는 당연하게도 External만 지정이 가능한 것이다. 그 효과는 컴파일시 external-library-path 로 반영된다. flexFramework에서는 Preloader가 그 일을 하지만 순수 액션스크립트 프로젝트에서는 그 일을 누가 하는걸까?
당연히 손수 구현하라는 의미다. ^^
손수 RSL의 절반을 구현하기
손수 구현하려면 일단 이미 구현하고 있는 Flex Framework이 어떤 일을 하는지 알아야 한다. 놀랍게도 온라인 문서에 이 과정을 자세하게 설명해주는 문건은 희귀하다. 대신 얼마 전 역서가 나왔던 프로그래밍 플렉스 3 에서는 완전 명쾌하게 알려주고 있다. 총 16단계로 되어있는 애플리케이션 초기작동 단계를 프리로딩이 끝나는 단계까지 살펴보자.
- SWF 파일의 첫 프레임이 다운로드 되면 SystemManager 인스턴스가 자동으로 생성된다.
- SystemManager 가 리소스 번들을 로드한다.
- SystemManager가 타임라인을 프레임 1에서 정지시키고 프레임 2의 내용은 이후까지 실행되지 않게 한다.
- SystemManager가 Preloader 인스턴스를 생성한다.
- SystemManager가 ResourceManager 클래스를 등록해 프레임 1에서 사용할 수 있게 하고, 질의 문자열이나 FlashVars를 이용해 지정된 리소스 모듈 전부를 ResourceManager에 전달한다.
- SystemManager가 Preloader 절차를 시작한다.
- Preloader 인스턴스가 프리로더 디스플레이를 생성한다.
- Preloader가 런타임 공유 라이브러리를 다운로드하기 시작한다.
- Preloader가 메인 SWF 다운로드와 런타임 공유 라이브러리 다운로드 진행률을 모니터링하고, 다운로드가 이뤄지는 동안 이벤트들을 디스패치해서 디스플레이에 통지한다.
- 메인 SWF 다운로드와 런타임 공유 라이브러리 다운로드가 완료되면 Preloader 인스턴스가 initProgress 이벤트를 디스패치하고, 이 이벤트는 SystemManager에 Application객체 초기화를 시작할 준비가 됐음을 통지한다.
* 한국어판 Programming Flex 3 ITC에서 발췌
하는 일을 읽어보면 최초 클래스 정의가 들어있는 swf를 로딩하고 그 로딩이 끝난 후에나 어플이 작동하도록 하고 있음을 알 수 있다. 그럼 액션스크립트에서도 똑같은 일을 하면 된다. 그 똑같은 일을 풀어서 써보면 아래와 같다.
- 최초 Main에서 어떠한 다른 일이 일어나기 전에 먼저 Loader를 통해 필요한 swf를 로딩한다.
- 로딩 시 AD를 currentAD로 지정한다.
- 로딩완료 리스너가 본체 로직을 시작한다.
외부에서 정의했던 클래스가 사용되기 전에 미리 Loader가 로딩하도록 하면 되는 정도다.
flex의 모듈은 어쩔까?
결국 동적 클래스로딩의 문제는 두 가지로 요약된다.
- Loader를 통해 swf를 로딩하고 로딩한 swf속의 클래스를 어떤 AD에 배치한다. 여기서 언제, 어떤 AD라는 문제가 발생한다.
- 개발 시 컴파일러가 존재하지 않은 클래스를 정적으로 에러체크하게 하고 싶다. 여기서 external 옵션을 활용하면 된다.
어찌보면 external 옵션은 매우 무책임한 옵션이다. 컴파일러는 당신이 알아서 그 클래스를 적시에 적소에 로딩 했을 거라 예상하는 가정하는 셈이다. 실제로 적시 적소에 그 클래스 정의가 로딩된다는 책임은 전부 당신의 몫이다.
flex의 모듈은 RSL이 위에 설명한대로 최초 로딩되기 때문에 불편한 점을 해소하기 위해 생각해낸 아이디어다. 중간에 원할 때 로딩하는 것이다. 이제 모듈이나 RSL이 아니라 동적 클래스 로딩에 대한 전체적인 이해가 생겼을테니 모든 건 손수 구현하거나 응용할 수 있을 것이다. 사실 flex는 누군가 이러한 이해를 바탕으로 제작한 클래스다. 여러분도 만들면 그만인 것이다.
그 외의 잡 생각들..
이러한 입장에서 고민해보면 모든 클래스 선언은 어쩌면 내부적으로는 ApplictionDomain.currentDomain.getDefinition() 이 생략된 셈이고 변수 선언시 정적으로 선언한 데이터형은 단지 컴파일러가 검사하기 위한 키위드라고 보는 편이 타당하다.
관련된 글:
예전에 이것때문에 무척 고민하고 공부한 적이 있었습니다. ApplicationDomain, Module, RSL… 생소한 개념이 머리 아프게 만들었는데 결국 말씀하신 것처럼 Loader에서 load함수로 swf를 로드할때 LoadContext의 2번째 인자 ApplicationDomain을 설정하는 것으로 모든 것이 귀결되는 것을 알면서 이거 참 좋은 아이디어 다라는 생각이 들더군요. 당시 문서가 많이 없어서 공부하기 무척 어려웠다는… ^^;
Flash Builder 자체에서는 아직까지 완벽한 환경의 Module 프로그래밍을 지원하지 않아서 아쉬운 편인데… 가령 모듈이 많아지면 컴파일이 느려진다는거죠. 이게 좀 압박입니다.
또한 RSL의 경우에도 Flex 자체가 필요할 때 로드할 수 있는 기능도 들어가면 더욱 좋을 것 같아요. 결국 그렇게 되면 모듈이겠지만 제 개념은 모듈과 비슷한 RSL 같은거죠.
RSL의 경우 단일 Application을 만드는 경우에는 별로 큰 이득이 없어 보입니다. 두개 이상의 Application이 존재하는 경우에 더욱 가치가 있어 보입니다. SWC를 가지고 external이나 merge냐 이런 거 프로젝트가 커지면 커질수록 복잡해지더군요. 이런것도 잘 정리하는게 중요한 것 같습니다. ^^
저 같은 경우에는 거의 모든 프로젝트가 actionscript project 인지라 거의 flex frameworks에서 제공하는 기능을 손수 구현해야하는 실정입니다 ^^;
보통 만들때 호스트코드에서 얼마나 간단하고 편리하게 사용하는가와 부하와 사이즈를 줄이는가에 중점을 두는데, flex가 생성해주는 서브모듈들은 프레임웍과 물려있어서 그런지 용량이 커서 실용성면에서 좀 떨어지는 듯합니다. 전 클래스로더를 따로 작성해서 쓰기 때문에 반해도 생성해야하는 swf도 워낙 작고 해서 유용하게 잘 쓰는 편입니다. 주로 큰 클래스들은 순수 클래스라기보단 EMBED클래스들인데 이것들도 로딩후 가비지컬렉팅이 잘되어서 나름 만족스럽습니다. 공식문서에서 클래스로더 샘플이 있는데 이것부터 시작해서 지금은 제법 이것저것 기능을 갖춘 클래스로더를 만들었습니다.
http://help.adobe.com/ko_KR/AS3LCR/Flash_10.0/flash/system/ApplicationDomain.html