Flex 4로 넘어오면서 생소하게 느꼈던 부분 중에 하나가 바로 자식(Child) 관리 메소드들의 변화가 아닌가 생각한다. 바뀐 부분이 무엇이고 왜 바뀌었는지 알아보도록 하겠다.

 

Flex 3에서 Canvas, Box 등의 컨테이너는 모두 mx.core.Container 클래스를 확장해서 만들어졌다. 이 Container 클래스는 flash.display.DisplayObjectContainer에서 정의되어 있는 자식관리 메소드인 numChildren, addChild(), addChildAt(), removeChild(), removeChildAt() 등을 그대로 Override(재정의) 해서 사용했다. 그런데 이렇게 재정의 한것이 문제가 컸다. Flex 3의 Container는 DisplayObjectContainer의 메소드를 그대로 사용하므로 자식은 모두 flash.display.DisplayObject로 받게 된다. 하지만 실제 자식으로 사용할 수 있는 것은 IUIComponent를 구현한 클래스만 자식으로 받을 수 있다. 즉 UIComponent만 자식으로 받을 수 있는 것이다. 아래 코드는 Flex 3의 Container에서 자식을 추가할 때 내부적으로 addingChild()메소드가 호출되는데 거기에 있는 코드이다.

var uiChild:IUIComponent = IUIComponent(child);

 

Flex 3에서 addChild()를 통해 받는 자식은 DisplayObject인데 런타임시 사용될 수 있는 컴포넌트는 IUIComponent이니 실제 Flex 3를 이용해서 개발하는 사람들은 오용할 소지가 크게 된 것이다. 나도 Container에 Sprite 객체를 넣으려고 하다가 안되서 애먹은 적이 있었는데 바로 이것때문이다.

 

Flex 4부터는 이 문제를 말끔히 없애고자 DisplayObjectContainer의 자식 관리 메소드를 재정의 하지 않고 다른 메소드를 만들었다. 그 다른 메소드는 무엇일까?

 

Flex 4에서는 아래와 같이 IVisualElementContainer 인터페이스를 정의했다. 이 인터페이스는 Flex 4의 모든 Spark, Halo 컨테이너에서 모두 구현하고 있다. 보면 알겠지만 addChild대신 addElement, removeChild대신 removeElement로 된 것이다. 결국 Child 대신 Element를 사용한거다.

package mx.core
{
public interface IVisualElementContainer
{
    function get numElements():int;
    function getElementAt(index:int):IVisualElement
    function addElement(element:IVisualElement):IVisualElement;
    function addElementAt(element:IVisualElement, index:int):IVisualElement;
    function removeElement(element:IVisualElement):IVisualElement;
    function removeElementAt(index:int):IVisualElement;
    function removeAllElements():void;
    function getElementIndex(element:IVisualElement):int;
    function setElementIndex(element:IVisualElement, index:int):void;
    function swapElements(element1:IVisualElement, element2:IVisualElement):void;
    function swapElementsAt(index1:int, index2:int):void;

}
}

 

위 인터페이스는 정확히 무엇을 의미하는가? 자식 추가할 때 이제 더이상 DisplayObject가 아니라는 것에 주목하자. DisplayObject대신 IVisibleElement를 관리하고 있다. 이것이 핵심이다. 즉 이제 런타임시가 아닌 컴파일 단계서부터 원천적으로 컨테이너에 자식들은 모두 IVisibleElement을 구현한 것만 다루겠다는 것이다. Flex 4는 Flex 3와 다르게 모든 컨테이너는 IVisibleElementContainer를 구현한 것이다. 그리고 mx.core.UIComponent는 IVisibleElement를 구현했다. 이로서 컨터이너에 붙는 자식들을 더욱 명확하게 사용할 수 있게 되었다.

 

그럼 addChild, addChildAt은 어떻게 했을까? 아래 코드는 Spark 컨테이너의 SkinnableComponent와 GroupBase에 재정의된 것이다. 즉 이들 메소드를 사용하면 런타임시 예외처리를 해준다. 이 클래스를 확장해서 만든 Spark 계열의 컨테이너인 SkinnableContainer, SkinnableDataContainer, Group, DataGroup, Panel 전부 이렇게 동작한다.

    /**
     *  @private
     */
    override public function addChild(child:DisplayObject):DisplayObject
    {
        throw(new Error(resourceManager.getString("components", "addChildError")));
    }
    
    /**
     *  @private
     */
    override public function addChildAt(child:DisplayObject, index:int):DisplayObject
    {
        throw(new Error(resourceManager.getString("components", "addChildAtError")));
    }

 

하지만 기존 Halo 기반의 컨테이너(mx.core.Container)는 addChild(), removeChild() 사용시 예외처리를 해두지 않았다. 물론 IVisualElementContainer는 구현했지만 말이다. 이는 기존 Flex 3 Halo 기반의 컴포넌트와의 호환성 문제 때문에 그리 한 것이라 판단한다.

 

Flex 4 문서를 보면 되도록이면 Halo보다 Spark 기반의 컴포넌트와 컨테이너를 사용할 것을 권장하고 있다. 지금까지 소개한 내용도 그렇게 권장하는 이유중에 하나일 것이다.

 

Flex SDK 개발자들이 Flex 2 시절 전부터 이런 문제를 알고 있었을 텐데 왜 지금에서야 바꿨는지... ㅎㅎㅎ

 

 

참고글

Spark Group - Functional and Design Specification

 

 

글쓴이 : 지돌스타(http://blog.jidolstar.com/559)

 

 

+ Recent posts