'Flex'에 해당되는 글 4건

  1. 2009/08/25 kineticroad Flash player 메모리 관리 Tip
  2. 2008/12/04 kineticroad 2008 Korea Web Award 대상?? (2)
  3. 2008/11/22 kineticroad Flex에서 DeepLinking하기 (1)
  4. 2008/09/16 kineticroad DateChooser 클래스의 비밀

Flash player 의 오랜 과제였던 메모리 관리 문제는 어떤 프로젝트를 진행하던지 간에 언제나 이슈로 대두 되었다.
특히나 Flex 내에서 모듈을 load하고 unload하는 과전에 있어서 메모리의 계속된 증가는
결국 사용자 PC의 브라우져가 과도한 메모리 점유로 인해 온갖 문제들을 야기 시켰는데
그것은 종국에 가서 클라이언트의 불만으로 이어지고
그런 불만을 해소하기 위해서 System.gc() 메서드나 LocalConnection 을 통한
강제 garbage collection을 해주고자 하였으나 근본적인 해결책은 되지 못했다.

한편 사람들은 메모리 문제에 대해서 removeListener를 통해 리스너를 삭제하는 방법들을
메모리 점유나 퍼포먼스에 대한 해결책으로 제시하고 있지만
문제는 무한이 늘어나는 인스턴스의 해결은 어찌 하느냐가 관건이었다.

전번 프로젝트를 통해 메모리를 줄여나가는 방법들에 대해서 여러가지로 연구하고
별의 별 방법들을 사용했고 시도했었다.
iframe을 통한 flash player의 브라우져 refresh는 Adobe사에서 권장하는 방법이었으나
다수의 Application의 생성이 불가피하고 rich internet 시장에 맞지 않는 작업들이라
클라이언트의 클래임을 설득하기 위한 회의는 연일 지속되어야만 했다.
게다가 refresh로 인해 Application의 다운로드 속도가 만족되어지지 않을 경우
RSL 방식이니 뭐니 하면서 Application 자체의 용량을 줄이기 위해 전쟁을 해야하는 상황들과 직면해야한다는 사실에는
변함이 없었다.
(그로인한 컴파일러 옵션값 조정에 몇 시간, 혹은 몇일의 시간을 날려먹었는지 모른다.)

프로젝트를 진행함에 있어서 가장 중요한것은 맨 마지막 산출물이 얼마나 rich한 작업이었는 지를
따지기 이전에 메모리의 양을 어찌 조절하느냐가 관건이 되었다.
앞뒤가 거꾸로 돌아가는 것이 아닌가 싶을정도로 고민과 번뇌로 코드를 들여다 봐야했고
메모리 점유율을 낮추기 위해 그리고 왜 낮춰지지 않는가에 대한 고민을 헤아릴 수 없이 해야만 했으며
생머리던 내 머리칼은 어찌나 쥐어뜯었는지 곱슬머리가 되어버렸다면 얼마나 많은 고민을 했을지 예상했으리라

그렇다면 왜 Flash player는 메모리를 낮추지 않을까?
왜 Flash player는 unload된 모듈에 대한 객체들을 지워주지 않을까에 대해 이해에 도움이 되기 위해서는
일단 Flash player 자체는 우리의 생각보다 멍청한 프로그램이고 뭐가 필요한 객체인지 아닌지에 대한
이해가 떨어지기 때문이라고 생각하면 속이 좀 편안해 질거다.

ActionScript에는 강한참조, 약한참조 가 있다는 사실은 조금만 공부해도 알 수 있는 내용이고
addEventListener 메서드로 이벤트를 등록할 때 useWeakReference 옵션 값을 true로 해줘서
약한참조로 만들어주면 gc가 돌때 등록된 이벤트를 같이 삭제한다고 이야기는 들었으나
이 약한참조라는게 어느 순간 목적 객체를 놓치는 경우가 종종 발생하여 문제는 계속해서 발생하게 된다.
(그 놈의 원인모를 문제는 다 여기서 나오는 거더라)

그렇다면 그냥 removeEventListener를 습관화 하는게 가장 좋은 방법이다.
묻지도 말고 따지지도 말고 그냥 무조건 remove시키는게 제일인것으로 결론이 났다.
그리고 가장 큰 문제인 ArrayCollection 의 강한참조가 문제다.

ArrayCollection은 정말 답이없는 객체다.
dataProvider로 DataGird 같은 List 객체나 Chart 객체에 Binding을 걸면 그것또한 낭패.
왜냐하면 ArrayCollection을 List 객체나 Chart객체 내부에선 dataProvider로 참조걸린 객체가 있다면
모듈 삭제시 인스턴스를 삭제 하지 않기때문이다.

한마디로 DataGrid가 삭제되도 내부의 데이터 값 자체는 삭제하지 못한다는 것이다.
이런 낭패가!
이벤트도 다 지웠겠다 UI객체들도 삭제했으니 당연히 메모리는 다시 원상 복구 되겠지!
하고 믿어 의심치 않고 있는 마당에 이런 뒤통수 맞는 경우가;;
Binding이라는게 쓸때는 참 편하고 좋다가도 이런 이유때문에 객체가 그대로 살아서 내 목을 조이고 있다고
생각하면 정말 믿는 도끼에 발등을 찍히는 기분이다.

그래서 UI객체가 삭제되는 시점을 틈타 FlexEvent.REMOVE 이벤트 발생시 객체들을 아래와 같이 삭제한다.


<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;

[Bindable]
private var tempAC:ArrayCollection;

override protected function createChildren():void
{
super.createChildren();
addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHanlder);
}

private function creationCompleteHandler(evnet:FlexEvent):void
{
removeEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);


tempAC = new ArrayCollection([
{data1:"temp1", data2:"temp1"},
{data1:"temp2", data2:"temp2"},
{data1:"temp3", data2:"temp3"},
]);

addEventListener(FlexEvent.REMOVE, removeHandler);
}

private function removeHandler(event:FlexEvent):void
{
removeEventListener(FlexEvent.REMOVE, removeHandler);

Object(dg.dataProvider).removeAll();
tempAC.removeAll();

removeAllChildren();
}
]]>

<mx:DataGrid id="dg" dataProvider="{tempAC}">
<mx:columns>
<mx:DataGridColumn headerText="sample1" dataField="data1" />
<mx:DataGridColumn headerText="sample2" dataField="data2" />
</mx:columns>
</mx:DataGrid>



removeHandler부분을 잘보면 ArrayCollection 객체에 null로 삭제하는 것이 아니라 removeAll() 메서드로 삭제하고 있고 모든 이벤트 들은 사용이 종료되었을 때 그때 그때 삭제해주는 버릇이 중요하다.
그래야 쓸데없는 인스턴스를 줄일 수 있고 나중에 클라이언트와의 언쟁을 조금이라도 줄일 수 있게 되니 서로 간에 스트레스 없이 좋게좋게 끝낼 수 있을 것이다.

2009/08/25 12:41 2009/08/25 12:41
사용자 삽입 이미지

7월 부터 고생고생하면서 만든 저 지경부 사이트가 2008 Korea Web Award에서 정부기관 부문 대상을 수상했단다..

진짜 고생많이 한 사이트인데
한가지 아쉬운 사실은 그 고생하면서 만든 사이트 소개에 우리 회사 이름이 없었다는게 가슴이 아플 따름이다.
협력업체라 어쩔 수 없다지만 속이 쓰린 건 어쩔 수 없는 사실.
게다가 컨텐츠의 6할이 넘는 분량이 Flex로 채워져있다는 사실을 감안하면 분개하지 않을 수 없을 터.
그래도 어쩔 수 없는 것이 이 사회라는 사실이 정말 가슴이 아프다.
게다가 내이름이 같이 올라가 있지만 이상한게 내가 다른 회사사람처럼 올라갔다는 사실이 조금 당황스러울 따름이다.
(게다가 PM이 내이름만 솔랑 올려버려서 완전 이상한 경우가 되어버렸다....-_-)

그리고 같이 밤새서 엉망인 코드들 고쳐가며 고생한 우리 바닐라人들.
정말 죄송스럽고 감사하다고 다시 말하고 싶다.

PS.
보안각서 때문에 더 자세한 이야기를 할 수 없지만 정말 하고 싶은 말이 많았던 프로젝트였다는 사실임에는 변함이 없다.

PS2.
어떤 경로를 통해서 상을 타게 되었는지 알 방법이 없으니...
허허.... 곤란하다 곤란해.
2008/12/04 00:34 2008/12/04 00:34

프로젝트를 하다가 가끔씩 마주치는 이슈가 있는데
그것은 바로 DeepLinking 기능이다.
Flex에서 최종산출물이 swf로 떨어지기 때문에 JSP와 같이 GET 방식의 주소를 통해 정해진 인자를 주소로 보내면 특정 페이지로 바로 접근하는 방법을 찾던도중 DeepLinking이란 기능을 알게 되었다.
물론 이것은 JSP 처럼 별다른 연산 없이 바로 접근 할 수 있는게 아니라 그저 Flex가 내부적으로 브라우져 주소입력창에 어떤 내용을 넣었는지 확인 할 수 있는 그런 기능일 뿐이다.
그래도 이것이 어딘가 싶어서
연구를 하다가 또 놀라운 사실을 접하게 되었는데 알고 보니 Flex의 단독기능이 아니라 JavaScript와 SWF 상호간의 ExternalInterface를 이용한 콤비네이션이었을 뿐이었고 나처럼 빌더를 쓰지않고 EditPlus로 개발하는 사람으로써는 그런 JavaScript를 구축하는데 꽤나 시간이 많이 소요된다는 점에서 조금 실망하지 않을 수 없었다.
그래서 나는 이 방법을 피하고 싶어 꽤나 요리조리 피해 클라이언트 들에게 이렇게 하면 어떻겠냐 저렇게 하면 어떻겠냐라고 하다가 이번엔 좀 제대로 걸려서 천상 이 DeepLinkg을 만들어야할 상황에 온김에 포스팅도 같이 고고.

아무튼 일단 빌더로 구축된 Flex 산출물 중에 history라는 디렉토리가 자동으로 생성된다는 사실을 알고 있을텐데 그 안에 보면 history.js 파일이 있다.
그 안에 보면 BrowserHistory = (funtion() { .......}) 하는 형식으로 짜여진 함수를 볼 수 있을 것이다.
그 안에 있는 내용이 DeepLinking과 관련 된 내용들이다.
(사실 Javascript는 문외한이라 잘 모르지만 통밥으로 느낄 수 있다!!)

뭐... 잘 살펴보면 "#" 이라는 구분자로 주소와 파라미터를 구분 한다는 것도 알 수 있고
그 파라미터를 Flex에서 가져갈 수 있게끔 리턴도 해준다.

일단 이런 내용은 빌더로 프로젝트를 하나 구축하면 자동으로 생성되므로 확인 할 필요가 없다.
Javascript는 알아서 만들어주니 걱정은 덜고 Flex에서 코딩을 어찌할지가 문제로구나.

이런 예제 소스는 여기저기 많다.
http://www.flexlive.net/?p=84 여기서는 샘플코드를 다운로드 해줄 수 있게 해준다.
http://labs.adobe.com/wiki/index.php/fl ··· _linking 여기는 Adobe Lab

Google에서 이것저것 검색하면 많이 나오므로 걱정하지말고 두드려라 그럼 열릴것이니.

자. 그렇다면 어떤 어떤 클래스를 import를 해야하는지 알아보자.

DeepLinking을 하려면 IBrowserManager 와 BrowserManager 두개의 클래스가 필요하다.
IBrowserManager를 주로 사용하는데 이 클래스는 결국 인터페이스 클래스이므로 Construct를 하기위해서는 BrowserManager.getInstance() 메서드가 필요하다.

var browserManager:IBrowserManager = BrowserManager.getInstance();

이런식으로 Construct를 해준다.
그럼 이제 주소표시줄의 내용을 가져오고 싶은데 그렇게 하려면 이벤트리스너를 등록해줘야한다.
BrowserChangeEvent 클래스가 있는데 여기에서 URL_CHANGE 이벤트를 받을 수 있게 해주면
주소표시줄에 # 이하의 파라미터가 있으면 이벤트가 발생하게 된다.(없으면 발생하지 않는다.)

그래서 간단하게 개념 정리를 하자면 이렇다.
Application이 initialize하게 되면 IBrowserManager를 Construct를 시키고
BrowserChangeEvent.URL_CHANGE 이벤트를 등록한다.

이벤트가 발생하면 파라미터값이 있는 것이므로 이벤트 핸들러에서 파라미터 값에 맞는 액션을 취해주면 된다.

결국 Flex소스 자체에는 큰 액션이 없다.
JavaScript가 하는 일을 그저 BrowserManager가 받아서 처리 해주는 일 뿐이다.
간단하게 소스를 짜면 아래와 같은 로직이 될것이다.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml initialize="initializeHandler()">
<mx:Script>
        <![CDATA[
        import mx.controls.Alert;
        import mx.managers.IBrowserManager;
        import mx.managers.BrowserManager;
        import mx.events.BrowserChangeEvent;

        private var browser:IBrowserManager;

        private function initializeHandler():void
        {
            // IBrowserManager 생성
            browser = BrowserManager.getInstnace();
            browser.addEventListener(BrowserChangeEvent.URL_CHANGE, urlChangeHandler);
        }
       
        private function urlChangeHandler(event:BrowserChangeEvent):void
        {
            Alert.show("파라미터 : " + browser.fragment);
        }
        ]]>
</mx:Script>
</mx:Application>

간단하게 이런식으로 파라미터를 받아와 DeepLinking 할 수 있게 코딩을 할 수 있다.
하지만 프로젝트 초반에 이런 기획이 없이 개발에 들어가면 나중에 소스를 죄다 뜯어 고쳐야할 소지가 짙으므로
미리 이런 기능이 필요한지, 아닌지에 대하여 논의를 하고 설계를 해야할 것이다.
물론 이런 반쪽뿐인 DeepLinking 기능이지만 그래도 이만하면 Flex도 할만큼 한게 아닐는지;;

2008/11/22 14:23 2008/11/22 14:23
Flex에 보면 DateChooser라는 클래스가 있다.
사실 이런 DateChooser는 Style을 마음대로 먹이기가 꽤 까다로운데
이번에 프로젝트를 하면서 재밌는 사실을 알아냈다.

DateChooser에서는 년도와 월을 선택해주는 기능 외에 다른 기능이 포함되어 있지 않다는 사실.

DateChooser에서 구현되었을 줄 알았던 달력부분은
사실 다른 클래스에서 가져와 기능을 하고 있다는 사실을 알아냈다.
그 클래스의 이름은 CalendarLayout이라는 클래스인데 그 클래스는 livedocs 에도 나와있지 않아 확장 컨포넌트를 사용할 줄 모르는 사람은 알아내기가 꽤 곤란하다.
이 클래스는 DateChooser 에서 달력 부분을 표시하는 기능을 하고 있고
이 클래스에는 displayedMonth, displayedYear과 같은 프로퍼티가 구현되어 있어
DateChooser와 똑같이 사용하면 된다.

참, 스타일도 거의 비슷하게 먹는데 DateChooser에만 있는 객체들에 대한 스타일 외에
달력에만 적용되는 스타일은 동일하게 적용된다.
아래는 CalendarLayout 클래스를 이용해 컨포넌트를 만든 화면.

p.s 프로젝트에 사용되는 화면이라 CalendarLayout을 확장해 만들었으니 이해해주삼

사용자 삽입 이미지


2008/09/16 17:40 2008/09/16 17:40