AdvancedDataGrid 의 AdbvancedDataGridColumnGroup 의 HeaderText 를 클릭했을때 HeaderText를 가져오는 방법.

 

//이벤트리스너 등록

this.adg.addEventListener(AdvancedDataGridEvent.HEADER_RELEASE,gridHeaderClickHandler);    

 

//HEADER_RELEASE 이벤트핸들러

private function gridHeaderClickHandler(event:AdvancedDataGridEvent):void
{
    var adgcg:AdvancedDataGridColumnGroup = event.itemRenderer.data as  AdvancedDataGridColumnGroup;

    Alert.show(adgcg.headerText);
}   

 

위의 것이 필요한 이유는 AdbvancedDataGridColumnGroup  에서 child 인 column 들을 클릭하면

AdvancedDataGridEvent.columnIndex 를 가져올 수 있어 이벤트가 발생한 AdbvancedDataGridColumnGroup  를 알아낼 수가 있는데 , headerText를 클릭하면 AdvancedDataGridEvent.columnIndex 의 모든 값이 -1 로 나와서 이벤트가 발생한 그룹을 알수가 없는 문제가 있어서 입니다.




restrict = "ㄱ-힣"            :  한글만 입력가능.
restrict = "A-z"              :  영어만 입력가능.
restrict = "0-9"               :  숫자만 입력가능.
restrict = "0-9\-"           : 숫자와 '-'만 입력가능.
restrict = "^a-z"             : 소문자 a부터 z까지 제외한 모든문자 입력가능.
restrict = "A-z0-9\@\." : 이메일 형식만 입력가능.
restrict = "\\\"           : '\' 문자만 입력가능.
restrict = "\^"               :  '^' 문자만 입력가능.

package {
    import mx.core.UIComponent;
    import flash.events.MouseEvent;
    import mx.containers.Panel;
    import mx.controls.Label;
    import mx.events.FlexEvent;
    import mx.states.State;
    import mx.states.Transition;
    import mx.states.SetProperty;
    import mx.effects.Parallel;
    import mx.effects.Move;
    import mx.effects.Resize;
    import mx.effects.Effect;
    
    public class TestUI extends UIComponent
    {
        public function TestUI() 
        {
            super();
            this.addEventListener( FlexEvent.CREATION_COMPLETE, onCreationComplete );
        }
        
        private function onCreationComplete( e:FlexEvent ):void
        {
            var p1:Panel;
            var p2:Panel;
            var p3:Panel;
            var label:Label;
            var sp:SetProperty;
            var parallel:Parallel;
            var moveEffect:Move;
            var resizeEffect:Resize;
            var transition:Transition;
            
            //Panel 1
            p1 = new Panel();
            p1.name = "One";
            p1.title = "one";
            p1.x = 0;
            p1.y = 0;
            p1.width = 100;
            p1.height = 100;
            p1.addEventListener( MouseEvent.CLICK, onMouseClick );
            label = new Label;
            label.setStyle( "fontSize", "24" );
            label.text = "One";
            p1.addChild( label );
            this.addChild( p1 );
            
            //Panel 2
            p2 = new Panel();
            p2.name = "Two";
            p2.title = "two";
            p2.x = 0;
            p2.y = 110;
            p2.width = 100;
            p2.height = 100;
            p2.addEventListener( MouseEvent.CLICK, onMouseClick );
            label = new Label;
            label.setStyle( "fontSize", "24" );
            label.text = "Two";
            p2.addChild( label );
            this.addChild( p2 );

            //Panel 3
            p3 = new Panel();
            p3.name = "";
            p3.title = "Three";
            p3.x = 110;
            p3.y = 0;
            p3.width = 200;
            p3.height = 210;
            p3.addEventListener( MouseEvent.CLICK, onMouseClick );
            label = new Label;
            label.setStyle( "fontSize", "24" );
            label.text = "Three";
            p3.addChild( label );
            this.addChild( p3 );

            //State 1
            var state:State; 
            var properties:Array;
            var property:Array;
            
            state = new State();
            state.name = "One";
            properties = [  [ p1, "x",     110   ], 
                            [ p1, "y",   0    ],
                            [ p1, "width",  200  ],
                            [ p1, "height", 210  ],
                            [ p2, "x",    0 ],
                            [ p2, "y",    0 ],
                            [ p2, "width" 100   ],
                            [ p2, "height", 100  ],
                            [ p3, "x",    0 ],
                            [ p3, "y",    110   ],
                            [ p3, "width" 100   ],
                            [ p3, "height", 100 ],
                        ];
            for each ( property in properties )
            {
                sp = new SetProperty( property[0], property[1], property[2] );
                state.overrides.push( sp );
            }
            this.states.push( state );
            
            //State 2
            state = new State();
            state.name = "Two";
            properties = [ 
                            [ p2, "x",    110   ],
                            [ p2, "y",    0 ],
                            [ p2, "width" 200   ],
                            [ p2, "height", 210  ],
                            [ p3, "x",    0 ],
                            [ p3, "y",    110   ],
                            [ p3, "width" 100   ],
                            [ p3, "height", 100 ],
                        ];
            for each ( property in properties )
            {
                sp = new SetProperty( property[0], property[1], property[2] );
                state.overrides.push( sp );
            }
            this.states.push( state );

            //transition 설정
            transition = new Transition;
            transition.fromState = "*";
            transition.toState = "*";

            parallel = new Parallel();
            parallel.targets.push( p1, p2, p3 );
            
            moveEffect = new Move();
            moveEffect.duration = 400;
            parallel.addChild( moveEffect );

            resizeEffect = new Resize();
            resizeEffect.duration = 400;
            parallel.addChild( resizeEffect );
            
            transition.effect = parallel as Effect;
            this.transitions.push( transition );
        }
        
        
        private function onMouseClick( e:MouseEvent ):void
        {
            var p:Panel = e.currentTarget as Panel;
            if( null != p )
            {
                this.currentState = p.name;
            }
        }
    }
}

*출처 : 지돌스타

// ActionScript file
import mx.containers.TitleWindow;
import mx.controls.CheckBox;
import mx.controls.advancedDataGridClasses.AdvancedDataGridColumn;
import mx.events.CloseEvent;

public static var popupColumns:TitleWindow = null;

private function loadData(_xml:XML):void{
 
 var gc:GroupingCollection = new GroupingCollection;
 
 var colPreferences:Array = Preferences.getInstance.getGridPreference("dataGridTotalSite");
 
  var g:Grouping = new Grouping;
  
  var gf1:GroupingField = new GroupingField;
  var gf2:GroupingField = new GroupingField;
  
  gc.source = GridList;
  gf1.name = "SITENAME";
  gf2.name = "MDNAME";
  
  var sr1:SummaryRow = new SummaryRow;
  var sr2:SummaryRow = new SummaryRow;
  
  sr1.summaryPlacement = "group";
  sr2.summaryPlacement = "group";
  
  var vcnt_field:SummaryField = new SummaryField;
  vcnt_field.dataField = "VCNT";
  vcnt_field.operation = "SUM";
  
  sr1.fields = [vcnt_field];
  sr2.fields = [vcnt_field];
  
  var vhname:AdvancedDataGridColumn = new AdvancedDataGridColumn;
  vhname.dataField = "VHNAME";
  vhname.headerText = "사이트 - 매체|상품";
  vhname.width = 200;
                
  var vcnt:AdvancedDataGridColumn = new AdvancedDataGridColumn;
  vcnt.dataField = "VCNT";
  vcnt.headerText = "방문자";
  vcnt.width = 100;
  vcnt.formatter = numFormat;
  vcnt.setStyle("textAlign", "right");
  
  dataGridTotalSite.groupedColumns = [vhname, vcnt];
  
  var _sf:SummaryField; 
  var _column:AdvancedDataGridColumn; 
  var _adgcg:AdvancedDataGridColumnGroup;
  
  for(var j:Number = 0; j<GridColumnTree.group.length(); j++){
    
   _adgcg = new AdvancedDataGridColumnGroup;
   _adgcg.headerText = GridColumnTree.group[j].label;
   
     for(var i:Number=0; i<GridColumnTree.group[j].fields.node.length(); i++){
      
      // SUM
      if(GridColumnTree.group[j].fields.node[i].operation=="SUM"){
        
     _sf = new SummaryField;
     _sf.dataField = GridColumnTree.group[j].fields.node[i].id;                 
     _sf.operation = GridColumnTree.group[j].fields.node[i].operation;
     
     sr1.fields = sr1.fields.concat(_sf);
     sr2.fields = sr2.fields.concat(_sf);
     
     _column = new AdvancedDataGridColumn;                  
     _column.dataField = GridColumnTree.group[j].fields.node[i].id;
     _column.headerText = GridColumnTree.group[j].fields.node[i].label;
     //_column.width = GridColumnTree.group[j].fields.node[i].colwidth;                  
     _column.setStyle("textAlign", GridColumnTree.group[j].fields.node[i].align);
     if(GridColumnTree.group[j].fields.node[i].format=="numFormat"){
      _column.formatter = numFormat;
     }else if(GridColumnTree.group[j].fields.node[i].format=="numUnderFormat"){
      _column.formatter = numUnderFormat;
     }
     
     if(colPreferences!=null){
      
      for(var k:Number=0; k<colPreferences.length; k++){
       
       if(colPreferences[k].dataField==_column.dataField){                
        (_column as AdvancedDataGridColumn).visible = colPreferences[k].visible;
        (_column as AdvancedDataGridColumn).width = colPreferences[k].width;
        break;
       }
       
      }
      
     }else{
      _column.width = GridColumnTree.group[j].fields.node[i].colwidth;      
     }
     
     _column.sortCompareFunction = sortColumns;
     
     
     if(_adgcg.children==null){
      _adgcg.children = [_column]; 
     }else{
      _adgcg.children = _adgcg.children.concat(_column);
     }
      
      // AVG 
      }else{
        
     _column = new AdvancedDataGridColumn;                  
     _column.dataField = GridColumnTree.group[j].fields.node[i].id;
     _column.headerText = GridColumnTree.group[j].fields.node[i].label;
     //_column.width = GridColumnTree.group[j].fields.node[i].colwidth;                  
     _column.setStyle("textAlign", GridColumnTree.group[j].fields.node[i].align);     
     if(GridColumnTree.group[j].fields.node[i].format=="numFormat"){
      _column.formatter = numFormat;
     }else if(GridColumnTree.group[j].fields.node[i].format=="numUnderFormat"){
      _column.formatter = numUnderFormat;
     }
     
     if(colPreferences!=null){
      
      for(var k:Number=0; k<colPreferences.length; k++){
       
       if(colPreferences[k].dataField==_column.dataField){                
        (_column as AdvancedDataGridColumn).visible = colPreferences[k].visible;
        (_column as AdvancedDataGridColumn).width = colPreferences[k].width;
        break;
       }
       
      }
      
     }else{
      _column.width = GridColumnTree.group[j].fields.node[i].colwidth;      
     }
     
     _column.sortCompareFunction = sortColumns;
     
     if(_adgcg.children==null){
      _adgcg.children = [_column]; 
     }else{
      _adgcg.children = _adgcg.children.concat(_column);
     }
     
     if(GridColumnTree.group[j].fields.node[i].labelFunc=="getROI_TOTAL"){
      _column.labelFunction = getROI_TOTAL;          
     }else if(GridColumnTree.group[j].fields.node[i].labelFunc=="getCPA_TOTAL"){
      _column.labelFunction = getCPA_TOTAL;
     }else if(GridColumnTree.group[j].fields.node[i].labelFunc=="getCRATE_TOTAL"){
      _column.labelFunction = getCRATE_TOTAL;           
     }else if(GridColumnTree.group[j].fields.node[i].labelFunc=="getROI_AD"){
      _column.labelFunction = getROI_AD;
     }else if(GridColumnTree.group[j].fields.node[i].labelFunc=="getCPA_AD"){
      _column.labelFunction = getCPA_AD;
     }else if(GridColumnTree.group[j].fields.node[i].labelFunc=="getCRATE_AD"){
      _column.labelFunction = getCRATE_AD;
     }else if(GridColumnTree.group[j].fields.node[i].labelFunc=="getCLICKRATE"){
      _column.labelFunction = getCLICKRATE;
     }
      }
   } // End of for(inner)
   
   dataGridTotalSite.groupedColumns = dataGridTotalSite.groupedColumns.concat(_adgcg);
   
  } // End of for(outer)
  
  gf1.summaries = [sr1];
  gf2.summaries = [sr2];
  
  g.fields = [gf1, gf2];
  gc.grouping = g;
  
  dataGridTotalSite.dataProvider = gc;
  dataGridTotalSite.addEventListener(AdvancedDataGridEvent.COLUMN_STRETCH, setColumnChoice);
  dataGridTotalSite.addEventListener(AdvancedDataGridEvent.HEADER_RELEASE, sortHeader);
  
  gc.refresh();
}

private function getSum(item:Object, col:AdvancedDataGridColumn):*{
 var total:Number = 0;
 for each(var num:* in item){
  if(num is Number)
   total += num;
 }
 return total;
}   


private function getROI_TOTAL(item:Object, col:AdvancedDataGridColumn):*{ 
 if(item.TOTAL_AMOUNT==0)
  return 0;
 else  
  return numUnderFormat.format((item.TOTAL_AMOUNT/item.ADSPEND)*100);
}

private function getROI_AD(item:Object, col:AdvancedDataGridColumn):*{
 if(item.AD_AMOUNT==0)
  return 0;
 else  
  return numUnderFormat.format((item.AD_AMOUNT/item.ADSPEND)*100);
}

private function getCPA_TOTAL(item:Object, col:AdvancedDataGridColumn):*{
 if(item.ADSPEND==0)
  return 0;
 else 
  return numUnderFormat.format(item.ADSPEND/item.TOTAL_CNT);
}

private function getCPA_AD(item:Object, col:AdvancedDataGridColumn):*{
 if(item.ADSPEND==0)
  return 0;
 else
  return numUnderFormat.format(item.ADSPEND/item.AD_CNT);
}

private function getCRATE_TOTAL(item:Object, col:AdvancedDataGridColumn):*{
 if(item.TOTAL_CNT==0)
  return 0;
 else
  return numUnderFormat.format((item.TOTAL_CNT/item.VCNT)*100);
}

private function getCRATE_AD(item:Object, col:AdvancedDataGridColumn):*{
 if(item.AD_CNT==0)
  return 0;
 else  
  return numUnderFormat.format((item.AD_CNT/item.VCNT)*100);
}

private function getCLICKRATE(item:Object, col:AdvancedDataGridColumn):*{
 if(item.CLICKCNT==0)
  return 0;
 else  
  return numUnderFormat.format((item.CLICKCNT/item.EXCNT)*100);
}

private function copyToClipBoard():void

 var columns:Array = dataGridTotalSite.columns;
 var textFromItems:String = "";
 var arr : Array = dataGridTotalSite.selectedItems
 if(arr.length == 0){
  arr = ArrayCollection(GridList).toArray();
 }
 for each (var it:Object in arr){
  for each (var c:AdvancedDataGridColumn in columns)
    textFromItems += it[c.dataField] + "\t";
  textFromItems += "\r\n";
 }
 if(textFromItems != ""){
  flash.system.System.setClipboard(textFromItems);
  if(dataGridTotalSite.selectedItems.length > 0){
   alert.info("선택된 ROW를 클립보드에 복사했습니다.\n\n문서에 붙여넣기 하시기 바랍니다.");   
  }else{
   alert.info("전체 ROW를 클립보드에 복사했습니다.\n\n문서에 붙여넣기 하시기 바랍니다.");   
  }
 } 
}


private function sortHeader(event:AdvancedDataGridEvent):void {
 columnToSort = event.dataField; 
}
  

/**
* Creates a titlewindow and pops it up on button click
*/   
private function createColList():void{
 var SPACING_X : int = 0;
 var SPACING_Y : int = 0;   
 popupColumns = new TitleWindow();
 popupColumns.id = "cvsColumnList";
 popupColumns.name = "cvsColumnList";
 popupColumns.title = "필드선택";
 popupColumns.showCloseButton = true;
 popupColumns.addEventListener("close",closePopup);
 popupColumns.styleName = "columnList";
 popupColumns.setStyle("horizontalGap",1);

 var rect:Rectangle = select_field_img.getRect(select_field_img);
 var pt:Point = select_field_img.localToGlobal(rect.bottomRight);   

 if((pt.x + SPACING_X + popupColumns.minWidth) > root.width){
  // show side
  popupColumns.x = pt.x - SPACING_X-popupColumns.minWidth;
  popupColumns.y = pt.y - SPACING_Y;
 }
 else {
  // show below
  popupColumns.x = pt.x + SPACING_X;
  popupColumns.y = pt.y + SPACING_Y; 
 }
 PopUpManager.addPopUp(popupColumns, select_field_img, true);
}

/**
* Closes the titlewindow.
*/   
private function closePopup(event:CloseEvent):void{
 setColumnChoice();
 popupColumns.visible = false;
}

/**
* Populates the popped up titlewindow with checkboxes, one
* each for each columns defined. The visible columns will
* be checked and the non-visible columns will be unchecked. 
*/   
private function columnList():void{
 
 if(popupColumns == null){
  createColList();
 }else{
  popupColumns.removeAllChildren();
  popupColumns.visible = true;
 }
  
     
 for(var i:int = 0; i<dataGridTotalSite.columnCount;i++){
  
  var chkb:CheckBox = new CheckBox();
  chkb.id = i.toString();
  
  if(dataGridTotalSite.columns[i].visible == false){
   chkb.selected = false;
  }else{
   chkb.selected = true;
  }
  
  var _flag:Boolean = false;
  
  for(var j:Number = 0; j<GridColumnTree.group.length(); j++){
   
   for(var k:Number=0; k<GridColumnTree.group[j].fields.node.length(); k++){
    
    if(GridColumnTree.group[j].fields.node[k].id==dataGridTotalSite.columns[i].dataField){     
     chkb.label = dataGridTotalSite.columns[i].headerText+"("+GridColumnTree.group[j].label+")";
     _flag = true;
     break;     
    }
   }
   
   if(_flag){
    break;
   }
  }
  
  if(!_flag){
   chkb.label = dataGridTotalSite.columns[i].headerText;
  }   
   
  //chkb.label = dataGridTotalSite.columns[i].headerText;
  chkb.addEventListener(Event.CHANGE,showhideCol);
  popupColumns.addChild(chkb);
 }
 
 PopUpManager.bringToFront(popupColumns);
 
}

/**
* Called on click of checkboxes. Hides or Shows columns
* based on checked status.
*/   
private function showhideCol(event:Event):void{
 var adgc:AdvancedDataGridColumn;
 adgc = dataGridTotalSite.columns[event.target.id];
 adgc.visible = event.target.selected; 
}

/**
* Saving column choice in SO
*/   
private function setColumnChoice(event:AdvancedDataGridEvent = null):void{
 var dCols:Array = new Array();
 for(var i:int=0;i<dataGridTotalSite.columnCount;i++){
   var colattr:Object =new Object();
   colattr.dataField = dataGridTotalSite.columns[i].dataField;
   colattr.visible = dataGridTotalSite.columns[i].visible;
   colattr.width = dataGridTotalSite.columns[i].width;   
   dCols.push(colattr);
 }
 Preferences.getInstance.setGridPreference("dataGridTotalSite", dCols);
}


private function sortColumns(obj1:Object, obj2:Object):int{
 var i:int = 0;
 if(obj1[columnToSort] != null && obj2[columnToSort] != null){ //checking for null
  if((Number)(obj1[columnToSort]) && (Number)(obj2[columnToSort])){ //checking for number
   var nval1:Number = obj1[columnToSort];
   var nval2:Number = obj2[columnToSort];

   if (nval1 < nval2) {
    i = -1;
   } else if (nval1 > nval2) {
    i = 1;
   }     
  } else{
   var tval1:String = obj1[columnToSort].toString().toLowerCase();
   var tval2:String = obj2[columnToSort].toString().toLowerCase(); 
   if(tval1 > tval2){
    i = -1;
   }
   else if(tval1 < tval2){
    i = 1;
   }
  }
  
 }
 return i;
}






[Bindable]
public var GridColumnTree:XML = <GridColumnTree>                  
         <group>
          <id>TOTAL</id>
          <label>전체</label>
          <fields>
           <node>
            <id>TOTAL_CNT</id>
            <label>전환수</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
           <node>
            <id>TOTAL_AMOUNT</id>
            <label>매출액</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
           <node>
            <id>TOTAL_ROI</id>
            <label>ROI</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>AVG</operation>
            <format>numUnderFormat</format>
            <labelFunc>getROI_TOTAL</labelFunc>
           </node>
           <node>
            <id>TOTAL_CRATE</id>
            <label>전환율</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>AVG</operation>
            <format>numUnderFormat</format>
            <labelFunc>getCRATE_TOTAL</labelFunc>
           </node>
           <node>
            <id>TOTAL_CPA</id>
            <label>CPA</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>AVG</operation>
            <format>numUnderFormat</format>
            <labelFunc>getCPA_TOTAL</labelFunc>
           </node>
          </fields>                  
         </group>            
         <group>
          <id>AD</id>
          <label>광고</label>
          <fields>
           <node>
            <id>AD_CNT</id>
            <label>전환수</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
           <node>
            <id>AD_AMOUNT</id>
            <label>매출액</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
           <node>
            <id>AD_ROI</id>
            <label>ROI</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>AVG</operation>
            <format>numUnderFormat</format>
            <labelFunc>getROI_AD</labelFunc>
           </node>
           <node>
            <id>AD_CRATE</id>
            <label>전환율</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>AVG</operation>
            <format>numUnderFormat</format>
            <labelFunc>getCRATE_AD</labelFunc>
           </node>
           <node>
            <id>AD_CPA</id>
            <label>CPA</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>AVG</operation>
            <format>numUnderFormat</format>
            <labelFunc>getCPA_AD</labelFunc>
           </node>
          </fields>                  
         </group>           
         <group>
          <id>RE</id>
          <label>재방문</label>
          <fields>
           <node>
            <id>RE_CNT</id>
            <label>전환수</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
           <node>
            <id>RE_AMOUNT</id>
            <label>매출액</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
          </fields>                  
         </group>            
         <group>
          <id>MEDIA</id>
          <label>매체DATA</label>
          <fields>
           <node>
            <id>EXCNT</id>
            <label>노출수</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
           <node>
            <id>CLICKCNT</id>
            <label>클릭수</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
           <node>
            <id>ADSPEND</id>
            <label>광고비</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>SUM</operation>
            <format>numFormat</format>
           </node>
           <node>
            <id>CLICKRATE</id>
            <label>클릭율</label>
            <colwidth>100</colwidth>
            <align>right</align>
            <operation>AVG</operation>
            <format>numUnderFormat</format>
            <labelFunc>getCLICKRATE</labelFunc>
           </node>
          </fields>                  
         </group>      
        </GridColumnTree>;







<mx:LineChart id="vs1_chart"
       showDataTips="true"
       itemClick="lineChartHandler(event)"
       dataTipFunction="myDataTipFunc"         
       width="100%"
       height="100%">

private function myDataTipFunc(e:HitData):String{
 
 var s:String;
 var dname:String = LineSeries(e.element).displayName;
 
 s = "<B>시간 : " + e.item.DATE_STR + "</B>\n";
 s += "------------------------\n";
  
  if(dname=="방문수"){
   s += "- 방문수 : <B><FONT COLOR='#FF0000'>"+numFormat.format(e.item.VCNT)+"</FONT></B>\n";
  }else{
   s += "- 방문수 : "+numFormat.format(e.item.VCNT)+"\n";
  }      
    return s;
}

public function drawChart():void{
 
 var seriesArray:Array = new Array();
 
 var _lineSeries:LineSeries = new LineSeries();
 
 var _stroke:Stroke;
 
 _lineSeries.id = "line_vcnt";
 _lineSeries.yField = "VCNT_RATE";
 _lineSeries.radius = 6;
 _lineSeries.setStyle("form","curve");
 _lineSeries.displayName = "방문수";
 _lineSeries.setStyle("showDataEffect","showEffects"); 
 _stroke = new Stroke(fontColorArr[0], 3); 
 _lineSeries.setStyle("lineStroke", _stroke); 
 _lineSeries.buttonMode = true;
 
 seriesArray.push(_lineSeries);
 
 if(REPORT_VISIT_DIV.selectedItem.code=="T"){
  
  if(cb_cnt.selected){
   
   _lineSeries = new LineSeries();
   _lineSeries.id = "line_ccnt";
   _lineSeries.yField = "TOTAL_CNT_RATE";
   _lineSeries.radius = 6;
   _lineSeries.setStyle("form","curve");
   _lineSeries.displayName = "전환수";
   _lineSeries.setStyle("showDataEffect","showEffects");   
   _stroke = new Stroke(fontColorArr[1], 3);   
   _lineSeries.setStyle("lineStroke", _stroke);  
   _lineSeries.buttonMode = true;
   
   seriesArray.push(_lineSeries);
   
  }
  
  if(cb_amount.selected){
   
   _lineSeries = new LineSeries();
   _lineSeries.id = "line_amount";
   _lineSeries.yField = "TOTAL_AMOUNT_RATE";
   _lineSeries.radius = 6;
   _lineSeries.setStyle("form","curve");
   _lineSeries.displayName = "매출액";
   _lineSeries.setStyle("showDataEffect","showEffects");   
   _stroke = new Stroke(fontColorArr[2], 3);   
   _lineSeries.setStyle("lineStroke", _stroke);
   _lineSeries.buttonMode = true;
      
   seriesArray.push(_lineSeries);
   
  }
  
  if(cb_crate.selected){
   
   _lineSeries = new LineSeries();
   _lineSeries.id = "line_crate";
   _lineSeries.yField = "TOTAL_CRATE_RATE";
   _lineSeries.radius = 6;
   _lineSeries.setStyle("form","curve");
   _lineSeries.displayName = "전환율";
   _lineSeries.setStyle("showDataEffect","showEffects");      
   _stroke = new Stroke(fontColorArr[3], 3);   
   _lineSeries.setStyle("lineStroke", _stroke);
   _lineSeries.buttonMode = true;
   
   seriesArray.push(_lineSeries);   
  }   
  
  cb_crate.visible = true;  
  li_crate.visible = true;
          
  
 }else if(REPORT_VISIT_DIV.selectedItem.code=="A"){
  
  if(cb_cnt.selected){
   _lineSeries = new LineSeries();
   _lineSeries.id = "line_ccnt";
   _lineSeries.yField = "AD_CNT_RATE";
   _lineSeries.radius = 6;
   _lineSeries.setStyle("form","curve");
   _lineSeries.displayName = "전환수";
   _lineSeries.setStyle("showDataEffect","showEffects");   
   _stroke = new Stroke(fontColorArr[1], 3);   
   _lineSeries.setStyle("lineStroke", _stroke);
   _lineSeries.buttonMode = true;
   
   seriesArray.push(_lineSeries);
   
  }
  
  if(cb_amount.selected){
   
   _lineSeries = new LineSeries();
   _lineSeries.id = "line_amount";
   _lineSeries.yField = "AD_AMOUNT_RATE";
   _lineSeries.radius = 6;
   _lineSeries.setStyle("form","curve");
   _lineSeries.displayName = "매출액";
   _lineSeries.setStyle("showDataEffect","showEffects");   
   _stroke = new Stroke(fontColorArr[2], 3);   
   _lineSeries.setStyle("lineStroke", _stroke);
   _lineSeries.buttonMode = true;
   
   seriesArray.push(_lineSeries);  
   
  }
  
  if(cb_crate.selected){
   
   _lineSeries = new LineSeries();
   _lineSeries.id = "line_crate";
   _lineSeries.yField = "AD_CRATE_RATE";
   _lineSeries.radius = 6;
   _lineSeries.setStyle("form","curve");
   _lineSeries.displayName = "전환율";
   _lineSeries.setStyle("showDataEffect","showEffects");   
   _stroke = new Stroke(fontColorArr[3], 3);   
   _lineSeries.setStyle("lineStroke", _stroke);
   _lineSeries.buttonMode = true;
   
   seriesArray.push(_lineSeries);
   
  }
    
  cb_crate.visible = true;  
  li_crate.visible = true;
  
 }else{
  
  if(cb_cnt.selected){
   
   _lineSeries = new LineSeries();
   _lineSeries.id = "line_ccnt";
   _lineSeries.yField = "RE_CNT_RATE";
   _lineSeries.radius = 6;
   _lineSeries.setStyle("form","curve");
   _lineSeries.displayName = "전환수";
   _lineSeries.setStyle("showDataEffect","showEffects");   
   _stroke = new Stroke(fontColorArr[1], 3);   
   _lineSeries.setStyle("lineStroke", _stroke);
   _lineSeries.buttonMode = true;
   
   seriesArray.push(_lineSeries);
   
  }
  
  if(cb_amount.selected){
   
   _lineSeries = new LineSeries();
   _lineSeries.id = "line_amount";
   _lineSeries.yField = "RE_AMOUNT_RATE";
   _lineSeries.radius = 6;
   _lineSeries.setStyle("form","curve");
   _lineSeries.displayName = "매출액";
   _lineSeries.setStyle("showDataEffect","showEffects");   
   _stroke = new Stroke(fontColorArr[2], 3);   
   _lineSeries.setStyle("lineStroke", _stroke);
   _lineSeries.buttonMode = true;
   
   seriesArray.push(_lineSeries);  
   
  }
  
  cb_crate.visible = false;
  li_crate.visible = false;
 }
 
 vs1_chart.series = seriesArray;
  
}




<mx:VBox width="100%" height="100%" verticalAlign="middle" horizontalAlign="center" id="vs1" verticalGap="0">
       
      <mx:HBox width="100%" horizontalAlign="center" verticalAlign="middle">
      
       <mx:HBox horizontalAlign="left" width="100%" verticalAlign="middle">
        <mx:Label text="재방문여부 >" />
        <mx:ComboBox id="REPORT_VISIT_DIV" height="20" dataProvider="{ReportVisitDivObj}" labelField="name" change="drawChart();" />
       </mx:HBox>
       
       <mx:HBox horizontalAlign="right" width="100%" verticalAlign="middle">
       
        <mx:Legend
         direction="horizontal"
         id="vs1_chart_legend"           
         horizontalAlign="right">
         
         <mx:HBox horizontalGap="3" verticalAlign="middle" id="legend_hbox">
          <mx:LegendItem label="방문수" horizontalGap="0" >
           <mx:fill>
            <mx:SolidColor color="{fontColorArr[0]}" />
           </mx:fill>
          </mx:LegendItem>         
          
          <mx:CheckBox id="cb_cnt" selected="true" click="drawChart()" />          
          <mx:LegendItem label="전환수" horizontalGap="0">
           <mx:fill>
            <mx:SolidColor color="{fontColorArr[1]}" />
           </mx:fill>
          </mx:LegendItem>
          
          <mx:CheckBox id="cb_amount" selected="true" click="drawChart()" />          
          <mx:LegendItem label="매출액" horizontalGap="0" >
           <mx:fill>
            <mx:SolidColor color="{fontColorArr[2]}" />
           </mx:fill>
          </mx:LegendItem>
          
          <mx:CheckBox id="cb_crate" selected="true" click="drawChart()" />          
          <mx:LegendItem label="전환율" horizontalGap="0" id="li_crate">
           <mx:fill>
            <mx:SolidColor color="{fontColorArr[3]}" />
           </mx:fill>
          </mx:LegendItem>
          
         </mx:HBox>
         
        </mx:Legend>
        
       </mx:HBox>
       
      </mx:HBox>
       
      <mx:LineChart id="vs1_chart"
       showDataTips="true"
       itemClick="lineChartHandler(event)"
       dataTipFunction="myDataTipFunc"         
       width="100%"
       height="100%">
      
       <mx:backgroundElements>
        <mx:GridLines direction="both" />        
       </mx:backgroundElements>
       
       <mx:horizontalAxis>
        <mx:CategoryAxis id="vs1_h_axis" categoryField="DATE_STR"/>
       </mx:horizontalAxis>
       
      </mx:LineChart>
       
     </mx:VBox>



/**************** Data String Format *********************/
private function formatDate(item:Object, col:DataGridColumn):String {
 var date:String = item.ENTRYDATE.toString();
 if(date==null || date.length<12)
  return "";
 else
  return date.substring(2, 4)+"."+date.substring(4, 6)+"."+
    date.substring(6, 8)+" "+date.substring(8, 10)+":"+date.substring(10, 12);
}


1.  Avoid embed containers inside other containers. Reducing use of relative size and relative position

When the element size in container is described by percent, any change of the size or postion will take the re-position for all subset in container. The calculation will be great if the level of embed is deep.

2. Using lightweight containers like Canvas As far as possible

Canvas is the smallest container and only support absolutely positioin. Most time it could instead HBOx and VBox. Besides, Canvas is a first choice for us when custom containers. It has basic container function and good expand ability.

3. Avoid using large components like DataGrid, AdvancedDataGrid
Large components have powerful function but need high requirement for memory and CPU. Because of the complexity, it is difficult to realize the skin, patterns and itemRenderer.

4. Using paging when deal with data

When using data type control, as far as possible to minimze the amount of showing data. For example Tilelist, it will create all the data whether need or not. It will waste large resoures. When ViewStack 、TabNavigator, etc. dealing with element, data will not be created until they’re shown first time. The unnecessary cost will be avoided.

5. setStyle and styleName

In fact,the skin of Flex components is a visual element. In process of components initialization, they will use current style(for example:styleName) to finish all the skin elements.If we reset the style, the size of components by setStyle, the postion will also be adjusted. Link to the first point, if components in a deep level embed container will cost large calculation.



오류를 범하기 쉬운 Flex 코딩 바로잡기

컴퓨팅 기술은 나날이 빠르게 발전하여 우리의 컴퓨터 환경을 바꾸고 있다. 불과 몇 년 전만 해도 텍스트 위주의 화면구성을 사용자에게 보여주었던 것에 불과한 것이 이제는 이미지와 텍스트 그리고 동영상을 수반한 UCC라는 개념이 등장했다. 이에 사용자들은 좀 더 사용자 위주의 UI를 원하게 되었고, 그에 따라 사용자들을 만족시키기 위해 새로운 개념 RIA(Rich Internet Application)이 탄생하게 되었다.


RIA 기반의 프로그램은 이전의 순차적 웹 페이지 방식이 아닌 이벤트 방식을 수반하고 있으며, 웹 개발자 또한 이에 발맞추어 새로운 방식으로 구조를 설계하고 개발할 필요가 있게 되었다. 그리고 Flex 또한 애플리케이션과 같은 이벤트 방식을 취하고 있다.

Flex는 애플리케이션과는 또 다르게 Flex만의 UI 특징이 있으며 이를 제대로 숙지하지 않은 상태에서 개발해나간다면 상당한 어려움에 빠질 수 있다. 이 문서는 Flex 개발시 고려해야 할 점을 이야기하며, 실무 개발에 도움이 됐으면 한다.


Effect가 지니고 있는 점

Effect는 Flex에서 아주 빈번하게 사용되지만 때때로 사용자의 예외적인 반응에 의해

제대로 작동을 하지 않아 사용자나 개발자 둘 다 당혹하게 만드는 경우가 있다. 그 문제는 다음의 Listing 1.1 에서 확인할 수 있다.


Left 버튼을 누르고 target으로 이동하는 사이에 Right 버튼을 클릭하면 당장 오른쪽으로 이동하지도 않을뿐더러 중간중간 멈칫하기도 한다. 따라서 Right 버튼을 누른 사용자는 자신이 원하는 방향으로 이동하지 않았기 때문에 이것을 버그라고 생각하게 된다.



<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">


       <mx:Move id="btnMover" duration="2000" />

       <mx:Button id="target" label="www.adobeflex.co.kr" moveEffect="{btnMover}"/>


       <mx:Button label="Left" click="target.x = 10" />

       <mx:Button label="Right" click="target.x = 500" />

</mx:Application>
 


Listing 1.1 - Effect 오류 가능성이 있는 Flex 소스


Move의 부모 클래스에 해당하는 Effect의 play() 메쏘드를 살펴보면 그 어디에도

이팩트가 작동 중이었을 경우 다시 실행하는 문제에 대해 처리하지 않고 있다. 이 문제는 isPlaying  속성을 사용하여 다음과 같은 방법으로 해결해 보았다.


<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">


       <mx:Move id="btnMover" duration="2000"/>

       <mx:Button id="target" label="www.adobeflex.co.kr" moveEffect="{btnMover}"/>

     

       <mx:Button label="Left" click="leftClick()" />

       <mx:Button label="Right" click="rightClick()" />

     

       <mx:Script>

             <![CDATA[

                    public function leftClick():void

                    {

                           if(btnMover.isPlaying) btnMover.stop();

                         

                           target.x = 10;

                           btnMover.play();

                    }

                  

                    public function rightClick():void

                    {

                           if(btnMover.isPlaying) btnMover.stop();

                         

                           target.x = 500;

                           btnMover.play();

                    }

             ]]>

       </mx:Script>

</mx:Application>


Listing 1.2 - Effect 오류 가능성 제거


함수 rightClick() 안쪽을 살펴 보면 isPlaying 속성의 반환 값을 체크한 뒤에 stop()

메쏘드를 호출하는 방법을 사용하였다.

이제 rightClick()는 안전하게 중지된 시점부터 다시 새로운 지점으로의 자연스런 이동이 가능하게 구현되었다. 다만 stop() 메쏘드는 SDK 3부터 지원하기 때문에 이전의 버전의 SDK를 사용한 애플리케이션은 이 방법을 사용할 수가 없다.


잘못된 addChild의 사용법

addChild, removeChild는 컨테이너에 컨트롤들을 등록하는 가장 쉬운 방법이다. 하지만 addChild를 사용할 때 반드시 지켜야 할 점이 있는데, 그것은 사용자 반응과 연동시켜서는 안 된다는 것이다.


<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"

       layout="vertical" initialize="initApp()">


       <mx:Button label="addChild" click="addChildClick()" />

       <mx:Button label="removeChild" click="removeChildClick()" />

     

       <mx:Script>

             <![CDATA[

                    import mx.controls.Label;

           

                    private var _child:Label = new Label();

                  

                    public function initApp():void

                    {

                          _child.text = "www.adobeflex.co.kr";

                    }

                  

                    public function addChildClick():void

                    {

                           addChild(_child);

                    }

                  

                    public function removeChildClick():void

                    {

                           removeChild(_child);

                    }

             ]]>

       </mx:Script>

</mx:Application>



 Listing 2.1 - 컨트롤과 addChild를 연동시킨 예


Listing 2.1에서 ‘addChild’버튼을 누르면 ‘www.adobeflex.co.kr’ 이라는 문구가 뜨고

‘removeChild’ 를 누르면 화면에서 사라집니다. 우리는 아주 간단한 동작만으로 치명적인 에러 메시지를 만들 수 있다. Remove 버튼을 두 번 눌러보면 콘솔 창에 다음과 같은 문구가 뜰 것이다.


ArgumentError: Error #2025: 제공된 DisplayObject는 호출자의 자식이어야 합니다.


여기에서 무엇이 잘못된 것일까? removeChild 안쪽에는 반드시 타겟이 해당 메쏘드의 자식으로 등록된 DisplayObject만이 해지할 수 있도록 만들어져 있다. 여기에 엉뚱한 다른 컨테이너의 자식을 넣게 되면 위와 같은 메시지가 발생하게 되는 것이다.

try~catch로 예외 처리를 하게 되면 이 오류메시지를 피할 수 있다. 하지만 이렇게 등록과 해제를 하게 되는 과정에서 외부 함수가 접근하려 했을 때 _child 의 접근에 신뢰를 가지기가 힘들게 된다. 여기서 말하고자 하는 점은 메쏘드 addChild가 아닌 사용자의 반응에 직접적으로 addChild를 호출한 점이 문제인 것이다.


좀 더 복잡한 경우 이 Label의 showEffect 또는 hideEffect가 등록되어 있다면 이 이팩트의 실행여부를 판단해주어야 하는 점도 있다. 이런 점은 visible 속성을 사용하는 것으로 해결할 수 있다.


<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"

       layout="vertical" initialize="initApp()">


       <mx:Button label="addChild" click="addChildClick()" />

       <mx:Button label="removeChild" click="removeChildClick()" />

     

       <mx:Script>

             <![CDATA[

                    import mx.controls.Label;


                                 private var btn:Label = new Label();

                                 

                                 public function initApp():void

                                 {

                                        btn.text = "www.adobeflex.co.kr";

                                        addChild(btn);

                                 }

                                 

                                 public function addChildClick():void

                                 {

                                        btn.visible = true;

                                 }

                                 

                                 public function removeChildClick():void

                                 {

                                        btn.visible = false;

                                 }


             ]]>

       </mx:Script>

</mx:Application>



Listing 2.2 - visible 속성의 사용


addChild를 사용자 반응과 관계 없는 initApp() 메쏘드에 삽입함으로써 유저의 버튼 동작에 신경 쓰지 않아도 되고, 전체적인 버튼 접근에 관한 잠재 가능성을 배제할 수 있게 되었다.


컴포넌트 밖 외부데이터와의 처리

Flex의 데이터 바인딩은 개발자의 복잡한 소스를 아주 쉽고 간단하게 만들어준다. 그 외에도 외부데이터의 응답시간을 신경 쓸 필요가 없다는 장점이 있다. 이는 itemRenderer  같은 데이터와 해당 컴포넌트의 시점을 가늠할 수 없을 때에도 사용된다.


Listing 3.1 은 바인딩 되지 않은 형태로 itemRenderer를 통해 컨트롤에 삽입해 봤다.


<mx:itemRenderer>

<mx:Component>

       <mx:Box>

             <mx:Script>

             <![CDATA[

            

                    override public function set data(value:Object):void

                    {

                           output.text = value.toString();

                           super.data = value;

                    }

                   

             ]]>

             </mx:Script>

            

             <mx:Label id="output" />

       </mx:Box>

</mx:Component>

</mx:itemRenderer>


Listing 3.1 - 외부데이터를 인라인 컴포넌트 컨트롤에 적용


Listing 3.1은 몇몇의 경우에는 문제를 일으키지 않을 경우가 있다. 하지만 웹 상에서 데이터를 읽어오는 경우 또는 컴포넌트가 생성되지 않았을 시점에 데이터가 들어오는 경우 null 객체 참조 에러가 발생하게 된다.


TypeError: Error #1009: null 객체 참조의 속성이나 메쏘드에 액세스할 수 없다.


그 외에도 null 이 데이터에 들어왔을 경우도 생각해야 한다. 데이터 바인딩은 이러한 경우에 많은 도움이 된다. 컴포넌트의 생성 시점을 생각할 필요가 없기 때문이다.


<mx:itemRenderer>

<mx:Component>

       <mx:Box>

             <mx:Script>

             <![CDATA[

                   

                    [Bindable] private var outputStr:String;

            

                    override public function set data(value:Object):void

                    {

                           outputStr = value.toString();

                           super.data = value;

                    }

                   

             ]]>

             </mx:Script>

            

             <mx:Label text="{outputStr}" />

       </mx:Box>

</mx:Component>

</mx:itemRenderer>


Listing 3.2 - 바인딩을 이용한 외부데이터 처리


위와 같이 외부 데이터와 컨트롤을 바인딩으로 묶어줌으로써 이 문제는 해결된다. 이처럼 외부데이터가 컨트롤에 직접 접근하는 것은 피해야 할 방법 중 하나이다.


코드와 뷰의 완벽한 분리

현재MXML 기반의 Flex는 화면을 다른 파일로 관리하지 않는 한 코드와 뷰를 분리하기란 쉽지가 않다. 따라서 이를 혼용 사용하다 보면 때때로 서로 섞이게 되어 난잡한 코드를 작성하게 될 경우가 많다.


<?xml version="1.0" encoding="utf-8"?>

<mx:Panel

       xmlns:mx="http://www.adobe.com/2006/mxml"

       layout="absolute" width="400" height="300"

       initialize="initPanel()">

     

   <mx:Script>

         <![CDATA[

                import mx.controls.Button;

       

                private var closeBtn:Button = new Button();

                private function initPanel():void

                {

                       closeBtn.width = 100;

                       closeBtn.height = 20;

                       closeBtn.label = "close";

                }

              

                override protected function createChildren():void

                {

                       super.createChildren();

                     

                       titleBar.addChild(closeBtn);

                }

         ]]>

   </mx:Script>


</mx:Panel>



Listing 4.1-  코드에 의존한 Panel 컴포넌트


Listing 4.1은 Panel titleBar에 버튼을 추가하는 내용을 코드에 너무 의존한 형태인데 Flex에 익숙하지 않은 프로그래머들이 자주 사용하게 되는 패턴이라고도 볼 수 있다. Flex 프로젝트의 특성상 디자인은 자주 바뀌게 되는데 이렇게 디자인 요소를 코드상에 표현하게 되면 유지보수가 어렵고 개발에 많은 시간이 소요된다. 이것을 태그 형태로 바꾸어 보도록 하자.


<?xml version="1.0" encoding="utf-8"?>

<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="400" height="300">

      

             <mx:Script>

                    <![CDATA[

                           override protected function createChildren():void

                           {

                                  super.createChildren();

                                  titleBar.addChild(closeBtn);

                           }

                    ]]>

             </mx:Script>

      

             <mx:Button id="closeBtn" width="100" height="20" label="close"/>

</mx:Panel>


Listing 4.2 - 디자인을 태그로 표현한 Panel 컴포넌트


보기에도 간편할 뿐 아니라 디자인 모드에서도 수정이 가능한 형태로 바뀌었다. 이처럼 태그는 코딩의 속도를 높여줄 뿐만 아니라 유지보수 및 소스분석에도 많은 장점들을 가지고 있다. 그렇다고 무작정 태그가 좋은 건 아니다. 태그의 표현에도 한계가 있기 때문이다. 여기서 태그와 코드가 가져가야 하는 부분을 나누어 생각해 보았다.


1. 비주얼 컴포넌트(Visual Component)는 모두 태그 기반으로 가야 한다.

2. 넌 비주얼 컴포넌트 중 이벤트 기반의 컴포넌트는 태그로 가야 한다.

      예) HTTPService , Timer , Effect , TargetTrace …

3. 넌 비주얼 컴포넌트 중 Formatter, Validator는 태그로 가야 한다.

이상으로 Flex 개발시 참고하면 좋은 사항을 살펴봤다. 현재 Flex의 요구가 점점 많아지고 있는 추세이며 새로운 Flex 개발자들이 늘어나고 있기에, 본 글이 개발시 조금이나마 도움이 됐으면 하나 바람이다. Flex의 특징을 이해하고 MXML 자체의 특성을 잘 받아들인다면 훨씬 훌륭한 코드를 작성하게 될 것이다.

* 출처 : http://www.adobeflex.co.kr/iwt/board/board.php?tn=pds_tech&id=163&mode=view



AS3.0이든 자바든..

누구나 다 한번씩은 겪었을 참조 복사. 즉 reference복사에 대해 몇 자 끄적여 보겠다.
어딜 가든 이에 대한 질문은 늘 올라오는 듯하여...ㅋㅋ

어떤 질문인가 들여다보자면...

"제가 A라는 객체를 B라는 객체로 복사해서 어떤 조작을 했는데 B객체 뿐만 아니라 A객체까지 값이 변했어요"
"제가 A라는 배열을 B라는 배열에 복사해서 B라는 배열의 값들에 대해 정렬을 했는데 A 배열의 값까지 정렬이 되버렸어요."

어떤 어플리케이션에서 프로그램 실행중에 저런 문제가 발생했다면 생각만해도 아찔하다.
특히나 돈이 걸려있는 금융쪽이라면 더더욱.^^;(의도되지 않은 코드라는 전제하에..ㅋㅋ)

자 ...우리 개발자들이 이런 기초적인 실수를 하지 않기 위해선
Shallow copy와 Deep copy에 대해 잘 기억하고 있어야 한다.

아! 본론으로 돌아와서 왜 저런 문제가 생기나요? 물으신다면...
이에 대한 답변은 당신은 "참조 복사, 즉 주소값만 복사했기 때문입니다." 이라고 답하겠다.
( Pass by Value 가 아니라 Pass by Reference임을 잊지 마시오~~^^)

Shallow copy. 얕은 복사. 단순 복사라고 부르는데.
자바나 AS3.0은 객체지향언어로서 모든 객체들은 레퍼런스, 즉 참조값이다.
우리가 흔히 지정하는 변수명은 실제 변수가 있는 메모리 공간을  가리키고 있는 것이다.
(자바와 AS3.0에서 참조(reference)라고 부르며....C언어의 포인터라 생각해도 무방할 듯 하다.
왜냐하면 자바에서도 엄연히 NullPointerException이 있지 않은가.-단순히 이렇게 생각해도 된다는 의미이지
포인터와 레퍼런스가 똑같다고 하는건 절대 아님^^;-)

자 어떤일이 생기는지 한번 보자. 플렉스에서 테스트....

            import mx.controls.Alert;

            public var original_array:Array = new Array("a","b","c");
            public var copy_array:Array = original_array;
           
            public function array_view():void{
              copy_array[0] = "z";
              Alert.show(original_array.toString()); // z,b,c
              Alert.show(copy_array.toString()); // z,b,c


            }

original_array와 copy_array를 각각 비교해보면 z,b,c가 나오는 것을 볼 수 있을 것이다.
바로 앞서 말한 Shallow copy가 이루어진 결과는 어떤가?
주소값만이 복사되어  배열의 요소값이 함께 변경되는 결과를 볼수 있다.

이를 피하기 위해서 실제 값까지 복사하는 것이 바로 Deep copy이다.

Deep copy. 깊은 복사. 완전 복사라고 부르는데.
AS3.0에선 ByteArray클래스를 사용해서 다음과 같이 할 수 있다.
         
            import flash.utils.ByteArray;
          import mx.controls.Alert;

            public function clone(source:Object):*{
                var myBA:ByteArray = new ByteArray();
                myBA.writeObject(source);
                myBA.position = 0;
                return(myBA.readObject());
            }
          
            public var original_array:Array = new Array("a","b","c");
            public var copy_array:Array = clone(original_array);
           
            public function array_view():void{
              copy_array[0] = "z";
              Alert.show(original_array.toString()); // a,b,c
              Alert.show(copy_array.toString()); // z,b,c

           }

original_array와 copy_array를 비교해보면 원본 객체의 변경없이

original_array는 a,b,c가 나오며 copy_array는 z,b,c가 나오는 것을 볼 수 있을 것이다..

플렉스에는 mx.utils.ObjectUtil클래스의 copy메소드가 있어서 이를 사용하면 된다.
다음과 같이 한 줄로....      

            import mx.controls.Alert;

            public var original_array:Array = new Array("a","b","c");
           
public var copy_array:Array =
                                        mx.utils.ArrayUtil.toArray
(mx.utils.ObjectUtil.copy
(original_array));             
           
            public function array_view():void{
              copy_array[0] = "z";
              Alert.show(original_array.toString()); // a,b,c
              Alert.show(copy_array.toString()); // z,b,c
           }

위에서 언급한 두가지 방법은  generic object에 대하여 Deep copy를 수행하는데 가장
좋은 방법이지만..문제가 좀 있다..

사용자 정의 클래스에 대한 Deep copy는 제대로 수행되지 않는다. 이게 머니 이게...
MyInfo라는 간단한 클래스를 하나 만들고  테스트 해보겠다.

package guerrilla.test
{
 
 public class MyInfo
 {
 
  private var _name:String;
  private var _age:int;
 
  public function MyInfo(){
   
  }
 
  public function get name():String{
   return this._name;
  }
 
  public function set name(name:String):void{
   this._name = name
  }
 
  public function get age():int{
   return this._age;
  }
 
  public function set age(age:int):void{
   this._age = age;
  }
 
 
  public function getName():String{
   return this.name;
  }
 
  public function getAge():int{
   return this.age;
  }

 }
 
}


자...다음과 같이 MyInfo 클래스의 인스턴스를 생성하고 테스트를 해보도록 하자..소스내의 clone메소드는 위에 작성한 그대로이다..

var original_MyInfo:MyInfo = new MyInfo();
   
   original_MyInfo.name = "guerrilla";
   original_MyInfo.age = 32;

var copy_MyInfo:Object = clone(original_MyInfo); //deep copy 수행
   
   copy_MyInfo.name="dora";
   copy_MyInfo.age=27;
     
var copy_MyInfo2:Object = mx.utils.ObjectUtil.copy(original_MyInfo); //deep copy 수행
     
  copy_MyInfo2.name="love";
   copy_MyInfo2.age=0;
   
   trace(original_MyInfo.name); // guerrilla
   trace(original_MyInfo.age);  // 32
   trace(original_MyInfo.getName()); // guerrilla
   trace(original_MyInfo.getAge()); // 32

     
   trace(copy_MyInfo.name); // dora
   trace(copy_MyInfo.age); // 27
   trace(copy_MyInfo.getName()); // TypeError: Error #1006: getName은(는) 함수가 아닙니다.
   trace(copy_MyInfo.getAge()); // TypeError: Error #1006: getAge(는) 함수가 아닙니다.
   
   trace(copy_MyInfo2.name); // love
   trace(copy_MyInfo2.age); // 0
   trace(copy_MyInfo2.getName()); // TypeError: Error #1006: getName은(는) 함수가 아닙니다.
   trace(copy_MyInfo2.getAge()); // TypeError: Error #1006: getAge(는) 함수가 아닙니다.
     


 자 ... 보시다시피 멤버필드는 이상없이 복사되었지만 어이없게도 멤버메소드는 함수가 아니란다..ㅡ.ㅡ;;
아 왜~~~!!! 함수가 아니라는거니...걍 함수해주면 안되겠니~~ 뭐 이리 쉽게 되는게 없어..이거..
하지만...늘 방법은 존재한다.. 프로그래밍 세계에선 안되는게 없다...무슨 수를 써서라도 되게 만들면 된다.ㅋ
바로  flash.net 패키지의 registerClassAlias라는 패키지레벨 함수로 해결할 수 있다..


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="
http://www.adobe.com/2006/mxml" layout="absolute" height="271" width="532"
 borderStyle="solid"
 borderColor="black"
 borderThickness="1" initialize="initApp()">
 
 <mx:Script>
 <![CDATA[
  import mx.controls.Alert;
  import mx.utils.ObjectUtil;
  import guerrilla.test.MyInfo;
  import flash.net.registerClassAlias;
  import flash.utils.ByteArray;
   
  registerClassAlias("myClass",MyInfo);
     
   
  private function clone(obj:Object):*{
   var myBA:ByteArray = new ByteArray();
   myBA.writeObject(obj);
   myBA.position = 0;
   return(myBA.readObject());
  }
 
  private function initApp():void
  {
   
var original_MyInfo:MyInfo = new MyInfo();
   
   original_MyInfo.name = "guerrilla";
   original_MyInfo.age = 32;

var copy_MyInfo:Object =
clone(original_MyInfo); //deep copy 수행
   
   copy_MyInfo.name="dora";
   copy_MyInfo.age=27;
     
var copy_MyInfo2:Object = mx.utils.ObjectUtil.copy(original_MyInfo); //deep copy 수행
     
   copy_MyInfo2.name="love";
   copy_MyInfo2.age=0;
   
   trace(original_MyInfo.name); // guerrilla
   trace(original_MyInfo.age);  // 32
   trace(original_MyInfo.getName()); // guerrilla
   trace(original_MyInfo.getAge()); // 32

     
   trace(copy_MyInfo.name); // dora
   trace(copy_MyInfo.age); // 27
   trace(copy_MyInfo.getName()); // dora
   trace(copy_MyInfo.getAge()); // 27
   
   trace(copy_MyInfo2.name); // love
   trace(copy_MyInfo2.age); // 0
   trace(copy_MyInfo2.getName()); // love
   trace(copy_MyInfo2.getAge()); // 0     
  }
 ]]>
 </mx:Script>
 
</mx:Application>

객체 복사하기 이리 번거로워서야...^^;;

다음엔 UIComponent 객체 복사에 대해서 알아봐야겠다.
UIComponent 객체는  clone메소드를 implement해야 한다고 한다..
(아직 위의 방법으로 UIComponent 객체에 대해 복사해보진 않았지만 문서에서는 저렇게 해야 한다함.)

내가 알기론 UIComponent를 일괄적으로 복사하는 건 불가능하다고 들었다..
(안해봤지만 강호의 고수분께서 가라사대...-이것도 해봐야겠고..할거 너무 많아..ㅠ.ㅠ-)
특정 클래스 인스턴스의 속성을 동적으로 알아오는 것이 불가능해서..
(for in 구문은 동적으로 추가된 속성만을 알려준다)
대신 ClassFactory와 ClassFactory의 properties 속성을  잘 이용하면 똑같은 인스턴스들을
한번에 여러곳에 쓸 수는 있다
(아이템 렌더러가 이런 방식임...실제 프로젝트에 적용해봤지만 이 부분도 면밀히 파봐야겠다..).
실제로 인스턴스를 on-Line Display List에 추가하기 전, off-Line Display List에 미리 전역으로 ClassFactory에 해당 인스턴스를 등록해 놓고,, 다른 곳에서 쓸 경우 이 ClassFactory의 newInstance() 메소드를 호출해 주면 된다..(이 부분은 예제소스를 올려놔야 겠군.. 제대로 한건지 태클 걸어줄 사람이 필요해..ㅠ.ㅠ)

아예 이런 방식이 아니고 서브 클래스 단에서 필요한 속성들만 대입해 새로운 인스턴스를 리턴하도록 하는 방식도 있다고 하고..... API에서는 이 방식을 말하는 듯 함...
 
그럼 이만^^
P.S.(python 처럼 copy(),deepcopy() 이렇게 제공되면 월매나 좋을꺼시여~~)

* 출처 : http://gogothing.tistory.com