2009년 8월 20일 목요일

Flex + BlazeDS + Java 웹프로젝트용 Ant Build.xml

Eclipse + Flex Builder로 개발한 웹애플리케이션을 WAR로 export하면 Flex 소스코드까지 그대로 묶여서 들어간다.
그래서, Flex나 Java 코드는 다시 compile하지 않고 웹애플리케이션으로 deploy할 때 꼭 필요한 컴파일된 파일들과 리소스 파일들을 별도의 디렉토리에 복사해 WAR로 묶은 다음 지정된 위치로 복사해 주는 기능이다.
사실 그냥 WAR로 묶어도 별 문제는 없을 것 같지만 혹시나 소스가 공개될 수도 있고, Flex 소스가 다 들어가면 WAR 파일이 너무 커져서 만들어 봤다.

 <!-- 현재 프로젝트의 컴파일된 코드를 WAR로 묶어 webapp.dir 속성에 지정된 디렉토리에 복사 . 추가로 컴파일은 하지 않음 -->
<project name="MyWebApp" basedir="." default="deploy">

    <!-- Directory to deploy the generated WAR file -->
    <property name="webapp.dir" value="C:/apache-tomcat-5.5.26/webapps" />
   
    <property name="web.app.name" value="FLEXAPP" />
    <property name="package.name" value="${web.app.name}.war" />
    <property name="dest.dir" value="target" />
    
    <property name="flex.file.dir" value="bin-debug"/>
    <property name="web.content.dir" value="WebContent"/>

    <target name="prepare">
        <mkdir dir="${dest.dir}" />
    </target>

    <target name="package" depends="prepare">
      <echo>Packaging...</echo>
     
      <!-- copy Flex and Java Web files -->
      <copy todir="${dest.dir}" overwrite="false">
        <fileset dir="${flex.file.dir}"/>
        <fileset dir="${web.content.dir}"/>
      </copy>
        
      <!-- package war file -->
      <war destfile="${dest.dir}/${package.name}"
              webxml="${dest.dir}/WEB-INF/web.xml"
              basedir="${dest.dir}"/>
    </target>

    <!-- deploy the generated war file -->
    <target name="deploy" depends="package">
      <echo>Deploying...</echo>
      <copy file="${dest.dir}/${package.name}"
              tofile="${webapp.dir}/${package.name}"
              overwrite="true" />
      <delete file="${dest.dir}/${package.name}" />
    </target>

</project>


2009년 7월 1일 수요일

Flex I18N 메시지 컴파일용 Ant Build.xml

플렉스를 하면서 다국어 처리를 하게 되었는데 할 때마다 명령 프롬프트에서 스크립트 실행하기도 귀찮고 함께 JSP도 사용하는데 따로 관리하려니 너무 머리가 아파서, 하나의 properties를 이용해서 mxmlc를 이용해 플렉스용 메시지파일을 생성하고, JSP용 properties 파일까지 만들어 주는 Ant 빌드파일을 만들었다. 다국어 메시지 파일을 한 번 만들고 끝날 게 아니라면 유용하게 사용할 수 있다.


기능 :
현재 다국어 메시지 파일을 Flex에서 사용할 수 있도록 swf로 컴파일하고 bin.debug.dir 속성의 디렉토리에 복사한 후에, 추가로 JSP에서 사용할 수 있도록 변환하고 java.locale.dir 속성의 디렉토리에 복사

사용법 :
나는 이클립스에서 사용하는데, 플렉스 프로젝트 루트디렉토리에 이 내용으로 build.xml을 만들고, 마우스 오른쪽 클릭하고 "Run As > Ant Build" 를 선택하면 실행된다.

-- 기본적인 다국어 컴파일에 관한 내용은 '예제로 배우는 플렉스'를 참고하시길.

<project name="LocaleCompile" basedir="." default="ko_kr">

    <property name="resource.dir" value="flex_src/resource" />
    <property name="bin.debug.dir" value="bin-debug/resource" />
    <property name="java.locale.dir" value="src/locale" />
  
    <property name="en.us.locale" value="en_US" />
    <property name="en.us.file" value="${en.us.locale}_ResourceModule.swf" />
    <property name="ko.kr.locale" value="ko_KR" />
    <property name="ko.kr.file" value="${ko.kr.locale}_ResourceModule.swf" />
  
    <property name="resource.bundles" value="collections,containers,controls,core,effects,formatters,flexsong,logging,SharedResources,skins,states,styles,utils,validators" />
  
    <!-- english -->
    <target name="en_us">
        <!-- flex -->
        <exec dir="${resource.dir}" executable="mxmlc">
            <arg line="-locale=${en.us.locale} -source-path=locale/{locale} -include-resource-bundles=${resource.bundles} -output ${en.us.file}"/>
        </exec>
        <copy file="${resource.dir}/${en.us.file}" tofile="${bin.debug.dir}/${en.us.file}"/>
      
        <!-- java -->
        <exec dir="${resource.dir}/locale/${en.us.locale}" executable="native2ascii">
            <arg line="-encoding UTF-8 flexsong.properties ${basedir}/${java.locale.dir}/flexsong_${en.us.locale}.properties"/>
        </exec>
    </target>
  
    <!-- korean -->
    <target name="ko_kr" depends="en_us">
        <!-- flex -->
        <exec dir="${resource.dir}" executable="mxmlc">
          <arg line="-locale=${ko.kr.locale} -source-path=locale/{locale} -include-resource-bundles=${resource.bundles} -output ${ko.kr.file}"/>
        </exec>
        <copy file="${resource.dir}/${ko.kr.file}" tofile="${bin.debug.dir}/${ko.kr.file}"/>
      
        <!-- java (JSP의 charset이 UTF-8이므로 encoding을 동일하게 처리하기 위해 encoding 옵션에 UTF-8을 추가했다) -->
        <exec dir="${resource.dir}/locale/${ko.kr.locale}" executable="native2ascii">
          <arg line="-encoding UTF-8 flexsong.properties ${basedir}/${java.locale.dir}/flexsong_${ko.kr.locale}.properties"/>
        </exec>
    </target>

</project>

2009년 5월 20일 수요일

DataGrid Paging 구현

일반 웹처럼 DataGrid에서 아래에 페이지번호를 달아서 페이징을 하면 좀 이상할 것 같아서, 우아하게 DataGrid의 스크롤이 맨 아래에 닿으면 다음 페이지를 읽어서 DataGrid의 아래에 추가해 주는 방법을 생각해냈다. 뭐, 사실 Grid를 사용하는 대부분의 프로그램들이 이렇게 한다. 하지만, Flex의 DataGrid에는 명시적으로 스크롤이 맨 아래에 닿았을 때 발생시키는 이벤트가 없어서 좀 고민을 많이 했다.

DataGrid에 scroll="scrollHandler(event)"

이렇게 scroll 이벤트를 이벤트핸들러에 연결시킨다.


아래는 이벤트핸들러 메소드

private function scrollHandler(event:ScrollEvent):void {
    if (grid.maxVerticalScrollPosition == event.position
       && event.delta > 0) {

        // do something
        Alert.show("end of Vertical scroll");
    }
}

event.delta > 0
이 부분은 스크롤을 맨 아래에 닿은 상태에서 계속 당기면 scroll 이벤트가 계속 발생하기 때문에, 끝에 닿은 상태에서 더 이상 못 움직일 때는 이벤트 처리를 하지 않도록 하기 위해서 사용했다.

이렇게 간단하게 끝날 문제는 사실 아니다. 스크롤바를 맨 아래서 올렸다 내렸다 하면 자꾸 이벤트가 발생하니까 같은 페이지를 중복해서 요청하지 않도록 하는 코드도 들어가야 할 것 같다.

2009년 5월 19일 화요일

id를 이용한 정적/동적 컴포넌트 참조

Flex에서 ActionScript를 사용하다 보면 컴포넌트를 직접 사용할 수도 있지만 id 문자열을 이용해 참조해야 할 경우가 종종 생긴다.

<mx:TextInput id="tiName" />

mxml 파일에 이런 TextInput이 선언되었다면,

tiName.text = "tiger";
이렇게 할 수도 있지만,

this["tiName"].text = "tiger";
이렇게 id 문자열을 이용해서도 참조가 가능하다.

지금까지는 정적으로 선언된 컴포넌트를 참조하는 법을 보았는데, 이외에 동적을 컴포넌트를 생성하는 경우도 가끔은 생긴다.

var textName:TextInput = new TextInput();
textName.id = "tiName";
someComp.addChild(textName);

이렇게 동적으로 생성한 컴포넌트는 this["id"] 형태로는 찾을 수가 없다. 그래서, 별도의 저장장소에 저장해 두고 사용해야 한다.

id가 원래 현재문서에서 unique한 것이므로, id를 key로 생성된 컴포넌트를 value로 해서 Dictionary에 저장해서 사용하면 적당할 것이다.

var dynamicComps:Dictionary = new Dictionary();
dynamicComps["tiName"] = textName;

위와 같이 저장하고, 참조하고 싶을 때는 dynamicComps["tiName"] 를 사용하면 된다.

Dictionary에 대한 보다 자세한 설명은 아래 링크 참조
http://flexsong.tistory.com/8

아주 유용한 이클립스 단축키

Ctrl + 마우스왼쪽클릭
변수에 마우스 포인터를 올려 놓은 상태에선 변수 선언 위치로, 클래스명 위에서 하면 클래스 소스로 이동한다.

Alt + ←

이전 편집 위치로 이동
파일간 이동이든, 파일내 이동이든 이 단축키를 누르면 이전 편집위치로 이동한다. 계속 누르면 계속 그 이전으로 이동한다. 'Ctrl + 마우스왼쪽클릭'으로 한참 이동했다가 다시 돌아올 때 유용하다.

Alt + →
'Alt + ←'과 반대로 이후 편집 위치로 이동

F4
이클립스의 프로젝트 익스플로러에서 클래스를 선택한 후에 F4를 누르면, 그 클래스의 상위 클래스구조를 트리형태로 보여준다.

Ctrl + Space
변수명이나 클래스명 등의 일부를 입력하고 이 단축키를 누르면 입력한 문자열로 시작하는 변수명과 클래스명 리스트가 나타나 선택할 수 있고, 선택대상이 하나면 리스트 없이 자동으로 입력된다. 클래스 변수 뒤에 '.'을 찍고 이 단축키를 누르면 해당 클래스에서 사용할 수 있는 변수와 메소드 리스트가 나타나 선택할 수 있다.

Ctrl + k
특정 문자열을 선택한 후에 이 단축키를 누르면 바로 다음에 나타나는 동일한 문자열로 이동한다. 울트라에디트나 아크로에디트의 'F3'과 동일한 기능이다. 단, 이 찾기 옵션은 이전에 'Ctrl + F'에서 사용한 옵션과 동일하게 적용되므로 원하는대로 못 찾는다면 이전에 '대소문자 구분'이나 '전체 단어' 등의 옵션 등이 선택했었는지 'Ctrl + F'를 눌러 확인해 보아야 한다.

Ctrl + Shift + k
Ctrl + k와 반대로 특정 문자열이 나타나는 바로 이전 위치로 이동한다. 울트라에디트나 아크로에디트의 'Shift + F3'과 동일한 기능이다.


물론, 위의 단축키들은 Flex 코드 개발시에도 동일하게 작동한다.

2009년 5월 15일 금요일

Flex에서 Java의 HashMap과 같은 기능 사용

Flex에도 Java의 HashMap과 같이 key=value 형태로 저장하고 조회할 수 있는 클래스가 있다. 그것이 바로 Dictionary.

아래와 같이 사용할 수 있다.
var dict:Dictionary = new Dictionary(); // 이렇게 생성해서
dict[key] = value; // 이렇게 값을 저장하고
dict[key]          // 이렇게 값을 꺼낸다
 
key와 value에는 아마도 Object가 들어가는 걸로 봐서 어떤 값이든 들어가는 것 같다. HashMap에서도 그랬듯이 나는 key에는 String, value에는 String 또는 Object를 넣는 데 주로 사용할 듯하다.


혹시나 이해가 안 갈까 봐, 아래는 간단한 예제 코드
var dict:Dictionary = new Dictionary();
dict["userid"] = "tiger";
Alert.show("User ID : " + dict["userid"]);

결과는 User ID : tiger


단, HashMap처럼 keySet이나 contains 같은 메소드는 전혀 없고, 오직 넣고 꺼내기만 된다.

2009년 5월 14일 목요일

Flex에서 HTML 페이지 띄우기

Flex에서 HTML(ASP, PHP, JSP 등 포함)를 띄울 일이 종종 생긴다.
이를 위해서 IFrame이라는 Flex Component가 있는데 이걸 제대로 사용하려면 wmode 같은 걸 설정하는데 이게 아무래도 Adobe 컴포넌트가 아니라서 DataGrid 스크롤이 휠로는 안 되는 등 뭔가 깔끔하게 처리되는 맛이 떨어진다. 그래서, 아예 웹브라우저를 하나 생성하는 게 가장 간편하고 확실한 방법인 듯하다.

아래 코드는 Flex에서 웹브라우저 팝업을 띄우는 예제이다.


// 작은 사이즈의 팝업으로 뜬다
if (ExternalInterface.available)
    ExternalInterface.call("window.open", url, "_blank", "width=800,height=600");
else {
    try {
        // 새로운 탭 또는 현재 웹브라우저와 같은 브라우저로 뜬다          
        navigateToURL(new URLRequest(url));
    }
    catch (e:Error) {
        Alert.show("Cannot open Popup : " + e.message);
    }
}


첫번째(if)에서 ExternalInterface.call은 현재 Flex가 동작하고 있는 웹브라우저에 자바스크립트 명령을 실행하는 방법이다. 즉, window.open(url, "_blank", "width=800,height=600"); 라는 자바스크립트 코드가 웹브라우저에서 실행된다. 이게 첫번째 옵션인 이유는 여기는 open 메소드의 세번째 인자로 다양한 옵션을 줄 수 있고, 팝업 차단이 되어 있어도 뜬다고 하기(확인 못 해 봤음) 때문이다. 나는 사이즈 옵션을 이용해 큰 브라우저가 아닌 작은 팝업을 만들고 싶었기 때문에 이것을 가장 우선으로 처리했다.
두번째(else)에서는
navigateToURL라는 메소드를 이용해 URL을 웹브라우저에 띄운다. 그런데, 여기는 별다른 옵션을 줄 수 없어 HTML 페이지가 새로운 탭에 열리거나 현재 웹브라우저와 같은 크기의 창이 열려 현재 Flex 화면을 가리기 때문에 ExternalInterface를 사용할 수 없을 경우에 어쩔 수 없이 사용하도록 처리했다.

참고:
navigateToURL을 이용해서 동적으로 생성된 컨텐츠를 다운로드 받게 할 수도 있다. navigateToURL에서 받는 URL의 페이지(ASP, PHP, JSP, Servlet 등)의 Contents Type을 text/html이 아닌 다른 값(예를 들면, Excel은 application/vnd.ms-excel)으로 하면 브라우저 창 대신 다운로드창이 떠서 다운로드할 수 있다.