Adobe AIR 2.7 버전이 정식 배포되었습니다. 개발자를 위한 SDK와 Runtime이 함께 배포되었습니다. Adobe AIR 2.7은 데스크탑 또는 모바일 환경 모두 동작하며 모바일의 경우 안드로이드 2.2 이상, BlackBerry Tablet OS, iOS 4.0 이상에서 동작합니다. 

현재 Flash Builder 4.5는 아직 Flex기반 iOS 어플 개발이 불가능하지만 Flash builder 4.5가 이번 달에 새로 배포되면 AIR 2.7이 같이 탑재되면서 Flex로 iOS 어플 개발이 가능하게 됩니다. 현재 Flash Builder 4.5에 AIR 2.7이 없어서 Flex로 iOS 개발을 막아놓았던 것 같네요.

iOS를 위한 AIR 어플은 CPU모드에서 4배이상 빠른 렌더링을 지원합니다. 기존에 Flex로 iOS기반 어플을 만들게 되면 퍼포먼스 문제가 있었는데 이 부분이 말끔하게 해결된 듯합니다. 다음 동영상을 보세요. 

Adobe AIR 2.7 : Faster app performance on iOS  http://goo.gl/VJxnR 

 iOS에서 디버깅 및 테스팅 환경이 많이 개선되었다고 합니다. 또한 Android기반에서 SD카드에 AIR 런타임을 설치할 수 있게 되었습니다. 

데스크탑환경에서 Media Measurement를 위한 통합기반이 지원됩니다. Adobe Flash Player 10.3에서 부터 지원되었으면 AIR 2.7에서도 이것을 지원합니다. 관련된 내용은 아래 링크를 참고하세요.

Measuring video consumption in Flash : http://goo.gl/wPcf0 

또한 Acoustic Echo Cancellation를 지원합니다. Flash Player 10.3에서도 지원하고 있습니다. 여기에 관련된 API가 도입됨에 따라 헤드셋없이도 애코조절이 훨씬 원할해집니다.

그리고 HTMLLoader에서 HTML 컨텐츠에 대한 로케이션 제어 권한이 훨씬 향상되었습니다. 과거에 이 부분때문에 고생한 적이 생각난다는..... 

다음 사이트들을 둘러보시면 AIR 2.7을 이해하는데 더욱 도움이 될 것입니다. 

Adobe AIR 2.7 Now Available : iOS apps 4x Faster! : http://goo.gl/5IEIz 
Adobe AIR 2.7 Developer Release Notes : http://kb2.adobe.com/cps/906/cpsid_90612.html 
Adobe AIR 공식페이지 : http://www.adobe.com/products/air/
Download Adobe AIR SDK : http://www.adobe.com/products/air/ 
Adobe Flash Builder : http://www.adobe.com/products/flash-builder.html

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


올해들어 Adobe Flash/AIR에 관련된 기술이 모바일의 흐름에 따라 급변하고 있습니다. 바쁘게 일하고 있는 사람으로써 이런 신기술을 흡수하기가 너무 어려울 정도입니다.

최근 새로운 CS 5.5버전이 발표되었고 Flash Builder 4.5가 함께 소개되었습니다. Flash Builder 4.5부터는 안드로이드 뿐만 아니라 블랙베리, iOS기반 어플도 개발할 수 있게 되었습니다. 현재 브라질에서 열리고 있는 Flash Camp에서 Flash Runtime의 미래라는 주제로 멀티스레드지원, 빠른 GC, 새로운 숫자형, Stage3D, StageVideo, Threaded video pipeline등의 차세대 Flash에 추가될 내용들이 소개되어지고 있고요. Adobe가 Flash의 GPU가속에 대한 지원을 아끼지 않으면서 3D렌더링에 대한 다양한 API가 만들어져 이미 Flash Player 11 Incubator 버전이 개발자들 사이에서 실험되고 있습니다. 모두 모바일에 초점이 맞추어 제한된 화면, 제한된 밧데리, 전혀 다른 사용자 경험에 따라 변해가고 있다는 생각입니다. 굉장히 전략적이고 빠른 대응을 하고 있습니다. 결국 시장의 급격한 변화에 Flash 기술이 따라가고 있으며 어찌보면 Cross Device, Cross Platform분야에서 만큼은 매우 선도적입니다.

Flash 기술이 급변함에 따라 예전처럼 쉽게 접근할 수 있던 Flash가 이제 더 이상 아닌 것처럼 느껴집니다. 더욱 전문성이 더해지기 시작했고 Flash의 원래 장점이였던 개발의 저변화를 통한 다양한 컨텐츠 생산이 약간 없어지는 상황이 발생하지 않을까라는 조심스런 추측도 합니다. 하지만 한편으로는 Flash IDE는 여전히 디자이너 접근을 용이하게 해주기 때문에 별 영향이 없을 것 같다는 생각도 합니다. 그러나 분명한 것은 모바일 디바이스의 대중화에 따른 사용자 경험이 바뀌고 이에 따라 기존 웹중심의 컨텐츠는 모바일 중심으로 변화하고 있다는 것에 대해 발빠르게 대응해야한다는 점은 인정해야할 사실이고 또한 준비해야합니다.

새로운 트랜드 대응에 도움을 주기 위해 관련된 정보를 링크해둡니다. 참고하시고 이를 통해 국내에서도 관련된 다양한 정보가 쏟아져 나오길 기대해 봅니다.


아래는 관련 동영상입니다.

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


Flash Player 10.2 beta가 2010년 12월 초에 배포가 되었다. 아무튼 강력한 GPU가속을 이용해 비디오 렌더링을 최적화 시킨 것이 가장 큰 변화라고 볼 수 있겠다. GPU를 이용하다보니 CPU 사용율이 급격히 줄어든다. 아래 동영상을 통해 Flash Player 10.2 beta의 GPU를 이용한 동영상 렌더링 효과를 눈으로 확인할 수 있다.


현재 Flash Player 10.2 beta는 정식 버전이 아니므로 자동으로 배포되어지지 않는다. 그러므로 다음 링크를 통해 다운로드 받을 수 있다. 개발자라면 debug 버전을 받을 것을 추천한다.


이번 배포의 변화는 다음과 같다.
1. Stage Video 하드웨어 가속
2. IE9에서 하드웨어 가속 지원
3. 네이티브 커스텀 마우스 커서 지원
4. 멀티모니터에서 풀스크린모드 지원 

이미 꽤 지난 내용이라 검색해보면 한글로된 설명글들도 있다.


Flash 분야에 손놓고 지낸지 오래라... 좀 파악하고 있는 중이다. ㅎㅎ

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

지난 Adobe MAX 2010에서 가장 이슈가 되었던 것중에 하나가 GPU 가속을 상시 지원해줄 수 있는 Molehill이라는 Flash Player 3D API 가 아닌가 싶다. Molehill은 Flash Player 3D APIs의 코드네임이다.

지금까지 Flash Player가 일부 GPU 가속을 지원해줬기 때문에 테스트 정도로만 가능했지만, 미래에 GPU를 상시 지원하게 되는 Molehill 3D API가 차세대 Flash Player에 장착하게 되면 파급효과가 클 것으로 생각된다. 

CPU에 의존해 왔던 3D 기존 프레임워크인 Away3D나 Alternativa3D등은 Molehill로 변환되어 배포될 것이고 많은 Flash 컨텐츠 제작사, 소셜게임제작사들이 새로운 Flash Player 3D API를 이용한 다양한 어플들을 내놓을 것이다. 더불어 이들 어플들은 고스란히 모바일에도 접목되게 될 것으로 예상이되어 파급효과가 클 것으로 전망이 된다.

지속적으로 Molehill에 대해서 관심을 가지고 지켜볼 필요가 있겠다.



위에 두 영상화면을 보고 Flash Player 에서 동작하는 모습이다라면 누가 믿겠는가? ㅎㅎ

읽을만한 글
Adobe Labs - 3D APIs for Adobe Flash Player and Adobe AIR
Adobe MAX 2010에서 소개된 차세대 GPU 가속 Flash API - Molehill
Molehill Flash Player 3D APIs 소개
Flash Player 3D APIs 간단한 소개글 - 잼있음
[동영상][at MAX 2010] GPU Acceleration on Adobe AIR "Molehill" - 우야꼬 군이 직접 미국에 MAX 행사에가서 찍은 영상입니다.
[동영상][at MAX 2010] Alternativa 3D in Adobe MAX 2010 - 우야꼬 군이 직접 미국에서 Molehill을 체험했군요.
[동영상] Adobe MAX 2010 - Alternativa 3D 시연 - 땡굴이 님께서 Alternativa의 시연모습을 동영상으로 담았습니다.
Molehill Programming Tutorial
ActionScript의 언어 순위는 몇 위일까?

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

(2011.07.21)이 글은 조금 오래된 글로 약간 현시점과 다른 내용이 있을 수 있습니다. 현재 Flash Builder는 4.5.1 까지 공식 배포중입니다. Flex 4.5.1 SDK, AIR 2.7도 함께 배포중이므로 참고하세요. 



한동안 아이폰 개발로 Flash 관련된 일을 전혀 하지 못하다 보니 Flash를 주제로 다룬 여기 블로그도 많이 정체되어 있다는 느낌을 지울 수 없더군요. 그래서 시간내서 간단하게 하나 적어봅니다.


지난 몇일전에 미국에서 Adobe MAX가 열렸었죠. 바쁜 나머지 관심있게 지켜보지 못해 주요 주제가 무엇인지는 잘 모르겠지만 여기저기 들리는 것을 보면 결국 "모바일"인 것 같습니다. 이번에 Preview 버전으로 배포된 Flash Builder "Burrito", Flex SDK "Hero", Flash Catalyst "Panini" 모두 모바일 개발에 대한 내용이 추가되었습니다.

저는 이 글에서 Flash Builder를 이용해 Flex나 AIR 어플리케이션을 만들어본 경험이 있는 분들에게 모바일 개발을 아주 쉽게 할 수 있는 일종의 팁을 전달하고자 합니다. 먼저 Flash Builder "Burrito"에 대해서 잠깐 소개하고 Adobe AIR Launchpad를 이용해 어떻게 모바일 AIR 어플리케이션을 만들 수 있는지 소개합니다.


Flash Builder와 Mobile이 만나다. - Flash Builder "Burrito"

Flash Builder "Burrito"에는 Flex SDK "Hero"가 이미 내장되어 있기 때문에 따로 설치할 필요가 없습니다. 여러분은 아래 링크에서 Flash Builder "Burrito"를 다운로드 받을 수 있습니다. 단, Adobe 회원에 먼저 가입하셔야 합니다.

http://labs.adobe.com/technologies/flashbuilder_burrito/

설치는 기존 Flash Builder 4 와 거의 동일합니다. 기존에 설치된 Flash Builder와 별도의 위치에 설치되므로 안심하고 설치하시면 됩니다.

Flash Builder "Burrito"를 실행하시면 먼저 아래와 같은 Start Page가 눈에 띕니다. 이 화면만 보아도 알 수 있듯이 Mobile과 Desktop&Web이 나눠져 있습니다. 여기를 따라 가면 간단한 예제가 있으니 쉽게 따라하실 수 있습니다.



Flash Builder "Burrito"는 아래처럼 모바일 프로젝트를 만들 수 있습니다.


현재는 아래처럼 안드로이드 플렛폼만 지원하고 있습니다. 테스트는 데스크탑에서 가상의 장비를 선택해 시뮬레이션을 할 수 있으며 또한 장비가 있다면 직접 물려서 테스트 해볼 수도 있습니다.


개인적으로 안드로이드 폰(HTC Desire)를 소유하고 있어서 Mobile 버전으로 간단하게 만들어 디바이스 시뮬레이팅을 할 수 있었습니다. 인증서 발급이나 별도의 설정없이 너무 간단하게 만들 수 있어서 기존 Flex 개발자라면 안드로이드용 어플 개발을 쉽게 할 수 있게 되었습니다. 아래 사진이 바로 그 실행결과입니다.



배포도 상당히 간단해서 기존에 AIR 어플리케이션에서 배포버전을 배포하듯이 간단한 인증서 발급만으로 APK로 배포할 수 있습니다.


Adobe AIR Launchpad - AIR 프로젝트 자동화 생성기
Adobe AIR 라운치 패드가 새로 배포되었습니다. 이것은 금방 소개한 Flash Builder "Burrito"를 겨냥해서 제작되었습니다. 이 어플은 Flex개발자들이 데스크탑 및 모바일 AIR 어플을 만드는데 도움을 주기 위한 일정의 자동화 프로젝트 생성기입니다.
 
Flash Builder에서 AIR 프로젝트를 만드는 것은 매우 쉽습니다. 하지만 AIR의 전반적인 기능을 이해해야 제대로된 설정을 할 수 있고 각종 설정은 사실 손이 많이 가는 작업일 수 있습니다. 능숙한 개발자야 문제가 안되지만 초보개발자라면 가이드라인을 제시받고 싶어할 것입니다. 이때 아주 유용할 것 같습니다.

설치하기 위해 다음 링크로 가시면 됩니다.
http://labs.adobe.com/technologies/airlaunchpad/

아래는 실행 첫화면입니다. 프로젝트를 자동으로 생성시켜주기 위한 첫번째 단계로 만들 어플이 데스크탑인지 모바일용인지 선택합니다.

어짜피 데스크탑용이야 많이 해봤으므로 저는 모바일을 선택했습니다. 아래와 같은 화면이 나옵니다. 프로젝트를 생성하기 위해 총 4단계(Setting, Configuration, Samples, Generate)를 거칩니다. 아래 화면은 그중 첫번째 단계지요.

위 화면에서 볼 수 있듯이 어플의 기본 설정을 도와줍니다. Application Name에 어플 이름을 등록하고 버전정보, 풀스크린, 자동회전, 인터넷 엑세스, GPS 등 사용여부를 결정할 수 있고 Icons를 선택하면 사용할 아이콘을 미리 등록할 수 있도록 합니다. 일반 AIR 프로젝트에서는 XML을 직접 편집해야했는데 이런 것을 이용하면 많이 편하겠네요. Install Location은 안드로이드 프로요 버전의 경우 내장 또는 외장(SD카드) 중 둘중에 한곳에 설치할 수 있습니다. 이때 설치 위치를 결정해줍니다. 일반적으로 Auto를 선택하면 되겠네요.

다음으로 아래화면과 같이 Configuration으로 넘어갑니다.  여기서는 소스 코드상에서 각종 핸들링을 지원할 것이냐 결정해줍니다. 필요한 것을 선택하면 원하는 코드가 미리 들어가 있게 됩니다. 참고로 Splash Screen은 실행전에 보여지는 로딩화면으로 생각하시면 됩니다. 여기서 미리 설정할 수 있게 해주네요. 아주 편합니다.
 


이제 아래 화면에서 볼 수 있듯이 Sample로 넘어갑니다. 초보개발자들에게는 이것처럼 좋은게 없겠다는 생각도 듭니다. 아무래도 아무것도 없는 코드보다 샘플이 일단 들어가 주면 개발에 도움이 될 것이기 때문입니다.



마지막으로 Generate 단계입니다. 지금까지 설정한 것을 토대로 AIR 프로젝트를 생성해 줍니다. 먼저 File location에서 프로젝트를 생성할 경로를 설정해줍니다. 그 다음 Generate AIR Project가 활성화 됩니다. 이 버튼을 클릭하면 생성이 완료됩니다. 이때 자동으로 탐색창을 띄워줍니다.


이제 만들어진 프로젝트를 Flash Builder "Burrito"로 열어봅시다. Flash Builder를 실행한 뒤 아래 처럼 File > Import Flash Builder Project를 선택합니다.


아래와 같은 창이 뜨면 만들어진 프로젝트를 여러분의 워크스페이스에 올려놓을 수 있습니다. 창에서 Import Project 를 보면 File과 Project folder를 볼 수 있습니다. Adobe AIR LaunchPad는 프로젝트를 폴더와 압축파일로 두개 만들어주므로 이들중 아무거나 선택해서 프로젝트를 등록하면 별 무리가 없습니다.

여기서는 File을 통해 프로젝트를 임포트 해보지요.


아래처럼 만들어진 프로젝트 압축파일을 선택하면 그 아래에 Import Method가 나옵니다. 여기서 압축된 프로젝트를 어느 곳에 어떤 이름으로 프로젝트를 압축을 풀어 만들 것인가 결정할 수 있습니다. 저는 그냥 Finish 버튼을 눌러 프로젝트를 임포트 하겠습니다.


이제 Flash Builder 에서 해당 프로젝트를 아래 처럼 열람할 수 있게 되었습니다. Adobe AIR LaunchPad를 통해 설정한 대로 프로젝트 명은 TestAIR가 되었고 각종 Sample View가 이미 만들어져 있습니다.


TestAIR 메인 페이지를 열어보았습니다. 아래처럼 mxml 코드를 볼 수 있습니다. 기존 AIR와 달라진 점은 Application이 MobileApplication이라는 점입니다. 당연한 이야기 겠죠? 참고로 firstView가 views.TestAIRHome으로 설정되어 있습니다. 위 패키지에서 views에 들어간 mxml중에 가장 최상위 뷰를 TestAIRHome으로 선택한 다는 것을 의미합니다.

Adobe AIR LaunchPad 의 2번째 단계에서 각종 핸들러를 선택해서 만들어진 코드입니다. 간단하게 trace가 출력되록 되어 있으며 여러분은 필요하다면 이 부분을 수정해서 원하시는 기능을 만드시면 됩니다.

디자인 모드로 들어가 봤습니다. 아래 화면과 같이 모바일에 최적화된 화면을 보여줍니다. Flash builder에서 모바일 어플을 만들 수 있게 된 것이 흥미롭습니다.


위 프로젝트를 실행해보겠습니다. Ctrl+F11 을 누르거나 메뉴에서 Run > Run을 선택해 실행합니다. 그러면 아래와 같은 창이 나오는데 이 화면은 이 글의 초반부에도 보였던 창의 모습입니다. 실행하기 위해 어떤 플랫폼에서 그리고 어떤 디바이스에서 시뮬레이팅을 할 것인지 결정해주는 역할을 합니다. 이 설정화면은 나중에 Run > Run Configurations에서 바꿀 수 있습니다.



위 화면에서 보면 타겟 플랫폼은 안드로이드입니다. 아직까지는 이것밖에 없습니다. 왜냐하면 Flash Builder "Burrito"가 Preview 버전이니깐요. 앞으로 정식버전이 나오면 윈도우, 아이폰 등도 지원하길 희망합니다. ^^

Launch method를 보면 On desktop과 On device가 있습니다. On desktop은 말그대로 데스크탑에서 모바일 시뮬레이터를 띄워줘 테스트 하는 겁니다. 안드로이드를 보니 제가 가지고 있는 HTC Desire도 있어서 기뻤습니다. 반대로 On device는 실제 장비에 물려서 테스트 합니다. 이들 모두 별 설정없이 실행이 가능했습니다. 

이제 Run만 누르면 바로 실행됩니다. 아래는 On device에서 실행해본 것입니다. Adobe AIR LaunchPad를 이용해 등록했던 각종 뷰 종류를 리스트에서 볼 수 있습니다. 마우스로 드래그 하면 아래 있는 나머지 리스트도 볼 수 있습니다. 하지만 데스크탑에서 시뮬레이션 하는 것이므로 Accelerometer나 Multitouch, Gesture등을 수행해 볼 수 없었습니다. 대신 안드로이드 폰이 있는 분은 직접 테스트 해볼 수 있습니다.







저는 제 안드로이드 폰인 HTC Desire에 위 어플을 실행해보고자 합니다. 먼저 메뉴에서 Run > Run Configuration을 선택합니다. 아래 화면처럼 좌측 메뉴에 Mobile Application에서 TestAIR를 선택한뒤 좌측상단에 복사하기 버튼을 누릅니다. 그럼 1개가 더 생성됩니다. 새로 생성된 것은 Name을 TestAIR on Device로 하고 기존것은 TestAIR on Desktop으로 명명합니다. 그리고 새로 생성된 것의 Launch method는 On device를 선택합니다. 그리고 Run을 누르면 자신의 안드로이드 폰에 해당 어플이 실행됩니다.


하지만 여기서 안드로이드 폰이 개발폰이 되도록 먼저 설정해야합니다.
1. 자신의 안드로이드 폰의 홈화면에서 menu > 설정을 누릅니다. 그리고 응용프로그램 > 개발 화면에서 USB 디버깅이 선택되어 있어야 합니다.
2. 자신의 안드로이드 폰에 AIR 런타임이 설치 되어야 합니다. 설치가 안된 안드로이드 폰이면 설치되도록 유도하니 Market에 직접 찾지 않고도 자연스레 설치할 수 있을겁니다.

아래는 제 폰에서 실제로 실행해보는 화면입니다.





정리하며

여기서 설명하지 않은 개발 방법이나 배포 방법에 대한 간단한 가이드는 다음 링크를 참고하면 좋을 것 같습니다.
Flex Test Drive for Mobile - Build a mobile application in an hour 

2009년 미국에서 Adobe MAX 를 참가해 Flash Builder로 아이폰 어플리케이션을 만드는 데모를 본 적이 있습니다. 당시에는 꽤 흥미를 느꼈습니다. 금방이라도 아이폰 어플을 Flash로 개발할 수 있을 줄 알았습니다. 하지만 Apple정책이 변경되면서 퇴색되었죠. 그것도 Flash CS5 런칭 몇일전에 Apple의 이기적인(?) 정책 발표였습니다. 그 후 1년이 지나 안드로이드 플랫폼이 강세가 이어지며 Flash기술은 다시 이야기 거리가 되고 있습니다. Apple도 안드로이드의 강세 행보에 위협을 느껴서(?) 일부 정책 수정있기도 했죠. 어쨌든 Flash가 모바일 디바이스에 자리 잡으려면 아직 해결해야할 과제가 산적해 있지만 그래도 노력한 만큼 성과는 있을 것이라 생각합니다. 


관련글
Flex Test Drive for Mobile - Build a mobile application in an hour
What's new in Flash Builder "Burrito"
Introducing Adobe Flex SDK "Hero"
Mobile development using Adobe Flex SDK "Hero" and Flash Builder "Burrito"
Coding productivity enhancements in Flash Builder "Burrito"



오랜만에 오픈캐스트를 올렸습니다. 즉흥적으로 만든거지만 유용하리라고 생각합니다.

좋은 정보가 되길 바랍니다.

http://opencast.naver.com/FL188/18

글쓴이 : 지돌스타(http://blog.jidolstar.com/694)
2010.04.27 10:00 AM 현재 27분이 참석인원으로 등록되었습니다.
맹기완, 김어진, 지용호, 김주호, 안세원, 박준우, 이성우, 한재화, 김범람, 문길섭, 정태현, 정진우, 사광호,  임대찬, 구준호, 이슬기, 이상훈, 윤도선, 김동진, 이진건, 유지남, 김학영, 박동준, 김민규, 김현우, 오현식 (이상 26명, 무작위순)

참석대상중에 개발자가 아니더라도 프로젝트에 관심이 있으신 회사대표나 이사급분들이 오셔도 좋을 것 같습니다. 아직 10분이 오실 수 있는 여유가 있으니 참석하실 분은 제게 문자(성함,회사,직급 정도)로 남겨주세요.



파워플에 대한 소개가 1주전에 있은 후에 많은 분들의 관심과 성원이 있었습니다. 그에 힘입어 파워플(Powerfl) 킥오프 모임을 하고자 합니다.

장소 : 한국 Adobe 대회의실. (서울 강남 교보타워 B동 16층 - 지도)
일정 : 2010년 4월 27일(화요일) 저녁 7시~9시
주최자 : 맹기완(Bsidesoft 대표), 지용호(위콘 커뮤니케이션즈 개발팀장, ACC)
연락 : 지용호 (010 - 2018 - 0321, jidolstar@gmail.com)
참여 대상
  • Flash,Flex,AIR 관련 업계 관계자 또는 개발자
  • 백엔드 서비스 관계자 또는 개발자
  • 각종 포탈, 에이전시, SI업계 관계자

내용

  • 파워플에 대해
  • 파워플 아키텍쳐 
  • 파워플 서비스 전략 및 로드맵
참석인원
  • 30명 내 (Adobe 대회의실 정원입니다.)
  • 정원이 제한되어 있는 관계로 참석을 희망하시는 분은 미리 아래 연락처로 연락주시길 바랍니다.
    지용호(010 - 2018 - 0321)
  • 기존 참가 신청자도 저한테 참석여부를 보내주시길 바랍니다.

공식페이지 : powerfl.com, powerfl.net, powerfl.co.kr 중 예정에 있습니다.
2차 모임 : 분위기 봐서 만들겠습니다. 그럼 회비가 필요하겠군요. ^^

파워플에 대해서 잘 모르시겠다면 다음 링크를 보세요.



파워플에 대한 이해를 돕기 위해 아래 이미지를 참고해주세요.




파워플은 단순히 Wonderfl 사이트를 모방한 한국 사이트가 아닙니다. Wonderfl을 밴치마킹했지만 Flex, Flash를 알고리즘 및 코드 단위가 아닌 아키텍쳐 기반의 솔루션 단위까지 승격한 서비스를 제공합니다. 솔루션 단위로 Flex, Flash 애플리케이션 및 라이브러리를 공유하게 되면 기업차원의 마케팅 및 홍보를 도모할 수 있게 되고 솔루션 프로젝트 단위로의 확장 및 재생산을 할 수 있게 됩니다. 그 결과 어도비 기술의 활용도를 최대한 끌어올림으로써 한국 IT분야의 새로운 혁신을 꽤할 수 있게 된다고 생각합니다.

애초에 Wonderfl는 flash이기 때문에 가능했던 겁니다. 반대로 Flash가 아니라면 이러한 시도는 할 수 없었습니다. 그만큼 Flash의 활용도는 뛰어납니다. Powerfl도 Flash이기 때문에 이러한 서비스가 가능해지는 겁니다. Flash의 다양한 활용성과 범용성에도 불구하고 한국 IT에서의 인식은 차갑기만 합니다. 파워플은 Flash 기술의 장점을 최대한 살려 사업적인 영역까지 재확대 시키고 사용자의 인식을 바로잡는데 크게 일조할 것이라 생각합니다.

여러분들이 그 주인공이 되었으면 합니다.

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

인터넷 상에 멋진 Flash 애플리케이션을 보면서 Flash 개발자라면 한번쯤은 어떻게 구현했을까 의문을 품어봤을 것이다. 필자 또한 그러했다. 하지만 남이 만든 소스를 보기 위해 SWF 파일을 디코드 하더라도 분석하기가 여간 까다로운게 아니다. 차라리 그럴바에 관련 내용을 담은 해외 서적을 하나 구입해서 공부하는게 더 빠를지도 모른다. 

이 문서는 자신의 Flash 제작 실력을 더욱 한층 높이기 위한 시도를 하고자 하는 분들을 위한 것이다. 중,고급 flash 개발 도약을 위해 도움이 되었으면 한다.

목차 
  • Wonderfl 소개 
  • Wonderfl의 주요기능 
  • Wonderfl 최고의 코드들 집합 Beautifl
  • Flash Builder 4에서 Wonderfl 코드 테스트 환경 만들기
  • Wonderfl 테스트 환경을 한번에 구축하기!
  • 정리하며
  • 참고사이트


Wonderfl 소개


Wonderfl(http://wonderfl.net)은 일본의 Kayac회사에서 제작하고 운영하고 있는 웹사이트로 ActionScript 3.0을 직접 인터넷 상에서 제작하고 컴파일할 수 있는 환경을 제공한다. Wonderfl은 Wonderful + flash가 합쳐진 단어로 생각된다. 매우 재미있고 기억하기 쉬운 단어 조합이다.

이 사이트는 단순히 컴파일하고 결과만 볼 수 있는 단계를 넘어 각종 공개된 라이브러리와 연동이 가능하며, 개발자들간에 코드 공유를 통해 더 좋은 코드로 개선할 수 있는 환경을 제공한다. 이로써 Flash 개발자간에 코드레벨 SNS를 실현했다고 생각한다.
 
개인적으로 Flash 개발자라면 이 사이트와 매우 가깝게 지내야한다고 생각한다. 실제로 일본의 매우 유명한 Flash 개발자들의 귀중한 노하우가 많이 공개되어 있기도 하다.

공개된 라이브러리를 활용하기 위한 환경을 구축하는 일을 매우 귀찮고 방법 또한 난해한 경우가 다분하다. 이런 경우 Wonderfl의 검색기능을 통해 필요한 기술을 쉽게 습득해서 그런 문제를 해결할 수도 있다.


Wonderfl 주요 기능

필자는 Wonderfl은 Flash 개발자간 코드공유 SNS라고 정의하고 싶다. Wonderfl의 몇가지 기능을 소개하면서 그 이유를 알아보도록 하자.

1. 코드 작성
Wonderfl의 가장 기본적인 기능 중에 하나로 코드 작성후 실시간으로 컴파일하고 결과를 볼 수 있는 기능이다. 이 기능은 Flex 컴파일러인 mxmlc을 활용해서 만든 것으로 파악한다. 그렇기 때문에 일반 ActionScript 3.0 코드외에도 MXML 태그로 작성해도 컴파일이 가능하다. 코드에 대한 라이센스도 함께 지정할 수 있고 작성이 완료된 코드는 블로그나 다른 커뮤니티에 퍼갈 수 있도록 되어 있다.


2. "Fork"기능을 이용한 다른 사람의 코드 수정
Fork기능은 이미 만들어진 코드를 퍼가서 자기 입맛대로 수정하는 것을 의미한다. wonderfl에 올라온 모든 코드는 이 작업이 가능하다. Fork의 강력함은 아래 소개하는 필자의 2개의 글을 참고하면 좋겠다.

10만개 입자를 이용한 유체 시뮬레이션 실험
Flash 속도 개선을 위한 실험 - 10만개 입자 유체 시뮬레이션 연장전!




3. 좋아하는 코드 찜하기
Fork 기능은 매우 강력하지만 또한 부담스러울 수 있다. 수정하지도 않을 코드를 Fork하는 것은 왠지 끌리지 않는다. 이럴때 사용할 수 있는 기능이 favorite 기능이다. 멋진 코드를 발견하면 "add to favorites"를 클릭하자. 그러면 다음에 설명할 "나의 페이지"에서 지금까지 favorite로 지정한 코드 리스트를 언제든지 참고할 수 있게 된다.


4. Following, Follower 기능
트위터와 같이 Following, Follower 기능이 존재한다. 코드 공유를 넘어 유용한 코드를 생산하는 사람을 Follow로 등록하면 나의 페이지에서 Following 리스트로 확인할 수 있고, 또 그 사람이 만들어내는 코드를 언제든지 쉽게 확인할 수 있게 된다. 그야말로 개발자 코드레벨 SNS 사이트인 것이다.

5. 나의 페이지
나의 페이지는 자신이 만든 코드, Fork나 favorite로 지정된 코드, Follow로 지정한 사람들의 동향을 확인할 수 있는 기능을 가지고 있다. 이 페이지는 "wonderfl.net/user/아이디"로 접근할 수 있다. 필자는 http://wonderfl.net/user/jidolstar 이다.



6. 질문&답변 기능
코드중에 이해하기 힘든 코드나 설명이 필요한 코드가 있다면 질문할 수 있다. 방법은 Fork를 하고 tag에 question을 달면 된다. 일본어, 영어로 해야하기 때문에 왠지 부담이 되겠지만 활용하면 분명 좋은 기능이다.





Wonderfl 최고의 코드들 집합 Beautifl
Beautifl(http://beautifl.net)은 Wonderfl에 올라온 멋진 코드들만 선별해서 종류별로 소개해준 사이트이다. 이 사이트는 Wonderfl에 올라온 코드중에 추천받은 코드만 엄선하여 보기 좋게 카테고리를 만들어 분류해주고 있다. 잘 이용하면 큰 도움이 되는 사이트이다.

Beautifl.net의 메인화면



Flash Builder 4에서 Wonderfl 코드 테스트 환경 만들기
Wonderfl에서 매우 좋은 코드 툴을 제공하고 있지만 약간의 수정외에는 Flash Builder 만큼 자유롭게 개발하기는 힘들다. Wonderfl에서 조금 놀다보면 이제 그곳에 있는 코드를 가져와 내것으로 만들어보고 싶게 된다.  

Wonderfl에는 Flash Player에서 제공하는 Native 클래스들(flash.* 패키지로 구성된 것들)뿐 아니라 외부 다른 유용한 라이브러리를 함께 활용할 수 있는 환경이기 때문에 그와 비슷한 환경을 만들어주는 것이 필요하게 된다. 필자처럼 Wonderfl에 공개된 코드를 매번 가져다가 테스트 해보는 사람은 필요할때 마다 라이브러리를 가져다 쓰는 것은 매우 귀찮은 작업이다. 그러므로 필요한 라이브러리를 한번에 가져와 언제든지 테스트 해볼 수 있는 환경을 구축할 필요성이 생긴다.

여기서는 라이브러리를 자신의 워크스페이스에 포함하는 방법과 그 라이브러리를 이용해 테스트를 하는 방법을 간단히 소개한다.

1. 라이브러리 가져오기
Wonderfl에서 사용하는 라이브러리 목록은 사이트 우측상단에 Wonderfl > libraries 메뉴로 들어가면 아래와 같은 화면을 통해 사용한 라이브러리의 종류와 버전을 확인할 수 있다.


각 라이브러리의 좌측에 download.swc를 직접 다운로드 받아 자신의 프로젝트에 포함시킬 수도 있다. 하지만 여러분은 단순히 라이브러리를 이용하는 차원이 아니라 직접 디버깅하며 코드를 분석할 필요가 있을 수 있다. 이러한 경우에는 직접 라이브러리 프로젝트를 만들어 소스를 항상 참조하는 환경을 만들어야 한다. 이러한 환경을 만들때 참고해야할 사항은 다음과 같다.

첫째. Wonderfl에 공개된 라이브러리는 다양한 배포 경로를 가진다.
공개된 라이브러리는 다양한 배포 경로를 가진다. 가령, Google Code에 최종 버전을 압축해서 배포하는 사람도 있지만 그냥 SVN에만 올려놓는 사람도 있다. 또 자신의 웹사이트(TweenMax의 경우)에서 직접 배포하는 경우도 있고 Google이 아닌 다른 커뮤니티(betweenAS3, thread의 경우 libspark.org)에서 배포하는 경우도 있다. 어떤 경우는 소스를 공개하지 않고 SWC만 공개하는 경우도 있다.(Alternative3D의 경우) 우리는 다양한 배포 경로에 맞게 알아서 참고해 사용해야한다.

둘째. Wonderfl에 사용된 라이브러리의 버전을 확인한다.
공개된 라이브러리는 왠만하면 Wonderfl에서 사용하고 있는 버전을 이용하는 것이 좋다. 그렇지 않으면 Wonderfl 코드를 사용할 때 버전차이로 컴파일 에러가 발생할 수 있다.  


셋째. 라이브러리간 의존성을 확인한다.
가령, jiglibflash 라이브러리의 경우 기본적으로 Papervision3D, Sandy3D, Alternative3D, away3d, five3d 라이브러리가 있는 경우에만 컴파일을 할 수 있다. 만약 jiglibflash에서 away3d, five3d를 제외한 다른 라이브러리만 사용하는 경우에는 해당코드를 찾아 컴파일 대상에서 제외해야한다. 아래 화면은 jiglibflash의 속성에서 필요없다고 판단한 클래스를 제외시키는 방법을 보여준다.



위와 같은 3가지 참고사항에 따라 Flash Builder의 워크스페이스에 라이브러리 프로젝트를 만들면 다음과 같은 Package Explorer의 모습을 기대할 수 있다.



2. Wonderfl 애플리케이션 제작 
위 과정을 통해 라이브러리를 자신의 워크스페이스에 받아왔으면 이제 테스트를 위한 애플리케이션 제작 프로젝트를 만들 차례이다. 

첫째. Flash Builder의 File>New를 통해 ActionScript 3.0 프로젝트를 만든다. 필자는 프로젝트 이름을 wonderfl로 했다.
둘째. 만들어진 프로젝트에 libs 폴더를 만들고 소스가 공개되지 않은 SWC 파일을 복사해둔다.

셋째. 라이브러리 경로를 잡아준다. 지금까지 만들어놓은 라이브러리를 전부 사용하기 위해 먼저 "Add Project..."버튼을 눌러 라이브러리를 모두 등록한다. 또 "Add SWC Folder..." 버튼을 눌러 libs를 입력해 libs 폴더에 SWC를 사용하는 라이브러리로 등록한다.

넷째, .actionScriptProperites의 <excludedEntries>부분을 모두 삭제한다.
<excludedEntries>는 FlexSDK를 이용해 mxmlc가 컴파일하는 과정에서 필요없는 라이브러리를 빼는 역할을 한다. 하지만 Wonderfl에 공개된 코드들은 FlexSDK의 모든 라이브러리를 거의 100% 사용하므로 이 부분을 제거할 필요가 있는 것이다. 하지만 .actionScriptProperites 파일은 숨김파일이므로 Package Explorer 창에 바로 보이지 않는다. 이 부분에 대해서는 http://blog.jidolstar.com/665 를 참고하고 <excludedEntries>를 제거하길 바란다.

다섯째, HTML wrapper 파일은 삭제해준다.
HTML wrapper 파일을 웹브라우저에서 실행하는 것을 가정하에 Flash 애플리케이션이 자동으로 HTML에 Embed처리 되도록 생성해주는 일종의 템플릿 코드이다. wonderfl의 코드들이 단독 Flash Player 에서 제대로 동작하는 경우가 있다. HTML wrapper 설정을 해지함으로써 제대로 동작하지 않는 것을 방지할 수 있는데, 굳이 이 설정을 한 상태로 하고 테스트 하다가 stageWidth, stageHeight관련 문제가 발생하면 http://blog.jidolstar.com/656를 참고하길 바란다.

 
여섯째, Wonderfl에서 마음에 드는 소스 코드를 복사한다.
일곱째, default package에 ActionScript 파일을 만들고 복사한 Wonderfl 코드를 붙인다. 이 때 주의할 것은 파일명과 클래스 명은 동일해야한다. 이때 이름규칙을 정해주면 좋다. bitmap실험은 bitmap_로, 게임은 game_으로, papervision3d면 pv3d_로 시작하도록 만들어놓으면 관리하기 용이해진다.

여덟째, 만들어진 ActionScript 코드를 실행하기 위해 Default Application으로 설정한다. (아래화면 참고)
이것은 .actionScriptProperties 파일의 <applications>에 등록하는 과정중 하나이다. 직접 .actionScriptProperties를 편집해도 되겠다.

아홉째. 실행하고자 하는 ActionScript 파일을 열고 Shift+Alt+X,W를 눌러 실행한다.
열째. 실행코드에 Wonderfl의 출처를 남겨두는 것이 좋다!
아래처럼 Wonderfl에서 가져온 코드의 출처를 남겨두면 나중에 다시 참고할 수 있게 된다.

마지막으로, 일본어 주석은 일본어 번역기를 이용한다.
필자의 경험상 네이버 일본어 번역기가 원본코드를 깨뜨리지 않고 알맞게 번역해주었다. 활용하면 도움이 된다.


Wonderfl 테스트 환경을 한번에 구축하기!
지금까지 라이브러리와 개발환경을 구축하는 방법을 소개했다. 하지만 이러한 개발환경을 만든다는 것은 너무 버겁고 귀찮은 작업이다. 필자는 이러한 문제를 한방에 해결할 수 있는 솔루션을 여러분께 선물하고자 한다.



필자는 Wonderfl 코드의 테스트 환경을 쉽게 만들어주기 위해 Naver 개발자 센터의 오픈프로젝트로 "원더플 개발환경구축 프로젝트"를 만들었다. 이 페이지는 Google Code와 유사한 오픈 프로젝트이다. SVN을 지원해주기 때문에 이 기능을 이용해서 위에서 설명한 라이브러리들을 단 한번의 Checkout으로 개발 환경을 만들 수 있다.

이것을 수행하기 위해 먼저 다음과 같은 툴이 필요하다.



위 과정을 다했으면 기본 개발 환경이 구축된 것이다. 다음으로 SVN에서 라이브러리를 다음과정을 통해 모두 checkout 한다. 

 
  • Flash Builder 4를 실행한후 새로운 워크스페이스를 만든다.
  • 메뉴에서 File>Import를 통해 창이 열리면 SVN>Checkout Projects from SVN을 선택한다.
  • Create a new repository location을 선택후 Next 버튼 누른다.
  • https://dev.naver.com/svn/wonderfl/trunk를 입력한다. 아이디는 네이버 아이디, 비밀번호는 anonsvn이다. 아이디, 비밀번호 모두 anonsvn 이다.
  • 열려있는 모든 프로젝트를 전부 선택한 뒤 Finish 버튼을 누른다. 자동으로 SVN에서 프로젝트에 필요한 라이브러리와 자료를 워크스페이스에 포함시키게 된다. 5~10분 소요됨
위 과정을 모두 완료했다면 Package Explorer에 다음과 같이 라이브러리 프로젝트들이 생성될 것이다.


또한 init_template도 있을 것이다. 이것은 테스트 프로젝트를 자동으로 만들기 위한 파일을 모아둔 것이다. 이중에 init.xml은 ANT구동 파일로서 그 역할을 담당하게 된다.(참고로 이런 방법론은 찬익님과 Hika님의 글에서 아이디어를 얻었다. 두분께 감사한다.)

테스트 프로젝트는 다음과 같은 과정으로 만든다. (복잡해 보일지 모르겠지만 한번 해보면 이보다 쉬운 방법은 없다고 생각할 것이다. ^^)


  • 워크스페이스에 ActionScript 3.0 프로젝트 생성한다.
  • init_template내에 init.xml을 생성한 프로젝트의 root에 복사한다.
  • init.xml을 열어 "프로젝트 이름을 넣는다" 안에 프로젝트 명을 넣는다.
  • Ant View에 init.xml을 드래그 해서 붙인다. 3번 항목에 붙인 프로젝트 이름이 ANT View에 표시될 것이다. 
    만약 Ant View가 없다면 Windows>Show View>Other를 선택해 ANT를 찾아 선택하여 열어준다.
    만약 ANT가 없다면 Flash Builder에 ANT 플러그인이 설치되지 않은 것이므로 먼저 플러그인부터 설치해야한다. 참고 : http://blog.flashplatform.kr/213
  • Ant View에 붙은 해당 프로젝트 이름 옆에 (+) 버튼을 눌러 init가 나오면 두번 클릭해 실행한다. 
    이때 Console창에 마지막에 BUILD SUCCESSFUL이 나오면 ANT 실행을 성공한 것이다.
  • 생성한 프로젝트를 선택한 다음 F5를 눌러 새로고침한다.  
    다음과 같은 변화가 일어난다. 
    - Main.as가 만들어지고 기본 Application으로 등록된다.
    - libs 폴더에 SWC 파일들이 복사된다.
    - remote 폴더가 생긴다. 이 폴더는 외부자원를 로드하는 예제를 작성할때 외부자원을 이곳에 놓고 쓰면 되겠다.
    - HTML Wrapper 폴더가 삭제된다. 
    - Wonderfl에서 사용하는 기본 라이브러리가 자동으로 등록된다. 


아래 화면은 위 과정을 통해 구축된 애플리케이션의 Package Explorer 화면이다. 이제부터 default package에 필요한 수만큼 Wonderfl에서 가져온 actionscript 코드를 테스트 할 수 있게 되었다.


더불어, 위 과정을 통해 개인적으로 만든 테스트 프로젝트들 모음을 아래 링크를 통해 볼 수 있다.
http://dev.naver.com/scm/viewvc.php/branches/jidolstar/?root=wonderfl

SVN의 https://dev.naver.com/svn/wonderfl/branches/jidolstar 경로로 부터 직접 받아볼 수 있으니 참고 바란다.




정리하며
개인적으로 Wonderfl은 필자에게 매우 도움이 되는 사이트이다. 한국의 Flash 개발자들도 Wonderfl을 적극적으로 활용했으면 하는 바램을 가지는 마음에서 이 문서를 작성했다. 아무쪼록 도움이 되었길 바란다. 


참고사이트



글쓴이 : 지돌스타(http://blog.jidolstar.com/669)
최근에 AIR 애플리케이션을 개발하면서 publisher ID이 누락되는 문제에 대한 글을 적은 적이 있었다.

Adobe AIR 애플리케이션 배포후 publisher ID 파일이 누락되는 문제

나는 이 문제를 단순히 설정이 틀어진다던가 버그라고만 생각했다. AIR 1.5 라이브 문서를 보면 다음과 같이 명백하게 밝히고 있다.

As of AIR 1.5.3, publisher IDs are deprecated. New applications (originally published with AIR 1.5.3 or later) do not need and should not specify a publisher ID.


AIR 1.5.3 릴리즈 노트를 살펴보면 PublisherID문제를 해결하기 위해 기존 버전의 AIR에서 어떻게 AIR 1.5.3으로 마이그레이션을 할 수 있을까 적어두었다.

Changes in AIR 1.5.3 

정리하자면 이렇다. AIR 1.5.3으로 처음 애플리케이션을 개발하는 사람은 그대로 개발하면 된다. 어짜피 AIR 애플리케이션이 배포되고 그 상태에서 업데이트 하는데는 문제가 없기 때문이다. 하지만 PublisherID가 필요한 경우가 있거나(그런 경우는 없는게 좋다) 기존 버전을 AIR 1.5.3으로 갈아타고 싶을 때는 AIR의 디스크립터 파일에 <publisherID></publisherID>를 삽입하여 컴파일 해줘서 기존버전과의 호환을 유지할 수 있게 된다. 이는 AIR 1.5.3 뿐 아니라 앞으로 배포될 AIR 2.0에도 해당하는 것으로 보인다.

아마도 이렇게 된데에는 중간에 인증서(certificate) 관련 파일이 변경되더라도 애플리케이션 배포에 문제가 없도록 한다는 취지가 있는 것이 아닐까 판단한다. 가령, 자기발급인증서 사용하다가 공인인증서로 바꿔도 배포에 문제가 없게 된 것이다. 그 반대의 경우도 마찬가지이고. 그렇다면 오히려 더 좋은거 아닌가?

이 AIR 애플리케이션이 고유하다는 것을 명시적으로 알려주는 것은 Publisher ID와 Application ID 였는데... 이제부터 그 고유성은 Publisher ID는 선택사항이고 Application ID가 증명해주게 되었다. 이렇게 되면 다른 사람이 제작한 Application ID가 중복되는 경우도 있는데... 그런 경우에는 com.jidolstar.air.myapp 형태로 해주면 어느정도 중복을 방지할 수 있다. 하지만 완벽히 피해갈 수는 없게 되었다.

앞으로 AIR 애플리케이션을 개발할 때 꼭 참고하자.

문제를 발견해주시고 알려주신 leigh님께 감사한다.

참고글
AIR: Migrating Expired Certificates using AIR 1.5.3
Changes in AIR 1.5.3
Packaging an AIR installation file using the AIR Developer Tool (ADT)

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

최근에 Flex 4.0 기반으로 Adobe AIR 애플리케이션을 개발하는 프로젝트를 맡았다. 현시점(2010.2.1) Flex 4.0 SDK에 함께 있는 AIR SDK 버전은 한단계 구버전인 1.5.2이기 때문에 가장 최신 버전인 1.5.3을 기존 Flex 4.0 SDK에 덮어씌운 상태에서 개발을 진행했었다. 문제없이 개발할 수 있다.

그런데 애플리케이션 배포시 문제가 발생했다. AIR 애플리케이션을 설치하면 설치 디렉토리 밑에 META-INF/AIR/publisherid 파일이 존재해야 한다. 그런데 이 파일이 없는 것이다. publisherid는 인증서의 고유 ID를 담고 있다. 이 파일이 없는 상태에서 배포, 업데이트 모두 잘되지만 badge에서 이미 설치된 AIR 애플리케이션이 있는지 여부를 판단할 수 없었다. 사실 배포,업데이트가 잘 되기 때문에 이 상태로 배포해도 문제는 없지만 너무 꺼름칙했다. (AIR의 장점중 하나가 쉽게 설치된 AIR 애플리케이션과 Flash의 연동이 큰 장점 아니던가....)

이 문제의 원인을 찾고자 별짓을 다하다가 SDK 문제라는 것에 귀결을 내리게 되었다. 문제는 Flex 4.0 SDK에 AIR 1.5.3 SDK를 덮은것에서 비롯되었다. 기존 Flex 4.0 SDK에서 AIR 1.5.2 기반으로 만들어 배포하면 Publisher ID 파일이 있지만 AIR 1.5.3으로 덮어씌우고 배포하면 이 파일이 없었던 것이다. 개발, 컴파일까지 모두 잘되는데 말이다. ㅎㅎ 

결국 나는 Flex 4.0 기반으로 애플리케이션을 거의 완성한 상태라서 AIR 1.5.2 기반으로 배포할 수 밖에 없게 되었다. 편리하고 빠른 개발을 위해 Flex 4.0를 선택했지만 AIR 버전이 구버전으로 인한 문제점은 정말 예상치 못한 일이었다.

개발하다 보면 별에 별일이 많은데... 정말 이렇게 크리티컬한 상황까지 보게 되니 정말 웃기기 까지 한다.

질문?
Flex 4.0에 AIR 1.5.3을 덮어씌워도 publisher id 파일이 누락되지 않도록 하는 방법은 뭘까요? 혹시 알게되면 댓글이나 트랙백 부탁합니다.

추가 1
Flex 3의 최신버전인 Flex 3.5의 경우에는 AIR 1.5.3 SDK가 함께 있는데... Flex 4는 왜???? ㅡㅡ;;

추가 2
Flex 4.0 SDK의 beta 2 버전은 4.0.0.10485 이다. 이 버전은 AIR 1.5.2 SDK이다. 하지만 최신 Nightly Builder 버전으로 다운로드 받으면 AIR 1.5.3이 기본이다. 그냥 이걸로 개발해야겠다.

추가 3
Adobe AIR 1.5.3 부터는 PublisherID가 기본적으로 제거됩니다. http://blog.jidolstar.com/655

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




지난 22일에 Adobe Labs에서 Adobe Stratus 2에 대한 소개가 있었습니다.

http://labs.adobe.com/technologies/stratus/

Stratus는 RTMFP(Real Time Message FlowProtocal)이라 불리우는 통신프로토콜을 이용해 Flash Player 10이 설치된 클라이언트 까지 P2P(Peer to Peer)가 가능하게한 기술입니다. TCP가 아닌 UDP기반으로 동작하고요.

이번 Stratus 2는 Adobe Flash Player 10.1 beta, Adobe AIR 2 Beta에서 동작하도록 만들어졌습니다. 재미있게도 이번 버전에서는 애플리케이션 수준에서 멀티케스트와 소스 공급자의 로드를 줄여주는 구조를 지원하게 되었습니다. 맨 위의 그림에서 좌측 그림은 기존 Flash Player끼리 통신하기 위해 서버를 항상 거쳐야만 했었습니다. 서버입장에서는 완전 부담이죠. 그런데 중간 그림에서 볼 수 있듯이 Stratus가 나오면서 서버와는 한번의 접속으로 하고 Flash Player끼리 통신이 되었습니다. 이것도 소스 공급자인 Flash Player 입장에서는 로드가 심할 수 있습니다. 이를 Flash Player 단위에서 분산시키도록 한 것이 이번 배포의 핵심인 것 같네요.

물론 Stratus 2는 아직 베타버전인 Flash Player 10.1과 AIR 2.0에서만 동작하므로 바로 실무에 사용할 수는 없습니다. 그러나 조만간 이 기술을 사용할 수 있게 되겠죠.

Stratus 1.0에 대한 많은 실험이 국내에도 있었습니다. 저도 조금 테스트 해봤었고요.
http://blog.jidolstar.com/498 

다른 분들의 글들입니다.
플래시 P2P RTMFP에 대해(예제 파일 첨부)
[Flex] Stratus를 이용한 P2P방식의 Flash간에 1:1 다이다이 오목게임-_-
Flash로 p2p를 만들어보자 - Stratus 라이브러리
Flash로 채팅을 구현해보자.


하지만 방화벽, 공유기를 통해 접근 문제가 해결되었는지는 미지수네요.(테스트 안해봤음)
이 문제만 해결되면 정말 멋진 애플리케이션이 많이 나올거라 생각됩니다. ^^

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





Adobe AIR에는 Webkit 브라우져 엔진이 내장되어 있어 IE나 FireFox와 같은 브라우져 기능을 만들 수 있다. 단순히 그런 기능만 제공하는 거라면 흥미가 떨어지겠지만 중요한 점은 로드되는 컨텐츠에 DOM형태로 접근할 수 있어 실제로 Element를 추가하거나 뺄 수 있고 함수까지 재정의도 가능하다. 이러한 작업을 Javascript에 대한 지식이 있다면 쉽게 할 수 있기 때문에 이것을 이용한 다양하고 재미있는 시도를 해볼 수 있다.

개발환경은 Flash Builder 라고 가정하겠다. 물론 Flash IDE에서 해도 무방하다.


AIR에서 DOM 접근의 예

AIR에서 DOM에 접근해보는 재미있는 예를 들어보겠다.

Javascript에는 alert() 함수가 존재한다. 알다시피 경고창 띄워주는 함수이다. 이 함수를 AIR에서 커스터마이징할 수 있다. 가령, 원래기능인 경고창을 띄워주지 않고 넘겨준 문자열 정보를 Flash Builder의 Console창에 출력하도록 하는 것이다. 이 기능을 구현해보자.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Test</title>
<style type="text/css">
	body { background-color: #aaaaaa; }
</style>
</head>
<body>
	<input type="button" value="alert( 'Hello' )" onclick="alert('Hello')"> 
	<input type="button" value="alert( 'I', 'love', 'you' )" onclick="alert('I', 'love', 'you')">
</body>
</html>

위 HTML코드는 Button을 누르면 alert() 함수로 경고창을 띄워준다.

AIR에서 이 HTML 문서를 로드하는데 HTMLLoader 클래스로 할 수 있다. 이 클래스는 Webkit 엔진을 한번 감싸서 AIR개발자가 손쉽게 사용할 수 있도록 한다. HTMLoader의 load()를 이용해 HTML문서를 로드하고 alert를 아래 코드와 같이 재정의 할 수 있다.

var _html:HTMLLoader = new HTMLLoader();
_html.addEventListener( Event.HTML_DOM_INITIALIZE, __ON_HTML_DOM_INITIALIZE );
_html.load( new URLRequest("test.html") );
stage.addChild(htmlLoader);

private function __ON_HTML_DOM_INITIALIZE($e:Event):void {
	//javascript의 alert 함수를 actionscript에서 재정의했다.
	_html.window.alert = function():void {
		trace( arguments );
	}			
}

참고 1. 위 코드는 완벽한 코드는 아니다. 중요부분만 떼어내었다.
참고 2. _html.window.alert 내에 재정의 된 함수에 arguments가 뭐냐고 물어보신다면... 
Adobe문서를 참고하자. 이런것이 가능한 것은 Actionscript와 Javascript가 모두 ECMAScript 규약을 따르고 있기 때문이다.

Event.HTML_DOM_INITIALIZE 이벤트는 HTML DOM이 만들어졌음을 알려주는 이벤트로 이때부터 HTML의 DOM에 접근할 수 있다. 하지만 모든 DOM이 만들어지는 것을 의미하지 않으며 가장 기본적인 구조만 접근이 가능해진다. 완벽한 DOM구조에 접근하려면 Event.COMPLETE 이벤트를 핸들링해야한다. 

위 ActionScript 코드의 __ON_HTML_DOM_INITIALIZE() 이벤트 핸들러에서 브레이크 포인트를 찍어서  디버깅 모드로 실행해보면 다음과 같은 DOM구조를 볼 수 있다.


위처럼 내부적으로 정의된 __HTMLScriptObject 객체가 HTMLLoader의 window속성으로 참조되어 있는 것을 확인할 수 있고 alert도 여기에 포함되어 있는 것을 확인할 수 있을 것이다. 저기에 있는 HTML 내부 속성을 모두 개발자가 커스터마이징 할 수 있다는데 매우 흥미를 느끼지 않는가?

Flash Builder에서 디버깅 모드로 실행해보면 다음 처럼 윈도우가 나오고 test.html이 HTMLLoader에 로드되는 것을 볼 수 있다. 



위 버튼을 누르면 누를때마다 alert창이 뜨지않고 다음처럼  Flash Builder의 Console창에 출력된다.


굳이 Event.COMPLETE가 아니라 Event.HTML_DOM_INITIALIZE 이벤트 발생시 alert를 재정의 하는 것은 Event.COMPLETE 이전에 Javascript가 얼마든지 실행될 수 있기 때문이다. 그러므로 어느때든지 alert가 원래기능을 수행하지 못하게 하는데 가장 적절한 시점은 바로 Event.HTML_DOM_INITIALIZE 이벤트가 발생할 때이다.


사용자 정의 Trace() 만들자.

AIR의 HTMLLoader를 통해 불려지는 HTML의 디버깅은 쉽지 않다. 왜냐하면 Flash Builder나 FireFox와 같은 디버깅 툴을 사용할 수 없기 때문이다. 이 때문에 복잡하게 만들어진 JavaScript 코드와 HTML이 섞여 있는 경우라면 어떤 경우에 문제가 발생하는지 찾는것 조차 어려워진다. 

Flash Builder에서는 trace()문을 사용하면 디버깅에 크게 도움이 된다. 비록 브레이크 포인트를 찍으면서 값을 추적할 수 없지만 trace()문 하나만으로도 많은 부분 문제를 해결하는데 도움을 준다.

그럼 JavaScript내에 강제적으로 trace()함수를 정의하고 개발자가 필요할때 HTML문서상에서 이 함수를 호출하면 AIR 애플리케이션에 trace으로 들어온 인자값을 출력해주면 어떨까? alert()를 커스터마이징 했었다는 것을 충분히 이해했다면 이 부분도 그리 어려운 것은 아니다.

private function __ON_HTML_DOM_INITIALIZE($e:Event):void {
	_html.window.trace = function():void {
		trace( arguments );
	}			
}

참고 : _html.window.trace는 로드되는 HTML DOM에 trace()함수를 정의한 것이고 함수내에 trace()는 ActionScript의 trace라는 것을 인식하자.


이미 보여준 ActionScript 코드에서 alert 재정의 대신 trace를 정의하도록 만들었다. 이미 언급한 HTML문서에서 onclick="alert()" 구문대신 onclick="trace()" 로 바꿔보자.

디버깅 모드로 실행하면 Flash Builder의 Console창에 버튼을 클릭할때마다 메시지가 보이는 것을 확인할 수 있을 것이다.

이 방법은 꽤 유용하다. 웹개발자가 만든 HTML문서가 AIR의 Webkit에도 제대로 동작하지 않는 경우도 꽤 발생한다. 이런경우 어느 지점에서 문제가 되는지 알 수 없어 alert()만 의지해서 디버깅하는 것은 웹개발자에게 너무 가혹하다. AIR개발자는 웹개발자를 위해 이 정도의 기능을 최소한으로 제공해서 웹개발자의 어려움을 덜어주어야 한다고 생각한다.


몇가지 문제점 극복하기

하지만 아직까지 문제는 있다.

1. IFrame으로 로드되는 컨텐츠는 AIR에서 정의한 trace를 직접사용할 수 없다.
이런 점을 극복하기 위해서 iFrame에 로드되는 문서에서는 parent.trace() 처럼 사용하면 어느정도 해결이 된다.

2. 기존에 사용하는 HTML문서가 이미 웹상에서 서비스되고 있을때 HTML내 실수로 trace()문을 그대로 남겨두게 되는 경우 문제가 발생한다. 왜냐하면 AIR에 로드되는 경우는 강제적으로 trace()함수를 정의하지만 일반 웹브라우져에서 로드되는 경우에는 trace()정의가 없으므로 trace()함수를 호출하면 에러를 던질 것이기 때문이다. 물론 이런 경우가 발생하지 않도록 웹개발자의 꼼꼼한 테스트가 필요하지만 그게 말처럼 쉬울까?

이처럼 웹상에서 trace()가 쓰여도 웹개발자의 실수로 인해 trace()를 지우지 못해 발생하는 문제를 해결할 방법은 있다. 먼저 아래처럼 common.js 파일을 만든다.
try {
	trace; //trace문이 정의되었는가?
} catch(e) {
	trace = function() {}; //정의되어 있지 않으면 강제로 만들어준다.
}

그 다음 서비스되고 있는 모든 HTML에 common.js를 아래처럼 import한다.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Test</title>
<script type="text/javascript" src='common.js'></script>
<style type="text/css">
	body { background-color: #aaaaaa; }
</style>
</head>
<body>
	<input type="button" value="trace( 'Hello' )" onclick="trace('Hello')"> 
	<input type="button" value="trace( 'I', 'love', 'you' )" onclick="trace('I', 'love', 'you')">
</body>
</html>

모든 HTML문서에 common.js가 있어야 한다는 점을 기억하자. 중요하게 봐야할 것은 common.js의 동작방식이다. 이 HTML문서를 AIR에서 로드되면 DOM이 생성되자마자 trace를 정의하므로 try..catch문에서 catch()문으로 가지 않는다. 하지만 일반 웹브라우져에서 로드하면 trace가 생성되지 않아 catch()문이 실행되게 된다. 그러니 웹개발자가 trace()문을 실수로 지우지 않아도 AIR에 로드되든 일반 웹브라우져에 로드되든 문제가 발생하지 않는다.


실용적으로 만들어보자. 

AIR에 로드되는 HTML문서에서 trace()문을 이용하는 방법을 알 수 있게 되었다. 이를 좀더 실용적으로 테스트 해볼 수 있도록 만들기 위해서는 trace()의 결과가 console창에만 나오는 것이 아니라 별도의 창을 띄워서 언제든지 trace()내용을 확인할 수 있도록 하는 것이 좋다. 이것은 AIR 개발자와 웹페이지 개발자가 다른 경우 AIR개발자가 웹페이지 개발자에게 해줄 수 있는 최소한의 배려이다.

여기서는 최소한의 기능만 보여주려고 한다. 아래처럼 창 2개가 있다. 좌측창은 HTML를 로드한 지금까지 예제이고, 우측창은 trace문의 결과물을 보여주는 창이다.


이것을 구현하기 위해 ActionScript 3.0으로 만들어보자. Flash Builder에서 개발한다면 Flex 프로젝트를 생성하되 Application type은 Desktop(AIR)로 하고 Main Application file 이름을 .mxml이 아닌 .as로 끝나는 확장자를 가지도록 만들어야한다. 나는 Main.as로 만들었다.

Main.as
package {
	import flash.display.NativeWindow;
	import flash.display.Sprite;
	import flash.events.Event;

	/**
	 * 메인 애플리케이션 
	 * @author jidolstar
	 */
	public class Main extends Sprite {
		public function Main() {
			//브라우져 띄우기 
			var window:BrowserWindow = new BrowserWindow();
			window.width = stage.stageWidth;
			window.height = stage.stageHeight;
			window.load( "test.html" );
			window.activate();
		}
	}
}


BrowserWindow.as


package {
	import flash.display.NativeWindow;
	import flash.desktop.NativeApplication;	
	import flash.display.NativeWindowInitOptions;
	import flash.display.NativeWindowType;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.html.HTMLLoader;
	import flash.net.URLRequest;

	/**
	 * Trace 테스트용 브라우저 윈도우
	 * @author jidolstar
	 */
	public class BrowserWindow extends NativeWindow {
		private var _html:HTMLLoader;
		private var _trace:TraceWindow;

		/**
		 * 생성자 
		 */
		public function BrowserWindow() {
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
			
			//윈도우 옵션
			var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
			initOptions.type = NativeWindowType.NORMAL;
			super(initOptions);
			
			//HTML 로더 
			_html = new HTMLLoader();
			_html.width = stage.stageWidth;
			_html.height = stage.stageHeight;
			_html.addEventListener(Event.HTML_DOM_INITIALIZE, function ($e:Event):void {
				//DOM에 trace 함수를 정의한다. 
				_html.window.trace = function():void {
					var str:String = "";
					for( var i:int = 0; i < arguments.length; i++ ) {
						str += arguments[i] + " ";
					}
					//결과물을 창에 출력해준다.
					_trace.addString( str );
				}
			});
			stage.addChild(_html);
			
			//Trace 결과물을 보여주는 창 
			_trace = new TraceWindow();
			_trace.width = 300;
			_trace.height = 300;
			_trace.activate();
			
			//창이 닫힐때 모든 윈도우를 닫는다.
			addEventListener(Event.CLOSING, function($e:Event):void {
				var openedWindows:Array = NativeApplication.nativeApplication.openedWindows;
				for each( var window:NativeWindow in openedWindows ) {
					window.close();
				}
			} );	
			
			title = "browser";
		}
		
		public function load($url:String):void {
			_html.load( new URLRequest( $url ) );
		}
		
	}
}


TraceWindow.as

package {
	import flash.display.NativeWindow;
	import flash.display.NativeWindowInitOptions;
	import flash.display.NativeWindowType;	
	import flash.text.TextField;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	
	/**
	 * Trace를 출력해주는 윈도우
	 * @author jidolstar
	 */
	public class TraceWindow extends NativeWindow {
		private var _textField:TextField;
		private var _traceText:String = "";

		public function TraceWindow() {
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;

			var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions();
			initOptions.type = NativeWindowType.NORMAL;
			super(initOptions);

			_textField = new TextField();
			_textField.width = stage.stageWidth;
			_textField.height = stage.stageHeight;
			stage.addChild(_textField);
			
			title = "trace window";
		}

		public function addString($value:String):void {
			_textField.appendText($value + "\n");
		}
	}
}


물론 이것은 ActionScript 3.0 코드이고 이미 언급한 HTML(test.html) 및 common.js 도 함께 소스에 포함해야한다.

아래는 위 코드들을 압축한 소스이다.


예제는 단순하지만 더 응용해서 컴포넌트화 시키면 좋을 것이다.


정리하며
Adobe AIR의 장점은 크로스 OS, 기존 Flash/Flex 개발자의 접근 향상 정도만 있는 것은 아니다. Adobe는 웹과 매우 친하면서도 데스크탑 영역으로까지 확장했다. 여기서 웹과 매우 친하다는 말에 주목할 필요있다. 웹브라우져에 올라온 Flash와 AIR가 별도의 설치 없이 서로 통신할 수 있고 웹브라우져에서 AIR를 실행할 수도 있다. 또한 오늘 언급한 것처럼 dom에 대한 접근이 매우 쉬운 것도 있다. 아직까지 AIR가 성능 및 기술적 한계가 분명 존재하지만 버전업을 거듭하면서 더욱 나아지고 있다. AIR의 장점을 명확히 인식한다면 AIR만의 매력을 느낄 수 있지 않을까 생각한다.

아래 링크로부터 AIR에 관련된 정보를 얻을 수 있다.

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


제가 ACP(Adobe Community Professional)가 되었습니다.
이로써 Adobe 공인 전문가 그룹에 등록 되었습니다.

http://lizfrederick.blogspot.com/2010/01/new-acps-for-2010.html

국적과 상관없이 약 300명 정도가 등록되었습니다. 분류로는 Acrobat, AfterEffect, AIR, Authorware, Captivate, ColdFusion, Creative Suite, Dreamweaver, Fireworks, Flash Media Server, Flash Mobile, Flash Platform, Flash Platform(RIA), Flash Professional, Flex, FrameMaker, Illustrator, InDesign, InDesign Server, Lightroom, LiveCycle ES, Photoshop, PhotoShop Elements, Premiere Pro, Production Premium, RoboHelp, Spry, Visual Communicator등이 있고 저는 Flash Platform(RIA) 분야로 등록되었네요.

저는 모르고 있었는데 우야꼬가 전화로 축하해주더군요. ^^;
메일을 보니까 한국 어도비로부터 축하 메일이 왔더군요.
저 외에 한국에서 ACP로 선정된 분들입니다.

강성규(땡굴이)
이정웅(블루메탈)
이준하(열이아빠)
배준균

ACP 후보 추천시 저에 대해서 작성된 내용입니다. (물론 영어로 번역되었겠죠...)

Flash Platform 기술에 대해 큰 관심을 가지고 있으며 그 기술에 대해 한국어 블로그(http://blog.jidolstar.com)를 운영하고 있다. 한국 어도비 RIA 홈페이지에 분기별로 기술문서를 기고하고 있다. 또한 ACC로써 Flash Platform 기술 전도사로 활동하고 있다(http://adoberia.co.kr/acc/acc.html). 한국어 Flex문서가 없다. 그래서 그는 flexdocs.kr 이라는 사이트를 구축했다. 그는 회사에서 Flash 개발팀장으로 근무하고 있으며 한국 SNS인 Starpl에 Flash 기술을 적용하는데 일조했다(http://starpl.com).

여러가지로 부족한데 선정해준 Adobe사에 감사하며 이름에 걸맞게 열심히 활동하고 실력도 쌓아가도록 노력하겠습니다.

글쓴이 : 지돌스타(http://blog.jidolstar.com)
HTML 문서내에 SWF와 JavaScript는 ExternalInterfacecalladdCallback 메소드를 이용하면 통신할 수 있다. ExternalInterface.addCallback( "myMethod", myMethod);로 정의하면 JavaScript에서 SWF의 myMethod를 호출할 수 있게 된다. myMethod가 function myMethod( params:Object ); 형태로 만들었다면 JavaScript에서 만든 {a:1, b:2}와 같은 값이 전달될 수 있다. 당연한거다.

AIR 1.5.3 환경에서 HTMLLoader 클래스를 이용해 위처럼 정의된 HTML 문서를 로드한다고 하자. 이 때는 Object값을 JavaScript에서 SWF내에 정의된 myMethod로 넘길 수 없다. String이나 Number 형태의 값이라면 상관없지만 Object는 안된다. 버그다. 결국 Object의 형태를 띈 String을 넘겨주고 그 String을 다시 SWF내에서 Object로 변환해주어야 한다. 이것을 구현하기 위해 json.js을 사용했지만 JSON객체를 찾을 수 없다며 에러를 던진다. 그래서 Prototype의 Object.toJSON을 이용했다. 물론 ActionScript쪽에서는 as3corelib에 있는 JSON 클래스를 활용하면 된다. String과 Object형태의 String을 어떻게 구분할지 묻는다면 try..catch 문을 활용하면 된다고 답변하고 싶다.

이 때문에 엄청 삽질하게 생겼다. ExternalInterface.call() 메소드의 두번째 인자가 ...arguments 형태라는 것을 감안하고 만들었다면 이런 고생 안했을텐데... 

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

LCDS, BlazeDS, ZendAMF등을 이용해 AMF(ActionScript Message Format)으로 데이터를 주고받는 형태는 이미 많은 예제들과 문서들이 있다. AMF는 데이터 통신을 위한 일종의 약속된 규약이고 이 데이터를 주고 받고 하는 과정에서 서로의 언어에 맞게 직렬화(Serialization)하는 것은 각 언어에서 지원해주는 라이브러리를 사용하면 된다. ActionScript 3.0은 기본 API에서 지원해준다. 자바의 경우는 BlazeDS나 LCDS를 사용하면 된다. PHP의 경우에는 ZendAMF를 사용하면 된다. 이들을 이용하면 가령, 자바와 java.lang.Interger는 ActionScript의 int나 uint로... java.lang.Double은 ActionScript의 Number형과 직렬화된다. 이는 일종의 기본적으로 지원되는 직렬화이다.

다음은 BlazeDS에서 기본적으로 지원되는 직렬화이다.
Serializing between ActionScript and Java

하지만 이런 기본 직렬화과정을 사용하지 않고 직렬화 자체를 커스터마이징(customizing)할 수 있다. 가령 클라이언트(예,Flash 애플리케이션)에서는 아이디, 이름, 속성, 가격의 정보가 중요하지만 서버(예,자바)에서는 재고(inventory)가 중요한 경우가 있다. 이런 경우에는 Flex와 Java쪽의 직렬화하는 클래스를 다음과 같이 디자인 할 수 있겠다.

// Product.as
package samples.externalizable {

import flash.utils.IExternalizable;
import flash.utils.IDataInput;
import flash.utils.IDataOutput;

[RemoteClass(alias="samples.externalizable.Product")]
public class Product implements IExternalizable {
    public function Product(name:String=null) {
        this.name = name;
    }

    public var id:int;
    public var name:String;
    public var properties:Object;
    public var price:Number;

    public function readExternal(input:IDataInput):void {
        name = input.readObject() as String;
        properties = input.readObject();
        price = input.readFloat();
    }

    public function writeExternal(output:IDataOutput):void {
        output.writeObject(name);
        output.writeObject(properties);
        output.writeFloat(price);
    }
}
}


// Product.java
package samples.externalizable;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Map;

/**
* This Externalizable class requires that clients sending and 
* receiving instances of this type adhere to the data format
* required for serialization.
*/
public class Product implements Externalizable {
    private String inventoryId;
    public String name;
    public Map properties;
    public float price;

    public Product()
    {
    }

        /**
        * Local identity used to track third party inventory. This property is
        * not sent to the client because it is server-specific.
        * The identity must start with an 'X'.
        */
        public String getInventoryId() {
            return inventoryId;
        }

        public void setInventoryId(String inventoryId) {
            if (inventoryId != null && inventoryId.startsWith("X"))
            {
                this.inventoryId = inventoryId;
            }
            else
            {
                throw new IllegalArgumentException("3rd party product
                inventory identities must start with 'X'");
            }
        }

        /**
         * Deserializes the client state of an instance of ThirdPartyProxy
         * by reading in String for the name, a Map of properties
         * for the description, and 
         * a floating point integer (single precision) for the price. 
         */
        public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
            // Read in the server properties from the client representation.
            name = (String)in.readObject();
            properties = (Map)in.readObject();
            price = in.readFloat();
            setInventoryId(lookupInventoryId(name, price));
        }
        /**
         * Serializes the server state of an instance of ThirdPartyProxy
         * by sending a String for the name, a Map of properties
         * String for the description, and a floating point
         * integer (single precision) for the price. Notice that the inventory 
         * identifier is not sent to external clients.
         */
        public void writeExternal(ObjectOutput out) throws IOException {
            // Write out the client properties from the server representation
            out.writeObject(name);
            out.writeObject(properties);
            out.writeFloat(price);
        }
        
        private static String lookupInventoryId(String name, float price) {
            String inventoryId = "X" + name + Math.rint(price);
            return inventoryId;
        }
}

위 코드는 ActionScript의 flash.utils.IExternalizable 인터페이스와 Java의 java.io.Externalizable 인터페이스를 이용해 기본직렬화를 무시하고 이들 인터페이스에 정의된 readExternal와 writeExternal 메소드를 호출하여 직렬화 자체를 커스터마이징 할 수 있다는 것을 의미한다. Externalizable 인터페이스를 구현한 클래스에 정의된 메소드가 기본 직렬화보다 우선순위가 높다는 것을 기억하면 되겠다.

Flex의 ArrayCollection을 다시 한번 보기 바란다. 이 클래스는 flash.utils.IExternalizable를 구현했다.
mx.collections.ArrayCollection

꼭 이런 경우만은 아니다. 쓸데없는 데이터의 크기를 줄이기 위한 방법도 해당한다. 예를들어 ActionScript 코드를 보면 다음과 같다.


class Example implements IExternalizable {
  
      public var one:Boolean;
      public var two:Boolean;
      public var three:Boolean;
      public var four:Boolean;
      public var five:Boolean;
      public var six:Boolean;
      public var seven:Boolean;
      public var eight:Boolean;
       public function writeExternal(output:IDataOutput) {
           var flag:int = 0;
           if (one) flag |= 1;
          if (two) flag |= 2;
          if (three) flag |= 4;
          if (four) flag |= 8;
          if (five) flag |= 16;
          if (six) flag |= 32;
          if (seven) flag |= 64;
          if (eight) flag |= 128;
           output.writeByte(flag);
      }
       public function readExternal(input:IDataInput) {
           var flag:int = input.readByte();
           one = (flag & 1) != 0;
          two = (flag & 2) != 0;
          three = (flag & 4) != 0;
          four = (flag & 8) != 0;
          five = (flag & 16) != 0;
          six = (flag & 32) != 0;
          seven = (flag & 64) != 0;
          eight = (flag & 128) != 0;
      }
 }

데이터 통신을 위해 쓸데없이 Boolean객체를 주고 받을 필요없다. 위 코드처럼 직렬화를 커스터마이징한다면 송수신 데이터 자체의 크기도 줄일 수 있다. 이는 매우 유용하다.


참고글
flash.utils.IExternalizable
커스텀 직렬화의 사용
ActionScript 3.0 데이터 유형 및 직렬화(serialization)
Serializing between ActionScript and Java
AMF 3 스팩
AS3 BitmapData AMF solution using IExternalizable
Flex and PHP: remoting with Zend AMF

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


Flash 애플리케이션을 만들때  통합개발환경(IDE)인 Flash CS3, CS4 나 Flex Builder, Flash Builder, FDT와 같은 툴을 주로 사용할 것이다. 실무에서는 당연히 이런 툴을 선택할 수 밖에 없다. 그러나 이들은 사실 유료이고 무겁다.

Flash MiniBuilder라는 프로그램은 간단하게 ActionScript 3.0 기반으로 개발할 수 있도록 Adobe AIR로 만들어진 가벼운 개발툴이다. 이것은 Victor Drâmbă라는 사람이 만들었으며 그의 블로그(http://www.victordramba.com)를 가보면 MiniBuilder에 대한 소식을 볼 수 있다.

MiniBuilder는 Flex SDK 3와 Java JRE 설치를 먼저 해야 정상적으로 사용할 수 있으며 사용하는 방법은 다음 동영상에서 잘 나와있다. 약간 복잡해 보이지만 중간에 start.bat를 실행해야하는 번거로움은 AIR 2.0에서는 없어질 것이라 생각한다.
http://www.youtube.com/watch?v=bjEc2eT_rCE&feature=player_embedded


이 프로그램은 Flex SDK에서 제공하는 컴파일러를 이용해서 만들어진 ActionScript 3.0코드를 컴파일하게 된다. 워낙 가볍기 때문에 Flash Builder에서 느껴지는 묵직함은 찾아볼 수 없다. 일반적인 코딩도 별 어려움이 없고 또한 코드힌트기능 및 에러메시지 출력 기능도 있기 때문에 이 또한 유용하다. 아쉬운 점은 디버깅을 할 수 없고 한글 주석도 못단다는 점.

MiniBuilder는 공개소스이다. GPL 라이센스를 가지며 Google Code(http://code.google.com/p/minibuilder/)에서 전반적인 설명과 소스코드를 볼 수 있다. 원한다면 Flex Builder 3나 Flash Builder 4에서 SVN으로 다운로드 받아 직접 소스를 테스트해볼 수도 있다. (본인도 SVN으로 다운받아 해봤다. 자바소스까지 포함되어 있으므로 Eclipse+Flex Builder 3 Plug-in 환경에서 작업할 것을 권한다. MXML 사용 안하고 전부 ActionScript 3.0으로 만들었다.)



MiniBuilder는 AIR뿐 아니라 Flash버전으로도 제작되어 유명한 ActionScript 3.0 코드 공유사이트인 Wonderfl의 에디터로도 활용되었다. 다음 제작자의 글을 보면 그에 대한 소개가 나와있다.
http://www.victordramba.com/?p=71



벌써 이것이 만들어져 소개된지 몇개월 되었는데 이렇게 멋진 공개 프로그램이 한국에 소개된게 딱 하나밖에 못본 것 같아 간단하게 소개해본다.

참고 링크
Victor Drâmbă의 블로그 : http://www.victordramba.com
Flash MiniBuilder Google Code : http://code.google.com/p/minibuilder
설치 및 사용방법 : http://www.youtube.com/watch?v=bjEc2eT_rCE&feature=player_embedded


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

로컬자원을 서버에 전송하기 위해 우리는 FileReference Class를 사용하면 된다.
FileReference로 파일을 서버에 전송하는 방법은 많이 공개가 되어 있다.
알다시피 FileReference의 browse()함수를 호출한 뒤, select 이벤트가 발생시 upload() 함수를 이용하여 선택한 로컬자원을 서버로 보낸다.

서버에서는 아주 단순하게 서버 임시 저장소에 저장된 파일을 원하는 곳에 복사하기만 하면 된다.
php의 경우 move_uploaded_file() 메소드를 사용하면 되겠다.

그럼 Flex 시행도중 캡쳐한 화면을 저장하는 경우에도 위와 같은 방법으로 저장이 가능할까?

답은 "된다"


나는 예전에 ImageSnapshot 클래스를 이용해 base64로 변환해서 서버로 전송한 뒤에 base64를 decode하여 저장하는 방법에 대해서 언급한적이 있다. (http://blog.jidolstar.com/301 참고)
이 방법의 단점은 이미지가 큰 경우 base64로 encode, decode 하는 과정에서 서버성능 및 클라이언트 성능에 따라 속도 및 부하에 영향을 준다. 그러므로 이 방법으로는 PNGEncoder 및 JPGEncoder로 PNG, JPG 파일 형식에 맞는 데이타를 만들었다고 해도, FileReference와 같이 데이타를 전송을 할 수 없었다.

하지만 Google을 열심히 돌아다녔다니 이 문제점에 대한 해결의 실마리를 찾을 수 있었다.
(역시 Google!!!)

간단히 방법을 요약하자면
화면을 BitmapData로 만들어 PNGEncoder나 JPGEncoder를 이용하여 encode한 다음, 그 결과값인 byteArray값을 서버에 전송한다. 전송된 데이타는 FileReference에서 upload()을 이용해 보낸 파일을 저장할때와 동일하게 저장하면 되겠다.

1. BitmapData로 캡쳐해라.

아래 target은 캡쳐할 UIComponent와 같은 DisplayObject 객체이다.

BitmapData로 캡쳐
var bitmapData:BitmapData = new BitmapData(target.width, target.height, true, 0xFFFFFF);
var drawingRectangle:Rectanglenew Rectangle(0, 0, target.width, target.height);
bitmapData.draw(target, new Matrix(), null, null, drawingRectangle, false);

단, BitmapData를 이용해서 화면을 캡쳐할 대상이 외부 동영상이나 사진같은 거라면 crossdomain.xml 에 대한 check가 있어야 한다. 컨텐츠 로드시 checkPolicyFile 속성을  true로 설정할 필요가 있겠다.
그리고 2880px 이상의 크기는 BitmapData로 만들 수 없다.



2. JPG나 PNG로 Encode 하여 ByteArray를 얻는다.


Flex 3 SDK에는 mx.graphics.codec 패키지에 JPGEncoder와 PNGEncoder가 있다. 인터넷을 뒤져보면 GIFEncoder등도 있을것이다. Flex 2 환경에서 작업한다면 Google code에 Adobe AS3 Corelib에 이들이 제공된다. 만약 JPGEncoder를 사용한다면 다음과 같이 하면 되겠다.

JPGEncoder를 이용하여  JPG  ByteArray값 만들기
import mx.graphics.codec.JPGEncoder;

var byteArray:ByteArray = new JPGEncoder().encode(bitmapData);


Flex 3 SDK는 이러한 Encoder가 IImageEncoder로 구현되었다. 필요하다면 언제 어디서나 Encoder를 바꿔야 하는 경우 IImageEncoder를 사용하는 것이 좋을 수 있겠다.
가령 아래 예제처럼 말이다.

다양한 Image Encoder 사용하기
import mx.graphics.codec.*;

var imageType:String = "jpg";
var imageEncoder:IImageEncoder;
if( imageType.toUpperCase() == "JPG" ) imageEncoder= new JPEGEncoder();
else if( imageType.toUpperCase() == "PNG" ) imageEncoder= new PNGEncoder();
var byteArray:ByteArray = imageEncoder.encode(bitmapData);



 

3. 서버에 ByteArray를 전송한다.

데이타를 전송할때는 FileReference를 사용하지 않는다.
바로 URLLoader와 URLRequest만 이용해서 전송이 가능하다. 참고 데이타는 POST방식으로 URLVariable을 이용해서 보낼 수 있다.

Flex/AIR 데이터 전송 방법
//assumed variable declarations
//var byteArray:ByteArray = 2번째 단계에서 JPG 데이타를 얻었다.
//var fileName:String = "desiredfilename.jpg" //저장할 파일 이름이다. 아무거나 적자!
//var uploadPath:String = "저장할 때 사용되는 서버측 script 경로"
//var parameters:URLVariables = 이미지 이외에 다른 보낼 다른 데이타가 있다면 이것을 이용한다.
//function onComplete(eventObj:Event):void {  성공적으로 데이타를 전송했을때 핸들러 함수 정의
//function onError(eventObj:ErrorEvent):void {  이미지 전송을 실패했을때 핸들러 함수 정의

var urlRequest:URLRequest = new URLRequest();
urlRequest.url = uploadPath;
//urlRequest.contentType = 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary();
urlRequest.method = URLRequestMethod.POST;
urlRequest.data = UploadPostHelper.getPostData(file, byteArray, parameters);
urlRequest.requestHeaders.push( new URLRequestHeader( 'Cache-Control', 'no-cache' ) );
urlRequest.requestHeaders.push( new URLRequestHeader( 'Content-Type', 'multipart/form-data; boundary=' +UploadPostHelper.getBoundary()) );

var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE, onComplete);
urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
urlLoader.load(urlRequest);


위에 진한 부분에 대한 클래스는 아래에 정의되어 있다. 당신은 이 클래스가 어떻게 구성되었는가 열심히 공부할 필요가 없다.(원한다면 해도 된다. 말리지 않음 ^^)

UploadPostHelper Class (Language : java)
package {

    import flash.events.*;
    import flash.net.*;
    import flash.utils.ByteArray;
    import flash.utils.Endian;

    /**
     * Take a fileName, byteArray, and parameters object as input and return ByteArray post data suitable for a UrlRequest as output
     *
     * @see http://marstonstudio.com/?p=36
     * @see http://www.w3.org/TR/html4/interact/forms.html
     * @see http://www.jooce.com/blog/?p=143
     * @see http://www.jooce.com/blog/wp%2Dcontent/uploads/2007/06/uploadFile.txt
     * @see http://blog.je2050.de/2006/05/01/save-bytearray-to-file-with-php/
     *
     * @author Jonathan Marston
     * @version 2007.08.19
     *
     * This work is licensed under a Creative Commons Attribution NonCommercial ShareAlike 3.0 License.
     * @see http://creativecommons.org/licenses/by-nc-sa/3.0/
     *
     */

    public class UploadPostHelper {

        /**
         * Boundary used to break up different parts of the http POST body
         */

        private static var _boundary:String = "";

        /**
         * Get the boundary for the post.
         * Must be passed as part of the contentType of the UrlRequest
         */

        public static function getBoundary():String {

            if(_boundary.length == 0) {
                for (var i:int = 0; i < 0x20; i++ ) {
                    _boundary += String.fromCharCode( int( 97 + Math.random() * 25 ) );
                }
            }

            return _boundary;
        }

        /**
         * Create post data to send in a UrlRequest
         */

        public static function getPostData(fileName:String, byteArray:ByteArray, parameters:Object = null):ByteArray {

            var i: int;
            var bytes:String;

            var postData:ByteArray = new ByteArray();
            postData.endian = Endian.BIG_ENDIAN;

            //add Filename to parameters
            if(parameters == null) {
                parameters = new Object();
            }
            parameters.Filename = fileName;

            //add parameters to postData
            for(var name:String in parameters) {
                postData = BOUNDARY(postData);
                postData = LINEBREAK(postData);
                bytes = 'Content-Disposition: form-data; name="' + name + '"';
                for ( i = 0; i < bytes.length; i++ ) {
                    postData.writeByte( bytes.charCodeAt(i) );
                }
                postData = LINEBREAK(postData);
                postData = LINEBREAK(postData);
                postData.writeUTFBytes(parameters[name]);
                postData = LINEBREAK(postData);
            }

            //add Filedata to postData
            postData = BOUNDARY(postData);
            postData = LINEBREAK(postData);
            bytes = 'Content-Disposition: form-data; name="Filedata"; filename="';
            for ( i = 0; i < bytes.length; i++ ) {
                postData.writeByte( bytes.charCodeAt(i) );
            }
            postData.writeUTFBytes(fileName);
            postData = QUOTATIONMARK(postData);
            postData = LINEBREAK(postData);
            bytes = 'Content-Type: application/octet-stream';
            for ( i = 0; i < bytes.length; i++ ) {
                postData.writeByte( bytes.charCodeAt(i) );
            }
            postData = LINEBREAK(postData);
            postData = LINEBREAK(postData);
            postData.writeBytes(byteArray, 0, byteArray.length);
            postData = LINEBREAK(postData);

            //add upload filed to postData
            postData = LINEBREAK(postData);
            postData = BOUNDARY(postData);
            postData = LINEBREAK(postData);
            bytes = 'Content-Disposition: form-data; name="Upload"';
            for ( i = 0; i < bytes.length; i++ ) {
                postData.writeByte( bytes.charCodeAt(i) );
            }
            postData = LINEBREAK(postData);
            postData = LINEBREAK(postData);
            bytes = 'Submit Query';
            for ( i = 0; i < bytes.length; i++ ) {
                postData.writeByte( bytes.charCodeAt(i) );
            }
            postData = LINEBREAK(postData);

            //closing boundary
            postData = BOUNDARY(postData);
            postData = DOUBLEDASH(postData);

            return postData;
        }

        /**
         * Add a boundary to the PostData with leading doubledash
         */

        private static function BOUNDARY(p:ByteArray):ByteArray {
            var l:int = UploadPostHelper.getBoundary().length;

            p = DOUBLEDASH(p);
            for (var i:int = 0; i < l; i++ ) {
                p.writeByte( _boundary.charCodeAt( i ) );
            }
            return p;
        }

        /**
         * Add one linebreak
         */

        private static function LINEBREAK(p:ByteArray):ByteArray {
            p.writeShort(0x0d0a);
            return p;
        }

        /**
         * Add quotation mark
         */

        private static function QUOTATIONMARK(p:ByteArray):ByteArray {
            p.writeByte(0x22);
            return p;
        }

        /**
         * Add Double Dash
         */

        private static function DOUBLEDASH(p:ByteArray):ByteArray {
            p.writeShort(0x2d2d);
            return p;
        }

    }
}



한가지 중요한 정보를 언급하겠다.
URLLoader를 이용해 서버에 전송할때, 프로그램이 같은 도메인상에 있는 경우에는 보안문제가 없다. 하지만 다른 도메인에 위치한 서버로 이미지를 전송할때는 반드시 crossdomain.xml을 check해야한다.

1. Security.loadPolicyFile(http://다른도메인/crossdomain.xml); 를 URLLoader의 load()함수를 호출하기 전에 호출한다.

2. Flash Player 9.0.124.0 버전부터는 HTTP Header 보안취약점을 해결하기 위해서 cross domain 정책이 변경되었는데.... 서버측에 있는 crossdomain.xml에 allow-http-request-headers-from가 추가되어져야 한다. 이것은 HTTP 헤더 전송을 허용할지 결정해준다.

crossdomain.xml (Language : xml)
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
      <allow-access-from domain="*.jidolstar.com" />
      <allow-http-request-headers-from domain="*.jidolstar.com" headers="*"/>
</cross-domain-policy>

위 처럼 서버측 crossdomain.xml에 allow-http-request-headers-from을 추가함으로 다른 도메인간에 HTTP 헤더 전송을 허용할 수 있다.

서로 다른 도메인에 SWF와 서버측 코드가 배치되어 있다면 반드시 이 사실을 숙지하길 바란다.


3. Flash Player 10에서는 사용자 인터렉션이 반드시 필요하다.

다음글을 참고하세요.

http://blog.jidolstar.com/411 


 

4. 서버측 코드 작성

만약 위 3번 코드에서 var parameters:URLVariables를 아래와 같이 작성했다고 하자.

URLVariables 설정 (Language : java)
var parameters:URLVariables = new URLVariables();
parameters.method = "id";
parameters.userId = "2000321";


그럼 PHP 코드로 아래와 같은 방법처럼 만들면 되겠다.(테스트는 안해봤음)

PHP 코드 예제 (Language : php)
<?php
$method = $_POST['method'];
$userId = $_POST['userId'];
$file_temp = $_FILES['Filedata']['tmp_name'];
$file_name = $_FILES['Filedata']['name'];
$file_size = $_FILES['Filedata']['size'];

if( $method == "id" )
{
  $movePath = "/home/myPath/images/$userId_$file_name";
}
else
{
  $movePath = "/home/myPath/images/time_".time()."_".$file_name;
}

move_uploaded_file($file_temp,$movePath);

echo "save Complete";
?>


마지막 save Comple 메시지를 받기 위해서는 Flex의 Complete 이벤트 발생시 아래와 같은 방법으로 받으면 되겠다. 이것을 알아내는데도 많이 힘들었다. 일종의 팁이다. ^^;

데이타 받기 (Language : java)
var loader:URLLoader = URLLoader(event.target);
var bytedata:ByteArray = loader.data;
var strData:String = bytedata.toString();
trace( strData)


실제 업무에도 잘 이용하고 있다.
이미지 에디터 등을 만들때 아주아주 유용한 자료가 될 것이다.

누가 예제 프로그램 만들어서 트랙백 걸어 주시면 고맙겠다.


자료 출처 
http://marstonstudio.com/2007/08/19/how-to-take-a-snapshot-of-a-flash-movie-and-automatically-upload-the-jpg-to-a-server-in-three-easy-steps/


지돌스타 블로그내 참고자료
 - ImageSnapshot 클래스에 대해 : http://blog.jidolstar.com/301
 - FileReference의 UploadCompleteData 이벤트 : http://blog.jidolstar.com/324
 - 동영상 캡쳐 방법 : http://blog.jidolstar.com/215


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


지난 2009년 12월 18일에 프로젝트 코드명 Squiggly(스퀴글리)가 Prerelease판으로 업데이트 되었다.
http://labs.adobe.com/technologies/squiggly/

Squiggly는 Flash Player 10, AIR 1.5이상에서 동작한다. 이 엔진은 ActionScript 3.0 기반이며 영어로 문장에서 사용된 단어를 영어사전의 그것과 비교하여 단어의 철자를 체크한다. 이번 업데이트는 TLF(Adobe Text Layout Framework)를 기반으로 하는 Flex 4 Spark 컴포넌트를 지원하도록 했다. Squiggly는 Flex Builder, Flash Builder, Flash CS4등 다양한 툴에서 사용할 수 있다. 단지 아쉬운 것은 영어단어만 체크한다는 점이다.


다음 페이지는 Squiggly 데모이다.
http://labs.adobe.com/technologies/squiggly/demo/

개발자는 언제든지 예제소스와 ASDoc을 아래 링크에서 받아볼 수 있다.


이것을 쓸지 모르겠지만 아무튼 알아두고 있어 나쁠 것도 없으니....

참고
http://adnaru.com/242

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

 


Flash, Flex, AIR, ActionScript 프로젝트를 진행하면서 모든 코드를 손수 만들어 사용하는 것은 그다지 추천할 만한 일은 아니다. 왜냐하면 아무리 실력있는 개발자라도 프로젝트에 필요한 코드모음을 전부 만드는 것은 너무 큰 노력이 들뿐만 아니라 코드의 유지보수에도 매우 많은 시간을 허비할 수 있기 때문이다.

Flash 기반 코드 라이브러리는 이미 수년간에 걸쳐 공개되어 있다. 조금만 노력을 기울이면 3D, 트윈, 물리엔진, 수학, 보안, 데이터관리, MVC,   as3corelib, Papervision3d, Away3d, Flex 컴포넌트, 각종 유틸등 ActionScript/Flex/AIR/Flash에 관련된 수십,수백가지 우량(?) 라이브러리들을 쉽게 사용할 수 있다.


위에 링크되어 있는 라이브러리만 해도 정말 현업에 바로 사용할 수 있는 것들이 정말 많다. 또한 단순히 공개차원을 넘어 지속적으로 업데이트가 되며 완성도가 높아지고 있다.

그럼 다시 생각해보자. 내가 3D 게임을 만들기 위해 3D 엔진을 만들어야할 것인가? 내가 트윈(tween)을 구현하기 위해 트윈엔진을 만들어야 할것인가? 각종유틸이 필요해서 그걸 다 내가 만들것인가? 만약 검증되지 않고 아직 별도로 공개되어 있지 않은 것이라면 당연히 만들어야할 것이다. 하지만 기본적으로 큰 노력없이 쉽게 사용할 수 있고 자동으로 업데이트 및 검증까지 해주는 사람들이 만든 훌륭한 라이브러리가 있는데... 왜!!! 그걸 다 만들 필요가 있는가이다.

재미있게도 위에서 소개한 라이브러리들은 대부분 Google 코드(http://code.google.com)에 공개되어 있다. 이는 무엇을 의미하는가? 업데이트할때마다 압축파일을 받아 라이브러리에 복사하는 수고가 필요없이 SVN으로 간단하게 업데이트 할 수 있다는 것을 말한다. 이것을 적극 활용하는 방법을 소개하는 것이 이 글을 쓰는 목적이다.

필자는 Flash 개발자라면 누구라도(?) 잘 알고 있는 Flash 3D 엔진인 Papervision3D과 Away3D를 SVN을 이용해 가져와 프로젝트에서 활용하는 방법을 언급하고자 한다. 개발환경은 Flash Builder 4를 기준으로 하겠다. (거의 모든 기능을 Flex Builder 3에서도 할 수 있다.)


1. Flex/Flash Builder에 SVN 플러그인 설치
이제부터 설명할 공개 라이브러리를 사용하기 위해 Flash builder에 SVN 플러그인을 설치해야한다. 방법은 어렵지 않다. 다음 링크를 참고한다.

Flex Builder 에서 Subversion(SVN) 사용하기


2. Google Code에서 Papervision3D SVN 주소 확인하기
Google 코드 사이트(http://code.google.com)로 들어가 papervision3d로 검색해서 Papervison3D 구글코드 페이지(http://code.google.com/p/papervision3d/)로 들어간다. Papervision3d의 라이센스와 각종 링크들을 보면 다른 정보들도 확인할 수 있다.

물론 화면 우측 Featured downloads에 있는 zip 및 swc 파일을 받아 직접 프로젝트에 복사해 사용할 수 있으나(많은 블로그에 이 방법을 소개하고 있다.) 앞서 말한데로 새로 업데이트되는 최신소스를 바로바로 사용해보려면 이 방법은 너무 불편하다. 게다가 공개 라이브러리를 사용하는게 4~5개 이상 넘어가면 지속적인 업데이트가 힘들다.

어떤 개발자는 너무 최신 버전이면 버그가 있을지도 모르지 않겠느냐? 라고 의심할 수 있을 것이다. 하지만 Papervision3D와 같이 이미 대중적(?)으로 인기를 모으고 있는 라이브러리라면 철저한 검증없이 함부로 최신화(commit) 하지 않는다.


위 Source 탭으로 들어가 Checkout 메뉴를 보면 아래처럼 SVN checkout 주소가 있다. 이 주소는 저장소의 주소라 불리운다. 여러분은 이 주소로 Papervision3D 소스 코드를 받아올 수 있겠다.




3. SVN을 이용해 Papervision3D 라이브러리 프로젝트 만들기
이제 본격적으로 Papervision3d를 SVN으로 받아와 라이브러리 프로젝트를 만들어보자.

Flash Builder를 실행한다. 실행되면 메뉴에서 File > Import 로 들어간다. 아래 처럼 창이 뜨면 SVN > Checkout Projects from SVN 을 선택한다음 Next 버튼을 누른다.



아래 처럼 보이면 새로운 저장소 위치를 만들기 위해 Create a new repository location을 선택후 Next 버튼을 누른다. 이는 새로운 저장소라는 것은 SVN을 접속할 주소를 의미한다고 보면 되겠다. 여기서는 Google 코드에서 봤던 주소 http://papervision3d.googlecode.com/svn/trunk 가 되겠다.

아래처럼 나오면 주소를 입력하고 Next 버튼을 누른다.



아래 화면처럼 나오면 http://papervision3d.googlecode.com/svn/trunk 저장소 내에 있는 Papervision3D 소스 경로를 볼 수 있다. 우리는 ActionScript 3.0 기반에서 제작하기 때문에 as3/trunk를 선택한다. 이때 선택하는 기준은 그 안에 src 폴더의 상위 폴더이어야 한다. 이 방식은 다른 라이브러리를 참조해서 하더라도 저장소내에 구조가 조금씩 다르지만 같은 방법을 사용하므로 기억하자.


위 창에서 Next 버튼을 누르면 다음 창으로 전환된다. 세부적으로 Check out 하는 방법을 설정할 수 있는데 우리는 최신 버전을 사용할 것이기 때문에 바로 Finish 버튼을 누른다.



이제 Papervison3D 라이브러리를 가져올 SVN 주소 설정을 끝냈으므로 이 라이브러리가 담길 프로젝트를 생성해야한다. 위 절차를 모두 끝내면 자동적으로 다음과 같이 New Project 창이 뜬다. 이때 Flex Library Project를 선택하면 되겠다.


다음 그림처럼 Project name을 Papervision3D(이름은 아무거나 써도 상관없다.)으로 설정하고 Flex SDK Version을 선택한 다음 Finish버튼을 누른다.


참고로 Flex SDK Version을 선택시에 라이브러리가 AIR 코드가 포함되어 있는 경우라면 Include Adobe AIR libraries를 선택해야한다.(예: as3corelib) 또한 라이브러리가 구동되기 위한 SDK가 무엇인지도 알아야한다. 가령 Flash Player 10부터 3D API를 사용한 라이브러리라면 그것을 지원하는 SDK를 사용해야한다는 것을 의미한다. SDK는 Adobe Labs에서 최신버전을 받아 사용하면 된다.(방법을 모르시면 댓글을 ^^)

아래처럼 Confirm Overwrite 메시지가 뜨면 그냥 OK 버튼을 누른다.



이제 구글 코드 저장소로부터 아래처럼 Papervision 3D 코드를 받아온다.


Package Explorer에 추가된 Papervion3D 라이브러리 프로젝트를 확인하자. 하지만 만약 에러메시지가 나온다면 다음과 같이 해보자. 해당 프로젝트를 선택하고 오른쪽 마우스 버튼을 눌러 컨텍스트 메뉴에서 Properties로 들어가보자. 아래와 같은 창이 나오면 왼쪽 메뉴에서 Flex Library Build Path를 누르고 우측 상단 탭에 Classes를 선택한다. 그리고 Select classes to include in the library를 선택한 다음 src 앞에 check박스를 체크한다. 그리고 OK 버튼을 누르면 다시 컴파일 되면서 컴파일 에러가 사라질 것이다.



여러분은 이제 최신버전의 Papervision3D를 사용할 수 있게 되었다. 아래처럼 해당 프로젝트를 선택하고 오른쪽 마우스 버튼을 눌러 컨텍스트 메뉴에서 Team > Update to Head를 선택하는 것만으로 최신버전으로 업데이트 할 수 있게 된다. 이러한 방법은 SVN으로 공유한 다른 라이브러리들을 항상 최신으로 유지하는데 쉬운 방법을 제공하게 된다.


한가지 언급하자면 위에서 소개하는 방법은 Papervision3D를 예로 들었기 때문에 다른 라이브러리의 공개 형태에 따라 수행하는 방법의 차이가 있을 수 있다는 점을 강조하고 싶다. 그러한 점은 개발자가 알아서 체크할 수 있어야 한다.


4. 조금만 더 가다듬어 보기 (관심있는 사람만 읽으면 됨)
3번까지 진행하는 것만으로도 개발하는데 전혀 문제없지만 아래 내용을 좀더 알고 가면 더욱 좋을 듯 싶다.

지금까지 구글 코드로부터 SVN 저장소를 통해 받아온 Papervison3d 라이브러리는 설정파일을 제외한 ActionScript 3.0 코드가 대부분이다. 그래서 실질적으로 프로젝트에 필요한 설정구성요소인 .actionScriptProperties나 .flexLibProperties등의 파일들은 Papervision3D 개발자가 처음부터 저장소에 공유하지 않는다. 왜냐하면 .actionScriptProperties, .flexLibProperties들은 Flex Builder나 Flash Builder와 같이 특정 개발툴에서 필요한 파일이기 때문에 다른 IDE에서는 무의미 할 수 있기 때문이다.

아래 처럼 Papervision3D의 프로젝트 이름에 SVN으로 공유되었으나 폴더그림에 * 표시가 되어 있다. 이 표시는 원래 SVN 저장소 내용과 프로젝트 내용이 다를때 나온다. 이 다른 내용이라는 것은 금방 언급했던 .actionScriptProperties, .flexLibProperties 등이다.


Flash Builder에서는 프로젝트내의 .actionScriptProperties, .flexLibProperties 파일을 직접 볼 수 없도록 되어 있다. 이는 Filter 처리가 되어 있기 때문이다. 아래 그림처럼 선택해 Filter 처리된 파일들을 전부 check out하자.



위 그림 처럼 Filter 처리된 파일을 check out하면 아래처럼 프로젝트 내에 .actionScriptProperties, .flexLibProperties 등을 볼 수 있게 된다. 이 파일의 아이콘에 ?로 표시된 것은 저장소에 이 파일이 존재하지 않음을 나타내는 것이다.

* 표시나 ? 표시를 없애기 위해서 이러한 파일들이 SVN 저장소와 공유되지 않도록 무시할 수 있도록 하면 된다. Flash Builder에 설치된 SVN 플러그인은 이러한 기능을 지원해준다. 메뉴에서 window > Preference로 들어가 창이 뜨면 Team > Ignored Resources로 들어가면 아래 처럼 나온다. 여기에 다음 내용을 Add Pattern하자.

.actionScriptProject
.actionScriptProperties
.flexProperties
.flexProject
.project
bin
.settings
참고 : http://labs.adobe.com/wiki/index.php/Flex_Builder:resources:plugins:subversion:filtering




이제 아래 그림처럼 * 표시나 ? 표시가 없어지고 저장소의 원본과 프로젝트의 소스가 일치함을 나타내는 표시로 바뀐다.




5. Away3D를 SVN으로 받아오기

Papervision3D 라이브러리를 이용해서 애플리케이션을 만드는 예제는 굳이 설명안해도 된다고 생각한다. 대신 Away3D를 활용하는 방법을 약간 언급하고자 한다.

Away3D도 Papervision3D처럼 구글코드(http://code.google.com/p/away3d/)를 통해 SVN으로 소스를 공개하고 있다. 하지만 Away3D는 Papervision3D와 다르게 Flash Player 9, Flash Player 10 버전을 함께 제공하면서 Flash Player 10 3D 라이브러리를 이용한 3D 엔진인 Away3DLite도 제공한다. 또한 몇가지 유용한 예제도 함께 제공하고 있다.


Away3D 저장소 주소(http://away3d.googlecode.com/svn/trunk)로 접속하면 아래처럼 나온다. fp9과 fp10은 각각 Flash Player 9, 10 버전을 의미한다. 아무래도 Flash Player 10 버전이 최신이면서 효율적으로 만들어졌으므로 fp10에 있는 공개 라이브러리와 예제소스를 참고할 것이다.

fp10에 보면 Away3D, Away3DLite 소스가 있음을 확인할 수 있다. 또한 그에 대한 예제는 Examples에 들어 있다.


아래는 해당 라이브러리와 예제의 저장소 주소이다.


여러분은 Papervision3D를 SVN으로 프로젝트를 만든 것처럼 Away3D에서도 그래도 하면 된다. 단. Examples의 경우 중간에 프로젝트 생성시 ActionScript 프로젝트를 선택하기 바란다. 물론 Flash Player 10 기준이라는 점도 명심하자.

예제 프로젝트에서는 해당 라이브러리 프로젝트의 경로를 포함시켜줘야한다. Away3D_Example은 Away3D 라이브러리를 Away3DLite_Examples는 Away3dLite 라이브러리를 포함해야한다. 방법은 간단하다. Away3D_Example와 Away3DLite_Examples내에 libs 폴더안에 각각의 라이브러리 프로젝트에서 만들어진 bin디렉토리에 있는 
swc를 복사하면 된다. 하지만 이는 애플리케이션을 전부 만들고 배포하고 난 다음에 백업을 위해 선택할 방법이고 실제로 개발할때는 다음과 같이 한다.

1. Away3D_Example 프로젝트를 선택한다.
2. 메뉴에서 Project > Properties를 선택한다.
3. 창이 뜨면 좌측 ActionScript Build Path(Flex의 경우 Flex Build Path)를 선택한다.
4. 상단 Library path 탭을 선택한다.
5. Add Project를 선택한 다음 Away3D 라이브러리를 선택한다.
6. OK 버튼을 누른다.
7. Away3D_Example의 .actionScriptProperties를 연다.
아래 그림처럼  default package에 있는 소스 들은 전부 애플리케이션들이다. 이 코드들이 진정한 애플리케이션으로서 동작하려면 .actionScriptProperties내에 application으로 등록해야한다. 수작업이긴 해도 전부 등록하자. 또는 해당 as 파일을 선택후 마우스 오른쪽 버튼을 눌러 Set as Default Application 항목을 선택해도 된다. 이는 애플리케이션으로 등록하는 동시에 Default 애플리케이션으로 잡아준다. Default 애플리케이션을 지칭하는 것은 .actionScriptProperties 파일 내에 actionScriptProperties 태그의 mainApplicationPath 속성이 그 역할을 한다.



위 처럼 설정하면 이제 예제 파일들을 아무 문제없이 동작시킬 수 있을 것이다.
8. Away3DLite_Example도 위와 비슷한 방법으로 하면 되겠다.

아래는 Away3DLite_Examples에 있는 Advanced_Mario1000.as 애플리케이션을 실행하는 화면이다.

아마 어떤 애플리케이션은 as3dmod 라이브러리가 필요하다는 에러가 나올것이다. 이것도 비슷한 방법으로 Google Code에서 가져올 수 있다. 단, 몇가지 다른 설정을 해야하는데 여기서 설명하지 않고 각자의 몫으로 돌리겠다.


정리하며

개발자라면 Google Code와 친해져야하지 않을까? 또한 기존 라이브러리가 있는가 검색을 해보는 성의가 있어야하지 않을까? 그리고 그 라이브러리를 잘 활용하는 방법을 스스로 깨달으려고 노력해야하지 않을까?

잘못된 사항이나 더 좋은 정보가 있다면 얼마든지 댓글이나 트랙백 부탁한다.


참고
bs-papervision : http://code.google.com/p/bs-papervision/ 
as3dmod : http://code.google.com/p/as3dmod/
sandy3d : http://www.flashsandy.org 
alternativa3D : http://alternativaplatform.com/en/alternativa3d/

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





Flash 엔터프라이즈 개발 업체를 제외하고는 플래시 개발자가 2~3명인 업체가 대부분입니다. 게다가 Flex/AIR등에 처음 접하는 경우도 다분하죠. 결국 Flash 프로젝트의 실무를 어찌 진행할 것인가 고민이 되는 것이 현실입니다. 그래서 도움이 될만한 글들을 모아봤습니다.

http://opencast.naver.com/FL188/15

이 캐스트는 베스트 No.로 12월 10일에 추천되었습니다. ^^
http://opencast.naver.com/home/bestNo.nhn?exposureDate=20091210 

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

지난 2009년 11월 어도비(Adobe)에서는 AIR 2.0 Beta를 새롭게 발표하면서 퍼포먼스 향상과 더 많은 OS의 자원을 사용할 수 있는 API 기능을 추가해서 발표했다. Flash Player 10.1 Prerelease와 함께 발표된 Adobe AIR 2.0의 새로운 기능 및 추가/개선 사항에 대해서 정리해보았다.

참고로 이 글은 Elad Elrom의 AIR 2 Enhancements Complete Overview  와 AIR 2 Rerelease Note를 참고해서 정리한 것이다.




  1. 새로운 기능 (이번 페이지에서 다룸)
    - File Promises
    - Screen reader
    - Native Processes
    - New networking support
    - Global Error Handling
    - Packaging an AIR application in a native installer
  2. 기존 API에 대한 추가된 기능 (다음 편에 다룸)
    - Max Size of NativeWindow
    - Idle time-out
    - Mac Vector Printing
    - Database Transaction Savepoints
    - Microphone Access API
    - Open file with Default Application
  3. 플랫폼 인식 관련 API (다음 편에 다룸)
    - Multi-touch functionality
    - Mass Storage Device Detection
  4. 퍼포먼스 향상 (다음 편에 다룸)


출처 : http://tinyurl.com/ydxevnu




Flash Builder 4 beta 2에서 Adobe AIR 2.0로 개발 환경 만들기 

여기에서 보여지는 예제는 모두 Flash Builder 4 beta 2에 기본으로 설치된 Flex 4 SDK를 이용한다. 하지만 AIR 2.0로 테스트해보기 위해서는 AIR 2.0 SDK를 따로 받아 다음과 같은 과정이 필요하다.

Windows 환경이라면 다음과 같이 하면 되겠다.

  • 만약 Flash Builder를 설치 안했다면 다음 링크를 통해 받는다.
    1. AIR 2.0 SDK와 Runtime을 다운로드 받는다.
    2. 다운받은 Runtime을 실행해 설치한다.
    3. Flash builder가 설치된 sdks/4.0.0과 sdks/3.4.1 폴더를 복사해서 sdks 폴더안에 붙인뒤 4.0.0_AIR2.0, 3.4.1_AIR2.0을 만든다. 이렇게 되면 sdks 폴더안에는 3.4.1, 4.0.0, 3.4.1_AIR2.0, 4.0.0_AIR2.0 이름을 가진 4개의 SDK가 만들어진다.
      본인의 경우는 C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks 내에 있다. 
    4. AIR 2.0 SDK는 압축을 풀고 그안에 있는 내용을 방금 만든 4.0.0_AIR2.0, 3.4.1_AIR2.0 폴더에 복사한다.
    5. Flash Builder를 실행한다.
    6. 메뉴에서 Window > Preference 로 들어간다.
    7. 창이 뜨면 왼쪽 메뉴에서 Flash builder > Installed Flex SDKs를 선택한다.
    8. 우측에 Add버튼을 눌러 위에서 새로 만든 AIR 2.0을 위한 SDKs들을 선택한다.  
      Flex SDK Name은 각각 Flex 3.4 AIR 2.0, Flex 4.0 AIR 2.0 등으로 이름을 바꿔도 된다.
      본인의 경우 아래 경로가 되겠다.
      C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks/3.4.1_AIR2.0/
      C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks/4.0.0_AIR2.0/
      아래처럼 Flex 4.0으로 체크되어 있다면 AIR 2.0기반의 Flex 4.0 SDK를 디폴트로 바꾸자.
    9. 이제 모든 개발환경이 완료되었다. 자신의 프로젝트 생성시에 원하는 SDK를 선택하면 되겠다.  물론 아래에서 소개하는 예시를 따르려면 Flex 3든 Flex 4든 AIR 2.0 SDK를 선택해야겠다.


    Mac 환경이라면 다음과 같이 하면 되겠다.

    Mac에서 AIR 2.0 Beta 개발환경 만들기 - Flash Builder 4 Beta 2 기반

    참고로 Flash CS4나 CS3, 드림위버나 기타 다른 IDE에서 개발하시는 분들에게는 본인의 경험이 없어 정보를 줄 수 없다.

  •  
    1. Adobe AIR 2.0 새로운 기능

    1.1 IPv6 지원
    AIR 2.0은 모든 Network API에 대해서 IPv6를 지원하게 되었다. 현재 인터넷에서 주로 사용하고 있는 IP 주소의 버전은 IPv4로서 무려 20년이 넘도록 사용하고 있다. IPv4는 32비트 주소체계를 가지며 4,294,967,296개의 제한된 갯수의 주소를 가진다. IPv6는 이 부족함을 해소하기 위해 만들어진 IP 주소 체계이다. IPv6는 128비트 주소체계를 가진다. IPv6가 일반적으로 사용되고 있는 것은 아니지만  가까운 미래에 IPv6가 범용적으로 도입되어 네트워크 IP 주소체계가 더욱 확장될 수 있으므로 미리 지원될 수 있도록 한 것으로 여겨진다. IPv6에 대한 자세한 내용은 아래 링크를 참고한다.




    1.2  UDP(User Datagram Protocol) 소켓, Server 소켓, TLS/SSL 소켓 지원
    AIR 2.0에서는 더욱 다양한 소켓 통신 관련 클래스들이 추가되었다. 이전버전까지 반쪽짜리 였다면 이제 어느정도 구색을 갖춘듯 하다. 

    AIR 2.0에서 지원하는 네트워크에 관련된 내용은 다음 링크를 참고한다.


    서버 소켓 : ServerSocket 클래스 
    기존 AIR 버전에서는 반쪽 소켓통신만 지원되었다. 즉, 클라이언트는 만들 수 있었지만 서버측을 만들지 못했다. 이번 AIR 2.0에서 서버 소켓이 지원됨에 따라 이를 이용한 다양한 네트워크 애플리케이션을 만들 수 있을 것으로 보인다.

    서버 소켓은 TCP(Transmissin Control Protocal)을 기반으로 한다. TCP는 연결지향 통신 규약으로 인터넷 상의 컴퓨터들 사이에 데이터를 통신하기 위해 IP와 함께 사용된다. TCP는 데이터의 통신 중간에 데이터가 유실되더라도 신뢰도를  체크해서 완벽한 데이터를 통신할 수 있는 신뢰형 기반의 데이터 통신시 사용된다. 일반적인 웹브라우져를 이용한 통신데이터는 모두 TCP를 근간으로 한다.



    AIR 2.0에서 지원하는 서버 Socket는 ServerSocket 클래스에서 관장한다.



    UDP 소켓 : DatagramSocket 클래스
    UDP는 User Datagram Protocal의 약어로 IP를 사용해 네트워크내에서 컴퓨터들간에 데이타 통신하기 위해 제한된 서비스만 제공하는 통신 규칙이다. TCP의 대안이기도 하며 IP와 함께 사용하면 UDP/IP라고 표현하기도 한다. UDP는 정확한 데이터를 전송할 의무가 주어지지 않은 비연결지향성을 가져 데이터의 흐름에 신뢰를 할 수 없는 것이 특징이다. TCP처럼 통신을 하기전 상대방을 확인하는 절차가 없기 때문에 데이터 정확도는 떨어질 수 있지만 빠른 데이타 전송에 유리하다. 그래서 음악 및 비디오 실시간 스트리밍 서비스에 잘 사용하는 프로토콜이다.



    AIR 2.0에서는 UDP 통신을 DatagramSocket 클래스와 DatagramSocketDataEvent 클래스를 지원하고 있다. 더욱 자세한 내용은 아래 링크를 참고한다.



    Secure 클라이언트 소켓 : SecureSocket 클래스 
    소켓서버에 접속하기 위해 SSLv4(Secure Socket Layer version 4) 또는 TLSv1(Transport Layer Security version 1) 기반으로 하는 프로토콜을 이용하여 SecureSocket 클래스로 접속할 수 있다. Secure 소켓을 이용하면 암호화된 데이터 전송으로 서버인증(server authentication), 데이터 무결성(data integrity), 메시지 기밀성(message confidentiality)등의 장점을 가진다. 




    1.3 네트워크 정보 
    AIR 2.0의 API로 네트워크 정보를 얻어낼 수 있는 클래스인 NetworkInfo가 추가되었다. 이 클래스를 이용하면 사용자의 기기의 Mac Address, IP정보 등의 네트워크 정보를 얻어올 수 있다.


    Adobe 문서에서 보여지는 예제에서는 var networkInfo:NetworkInfo = new NetworkInfo() 처럼 생성해서 사용하도록 되어 있는데 잘못된 것이다. var networkInfo:NetworkInfo = NetworkInfo.networkInfo; 처럼 static 속성으로 접근해야한다.
    그리고 NetworkInterface에 networkInterfaceDefault 속성 또한 존재하지 않는다.  
    package { 
    	import flash.display.Sprite; 
    	import flash.net.InterfaceAddress; 
    	import flash.net.NetworkInfo; 
    	import flash.net.NetworkInterface; 
    	
    	public class NetworkInformationExample extends Sprite 
    	{ 
    		public function NetworkInformationExample() 
    		{ 
    			var networkInfo:NetworkInfo.<NetworkInterface> = NetworkInfo.networkInfo;
    			var interfaces:Vector.<NetworkInterface> = networkInfo.findInterfaces(); 
    			
    			if( interfaces != null ) 
    			{ 
    				trace( "Interface count: " + interfaces.length ); 
    				for each ( var interfaceObj:NetworkInterface in interfaces ) 
    				{ 
    					trace( "\nname: "             + interfaceObj.name ); 
    					trace( "display name: "     + interfaceObj.displayName ); 
    					trace( "mtu: "                 + interfaceObj.mtu ); 
    					trace( "active?: "             + interfaceObj.active ); 
    					//trace( "default?: "         + interfaceObj.networkInterfaceDefault ); 
    					trace( "parent interface: " + interfaceObj.parent ); 
    					trace( "hardware address: " + interfaceObj.hardwareAddress ); 
    					if( interfaceObj.subInterfaces != null ) 
    					{ 
    						trace( "# subinterfaces: " + interfaceObj.subInterfaces.length ); 
    					} 
    					trace("# addresses: "     + interfaceObj.addresses.length ); 
    					for each ( var address:InterfaceAddress in interfaceObj.addresses ) 
    					{ 
    						trace( "  type: "           + address.ipVersion ); 
    						trace( "  address: "         + address.address ); 
    						trace( "  broadcast: "         + address.broadcast ); 
    						trace( "  prefix length: "     + address.prefixLength ); 
    					} 
    				}             
    			} 
    		}     
    	} 
    }
    

     
    위 프로그램은 NetworkInfo를 사용하는 아주 간단한 예시이다. Flash Builder 4 에서 이 예시를 테스트해볼때는 AIR 프로젝트 생성시 마지막에 mxml대신 as로 확장자를 두고 생성해서 테스트 하면 되겠다.


    1.4 DNS lookup
    AIR 2.0에서부터 DNSResolver 클래스를 제공하여 DNS(Domain Name System) 정보를 찾을 수 있게 되었다. 정보를 찾게되면 DNSResolverEvent가 발생한다. 

    DNS는 각 도메인 이름(가령 examples.com)을 IP주소로 가리킨다. 일반적으로 사람은 111.123.431.11와 같은 IP주소보다 examples.com처럼 도메인 이름과 같은 단어조합을 더욱 인지하기 쉽다. 이러한 이유로 실제로는 도메인 이름을 사용하면 DNS를 걸쳐 이에 해당하는 IP주소로 연결시켜 관련 서버로 접속하게 한다. AIR 2.0에서 제공하는 DNSResolver 클래스를 이용하면 이러한 관계를 조사해볼 수 있게 된다. 

    IP주소는 IPv4(32비트) 또는 IPv6(128비트)를 사용할 수 있다. 

    DNSResolver 클래스의 lookup() 함수를 통해 host이름과 레코드 형태(record type)을 넘겨주면 DNSResolverEvent.LOOK_UP 이벤트를 발생하여 관련 레코드 타입에 대한 정보를 반환해준다. 
     



     다음은 예제이다.
    package
    {
    	import flash.desktop.NativeApplication;
    	import flash.display.Sprite;
    	import flash.events.DNSResolverEvent;
    	import flash.events.ErrorEvent;
    	import flash.net.dns.AAAARecord;
    	import flash.net.dns.ARecord;
    	import flash.net.dns.DNSResolver;
    	import flash.net.dns.MXRecord;
    	import flash.net.dns.PTRRecord;
    	import flash.net.dns.SRVRecord;
    	import flash.utils.getQualifiedClassName;
    	
    	public class DNSResolverExample extends Sprite
    	{
    		private var lookupCount:int = 0;
    		public function DNSResolverExample()
    		{
    			//Create the resolver object
    			var resolver:DNSResolver = new DNSResolver();
    			resolver.addEventListener( DNSResolverEvent.LOOKUP, lookupComplete );
    			resolver.addEventListener( ErrorEvent.ERROR, lookupError );
    			
    			//Look up records
    			resolver.lookup( "yahoo.com", ARecord );
    			resolver.lookup( "yahoo.com", AAAARecord );
    			resolver.lookup( "yahoo.com", MXRecord );
    			resolver.lookup( "209.191.93.53", PTRRecord );
    			resolver.lookup( "_sip._tcp.yahoo.com.", SRVRecord );
    		}
    		
    		private function lookupComplete( event:DNSResolverEvent ):void
    		{
    			trace("-------------------------------------");
    			trace( "Query string: " + event.host );
    			trace( "Record type: " +  flash.utils.getQualifiedClassName( event.resourceRecords[0] ) + 
    				", count: " + event.resourceRecords.length );
    			for each( var record:* in event.resourceRecords )
    			{
    				if( record is ARecord ) trace( record.name + " : " + record.address );
    				if( record is AAAARecord ) trace( record.name + " : " + record.address );
    				if( record is MXRecord ) trace( record.name + " : " + record.exchange + ", " + record.preference );
    				if( record is PTRRecord ) trace( record.name + " : " + record.ptrdName );
    				if( record is SRVRecord ) trace( record.name + " : " + record.target + ", " + record.port +
    					", " + record.priority + ", " + record.weight );
    			}    
    			if( ++lookupCount == 5 )
    			{
    				NativeApplication.nativeApplication.exit();
    			}
    		}
    		
    		private function lookupError( error:ErrorEvent ):void
    		{
    			trace("-------------------------------------");
    			trace("Error: " + error.text );
    			if( ++lookupCount == 5 )
    			{
    				NativeApplication.nativeApplication.exit();
    			}
    		}
    	}
    }
    



    1.5 스크린 리더 지원(Screen Reader Support)

    AIR 2.0은 스크린 리더를 지원하기 위한 Accessibility 클래스가 추가되었다. 스크린 리더는 시각 장애 사용자들을 위한 보조 기술로서 화면 내용을 오디오 버전으로 제공하는 것이다. 이 클래스는 현재 Windows 환경에서만 제공된다. 이것이 지원되는지 확인하려면 Capabilities.hasAccessibility 정적속성이 true이어야 한다. 버튼, 무비클립, 텍스트필드와 같은 객체에 액세스 가능한 속성을 얻고 설정하기 위해 DisplayObject.accessibilityProperties 속성을 사용하며 이 값이 바뀌었다는 것을 알려주기 위해 Accessibility.updateProperties() 정적메소드를 이용한다.

    이 기능을 사용하기 위해 프로젝트의 컴파일 옵션을 추가해야한다. Flash builder 4에서 해당 프로젝트를 선택후 메뉴의 Project > Properties를 선택한다. 새창이 나오면 Flex Compiler를 선택후 컴파일 옵션에 Generate accessible SWF file을 선택후 OK 버튼을 누른다.


    package {
    	import flash.accessibility.Accessibility;
    	import flash.desktop.NativeApplication;
    	import flash.display.Sprite;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	import flash.utils.setTimeout;
    	
    	public class AccessibilityExample extends Sprite {
    		public static const BUTTON_WIDTH:uint = 90;
    		public static const BUTTON_HEIGHT:uint = 20;
    		
    		private var gutter:uint = 5;
    		private var menuLabels:Array = new Array("PROJECTS", "PORTFOLIO", "CONTACT");
    		private var menuDescriptions:Array = new Array("Learn more about our projects"
    			, "See our portfolio"
    			, "Get in touch with our team");
    		
    		public function AccessibilityExample() {
    			configureAssets();
    			NativeApplication.nativeApplication.autoExit = true;
    			stage.align = StageAlign.TOP_LEFT;
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			stage.nativeWindow.activate();
    			setTimeout(updateAccessibility, 2000); 
    		}
    		
    		private function updateAccessibility():void {
    			trace("Accessibility.active: " + Accessibility.active);
    			if(Accessibility.active) {
    				Accessibility.updateProperties();
    			}
    		}
    		
    		private function configureAssets():void {
    			var child:CustomAccessibleButton;
    			for(var i:uint; i < menuLabels.length; i++) {
    				child = new CustomAccessibleButton();
    				child.y = (numChildren * (BUTTON_HEIGHT + gutter));
    				child.setLabel(menuLabels[i]);
    				child.setDescription(menuDescriptions[i]);
    				addChild(child);
    			}
    		}
    	}
    }
    
    import flash.accessibility.AccessibilityProperties;
    import flash.display.Shape;
    import flash.display.SimpleButton;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.text.TextFormat;
    import flash.text.TextFormatAlign;
    import flash.text.TextField;
    
    class CustomAccessibleButton extends Sprite {
    	private var button:SimpleButton;
    	private var label:TextField;
    	private var description:String;
    	private var _name:String;
    	
    	public function CustomAccessibleButton(_width:uint = 0, _height:uint = 0) {
    		_width = (_width == 0) ? AccessibilityExample.BUTTON_WIDTH : _width;
    		_height = (_height == 0) ? AccessibilityExample.BUTTON_HEIGHT : _height;
    		
    		button = buildButton(_width, _height);
    		label = buildLabel(_width, _height);
    		
    		addEventListener(Event.ADDED, addedHandler);
    	}
    	
    	private function addedHandler(event:Event):void {
    		trace("addedHandler: " + this._name);
    		var accessProps:AccessibilityProperties = new AccessibilityProperties();
    		accessProps.name = this._name;
    		accessProps.description = description;
    		accessibilityProperties = accessProps;
    		removeEventListener(Event.ADDED, addedHandler);
    	}
    	
    	private function buildButton(_width:uint, _height:uint):SimpleButton {
    		var child:SimpleButton = new CustomSimpleButton(_width, _height);
    		addChild(child);
    		return child;
    	}
    	
    	private function buildLabel(_width:uint, _height:uint):TextField {
    		var format:TextFormat = new TextFormat();
    		format.font = "Verdana";
    		format.size = 11;
    		format.color = 0xFFFFFF;
    		format.align = TextFormatAlign.CENTER;
    		format.bold = true;
    		
    		var child:TextField = new TextField();
    		child.y = 1;
    		child.width = _width;
    		child.height = _height;
    		child.selectable = false;
    		child.defaultTextFormat = format;
    		child.mouseEnabled = false;
    		
    		addChild(child);
    		return child;
    	}
    	
    	public function setLabel(text:String):void {
    		label.text = text;
    		this._name = text;
    	}
    	
    	public function setDescription(text:String):void {
    		description = text;
    	}
    }
    
    class CustomSimpleButton extends SimpleButton {
    	private var upColor:uint = 0xFFCC00;
    	private var overColor:uint = 0xCCFF00;
    	private var downColor:uint = 0x00CCFF;
    	
    	public function CustomSimpleButton(_width:uint, _height:uint) {
    		downState = new ButtonDisplayState(downColor, _width, _height);
    		overState = new ButtonDisplayState(overColor, _width, _height);
    		upState = new ButtonDisplayState(upColor, _width, _height);
    		hitTestState = new ButtonDisplayState(upColor, _width, _height);
    		useHandCursor = true;
    	}        
    }
    
    class ButtonDisplayState extends Shape {
    	private var bgColor:uint;
    	private var _width:uint;
    	private var _height:uint;
    	
    	public function ButtonDisplayState(bgColor:uint, _width:uint, _height:uint) {
    		this.bgColor = bgColor;
    		this._width = _width;
    		this._height = _height;
    		draw();
    	}
    	
    	private function draw():void {
    		graphics.beginFill(bgColor);
    		graphics.drawRect(0, 0, _width, _height);
    		graphics.endFill();
    	}
    }
    


    1.6 네이티브 프로세스의 직접 실행과 상호 작용( Launching and Interacting with Native Processes )
    네이티브 프로세스에 대해서 본인도 처음 접하는 용어라 해석하기 어려웠는데, 이해한바 각 운영체제에 실행될 수 있는 코드라고 생각하면 될 것 같다. 가령, 윈도우에서는 exe, dll, ocx등이겠고 맥이라면 dmg, app 등이 아닐까 생각한다.

    AIR 2.0부터 기존의 애플리케이션을 실행해 AIR 애플리케이션과 표준입출력을 통한 상호작용할 수 있는 기능이 추가되었다. 가령, C언어로 만든 프로그램을 자신의 AIR 애플리케이션과 함께 배포했다고 하자. AIR 애플리케이션이 이 C언어로 만든 프로그램을 실행하고 표준입출력을 이용해 통신이 가능해진다. 또는 이미 설치된 Editplus에 File을 넘겨서 실행하고 종료시점도 알 수 있다.

    자세한 내용은 다음을 참고한다.

    이 기능을 수행하려면 반드시 AIR 디스크립터 파일에 다음 코드를 꼭 넣어야 한다.


    <supportedProfiles>extendedDesktop</supportedProfiles>
    


    아래는 Editplus를 이용해 Text파일을 열고 Editplus가 닫힐 때 감지하는 간단한 Flex 4를 이용한 AIR 2.0 애플리케이션 코드이다. NativeProcess와 NativeProcessStartupInfo 클래스의 용도를 잘 살펴보자.
    <?xml version="1.0" encoding="utf-8"?>
    <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
    					   xmlns:s="library://ns.adobe.com/flex/spark" 
    					   xmlns:mx="library://ns.adobe.com/flex/halo">
    	<fx:Script>
    		<![CDATA[
    			import flash.events.NativeProcessExitEvent;
    			
    			public function executeNativeProcess():void 
    			{
    				var executable:File = new File("C:/Program Files/EditPlus 3/editplus.exe"); //c:/Windows/System32/notepad.exe
    				var workingDirectory:File = new File("c:/");
    				
    				var nativeProcess:NativeProcess = new NativeProcess();
    				
    				if (NativeProcess.isSupported) 
    				{
    					trace("Native Process Supported");
    				}
    				
    				var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
    				nativeProcessStartupInfo.executable = executable;
    				nativeProcessStartupInfo.workingDirectory = workingDirectory;
    				
    				var args:Vector.<String> = new Vector.<String>();   
    				args.push("c:/LAN.log"); // open file that was given with the executable application 
    				nativeProcessStartupInfo.arguments = args;
    				
    				nativeProcess.addEventListener( NativeProcessExitEvent.EXIT, onExitError );
    				
    				try {
    					nativeProcess.start(nativeProcessStartupInfo);
    				} catch (error:IllegalOperationError) {
    					trace("Illegal Operation: "+error.toString());
    				} catch (error:ArgumentError) {
    					trace("Argument Error: "+error.toString());
    				} catch (error:Error) {
    					trace ("Error: "+error.toString());
    				}
    				
    				if (nativeProcess.running) 
    				{
    					trace ("Native Process Support");
    				}       
    			}
    			
    			public function onExitError(event:NativeProcessExitEvent):void 
    			{
    				trace( "Native Process Exit code: "+event.exitCode );
    			}            
    			
    		]]>
    	</fx:Script>
    	
    	<s:Button id="button"
    			  label="Open File foobar.txt with text editor"
    			  click="executeNativeProcess();"
    			  width="250" />
    	
    </s:WindowedApplication>
    

    Mac인 경우 Mac에 설치되어 기본으로 설치되어 있는 텍스트 에디터를 실행할 수도 있다. 이것도 별다른 설정없이 같은 방법으로 실행할 수 있다. new File()에는 "/Applications/TextEdit/TextEdit.app/Contents/MacOS/TextEdit"와 "/" 로 대체하고 args.push()부분은 foobar.txt가 데스크톱에 있을때 "/User/사용자계정/Desktop/foobar.txt"로 대체해서 실행하면 된다. 

    NativeProcess 클래스는 단순히 실행과 종료만 담당하지 않는다. AIR 애플리케이션과 Native 실행 소스간에 통신도 가능하다. 다음 링크는 HTML/Javascript, Flex, Flash 개발자들을 위한 예제이다. AIR코드 뿐 아니라 C코드도 있으니 꼭 다운받아 실행해보길 바란다.

    다음과 같이 Flex 4 코드로 변경해봤다.


    <?xml version="1.0" encoding="utf-8"?>
    <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
    					   xmlns:s="library://ns.adobe.com/flex/spark" 
    					   xmlns:mx="library://ns.adobe.com/flex/halo" 
    					   width="350" height="150"
    					   applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
    	<fx:Script>
    		<![CDATA[
    			import flash.events.ProgressEvent;
    			import flash.system.Capabilities;
    			import flash.utils.IDataInput;
    			
    			import mx.controls.Alert;
    			import mx.events.FlexEvent;
    			
    			private var process:NativeProcess;
    			
    			private function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void {
    				//Native Process가 지원되는지 확인. 
    				//지원되려면 반드시 디스크립터에  <supportedProfiles>extendedDesktop</supportedProfiles>을 추가해야한다.
    				if( NativeProcess.isSupported ) {
    					launchEchoTest();
    				} else {
    					Alert.show("NativeProcess not supported.","Error");
    				}
    			}			
    			private function launchEchoTest():void {
    				//실행할 Native 애플리케이션 선택
    				var file:File = File.applicationDirectory;
    				file = file.resolvePath("NativeApps");
    				if( Capabilities.os.toLocaleLowerCase().indexOf("win") > -1 ) {
    					file = file.resolvePath("Windows/bin/echoTestWin.exe");
    				} else {
    					file = file.resolvePath("Mac/bin/echoTestMac");
    				}
    				
    				//Native Process를 진행하기 위한 정보 설정 
    				var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
    				info.executable = file;
    				
    				//Native Process생성
    				process = new NativeProcess();
    				
    				//보내고 받을때 발생하는 이벤트에 대한 처리 
    				process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, function(event:ProgressEvent):void {
    					var date:Date = new Date();
    					var bytes:IDataInput = process.standardOutput;
    					textReceived.text = bytes.readUTFBytes( bytes.bytesAvailable );
    					launchEchoTest();
    					lbDateStamp.text = date.toString();
    				} );
    				process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, function(event:ProgressEvent):void {
    					process.closeInput();
    				} );
    				//Native Process 시작 
    				process.start( info );
    			}
    			private function writeData():void {
    				process.standardInput.writeUTFBytes( textToSend.text + "\n" );
    			}
    		]]>
    	</fx:Script>
    	<fx:Declarations>
    		<!-- Place non-visual elements (e.g., services, value objects) here -->
    	</fx:Declarations>
    	<s:VGroup paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10">
    		<mx:Form>
    			<mx:FormItem label="Text to send" direction="horizontal">
    				<s:TextInput id="textToSend"/>	
    				<s:Button label="writeData()" click="writeData()"/>
    			</mx:FormItem>
    			<mx:FormItem label="Text received">
    				<s:TextInput id="textReceived" editable="false"/>
    			</mx:FormItem>
    		</mx:Form>
    		<s:Label id="lbDateStamp"/>
    	</s:VGroup>
    </s:WindowedApplication>
    
    


    아래 코드는 AIR 애플리케이션과 통신할 C코드(Mac용)이다.
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    #define BUFFER_SIZE 8192
    
    int main(int argc, char** argv)
    {
    	char buf[BUFFER_SIZE];
    	int  cnt;
    	
    	while ( !feof(stdin) )
    	{
    		cnt = read(STDIN_FILENO, buf, sizeof buf); 
    		if (-1 == cnt) 
    		{
    			perror("read");
    			exit(1);
    		}
    		if (0 == cnt)
    		{
    			// eof reached...
    			exit(0);
    		}
    
    		write(STDOUT_FILENO, buf, cnt ); 
    	}
    	
    	return 0;
    }
    
    




    NativeProcess의 standardInput과 standardOutput 속성은 각각 IDataOutput, IDataInput을 입력과 출력값으로 받고 있다. 그러므로 이 속성을 이용해 표준입출력(standard input/output)을 이용해 서로 통신하게 된다.

    매우 다양한 형태로 응용이 가능할 것이라 판단한다.


    1.7 File Promise 지원

    AIR 2.0부터 FilePromise 클래스가 새로 추가되어 드래그&드롭 형태로 리모트 파일을 AIR 애플리케이션 외부로 다운로드 받을 수 있게 되었다. URLFilePromise 클래스를 이용해 이 작업을 할 수 있다.

    이 기능은 Windows와 Mac에서만 지원되며(Clipboard.supportsFilePromise로 확인) 반드시 AIR 애플리케이션 외부로 드롭하는 경우만 해당한다. 일반적인 클립보드 복사&붙이기 기능으로는 할 수 없다.

    다음 코드는 예시이다.

     

    <?xml version="1.0" encoding="utf-8"?>
    <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
    					   xmlns:s="library://ns.adobe.com/flex/spark" 
    					   xmlns:mx="library://ns.adobe.com/flex/halo"
    					   applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
    	<fx:Script>
    		<![CDATA[
    			import mx.controls.Alert;
    			import mx.events.DragEvent;
    			import mx.events.FlexEvent;
    			
    			private var clipboard:Clipboard;
    			private function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void {
    				clipboard = new Clipboard();
    				if( !clipboard.supportsFilePromise ) {
    					Alert.show( "File Promise를 지원하지 않습니다." );
    				}
    			}
    			private function onDragOut(event:DragEvent):void {
    				trace( event.target, event.type );
    				var items:Array = fileData.selectedItems;
    				var promises:Array = new Array();
    				for each (var item:Object in items) {
    					var filePromise:URLFilePromise = new URLFilePromise();
    					var request:URLRequest = new URLRequest(item.url);
    					filePromise.request = request;
    					filePromise.relativePath = item.name;
    					promises.push(filePromise);
    				}
    				clipboard.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, promises);
    				NativeDragManager.doDrag(fileData, clipboard);     
    			}
    			private function onDragOutComplete(event:NativeDragEvent):void {
    				trace( "Drag out complete");
    				clipboard = new Clipboard();
    			}            
    		]]>
    	</fx:Script>
    	
    	<fx:Declarations>
    		<mx:ArrayCollection id="arrColl">
    			<mx:source>
    				<fx:Array>
    					<fx:Object name="rhall.jpg" url="http://a1.twimg.com/profile_images/57117466/robert_m_hall_bio_photo_big_normal.jpg" />
    					<fx:Object name="bobjim.jpg" url="http://a1.twimg.com/profile_images/51723308/ryancampbell3_normal.jpg"/>
    					<fx:Object name="jenschr.jpg" url="http://a1.twimg.com/profile_images/43222252/jenschr_mugshot3_normal.jpg"/>
    					<fx:Object name="adamflater.jpg" url="http://a1.twimg.com/profile_images/21503622/Photo_8_normal.jpg"/>
    					<fx:Object name="reboog711.jpg" url="http://a1.twimg.com/profile_images/16984682/DSCF0044_normal.jpg"/>
    				</fx:Array>
    			</mx:source>
    		</mx:ArrayCollection>
    	</fx:Declarations>
    	
    	<mx:DataGrid id="fileData" dragEnabled="true"
    				 dataProvider="{arrColl}" 
    				 dragStart="onDragOut(event)"
    				 nativeDragComplete="onDragOutComplete(event)" width="100%">
    		<mx:columns>
    			<mx:DataGridColumn dataField="name" width="100"/>
    			<mx:DataGridColumn dataField="url"/>
    		</mx:columns>
    	</mx:DataGrid>
    	
    </s:WindowedApplication>
    




    1.8 Global Error Handling

    AIR 애플리케이션이 런타임 도중에 에러가 발생할 때 국부적 Error 처리는 이미 가능했다. 하지만 개발자가 애플리케이션의 어느 부분에서 Error가 발생할지 또는 문제가 발생할지 찾기 힘든 것이 현실이다. 국부적으로 일어나는 에러를 처지하지 못하는 경우 애플리케이션이 동작중에 어떤 부분에서 에러가 발생하든지 대처할 수 있도록 Global Error를 처리할 수 있는 기능이 AIR 2.0과 Flash Player 10.1에 추가가 되었다. 다음 글을 참고하자.

    Global Error를 처리하는데 UncaughtErrorEvent 클래스를 이용한다. 이름에서도 볼 수 있듯이 기존의 try..catch문으로 처리하지 못한 Error가 발생할 때 발생함을 알 수 있다.

    발생하는 UncaughtErrorEvent를 처리하기 위해 LoaderInfo 객체의 uncaughtErrorEvents 속성에 이벤트 처리를 위한 함수를 등록하면 된다.

    loaderInfo.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, handler );

    단, 로드된 SWF 객체의 경우에는 Loader의 uncaughtErrorEvents 속성에 이벤트 처리 함수를 등록한다.

    loader.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, handler );


    하나의 SWF가 다른 SWF에 로드되는 경우 UncaughtErrorEvent 이벤트 전파에 대한 처리가 필요하다.  A.swf가 B.swf를 Loader를 이용해 로드했다면 다음과 같은 순서로 LoaderInfo와 Loader 객체에 UncaughtErrorEvent 가 전파된다.

    1. (Capture phase) A.swf의 LoaderInfo
    2. (Capture phase) A.swf내의 Loader 
    3. (Target phase) B.swf의 LoaderInfo
    4. (Bubble phase) A.swf의 Loader
    5. (Bubble phase) A.swf의 LoaderInfo

    Loader객체의 uncaughtErrorEvents 속성은 결코 이벤트 전파시 target phase가 발생하지 않음에 주목하자. 그리고 이렇게 이벤트가 전파되는데는 디스플레이 리스트에 등록되어 있어야만 한다. 즉 addChild()된 객체어야한 한다. 중간에 이벤트 전파를 중단하려면 stopPropagation()이나 stopImmediatePropagation() 메소드를 이용하면 되겠다. 또한 ADL 기반나 디버그용 Flash Player에서 구동 경우 에러표시 다이얼로그가 뜨는 것을 방지하기 위해 UncaughtErrorEvent 이벤트 발생시 preventDefault() 메소드를 사용해 안뜨도록 할 수 있다.

    아래는 AIR 2.0 예제이다.

    package {
    	import flash.desktop.*;
    	import flash.display.*;
    	import flash.events.*;
    	public class UncaughtErrorEventExample extends Sprite {
    		public function UncaughtErrorEventExample()
    		{
    			var options:NativeWindowInitOptions = new NativeWindowInitOptions();
    			options.type = NativeWindowType.UTILITY;
    			
    			var window:NativeWindow = new NativeWindow(options);
    			window.width = 200;
    			window.height = 200;
    			window.title = "UncaughtErrorEventExample";
    			
    			window.stage.scaleMode = StageScaleMode.NO_SCALE;
    			window.stage.align = StageAlign.TOP_LEFT;
    			
    			window.activate();
    			window.addEventListener(Event.CLOSING, function($event:Event):void {
    				NativeApplication.nativeApplication.exit();
    			});			
    
    			var btn:Sprite = new Sprite();
    			btn.useHandCursor = true;
    			btn.buttonMode = true;
    			btn.graphics.clear();
    			btn.graphics.beginFill(0xFFCC00);
    			btn.graphics.drawRect(0, 0, 100, 50);
    			btn.graphics.endFill();
    			btn.addEventListener(MouseEvent.CLICK, clickHandler);			
    			window.stage.addChild(btn);
    			
    			loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);
    		}
    		private function uncaughtErrorHandler(e:UncaughtErrorEvent):void {
    			if (e.error is Error) {
    				var error:Error = e.error as Error;
    				// do something with the error
    				trace(error.errorID, error.name, error.message);
    			} else if (e.error is ErrorEvent) {
    					var errorEvent:ErrorEvent = e.error as ErrorEvent;
    					// do something with the error
    					trace(errorEvent.errorID);
    			} else {
    				// a non-Error, non-ErrorEvent type was thrown and uncaught
    				trace( " a non-Error, non-ErrorEvent type was thrown and uncaught " );
    			}
    			e.preventDefault();
    		}
    		private function clickHandler(event:MouseEvent):void
    		{
    			throw new Error("Gak!");
    		}
    	}
    }
    


    위 예제에서 볼 수 있듯이 AIR 애플리케이션의 노란 부분을 클릭하면 강제로 에러를 발생시킨다. 이때 이 에러를 처리하기 위한 try...catch문이 없으므로 UncaughtErrorEvent가 발생하고 그것을 처리하고 있다. 마지막에 e.preventDefault()를 둬서 에러표시 다이얼로그가 뜨는 것을 방지한다.


    1.9 Packaging an AIR application in a native installer

    AIR 2.0가 생소하게 다가오는 이유중에 하나는 바로 AIR 2.0을 설치할 때 과정이다. 뱃지(Badge)기능을 이용해 웹페이지에서 AIR 런타임 및 해당 AIR 애플리케이션을 다운로드 받을 수 있지만 그 과정이 일반 사용자에게 생소하게 다가올 수 있다. AIR 2.0에서는 이러한 생소함을 덜어주기 위해 각 OS별로 설치패키지를 따로 구성할 수 있도록 지원하고 있다. 가령, Windows는 exe, Mac은 dmg으로 설치 패키지를 구성할 수 있다는 말이다.

    AIR 2.0의 ADT의 옵션설정으로 설치 패키지를 만들 수 있다. 다음 링크를 참고한다.


    연관글






    정리하며 

    이번 글은 Adobe AIR 2.0 Overview 첫번째 글로 AIR 2.0에 추가된 새로운 기능에 대해서만 언급했다.  "기존 API에서 추가된 기능", "플랫폼 인식 관련 API", "퍼포먼스 향상"에 관련된 내용도 조만간 언급하겠다. 이 글을 읽고 AIR 2.0 기반으로 하는 다양한 애플리케이션 예제들이 많이 나왔으면 하는 바램이다.

    글쓴이 : 지돌스타(http://blog.jidolstar.com/629)
    어떤 분이 Flex 3.4환경에서 MenuBar에 색을 입히려하다가 잘 안되서 본인의 블로그에 질문을 해주셨다.


     <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    	<mx:Style>
    		MenuBar { 
    			font-weight:bold; 
    			color:#FFFFFF;
    			fill-colors:#0D0A92, #1B16F4, #37920A, #59EB12;
    			fill-alphas:1.0,1.0;
    		}
    		MenuBarItem {
    			theme-color:#0000FF;
    		}
    		Menu { 
    			alternatingItemColors:#ffffff,#eff4fa;
    			color:#000000;
    			top:4px;
    			bottom:2px;
    			horizontalGap:2px;	
    			roll-over-color:#00ff00;
    		}
    	</mx:Style>
    	<mx:MenuBar id="myMenuBar" labelField="@label" >
    		<mx:XMLList>
    			<menuitem label="MenuItem A">
    				<menuitem label="SubMenuItem A-1" enabled="false"/>
    				<menuitem label="SubMenuItem A-2"/>
    			</menuitem>
    			<menuitem label="MenuItem B"/>
    			<menuitem label="MenuItem C"/>
    			<menuitem label="MenuItem D">
    				<menuitem label="SubMenuItem D-1" 
    						  type="radio" groupName="one"/>
    				<menuitem label="SubMenuItem D-2" 
    						  type="radio" groupName="one"
    						  selected="true"/>
    				<menuitem label="SubMenuItem D-3" 
    						  type="radio" groupName="one"/>
    			</menuitem>
    		</mx:XMLList>
    	</mx:MenuBar>
    </mx:Application>
    



    질문은 MenuBarItem의 themeColor를 #0000FF로 지정했는데 저렇게 나오냐는 것이였다. 참고로 MenuBar는 저 파란색의 바를 말하는 것이고 MenuBarItem은 MenuItem A, MenuItem B...등으로 표시된 것이다. 이들은 모두 UIComponent를 상속했다. 마지막으로 Menu는 서버메뉴로써 List를 확장한 것이다.

    질문처럼 직관적으로 봐도 문제가 있어 보인다. 이렇게 나오는 이유를 알기 위해서는 스킨부분을 찾아봐야 한다.

    (아래내용이 약간 복잡할 수 있으나 천천히 따라오면 그리 복잡한 것도 아니다. 원래 이런걸 말로 설명해야 빠른 건데 글로 설명하려 하니 어렵다. ^^;)


    원인 찾기

    MenuBar의 아이템렌더러는 menuBarItemRenderer속성에 지정된다. 기본적으로 MenuBarItem 클래스가 된다. 쉽게 이야기해 위 그림에서 MenuItem A, MenuItem B... 등은 다 MenuBarItem으로 생성되어 그려진 것이다. 만약 MenuBarItem을 다른 것으로 대체하고 싶다면 IMenuBarItemRenderer 인터페이스를 구현해서 다른 MenuBarItem을 만들면 되겠다. 아래는 MenuBar의 menuBarItemRenderer 속성을 설명하고 있다.

    (문제의 원인을 파악하기 위해 가장 먼저 찾아야 할 것은 API 문서이다.)



    MenuBarItem의 스킨은 무엇일까? 아래 MenuBar에서 Style부분을 보자. MenubarItem의 스킨은 바로 ActivatorSkin 클래스라는 것을 알 수 있다. 결국 스킨도 내 마음대로 바꿀 수 있는 것이다.

    Flex 3.4에서 스킨은 크게 그래픽기반 스킨과 프로그램적 스킨(Programmatic Skin)이 있다. 그래픽기반 스킨은 이미지를 생각하면 된다. 하지만 프로그램적 스킨은 ActionScript 3.0 코드이다. MenuBar의 itemSkin 스타일 속성으로 mx.skins.halo.ActivatorSkin을 썼다는 것은 바로 프로그램적 스킨을 사용한다는 이야기이다.

    (문서를 봐도 해결의 실마리가 안보인다면 SDK를 뜯어보자!)

    그럼 이들이 어떻게 동작하는 것일까? MenuBar부터 시작해 MenuBarItem, ActivatorSkin으로 자연스럽게 찾아보면 된다. Flex 3.4 SDK 소스는 공개되어 있으므로 아래 그림처럼 Flash builder 4에서 쉽게 찾아볼 수 있다. Flex Builder 3를 사용한다면 이 기능은 사용할 수 없다.

    Flex SDK 클래스를 보려면 framework.swc를 펼쳐보면 된다.



    MenuBar 소스에서 mouseOverHandler, mouseUpHandler, mouseDownHandler 메소드들을 보자. 이들 메소드는 마우스 이벤트를 처리하는데 잘 보면 이런 코드들이 있다.

    item.menuBarItemState = "itemDownSkin";
    


    여기서 item은 IMenuBarItemRenderer 인터페이스의 참조이다. 그리고 MenuBarItem 클래스는 이 인터페이스를 구현한 것이다. 결국 MenuBarItem의 menuBarItemState 속성에 itemDownSkin이라고 지정한 것이다.

    그럼 MenuBarItem 소스의 menuBarItemState 부분을 보자.


        public function set menuBarItemState(value:String):void
        {
            _menuBarItemState = value;
            viewSkin(_menuBarItemState);
        }   
    
        private function viewSkin(state:String):void
        {  	
        	var newSkinClass:Class = Class(getStyle(state));
        	var newSkin:IFlexDisplayObject;
        	var stateName:String = "";
        	    	
        	if (!newSkinClass)
        	{
        		// Try the default skin
        		newSkinClass = Class(getStyle(skinName)); 		
        		    	
        		if (state == "itemDownSkin")
        			stateName = "down";
        		else if (state == "itemOverSkin")
        			stateName = "over";
        		else if (state == "itemUpSkin")
        			stateName = "up";
        		
        		// If we are using the default skin, then 
        		if (defaultSkinUsesStates)
        			state = skinName;
        		
         		if (!checkedDefaultSkin && newSkinClass)
        		{
    	    		newSkin = IFlexDisplayObject(new newSkinClass());
    	    		// Check if the skin class is a state client or a programmatic skin
    	    		if (!(newSkin is IProgrammaticSkin) && newSkin is IStateClient)
    	    		{
    	    			defaultSkinUsesStates = true;
    	    			state = skinName;
    	    		}
    	    		
    	    		if (newSkin)
    	    		{
    		    		checkedDefaultSkin = true;
    		    	}
        		}
        	}
        	
          	newSkin = IFlexDisplayObject(getChildByName(state));
    
            if (!newSkin)
            {
                if (newSkinClass)
                {
                    newSkin = new newSkinClass();
    
                    DisplayObject(newSkin).name = state;
    
                    if (newSkin is ISimpleStyleClient)
                        ISimpleStyleClient(newSkin).styleName = this;
    
                    addChildAt(DisplayObject(newSkin), 0);
                }
            }
    
            newSkin.setActualSize(unscaledWidth, unscaledHeight);
    
            if (currentSkin)
                currentSkin.visible = false;
    
            if (newSkin)
                newSkin.visible = true;
    
            currentSkin = newSkin;
            
            // Update the state of the skin if it accepts states and it implements the IStateClient interface.
    		if (defaultSkinUsesStates && currentSkin is IStateClient)
    		{
    			IStateClient(currentSkin).currentState = stateName;
    		}
        }
    

    MenuBarItem의 menuBarItemState에 상태변화 값을 넣어주면 MenuBarItem의 viewSkin() 메소드에서 각 상태값에 따라서 스킨을 만들어준다. viewSkin 메소드에서 newSkin = IFlexDisplayObject(new newSkinClass()); 은 새로운 스킨을 만드는 부분이 newSkinClass가 바로 ActivatorSkin 클래스가 된다. 그리고 newSkin = IFlexDisplayObject(getChildByName(state));  부분은 이전에 만든 스킨이 있는지 확인하는 부분이다. viewSkin() 메소드는 그래픽적 스킨, 프로그램적 스킨등에 모두 대응되며 게으른(lazy) 스킨 생성을 한다. 여기서 게으른 생성이라는 것은 가령, 애플리케이션이 실행 당시에 마우스 Over스킨을 생성하는 것이 아니라 마우스Over시에 스킨은 바로 그때 생성해준다는 것이다. viewSkin() 메소드가 결국 ActivatorSkin 클래스로 하여금 스킨을 만들어주는 것이다.

    ActivatorSkin 클래스의 일부분을 보자.

    	private static function calcDerivedStyles(themeColor:uint,  fillColor0:uint, fillColor1:uint):Object
    	{
    		var key:String = HaloColors.getCacheKey(themeColor,fillColor0, fillColor1);
    				
    		if (!cache[key])
    		{
    			var o:Object = cache[key] = {};
    			
    			// Cross-component styles.
    			HaloColors.addHaloColors(o, themeColor, fillColor0, fillColor1);
    		}
    		
    		return cache[key];
    	}
    
    	/**
    	 *  @private
    	 */
    	override protected function updateDisplayList(w:Number, h:Number):void
    	{
    		super.updateDisplayList(w, h);
    
    		if (!getStyle("translucent"))
    			drawHaloRect(w, h);
    		else
    			drawTranslucentHaloRect(w, h);
    	}
    
    	/**
    	 *  @private
    	 */
    	private function drawHaloRect(w:Number, h:Number):void
    	{
    		var fillAlphas:Array = getStyle("fillAlphas");
    		var fillColors:Array = getStyle("fillColors");
    		var highlightAlphas:Array = getStyle("highlightAlphas");				
    		var themeColor:uint = getStyle("themeColor");
    
    		var themeColorDrk1:Number =
    			ColorUtil.adjustBrightness2(themeColor, -25);
    
    		// Derivative styles.
    		var derStyles:Object = calcDerivedStyles(themeColor, fillColors[0], fillColors[1]);
    												 
    		graphics.clear();
    
    		switch (name)
    		{
    			case "itemUpSkin": // up/disabled
    			{
    				// invisible hit area
    				drawRoundRect(
    					x, y, w, h, 0,
    					0xFFFFFF, 0);
    				break;
    			}
    
    			case "itemDownSkin":
    			{
    				// face
    				drawRoundRect(
    					x + 1, y + 1, w - 2, h - 2, 0,
    					[ derStyles.fillColorPress1, derStyles.fillColorPress2], 1,
    					verticalGradientMatrix(0, 0, w, h )); 
    									
    				// highlight
    				drawRoundRect(
    					x + 1, y + 1, w - 2, h - 2 / 2, 0,
    					[ 0xFFFFFF, 0xFFFFFF ], highlightAlphas,
    					verticalGradientMatrix(0, 0, w - 2, h - 2));
    
    				break;
    			}
    
    			case "itemOverSkin":
    			{
    				var overFillColors:Array;
    				if (fillColors.length > 2)
    					overFillColors = [ fillColors[2], fillColors[3] ];
    				else
    					overFillColors = [ fillColors[0], fillColors[1] ];
    
    				var overFillAlphas:Array;
    				if (fillAlphas.length > 2)
    					overFillAlphas = [ fillAlphas[2], fillAlphas[3] ];
      				else
    					overFillAlphas = [ fillAlphas[0], fillAlphas[1] ];
    
    				// face
    				drawRoundRect(
    					x + 1, y + 1, w - 2, h - 2, 0,
    					overFillColors, overFillAlphas,
    					verticalGradientMatrix(0, 0, w, h )); 
    
    				// highlight
    				drawRoundRect(
    					x + 1, y + 1, w - 2, h - 2 / 2, 0,
    					[ 0xFFFFFF, 0xFFFFFF ], highlightAlphas,
    					verticalGradientMatrix(0, 0, w - 2, h - 2));
    				
    				break;
    			}
    		}
    
    		filters = [ new BlurFilter(2, 0) ];
    	}
    


    그림을 그릴때 drawHaloRect() 메소드를 호출해서 각 상태값에 따라서 그림을 그려준다. 우리가 처음 themeColor를 지정했음에도 불구하고 다른 색으로 보였던 부분을 알기 위해서는 drawHaloRect()에 switch분기점에서 "itemDownSkin"을 보면 된다. derStyles.fillColorPress1, derStyles.fillColorPress2 색을 사용하는 것을 볼 수 있으며 코드에서 derStyles는 calcDerivedStyles(themeColor, fillColors[0], fillColors[1]);에 의해 만들어진다. calcDerivedStyles() 메소드를 보면 결국 HaloColors.addHaloColors(o, themeColor, fillColor0, fillColor1); 으로 색이 결정됨을 볼 수있다. 여기서 HaloColors 클래스내에서 addHaloColors() 정적 메소드를 보면 다음과 같다.

    public static function addHaloColors(colors:Object,  themeColor:uint,  fillColor0:uint, fillColor1:uint):void
    {
    	var key:String = getCacheKey(themeColor, fillColor0, fillColor1); 
    	var o:Object = cache[key];
    	
    	if (!o)
    	{
    		o = cache[key] = {};
    		
    		// Cross-component styles
    		o.themeColLgt = ColorUtil.adjustBrightness(themeColor, 100);
    		o.themeColDrk1 = ColorUtil.adjustBrightness(themeColor, -75);
    		o.themeColDrk2 = ColorUtil.adjustBrightness(themeColor, -25);
    		o.fillColorBright1 = ColorUtil.adjustBrightness2(fillColor0, 15);
    		o.fillColorBright2 = ColorUtil.adjustBrightness2(fillColor1, 15);
    		o.fillColorPress1 = ColorUtil.adjustBrightness2(themeColor, 85);
    		o.fillColorPress2 = ColorUtil.adjustBrightness2(themeColor, 60);
    		o.bevelHighlight1 = ColorUtil.adjustBrightness2(fillColor0, 40);
    		o.bevelHighlight2 = ColorUtil.adjustBrightness2(fillColor1, 40);
    	}
    	
    	colors.themeColLgt = o.themeColLgt;
    	colors.themeColDrk1 = o.themeColDrk1;
    	colors.themeColDrk2 = o.themeColDrk2;
    	colors.fillColorBright1 = o.fillColorBright1;
    	colors.fillColorBright2 = o.fillColorBright2;
    	colors.fillColorPress1 = o.fillColorPress1;
    	colors.fillColorPress2 = o.fillColorPress2;
    	colors.bevelHighlight1 = o.bevelHighlight1;
    	colors.bevelHighlight2 = o.bevelHighlight2;
    }
    

    위에서 우리가 주목할 점은 o.fillColorPress1와 o.fillColorPress2를 설정할 때 ColorUtil.adjustBrightness2(themeColor, 85);와 같은 방법으로 새로 색을 설정하는 부분이다.

    결국 무엇인가? 지금까지 MenuBar, MenuBarItem, ActivatorSkin 클래스를 쭉 따라가며 themaColor 스타일값이 원하는대로 보여지지 않은 것은 바로 HaloColors 클래스의 addHaloColors() 부분에서 찾을 수 있는 것이다.


    해결하기

    위처럼 themeColor가 원하는대로 적용되지 않은 것 처럼 보인 이유를 분명히 알았다. Flex 3.4가 매우 잘 만들어졌다고 할지라도 이것도 결국 사람이 만든거라 완벽히 원하는대로 기능을 쓸 수 없는 경우가 생긴다. 그러면서 발생하는 문제를 해결하기 위해 단지 피상적으로 문서만 의지해서 해결하려고 하면 절대 답을 못찾을 수 있다. 표면상 문제를 해결할 수 없다면 문제의 부분을 찾기위해 SDK 소스를 위와 같이 찾아보는 스킬을 길러야 한다.
     
    어떤 문제인지 파악했으니 이제 해결해보자. 일단 기본적으로 CSS로 themeColor 와 같은 설정값으로 해결못하는 것을 알았으니 MenuBarItem에 새로운 ActivatorSkin 와 비슷한 MyActivatorSkin 을 만들고 더불어 HaloColors도 동일하게 MyHaloColors를 만들자. 이들은 기존 ActivatorSkin.as와 HaloColors.as 소스를 복사해서 만들면 된다. 물론 package부분 설정 및 include부분 설정으로 인한 에러는 제거해주자.

    MyHaloColors 클래스내에 o.fillColorPress1, o.fillColorPress2에서 ColorUtil.adjustBrightness2(themeColor, 85); 처럼 된 것을 ColorUtil.adjustBrightness2(themeColor, 0);으로 바꾸자.

    그런 다음 메인소스에 CSS에서 MenuBar 셀렉터에 itemSkin을 다음처럼 추가한다.

    MenuBar { 
    	font-weight:bold; 
    	color:#FFFFFF;
    	fill-colors:#0D0A92, #1B16F4, #37920A, #59EB12;
    	fill-alphas:1.0,1.0;
    	itemSkin: ClassReference("skins.MyActivatorSkin");
    }
    


    문제가 해결되었다.


    결론
    나는 개인적으로 Flex 프레임워크가 완벽하다고 보지 않는다. 그래도 전체적으로 잘 구조가 잘 잡혀있는 만큼 구조를 이해하고 잘 따라가보면 표면상으로만 해결하지 못한 부분을 해결할 수 있다. 너무 책과 문서에만 의존하지 말고 개발자라면 Flex 소스도 조금씩 분석해보길 바란다.

    참고로 몇가지 덧붙여 말하면...
    사실 본인은 Flex 3를 손놓은지 꽤 되었다. Flex 4도 지금 입문단계정도이다. 기본적으로 실무에서 Flex 프레임워크로 개발한다기 보다 애플리케이션의 퍼포먼스나 개발적 스타일의 문제로 ActionScript 3.0기반으로 개발한다. Flex 는 너무 유연하게 만들려고 노력해서 그런지 전체적으로 무겁다는 것을 지울 수 없어 간단히 또 빠르고 사용자에게 불편함이 없는 그런 개발이 필요할때는 Flex를 이용하지만 속도,메모리가 중요한 개발은 무조건 ActionScript 3.0으로 개발한다.

    Flex는 ActionScript 3.0으로 만들어진 프레임워크 일종이며 mxml이라는 것은 이 Flex 프레임워크를 손쉽게 쓰기 위해 도입된 xml이다. 이를 파싱해서 ActionScript 3.0으로 해석해 다시 swf로 만드는 컴파일러가 compc, mxmlc인 것이다. Flex에 대해서 이러한 점을 알고 공부하고 개발하신다면 여러가지로 도움이 될 것이다. 


    참고글

    [Flex] 자식 컴포넌트에 CSS를 적용시키는 통상적인 방법 소개
    [Flex] 스킨(Skin)을 이용하여 확장가능한 게이지 컴포넌트 제작


    글쓴이 : 지돌스타(http://blog.jidolstar.com/627)
    어제 Adobe AIR에서 CPU 사용을 줄이는 방법에 대해서 소개했다. 이 글에 Hika님과 찬익님이 그에 대해 댓글을 달아주시며 다른 정보를 공유해주셨다. 정말 감사드린다. 이런 것이 블로그의 매력일 것이다. 블로그를 내 완벽한 지식을 전달하기 위해 쓴다는 것은 다소 어폐가 있다. 완벽한 것은 없다. 블로그는 지식 소통의 도구로 잘 활용하면 좋은 정보 공유를 통한 더욱 완전해지는 지식습득이 가능하게 한다. 난 지난 몇년간 블로그를 하면서 정말 몸소 체험했다. 지금 이글을 보고 있는 분들도 꼭 블로그를 하길 권장한다.

    각설하고. 찬익님이 Flex 4 기반에서 AIR 애플리케이션을 만들때 FrameRate를 조절하는 방법으로 backgroundFrameRate를 소개해주었다. 이것은 Event.ACTIVATE와 Event.DEACTIVATE를 이용해서 이미 Flex 4의
    WindowedApplication 클래스에 적용되어 있다. 아래 코드는 WindowedApplication 클래스의 일부이다.

    /**
    *  Constructor.
    *  
    *  @langversion 3.0
    *  @playerversion AIR 1.5
    *  @productversion Flex 4
    */
    public function WindowedApplication()
    {
    	super();
    
    	addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
    	addEventListener(FlexEvent.PREINITIALIZE, preinitializeHandler);
    	addEventListener(FlexEvent.UPDATE_COMPLETE, updateComplete_handler);
    	addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);
    
    	var nativeApplication:NativeApplication = NativeApplication.nativeApplication;
    	nativeApplication.addEventListener(Event.ACTIVATE, nativeApplication_activateHandler);
    	nativeApplication.addEventListener(Event.DEACTIVATE, nativeApplication_deactivateHandler);
    	nativeApplication.addEventListener(Event.NETWORK_CHANGE, dispatchEvent);
    
    	nativeApplication.addEventListener(InvokeEvent.INVOKE, nativeApplication_invokeHandler);
    	initialInvokes = new Array();
    
    	//Force DragManager to instantiate so that it can handle drags from
    	//outside the app.
    	DragManager.isDragging;
    }
    
    /**
    *  @private
    *  Storage for the backgroundFrameRate property.
    */
    private var _backgroundFrameRate:Number = 1;
    
    /**
    *  Specifies the frame rate to use when the application is inactive.
    *  When set to -1, no background frame rate throttling occurs.
    *
    *  @default 1
    *  
    *  @langversion 3.0
    *  @playerversion AIR 1.5
    *  @productversion Flex 4
    */
    public function get backgroundFrameRate():Number
    {
    	return _backgroundFrameRate;
    }
    
    /**
    *  @private
    */ 
    public function set backgroundFrameRate(frameRate:Number):void
    {
    	_backgroundFrameRate = frameRate;
    }
    
    /**
    *  @private
    */
    private function nativeApplication_activateHandler(event:Event):void
    {
    	dispatchEvent(new AIREvent(AIREvent.APPLICATION_ACTIVATE));
    
    	// Restore throttled framerate if appropriate when application is activated.
    	if (prevActiveFrameRate >= 0 && stage)
    	{
    	    stage.frameRate = prevActiveFrameRate;  
    	    prevActiveFrameRate = -1;
    	}
    }
    
    /**
    *  @private
    */
    private function nativeApplication_deactivateHandler(event:Event):void
    {
    	dispatchEvent(new AIREvent(AIREvent.APPLICATION_DEACTIVATE));
    
    	// Throttle framerate if appropriate when application is deactivated.
    	// Ensure we've received an updateComplete on the chance our layout
    	// manager is using phased instantiation (we don't wish to store a
    	// maxed out (1000fps) framerate).
    	if ((_backgroundFrameRate >= 0) && (ucCount > 0) && stage)
    	{
    	    prevActiveFrameRate = stage.frameRate;
    	    stage.frameRate = _backgroundFrameRate; 
    	}
    }
    


    이는 Flex 4를 이용해 AIR 애플리케이션을 만들때 별도의 조치없이 backgroundFrameRate 속성 설정만으로 frameRate를 조절할 수 있다. 이 값은 기본으로 1이다. 더 좋은 값은 0.01이라고 한다. 이는 거의 멈추게 하는 수준이다. 이 속성은 Flex 4에서 AIR를 위해 WindowedApplication 에만 적용되어 있다. Flash기반인 Application 클래스에는 이 속성이 없다.
    참고글
    Adobe AIR에서 CPU 사용을 줄이는 방법

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

    오랜만에 네이버 오픈케스트에 케스팅했습니다. 이만저만 하는게 많다보니 여기에 신경을 전혀 못쓰고 있었네요. 요즘에는 AIR분야를 좀 해보려고 하고 있습니다. 항상 눈팅만하다가 본격적으로 결과물을 내어야할 것이 생겨서 이것저것 해보는 중입니다. 이런걸 정리하다 보면 저 자신도 너무 모르는게 많고 항상 부족함을 느낍니다. 아무튼 혼자 아는 것보다 함께 알아서 서로 공유해 서로 커나가는 것이 제 생각입니다. 좋은 정보가 되길 바랍니다.

    http://opencast.naver.com/FL188/14

    글쓴이 : 지돌스타(http://blog.jidolstar.com)
    Adobe AIR는 다양한 OS(Windows, Mac, Linux)에 구동될 수 있는 애플리케이션을 만드는데 사용하는 일종의 Flash Platfom기술이다. Flash ActionScript 3.0이나 Ajax만으로도 AIR 애플리케이션을 만들 수 있기 때문에 기존에 웹개발자들이 다른 언어를 배우지 않고 일반 데스크탑용 애플리케이션을 만드는데 있어서 접근성이 좋다. 앞으로 AIR는 데스크탑뿐 아니라 모바일등과 같은 다양한 기기에도 적용될 예정이다.

    하지만 AIR외에 다른 데스크탑용 애플리케이션과 비교해 그 적용범위가 아직까지 많은 부분 부족하고 복잡한 UI를 다루는데 있어서 성능문제가 걸릴 수 있다. 하지만 AIR 2.0 Beta 버전이 오픈된 것을 볼 수 있었듯이 계속 발전해나갈 것이다. 물론 성능면에서도 마찬가지 일 것이다. 그래도 약간은 부족하다. 기능은 그렇다 쳐도 지금의 성능을  개발자가 조금만 신경을 쓴다면  획기적으로 향상시킬 수 있다.

    일전에 Reducing CPU usage in Adobe AIR라는 글을 보았다. 여기서는 매우 단순하고 쉬운 방법으로 framerate를 줄임으로서 CPU 사용율을 현격히 줄이는 방법을 소개하고 있다.

    이 방법이 퍼포먼스를 올리기 위한 방법의 하나의 예시는 될 수 있지만 그 전부는 아니라는 점은 밝혀둔다.

    (이 글을 완벽히 이해하기 위해서는 ActionScript 3.0과 AIR에 대한 전반적 이해가 요구됩니다.)

    FrameRate를 줄임
    Framerate를 줄임(Framerate throttling)은 애플리케이션의 휴면(idle)이 있을때 자원 사용을 줄여 퍼포먼스를 증가시키는 기술을 의미한다. 이를 구현하기 위해 ActionScript 3.0에서 매우 유용한 속성으로 Stage.frameRate가 있다. 이것을 이용하면 런타임시에 애플리케이션의 framerate를 변경시킬 수 있다. 

    여기서 보여지는 예제는 Reducing CPU usage in Adobe AIR에서 소개한 글에 나온 예제를 조금더 실용적으로 만들었다.(실제로 실행해 볼 수 있도록) Flash Builder 4와 AIR 2.0 SDK를 설치한 사람들이라면 이 예제를 실제로 테스트 해볼 수 있다. 아래 순서대로 개발/테스트 환경을 구축하면 된다.

  • 만약 Flash Builder를 설치 안했다면 다음 링크를 통해 받으세요.
  • SDK와 Runtime을 다운로드 받습니다.
  • 다운받은 Runtime을 실행해 설치합니다.
  • (MS Windows의 경우)SDK는 압축을 풀고 그안에 있는 내용을 Flash builder가 설치된 sdks/4.0.0과 sdks/3.4.1 폴더에 각각 덮어씌웁니다. 제 경우는 C:\Program Files\Adobe\Adobe Flash Builder Plug-in Beta 2\sdks\4.0.0
  • Flash Builder를 실행합니다.
  • 메뉴에서 File > New > Flex Project를 선택합니다.
  • 프로젝트 이름을 적고 Application Type은 AIR를 선택합니다.
  • Next버튼을 두번 클릭후 Main Application file이름이 프로젝트명.mxml로 되어 있다면 Novice.as로 바꾸세요. 그리고 Finish 버튼을 누릅니다.
  • 아래 첫번째 초급 코드를 복사해서 Novice클래스를 열어 붙힙니다.
  • 디버그 모드로 테스트합니다.
  • 두번째, 세번째의 경우 만들어진 프로젝트의 소스폴더에 해당 클래스 이름으로 Class를 만듭니다. File > New > ActionScript Class를 선택한뒤 아래 소스를 복사해 붙여넣으면 되겠죠?
  • Package Explorer에 프로젝트명을 선택후 마우스 우클릭해서 컨텍스트 메뉴에서 Properties를 선택합니다.
  • 창이 뜨면 좌측 Flex Applications를 선택하고 오른쪽에 Add를 눌러 추가한 클래스(Intermediate.as, Expert.as)를 추가한뒤 OK를 합니다. Intermediate-app.xml과 Expert-app.xml이 자동 생성됩니다.
  • 이제 디버그 모드로 실행하면서 동작하는 형태를 학습하세요.
  • 아래 소스는 다음 링크에서 다운로드 받으셔도 됩니다.



  • 초급
    Framerate를 줄일때의 시점을 선택하는데 가장 쉽고 유용한 방법은 NativeApplication에서 Event.ACTIVATE 와 Event.DEACTIVATE 이벤트를 사용하는 것이다. AIR로 만들어진 빈윈도우를 선택해서 사용할때 CPU사용율이 1.8%라면 다른 윈도우를 선택해 그 윈도우의 CPU 사용율이 0.4%까지 떨여졌다고 한다. 또한 framerate를 0.01로 지정하면 0.2%까지 떨어진다고 한다. 이에 대한 예제는 다음과 같다. 예제가 이해하기 쉬우므로 따로 설명하지 않겠다.


    package {
    	import flash.desktop.NativeApplication;
    	import flash.display.NativeWindow;
    	import flash.display.NativeWindowInitOptions;
    	import flash.display.NativeWindowType;
    	import flash.display.Sprite;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	import flash.events.Event;
    	import flash.text.TextField;
    	import flash.text.TextFieldAutoSize;
    	import flash.utils.getTimer;
    	
    	/**
    	 * CPU 사용 줄이기 예제 1 
    	 * @author Yongho, Ji
    	 * @since 2009.12.1
    	 * @see http://blog.jidolstar.com/622
    	 */ 
    	public class Novice extends Sprite {
    		private var __isActive:Boolean = false;
    		private var __window:NativeWindow;
    		private var __textField:TextField;
    		public function Novice() {
    			__init();
    			var options:NativeWindowInitOptions = new NativeWindowInitOptions();
    			options.type = NativeWindowType.UTILITY;
    			
    			__window = new NativeWindow(options);
    			__window.width = 200;
    			__window.height = 200;
    			__window.title = "Novice";
    			
    			__textField = new TextField();
    			__textField.autoSize = TextFieldAutoSize.LEFT;
    			__textField.text = "";
    			
    			__window.stage.scaleMode = StageScaleMode.NO_SCALE;
    			__window.stage.align = StageAlign.TOP_LEFT;
    			
    			__window.stage.addChild(__textField);
    			__window.activate();
    			__window.addEventListener(Event.CLOSING,__onClosing);
    		}
    		private function __init():void {
    			NativeApplication.nativeApplication.addEventListener(Event.ACTIVATE, __onActive );
    			NativeApplication.nativeApplication.addEventListener(Event.DEACTIVATE, __onDeactive );
    			stage.addEventListener(Event.ENTER_FRAME,__onEnterFrame);
    		}
    		private function __onActive($event:Event):void {
    			stage.frameRate = 50;
    			__isActive = true;
    		}
    		private function __onDeactive($event:Event):void {
    			stage.frameRate = 1;
    			__isActive = false;
    		}
    		private function __onEnterFrame($event:Event):void {
    			__textField.text =  "active:" + __isActive + " " + getTimer();
    		}
    		private function __onClosing($event:Event):void {
    			NativeApplication.nativeApplication.exit();
    		}
    	}
    }
    


    중급

    위의 예제보다 조금더 고급적으로 framerate를 조절할 필요가 있다. 가령, 마우스 휠 이벤트에 따라 스크롤이 되는 컨텐츠가 있는 경우가 그것인데 평소에는 작은 framerate를 유지하다가 스크롤시에 빠른 렌더링이 필요하므로 framerate를 올려주는 것이다. 구체적으로 MouseEvent.MOUSE_WHEEL이 발생시 framerate를 올려주고 Event.ENTER_FRAME 이벤트에서 스크롤링후 500ms이 지난 다음 다시 framerate를 줄이는 것이다. 이도 휴면(idle)상태에서 쓸데없이 렌더링되는 것을 방지하고 필요한 동작할 때만 빠른 렌더링을 요구하도록 함으로써 CPU 사용율을 줄여주는 것이다.

    package {
    	import flash.desktop.NativeApplication;
    	import flash.display.NativeWindow;
    	import flash.display.NativeWindowInitOptions;
    	import flash.display.NativeWindowType;
    	import flash.display.Sprite;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	import flash.events.Event;
    	import flash.events.MouseEvent;
    	import flash.text.TextField;
    	import flash.text.TextFieldAutoSize;
    	import flash.utils.getTimer;
    	/**
    	 * CPU 사용 줄이기 예제 2 
    	 * @author Yongho, Ji
    	 * @since 2009.12.1
    	 * @see http://blog.jidolstar.com/622
    	 */ 
    	public class Intermediate extends Sprite {
    		private static const ACTIVE:int = 50;
    		private static const INACTIVE:int = 1;
    		private var __isActive:Boolean = false;
    		private var __isScrolling:Boolean = false;
    		private var __buffer:int;
    
    		private var __window:NativeWindow;
    		public function Intermediate() {
    			__init();
    			var options:NativeWindowInitOptions = new NativeWindowInitOptions();
    			options.type = NativeWindowType.UTILITY;
    			
    			__window = new NativeWindow(options);
    			__window.width = 200;
    			__window.height = 200;
    			__window.title = "Intermediate";
    			
    			var textField:TextField = new TextField();
    			textField.y = 20;
    			textField.width = 195;
    			textField.height = 175;
    			textField.multiline = true;
    			textField.wordWrap = true;
    			textField.border = true;
    			textField.borderColor = 0xff0000;
    			textField.text = "Adobe AIR는 다양한 OS(Windows, Mac, Linux)에 구동될 수 있는 애플리케이션을 만드는데 사용하는 일종의 Flash Platfom기술이다. Flash ActionScript 3.0이나 Ajax만으로도 AIR 애플리케이션을 만들 수 있기 때문에 기존에 웹개발자들이 다른 언어를 배우지 않고 일반 데스크탑용 애플리케이션을 만드는데 있어서 접근성이 좋다. 앞으로 AIR는 데스크탑뿐 아니라 모바일등과 같은 다양한 기기에도 적용될 예정이다.하지만 원천적으로 데스크탑용 애플리케이션과 비교해 그 적용범위가 아직까지 많은 부분 부족하고 복잡한 UI를 다루는데 있어서 성능문제가 걸릴 수 있다. 적용되는 범위는 AIR 2.0 Beta 버전이 오픈된 것을 볼 수 있었듯이 계속 발전해나갈 것이다. 또한 성능면에서도 마찬가지 일 것이다. 그래도 성능면에 있어서 개발자가 조금만 신경을 쓴다면 어떤 부분에 있어서 획기적으로 AIR 애플리케이션의 성능을 향상시킬 수 있다. 일전에 Reducing CPU usage in Adobe AIR라는 글을 보았다. 여기서는 매우 단순하고 쉬운 방법으로 framerate를 줄임으로서 CPU 사용율을 현격히 줄이는 방법을 소개하고 있다. ";
    
    			__window.stage.scaleMode = StageScaleMode.NO_SCALE;
    			__window.stage.align = StageAlign.TOP_LEFT;
    			
    			__window.stage.addChild(textField);
    			__window.activate();
    			__window.addEventListener(Event.CLOSING,__onClosing);
    		}
    		private function __init():void {
    			NativeApplication.nativeApplication.addEventListener(Event.ACTIVATE, __onActive );
    			NativeApplication.nativeApplication.addEventListener(Event.DEACTIVATE, __onDeactive );
    			stage.addEventListener(MouseEvent.MOUSE_WHEEL, __onMouseWheel, true);
    		}
    		private function __onActive($event:Event):void {
    			stage.frameRate = ACTIVE;
    			__isActive = true;
    			trace( "active" );
    		}
    		private function __onDeactive($event:Event):void {
    			stage.frameRate = INACTIVE;
    			__isActive = false;
    			trace( "deactive" );
    		}
    		private function __onMouseWheel($event:MouseEvent):void {
    			if( !__isActive ) {
    				if ( !__isScrolling ) {
    					stage.addEventListener(Event.ENTER_FRAME, __onEnterFrame, true);
    				}
    				stage.frameRate = ACTIVE;
    				__isScrolling = true;
    				__buffer = getTimer()+500;
    			}
    		}
    		private function __onEnterFrame($event:Event):void {
    			if( __buffer < getTimer() ) {
    				stage.frameRate = INACTIVE;
    				__isScrolling = false;
    			}
    		}
    		private function __onClosing($event:Event):void {
    			NativeApplication.nativeApplication.exit();
    		}
    	}
    }
    


    고급

    위에서 소개한 경우보다 약간더 어려운 주제로 넘어가보자. 먼저 창이 보이지 않을때와 보일때에 어떻게 처리할 것인가이다. 보이는 경우라면 일단 최소한의 framerate를 5로 주고 안보인다면 1로 준다. 이 경우는 MS Windows에서는 Tray Icon으로 바뀌며 창이 안보여질 때나 Mac에서 Dock으로만 표시될 필요가 있을때 사용될 수 있을 것이다. 굳이 보여지지 않는데 지나친 framerate를 줄 필요가 없기 때문이다. 평상시 Active한 상태에서는 24 framerate를 유지하지면 때에 따라서 부드럽게 운동하는 모습을 렌더링할 필요가 있을 때가 있다. 상태변화에 따라 Tweener 기능을 사용하는 경우가 그것인데 이때는 평소보다 framerate를 올려줄 필요가 있을 수 있다.

    아래 AIR 애플리케이션 소스코드는 두개의 버튼이 있다. 한개는 창의 visible을 false로 지정했다가 1초후 다시 true로 해주는 것이고 또 하나는 가상의 애니메이션이 있다고 가정하고 1초정도 여분을 둔다. 각 상태가 변할때마다 framerate를 Dubugging 시에 콘솔창에서 상태변화를 확인할 수 있도록 짜여있다.

    package {
    	import flash.desktop.NativeApplication;
    	import flash.display.NativeWindow;
    	import flash.display.NativeWindowInitOptions;
    	import flash.display.NativeWindowType;
    	import flash.display.Shape;
    	import flash.display.SimpleButton;
    	import flash.display.Sprite;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	import flash.events.Event;
    	import flash.events.MouseEvent;
    	import flash.filters.BevelFilter;
    	import flash.text.TextField;
    	import flash.text.TextFormat;
    	import flash.utils.getTimer;
    	import flash.utils.setTimeout;
    	/**
    	 * CPU 사용 줄이기 예제 3 
    	 * @author Yongho, Ji
    	 * @since 2009.12.1
    	 * @see http://blog.jidolstar.com/622
    	 */ 
    	public class Expert extends Sprite {
    		public static const ANIMATING:int = 50;
    		public static const ACTIVE:int = 24;
    		public static const INACTIVE_VISIBLE:int = 5;
    		public static const INACTIVE_INVISIBLE:int = 1;
    		
    		private var __isActive:Boolean = false;
    		private var __isAnimating:Boolean = false;
    		private var __window:NativeWindow;
    		private var __buffer:int;
    		
    		public function Expert() {
    			__init();
    			var options:NativeWindowInitOptions = new NativeWindowInitOptions();
    			options.type = NativeWindowType.UTILITY;
    			
    			__window = new NativeWindow(options);
    			__window.width = 200;
    			__window.height = 200;
    			__window.title = "Expert";
    			
    			__window.stage.scaleMode = StageScaleMode.NO_SCALE;
    			__window.stage.align = StageAlign.TOP_LEFT;
    			
    			//창을 감추기 버튼 
    			var buttonSkin:Sprite = new Sprite;
    			buttonSkin.graphics.beginFill( 0xff0000, 1.0 );
    			buttonSkin.graphics.drawRect(0,0,100,50);
    			buttonSkin.graphics.endFill();
    			buttonSkin.filters = [new BevelFilter()];
    			var textFormat:TextFormat = new TextFormat();
    			textFormat.color = 0xffffff;
    			textFormat.align = "center";
    			var textField:TextField = new TextField();
    			textField.defaultTextFormat = textFormat;
    			textField.text = "창을 감추기(1초후에 나타남)";
    			textField.width = 100;
    			textField.multiline = true;
    			textField.wordWrap = true;
    			textField.y = buttonSkin.height/2 - textField.textHeight/2; 
    			buttonSkin.addChild( textField );
    			var button:SimpleButton = new SimpleButton(buttonSkin,buttonSkin,buttonSkin,buttonSkin);
    			button.addEventListener(MouseEvent.CLICK, __onHide );
    			__window.stage.addChild( button );
    			
    			//Animation 동작 시키기 버튼 
    			buttonSkin = new Sprite;
    			buttonSkin.graphics.beginFill( 0x0000ff, 1.0 );
    			buttonSkin.graphics.drawRect(0,0,100,50);
    			buttonSkin.graphics.endFill();
    			buttonSkin.filters = [new BevelFilter()];
    			textFormat = new TextFormat();
    			textFormat.color = 0xffffff;
    			textFormat.align = "center";
    			textField = new TextField();
    			textField.defaultTextFormat = textFormat;
    			textField.text = "동작시키기(1 second)";
    			textField.width = 100;
    			textField.multiline = true;
    			textField.wordWrap = true;
    			textField.y = buttonSkin.height/2 - textField.textHeight/2; 
    			buttonSkin.addChild( textField );
    			button= new SimpleButton(buttonSkin,buttonSkin,buttonSkin,buttonSkin);
    			button.y = 52;
    			button.addEventListener(MouseEvent.CLICK, __onAnimate );
    			__window.stage.addChild( button );			
    						
    			__window.activate();
    			__window.addEventListener(Event.CLOSING,__onClosing);
    		}
    		private function __init():void {
    			NativeApplication.nativeApplication.addEventListener(Event.ACTIVATE, __onActive );
    			NativeApplication.nativeApplication.addEventListener(Event.DEACTIVATE, __onDeactive );
    		}
    		private function setFrameRate( frameRate:Number ):void {
    			stage.frameRate = frameRate;
    			trace( frameRate );
    		}
    		private function animate($duration:int = 1000):void {
    			trace( "동작시작");
    			setFrameRate( 50 );
    			__buffer = getTimer() + $duration;
    			if(!__isAnimating) {
    				stage.addEventListener(Event.ENTER_FRAME,__onEnterFrame);
    			}
    		}
    		private function active():void {
    			if(!__isAnimating) {
    				setFrameRate( ACTIVE );	
    			} 
    			trace( "active " );
    		}
    		private function deactive():void {
    			if (!__isAnimating) {
    				setFrameRate( (__window.visible) ? INACTIVE_VISIBLE : INACTIVE_INVISIBLE );
    			}
    			trace( "deactive " );
    		}
    		private function show():void {
    			__window.visible = true;
    			active();
    		}
    		private function hide():void {
    			__window.visible = false;	
    			deactive();
    		}
    		private function __onActive($event:Event):void {
    			__isActive = true;
    			active();
    		}
    		private function __onDeactive($event:Event):void {
    			__isActive = false;
    			deactive();
    		}
    		private function __onEnterFrame($event:Event):void {
    			if( __buffer < getTimer() ) {
    				trace( "동작끝");
    				stage.removeEventListener(Event.ENTER_FRAME,__onEnterFrame);
    				__isAnimating = false;
    				if( __isActive ) {
    					active();
    				} else {
    					deactive();
    				}
    			}
    		}
    		private function __onHide($event:MouseEvent):void {
    			hide();
    			setTimeout(show,1000);
    		}
    		private function __onAnimate($event:MouseEvent):void {
    			animate( 1000 );
    		}
    		private function __onClosing($event:Event):void {
    			NativeApplication.nativeApplication.exit();
    		}
    	}
    }
    


    참고글
    Reducing CPU usage in Adobe AIR 
    [머드초보]시스템트레이아이콘(system tray icon) 예제


    글쓴이 : 지돌스타(http://blog.jidolstar.com/622)
    찬익님의 블로그(http://blog.chanik.com/25)로 부터 매우 재미있는 내용을 봤다. 바로 커스텀 메타데이터 태그(custom metadata tag)에 대한 내용인데 커스텀 컴포넌트, 커스텀 이벤트등의 용어는 들어봤어도 커스텀 메타데이터 태그는 생소하다. 말그대로 Flex에서 사용하는 메타데이터 태그인 [Event], [Bindable] 이런 것 들은 사실 예전부터 mxmlc로 컴파일될 때에 ActionScript 3.0으로 변환된다는 것은 알고 있었지만 메타데이터 태그가 이렇게 사용되는지에 대한 내막은 전혀 알지 못했던 것이 사실이다. 아무튼 너무 좋은 정보라 공유하고자 한다.

    사실 Flex 2시절부터 커스텀 메타데이타에 대한 이야기가 있었다고 한다. 하지만 Flex 3서부터 컴파일 옵션에 "-keep-as3-metadata MyTag"와 같은 형태로 사용할 수 있게 되었다. 이는 다음과 같이 ActionScript 3.0 코드에서 사용할 수 있게 된다.
    [Listen(obj="this.closeButton", event="click")]
    public function closeClickHandler(event:MouseEvent) {...}
    

    메타데이타 태그는 위처럼 메소드 뿐 아니라 변수 및 클래스에도 사용할 수 있다.


    커스텀 메타데이타를 사용하는 방법

    커스텀 메타데이타를 사용하는 방법은 3단계로 이뤄진다.

    1. 컴파일러 구성
    Flex 프로젝트 컴파일 옵션으로 "-keep-as3-metadata+=Meta1, Meta2" 를 삽입한다. 만약 라이브러리 프로젝트에서 한다면 거기에 이 옵션을 사용하면 이 라이브러리 프로젝트를 참조하는 다른 프로젝트에 따로 줄필요는 없다.

    2. metadata를 작성

    package
    {
        [Meta2(param1 = "param 1 value")]
        public class TestClass
        {
    
            [Meta1(param1 = "param 1 value", param2 = "param 2 value")]
            public var test1:String;
            
            
            [Meta2(paramA = "param 1 value", paramB = "param 2 value")]
            public function get test2():String
            {
                return null;
            }
            
            public function set test2(val:String):void
            {
            }
    
            [Meta1(param1 = "param 1 value")]
            public function someMethod():void
            {
            };
    
        }
    }
    

    3. 런타임(runtime)시에 작성한 메타데이타를 사용
    describeType(TestClass)를 이용한다. 위처럼 클래스와 메소드에 메타데이타를 작성했다면 다음과 같이 E4X형태의 XML로 접근할 수 있다.

    <type name="TestClass" base="Class" isDynamic="true" isFinal="true" isStatic="true">
      <extendsClass type="Class"/>
      <extendsClass type="Object"/>
      <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
      <factory type="TestClass">
    
        <metadata name="Meta2">
          <arg key="param1" value="param 1 value"/>
        </metadata>
    
        <extendsClass type="Object"/>
    
        <method name="someMethod" declaredBy="TestClass" returnType="void">
          <metadata name="Meta1">
            <arg key="param1" value="param 1 value"/>
          </metadata>
        </method>
    
        <variable name="test1" type="String">
          <metadata name="Meta1">
            <arg key="param1" value="param 1 value"/>
            <arg key="param2" value="param 2 value"/>
          </metadata>
        </variable>
    
        <accessor name="test2" access="readwrite" type="String" declaredBy="TestClass">
          <metadata name="Meta2">
            <arg key="paramA" value="param 1 value"/>
            <arg key="paramB" value="param 2 value"/>
          </metadata>
        </accessor>
    
      </factory>
    </type>
    

    만약 Class의 [Meta2]의 param1에 접근한다면 다음과 같처럼 하면 된다.
    var testClass:TestClass = new TestClass();
    var xml:XML = describeType(testClass);
    trace(xml.metadata.(@name==Meta2).arg.(@key==param1).@value);
    

    위에 대한 결과는 'param 1 value'이 된다.


    어떻게 활용할 수 있을까?
    이것을 어떻게 활용할지에 대해서는 찬익님의 언급처럼 FlexUnit 4에서 지원하는 [Test] [BeforeClass]등과 같은 좋은 예시가 있다. 그 외에 것도 예제가 있는지 확인해봤다. 그 결과 재미있는 글을 발견했다.

    Annotating ActionScript Classes with Custom Metadata + Simple ORM Framework for AIR

    이 글에서 말하고 있는 요지는 커스텀 메타데이타를 이용해서 Value Object로 작성한 클래스에 이로 만들어진 객체가 데이타베이스의 어떤 테이블과 어떤 필드에 들어가는지 결정해줄 수 있다는 것이다.


    package
    {
    	[Bindable]
    	[Table(name="contact")]
    	public class Contact
    	{
    		[Id]
    		[Column(name="contact_id")]
    		public var contactId:int;
    
    		[Column(name="first_name")]
    		public var firstName:String;
    
    		[Column(name="last_name")]
    		public var lastName:String;
    		public var address:String;
    		public var city:String;
    		public var state:String;
    		public var zip:String;
    		public var phone:String;
    		public var email:String;
    	}
    }
    
    

    위처럼 작성된 클래스는 contactId 값은 contact 테이블의 contact_id 필드와 매치가 되도록 만든다는 것이다. [Id]메타데이타는 Primay Key와 같은 존재이다. 결국 이것을 해석해서 DB와 연동하는 AIR기반의 EntityManager를 만들어 위 클래스의 객체를 다음과 같이 사용하면 DB연동까지 된다.


    var contact:Contact = new Contact();
    contact.firstName = "Christophe";
    contact.lastName = "Coenraets";
    contact.email = "ccoenrae@adob.com";
    entityManager.save(contact);
    


    이를 수정하려면 다음처럼 쓴다.
    contact.firstName = "Chris";
    entityManager.save(contact);
    


    삭제하려면 다음처럼 쓴다.
    entityManager.remove(contact);
    

    너무 기발하다. 만약 Metadata를 사용하지 않았다면 Value Object와 DB 테이블의 필드명을 맞추기 위해 좀 노가다를 해야할지 모른다. 그런데 Metadata를 이용함으로써 프레임워크 단위로 말끔하게 이 문제를 해결해준다. 

    이에 대한 소스와 테스트 자료는 [여기]에서 다운로드 받는다.
    혹시 링크가 깨졌다면 다음 링크에서 받길 바란다. 
     
    이렇게 활용할 수 있다는 것이 감동이다. 
     

    정리하며
    찬익(http://blog.chanik.com)님 덕분에 아침부터 재미있는 정보를 얻을 수 있었다. 좋은 정보를 공유해주신 찬익님께 감사하며 커스텀 메타데이타에 대한 활용을 담은 글들이 많이 나왔으면 한다.


    참고글
    Custom metadata in three simple steps.
    Annotating ActionScript Classes with Custom Metadata + Simple ORM Framework for AIR
    찬익님의 커스텀 메타데이터 태그


    위 모든 예제는 Flash Builder 4 및 Flex Builder 3에서 테스트 해볼 수 있습니다.


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

    Flash Platform 개발자 분들에게 반가운 소식이 있습니다. AIR 2.0 Beta와 Flash Player 10.1 Prelease 버전이 공개되었습니다. 이제 Adobe Labs(http://labs.adobe.com/)에서 직접 다운로드 받을 수 있습니다.


     

    Adobe AIR 2.0 beta

    AIR 2.0은 다음과 같은 새로운 feature들이 추가되었습니다.(영어 압박? 그럼 : http://blog.chanik.com/entry/AIR-20-Beta-릴리즈 )

    • Support for the detection of mass storage devices.
    • Advanced networking capabilities like secure sockets, UDP support, and the ability to listen on sockets.
    • Support for native code integration.
    • The ability to open a file with its default application.
    • Multi-touch and gesture support.
    • New APIs for access to raw microphone data.
    • Webkit update with HTML5/CSS3 support.
    • Global error handling.
    • Improved cross-platform printing
    • Improved security and support for enterprise and government standards.

    AIR 2.0에 대해서 자세한 설명 다음 글들을 참고하세요. 



    AIR 2.0으로 개발테스트 하시려면 다음과 같이 합니다. 윈도우, Flash Builder 4 beta 2 환경을 가정하고 설명드립니다.

    더 쉽게 환경을 구축하고 테스트 해보시겠다면 다음 링크를 참고하세요.
    http://blog.jidolstar.com/619 

     

    1. 만약 Flash Builder를 설치 안했다면 다음 링크를 통해 받으세요.
    2. SDK와 Runtime을 다운로드 받습니다.
    3. 다운받은 Runtime을 실행해 설치합니다.
    4. SDK는 압축을 풀고 그안에 있는 내용을 Flash builder가 설치된 sdks/4.0.0과 sdks/3.4.1 폴더에 각각 덮어씌웁니다. 제 경우는 C:\Program Files\Adobe\Adobe Flash Builder Plug-in Beta 2\sdks\4.0.0
    5. Flash Builder를 실행합니다.
      Windows>Preferences에 들어가 Flash Builder > Installed Flex SDKs를 확인해보세요.
    6. Learn to Use Adobe AIR 2 Beta에 들어가 관련 내용을 학습합니다.
    7. Adobe AIR 2 Sample Applications에 들어가 Sample을 받아보고 실제 Flash Builder에서 작업해봅시다. (해당작업화면을 이미지 캡쳐하고 이런거 너무 힘들어서요. 그냥 말로 주절주절 써봅니다.)
      저는 일단 수많은 예제들중에 Microphone 예제의 소스를 다운로드 받았습니다.
      Flash Builder에서 Flex Project를 생성합니다. 물론 Application Type은 Desktop으로 설정해야겠죠? 예제를 다운받아 사용하므로 프로젝트 이름은 예제 소스 압축을 풀어보면 메인소스의 이름을 선택해주면 됩니다. 저의 경우 MicrophoneExamples군요. 프로젝트를 생성했으면 그 다음으로 Flash Builder에서 방금 생성한 프로젝트를 선택한 상태에서 마우스 오른쪽 버튼을 눌러 Import로 들어갑니다. 창이 뜨면 General > Archive File을 선택하고 다음 버튼을 누릅니다. Browse...버튼을 눌러 다운받은 소스압축파일을 선택합니다. 그리고 Finish하세요. Overwrite할거냐 물으면 Yes To All 을 선택하시면 됩니다. 초반에 sdk 에러가 뜰겁니다. 원본 소스의 sdk 설정이 달라서 그러는데요. 이것을 해결하기 위해 프로젝트명을 선택한후 마우스오른쪽 버튼을 눌러 Properties로 들어가 Flex Compiler를 선택한 다음 Use a specific SDK를 Flex 3.4로 선택해주시면 됩니다. 4.0이 아닌 이유는 예제 소스가 mx:WindowedApplication으로 만든 것으로보아 Flex 3.4 기반으로 만들어졌기 때문입니다. 다른 예제가 s:WindowedApplication으로 만들어져 있다면 Flex 4.0을 선택하세요. 다 끝났습니다. 이제 실행해보세요.


      앗... 제 데톱에 마이크가 없군요... ㅡㅡ; 아무튼 이렇게 하면 됩니다. Sample에는 Flex뿐 아니라 Ajax, Flash용도 있으니 잘 활용하시면 학습하는데 도움이 될겁니다.

     

    Flash Player 10.1 Prelease

    Flash Player 10.1의 가장 큰 특징은 바로 모바일 지원입니다. 제 블로그에서 앞서 설명했지만 Flash Player 10.1은 오픈소스프로젝트를 통해 탄생된 결과물입니다. 데스크탑 뿐아니라 다양한 기기에서도 Adobe Flash Platform 기술이 적용될 수 있도록 Flash Player 10.1을 만든겁니다. 이는 모바일과 같이 저급(?) 하드웨어 기반에서 항상 이슈가 되어 왔던 메모리, 전력소비, 속도, 하드웨어가속등의 문제를 해결한 Flash Player를 만들었다는 것을 의미합니다. 이외에도 차세대 모바일에 걸맞는 몇가지 기능(멀티터치등)을 구현할 수 있는 API도 추가되었죠. 또한 H.264 비디오도 지원해줍니다.

    Flash Player 10.1에 대해서 더욱 자세히 알고 싶다면 Flash Player 10.1 Beta Release Notes를 참고하시면 될 것 같습니다.

    Flash Player 10.1 기반의 프로그램을 개발하고 싶다면 Flash Player 10.1 플러그인을 자신의 컴퓨터에 먼저 설치한뒤 Player Global SWC를 다운받아 개발하면 됩니다. 일련의 절차를 간단하게 설명드립니다.

    참고로 Flash Player 10.1은 정식 배포버전이 아닙니다. 테스트 목적으로 하시고 실제 배포는 하지 마세요.

    Flash Player 10.1 플러그인을 http://labs.adobe.com/downloads/flashplayer10.html 에서 다운로드 받습니다. 기존에 설치된 Flash Player는 삭제를 하시고 설치하세요. 윈도우의 경우 파이어폭스와 모질라 계열이라면 Download plug-in for Windows (EXE, 2.2 MB)를 받고 IE라면 Download active-x for Windows (EXE, 2.2 MB)를 다운로드 받으세요. 아쉽게도 개발자를 위한 디버그 버전은 아직 없는 것 같습니다.

    Flash Player 10.1 기반 개발을 위해 해당 API로 개발할 수 있도록 지원해주는 SWC를 다운로드를 같은 페이지에서 받을 수 있습니다. 다운받아 압축을 풀면 playerglobal.swc가 있습니다. 이것을 자신의 프로젝트에 포함하여 개발하면 됩니다. 함께 포함된 readme.txt파일을 보면 사용하는 방법이 잘 나와 있는데 여기서도 간단히 설명해 드리죠. (단, Flash Builder 4 beta 2 기준입니다.)

    1. 만약 Flash Builder를 설치 안했다면 다음 링크를 통해 받으세요.
    2. 새로운 애플리케이션 프로젝트를 만듭니다.
    3. playerglobal.swc를 playerglobal10.1.swf로 이름을 바꾸고 만들어진 프로젝트의 libs폴더에 복사합니다. libs폴더에 복사하면 자동으로 Referenced Libraries로 설정됩니다. (여기서 이름을 바꾼 이유는 다음 내용을 설정할 때 playerglobal.swc의 Link Type을 수정못하도록 Flash Builder가 만들어져 있기 때문입니다.)
    4. 프로젝트명을 선택후 마우스 오른쪽 버튼을 눌러 properties를 선택한 뒤 Flex Builder Path를 선택합니다. 그런 다음 Library path 탭을 선택한뒤 libs 폴더를 열어 Link Type을 External로 바꿉니다. 그런 다음 Flex 4.0 를 열어 기존 playerglobal.swc는 삭제합니다. Link type이 External이라는 것은 컴파일시 사용한 클래스를 빼놓는다는 뜻입니다. 어짜피 playerglobal.swc에 정의된 클래스는 이미 Flash player 10.1에서 지원해주기 때문에 굳이 swf안에 포함할 필요가 없기 때문이지요.
    5. Flash Player 10.1을 요구하는 HTML이 되도록 만들어주기 위해 Properties 창에서 Flex Compiler를 선택합니다. Adobe Flash Player options에서 Use a specific version을 10.1.0으로 바꾸거나 Additional compiler arguments에 -target-player=10.1.0 을 추가합니다.

    이로써 Flash Player 10.1 에서 돌아가는 Flash 애플리케이션을 만들때 환경 구축을 모두 완료했습니다.

    아래처럼 Flash Player 10.1 API인 TouchEvent, TransformGestureEvent 등을 사용할 수 있게 되었습니다.



    아래 링크를 통해 Flash Player 10.1에 대한 다양한 정보를 얻을 수 있습니다.


    앞으로 정식버전이 배포되고 Flash CS5도 나오게 되면 아이폰 및 다양한 기기에서 동작하는 재미있는 애플리케이션들을 만들 수 있게 될 것입니다. 

    Flash Platform 개발자들이여.. 향후 10년 밥벌이 벌었습니다. ㅋㅋㅋ


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



    Flash, Flex, AIR 개발자들이 한권정도는 꼭 가지고 있을 만한 책이다. 이 책은 콜린 무크의 유명한 책인 Essential ActionScript 3.0을 지금도 업계에서 활발히 일하고 계신 유윤선, 송호철님이 번역한 책이다. ActionScript 3.0에 관한한 거의 바이블 수준이다. 시간될 때마다 쭉 훑어보며 느낀 거지만 ActionScript 3.0에 대한 전반적인 개념을 습득하는데 이만한 책이 없다고 생각된다. 

    Flash, Flex, AIR를 개발할 때 가장 기초가 되는 개념은 ActionScript 3.0이다. ActionScript 3.0에 대한 전반적인 개념 및 지식없이 Flex에 접근하다 보면 결국 한계를 느끼게 될 것이다. 왜냐하면 Flex 프레임워크가 모든 기능을 수행해 줄 수 있도록 만들어진 것은 아니기 때문이다. Flex는 프레임워크로서 가치가 있는 것이지 모든 기능을 Flex에서 찾으면 안된다. 또한 Flash로 개발할 때도 마찬가지 인데 타임라인만 의지하다가 뭔가 고급기술을 다뤄야할 필요가 있을때 ActionScript 3.0을 알아야 가능해진다. Flash든 Flex든 AIR든... Flash Platform 개발자가 접근할 수 있는 가장 기초가 되는 것은 결국 ActionScript 3.0이다. (예외적으로 AIR의 경우 Javascript로 만들 수 있는데 이는 컴파일러를 지원하는 차원이지 근간은 ActionScript 이다.)  

    이 책의 한가지 단점이라면 Flash Player 10 API에 해당하는 내용까지 다루지는 않고 있다. 이와 관련된 내용은 여전히 온라인의 ActionScript 3.0 라이브 독을 참고해야한다.

    글쓴이 : 지돌스타(http://blog.jidolstar.com/615
    다시 오픈했습니다. http://blog.jidolstar.com/618



    사용자 삽입 이미지

    Flex 2 기반으로 근 2년 이상 한글문서를 담당했던 Flexdocs.kr 서비스를 하지 못하게 되었습니다. 그동안 Flexdocs.kr은 제 개인서버에서 서비스 했습니다. 한글작업을 위해 몇몇분들이 관심을 가지고 지속적으로 수정했지만 개인서버라는 제약과 관심부족 그리고 그 서버를 유지하는데 있어서 필요이상의 개인자본을 넣어야한다는 부담이 있었습니다. 그래서 집에서 간단한 서버 구축으로 유지하려고 했으나 인터넷공급업체에서 80번 포트를 막아놓아 더 이상 서비스를 하기에는 불가능하게 되었습니다.(요즘 대부분 인터넷 공급업체는 80번 포트 이용을 막고 있습니다.)

    flexdocs.kr의 문서는 모두 제가 가지고 있습니다. 만약 flexdocs.kr을 계속 유지해야한다고 생각하시고 서버 호스팅을 무료로 해주실 수 있는 분이 있으시다면 연락부탁드리겠습니다. flexdocs.kr은 Apache, php, mysql이 지원되야하며 svn과 mod_rewrite등이 지원되면 되겠습니다.

    이 문서는 누구의 소유가 아닙니다. 상업적 용도로 사용할 수 없습니다.

     

     

    실무에 바로 쓸 수 있는있는 유용한 Flash/Flex 기반 ActionScript 3.0 라이브러리들입니다. 3번째 라이브러리도 조만간 소개하겠습니다. ^^

     

    http://opencast.naver.com/FL188/12

     

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

    + Recent posts