윈도우 환경에서 Eclipse Galileo 버전으로 C, C++ 개발을 위한 환경을 만드는 것을 설명하는데 목표가 있다. 기존에 Eclipse기반으로 Flash Builder 플러그인을 설치해 Flash 개발을 하거나 Java 개발하시는 분들이 같은 환경에서 C, C++을 개발하고자 한다면 이 글은 유용한 팁정도가 될 것이다.


1. MinGW를 설치한다.
MinGW(한국어 발음 밍우?)는 무료로 쓰고 배포할 수 있는 MS 윈도우 헤더 파일과 라이브러리로, 어떠한 써드 파티 C 런타임 DLL에 의존하지 않고 네이티브 윈도우 프로그램을 만들 수 있는 GNU 툴을 제공한다. 쉽게 이야기해 MinGW는 윈도우에서 툴이나 dll에 의존하지 않는 동작하는 프로그램을 만들 수 있도록 도와준다.

MinGW에 대해 : http://ko.wikipedia.org/wiki/MinGW 

물론 C/C++를 개발하기 위해 Cygwin을 이용해도 된다. 하지만 cygwin 기반으로 제작한 것은 항상 cygwin1.dll이 필요한다. 또한 개발한 결과물은 라이센스 문제로 상용으로 팔기가 곤란하다. 게다가 윈도우에서 직접 개발한다기 보다 가상의 리눅스 콘솔을 이용하는 것이다. 그래서 여러가지로 MinGW이 장점이 많다.


1.1 MinGW 받기
공식 사이트 http://www.mingw.org/ 로 간다. 좌측 메뉴에 Downloads 페이지로 이동한다. Download Now 버튼을 눌러 최신버전인 MinGW-5.1.6.exe(2010.04.20)를 다운로드 받는다.


1.2 MinGW 설치
  • 다운받은 MinGW-5.1.6.exe를 실행한다.
  • Welcome 화면 Next를 클릭한다.
  • Download and install을 선택후 Next 버튼을 클릭한다.
  • I agree를 클릭한다.
  • Candidate를 선택후 Next를 클릭한다.
  • 설치하고자 하는 것을 선택후 Next를 클릭한다. g++ compiler와 MinGW Make는 반드시 선택한다.
  • 설치하고자 하는 디렉토를 선택한 다음 Next 클릭
  • Install을 클릭하면 이제 필요한 것을 다운로드 받아 설치를 시작한다.
  • 설치가 완료되면 Next 클릭후 Finish를 누른다.

 

1.3 GCC를 실행하기 위한 윈도우 환경변수 설정하기

이제 어느 경로에 있던지 gcc 컴파일러를 실행할 수 있도록 환경을 조성해줄 필요가 있다.

이 방법은 Cygwin을 사용하지 않고 오로지 MinGW만 사용하는 경우에 해당한다. 만약 Cygwin을 함께 사용하시는 분이라면 배치파일을 만들어 사용하는 방법도 있다. (http://kldp.org/node/48962)

  • 제어판에서 시스템 폴더 클릭. window 7환경에서 개발한다면 검색창에서 시스템을 입력한뒤 시스템 환경 변수 편집을 선택한다.
  • 아래 표시된 방법대로 입력하면 되겠다. 최종적으로 MinGW 설치된 폴더에 Bin 폴더를 입력하는 것을 목표로 한다.


  • CMD창에서 gcc --version과 mingw32-make를 입력하고 다음과 같이 나오면 성공적으로 설치된것임

 이제 MinGW가 설치되었으므로 window 기반에서 C, C++등을 개발할 수 있는 환경이 만들어진 것이다. 다음에 나오는 Eclipse기반이 아니더라도 메모장에서 C코드를 짜고 GCC로 컴파일할 수 있다. 하지만 개발툴을 메모장을 사용할 수는 없는 노릇아닌가?


2. Eclipse Galileo 버전을 설치한다.
Eclipse는 기존에 설치했던 사람이 대부분일 것이다. 이 설명은 필요없는 내용일 수 있으나 그냥 적어본다.

일단 Eclipse는 아래 링크에서 자신의 개발하고자 하는 목적별(Java, C/C++, PHP등)로 다운로드 받아 설치할 수 있다.

http://www.eclipse.org/downloads/

필자는 Java기반에서 개발하는 일이 많으므로 Java EE Developers를 위한 Eclipse IDE를 설치했다. Windows 32bit 기반을 다운로드 받았다. 설치는 받은 압축파일을 원하는 곳에 압축만 풀어주는 것으로 완료가 된다. 본인은 E:\eclipse 에 설치했다. C 드라이브에 설치하지 않은 이유는 나중에 운영체제를 다시 설치하는 경우에 Eclipse를 보존하기 위함이다.

Eclipse를 처음 설치하는 사람이라면 반드시 JRE가 자신의 컴퓨터에 미리 설치가 되어 있어야 Eclipse 구동이 가능하다. 다음 링크에서 JRE나 JDK 최신버전을 설치하면 되겠다.

http://java.sun.com/javase/downloads/index.jsp

만약 C/C++기반인 Eclipse를 다운로드 받아 설치하면 다음에 "3. CDT 플러그인을 설치한다"를 넘겨도 된다.


3. CDT 플러그 인을 설치한다.

CDT는 C/C++ Development Tool이다. 기존에 이클립스 기반으로 개발하던 사람이라면 C/C++도 Visual Studio와 같은 툴을 활용하지 않고 개발하고자 하는 욕구(?)가 들지도 모르겠다. CDT를 Eclipse에 설치하면 C,C++ 개발이 가능한 환경이 된다.

CDT는 개발하는 툴이지 컴파일러가 아니다. CDT를 설치했더라도 각각 운영체제 기반에서 제공하는 C/C++ 컴파일러와 연결하는 작업은 필요하다. 여기서는 CDT를 Eclipse에 설치하는 방법만 소개한다.

Eclipse 메뉴에서 Help > Install New Software... 를 선택한다.

아래와 같은 화면이 나오면 Add 버튼을 누른다.


http://www.eclipse.org/cdt/downloads.php 에 가면 최신버전(현재 CDT 6.0.x)을 다운로드 받을 수 있는 링크가 소개되어 있다. Eclipse가 Galileo버전이므로 http://download.eclipse.org/tools/cdt/releases/galileo 를 URL로 삼아 플러그인을 설치하면 되겠다.

아래처럼 Add Site창에 Name과 Location을 입력한다. Name은 아무거나 입력하면 된다.


참고로 위처럼 등록하면 Windews > Preferences > Install/Update > Avaliable Software Sites에 아래처럼 등록되어 있다.


이제 Work with 란에 CDT를 입력해보면 금방 등록했던 CDT 정보를 선택할 수 있다. 그럼 아래처럼 나온다. CDT Main Features, CDT Optional Features 모두 Check하고 Next버튼을 누른다.


아래처럼 설치할 목록들이 나온다. Next버튼을 누른다.



아래와 같은 화면이 나오면 I accept the terms of the license agreements를 선택후 Finish 버튼을 누르면 설를 시작한다. 시작이 완료된 다음에는 Eclipse를 재구동한다.



Eclipse가 구동된 다음 C/C++ Perspective를 열어보자. Windows>Open Perspective>Other 를 선택한다. 아래처럼 창이 뜨면 C/C++를 선택한다. 
 

Perspective창에 아래처럼 추가 된것을 확인하자. 이제 Eclipse에서

File > New를 가도 C, C++ 프로젝트를 만들 수 있게 되었다.


4. 간단한 C/C++ 개발해보기

MinGW의 bin 폴더에 보면 gcc, g++등 각종 컴파일 도구가 있다. 이클립스의 CDT환경에서 C/C++로 개발할때는 MinGW의 컴파일 도구를 이용해 컴파일 한다는 사실을 기억하자.

다음과 같이 C++ 프로젝트를 만들어보자.
  • 이클립스 메뉴에서 File > New > C++ Project를 선택한다.
  • 다음 그림과 같이 프로젝트를 만들기 위한 창이 나오면 Project name을 넣고 Project type을 Executable > Hello World C++ Project를 선택한다음 Toolchains를 MinGW GCC로 선택하도록 한다. 이렇게 하면 자동으로 Hello World CPP가 만들어지면서 컴파일은 MinGW의 g++컴파일러를 이용해 컴파일 할 수 있도록 설정 되는 것이다. Next 버튼을 누른다.
  • 간단한 셋팅을 하는데 이 설정에서 중요한 것은 Source 파일은 src 폴더에 들어가게 된다는 것이다. Next 버튼을 누른다.


  • 다음 그림과 같이 Debug, Release 두 버전으로 프로그램 결과를 별 수 있도록 셋팅된다. Finish 버튼을 누른다.

 

이클립스의 Project > Build Automatically가 선택되어 있다면 위처럼 프로젝트를 만들게 되면 자동으로 MinGW의 g++을 찾아 컴파일을 실시하게 된다. 아래와 같은 메시지가 이클립스의 Console창에 나오면 제대로 실행된 것이다.


위 메시지 처럼 컴파일을 하지 못하고  아래처럼 Nothing to build for Test1 메시지가 뜬다면 이것은 수정된 내용이 없으므로 컴파일할 필요가 없다는 것을 의미한다. 대신 Ctrl+B 하면 강제로 컴파일한다.


이 프로젝트는 C++ 프로젝트이므로 gcc 컴파일러가 연결되어야 한다. 만약 컴파일에 실패한다면 Mingw/bin에 gcc.exe를 확인해보자. 만약에 없다면 위에 Mingw 설치하기를 다시 한번 따라해보길 바란다.

실행해보자. 아래처럼 만들어진 프로젝트의 src폴더를 열어 Test1.cpp를 선택한다. 그리고 빨간 박스로 표시된 아이콘을 클릭하거나 Ctrl+F11을 하면 실행할 수 있다.

Console창에 아래처럼 Hello World가 출력된것을 확인하자.

Build Automatically를 체크한 경우에는 코드를 수정하고 저장할 때마다 자동 Build처리된다. 하지만 수동으로도 할 수 있는데 Ctrl+B 또는 아래 그림처럼 망치툴을 클릭해서 Build할 수 있다.



개발시에는 Debug버전과 Release 버전이 있다는 것을 기억하자. 아래 보이는 도구는 현재 개발 환경이 Debug인지 Release인지 결정해 주는 도구이다. Debug는 개발용이고 Release는 외부로 배포할때 사용한다.


실제로 이 도구를 이용해 Debug에서 Release로 바꿔보자. 아래처럼 Release 폴더가 생겨나고 컴파일 결과가 이 폴더안에 만들어진다. 이때부터는 실행할때 Release폴더에 있는 exe파일이 실행되게 된다.


참고로 makefile을 기반으로한 개발을 하고 싶은 경우가 있을 수 있다. 이런 경우에는 C++ 프로젝트를 생성시에 아래 그림처럼 Makefile Project를 선택해서 생성해야한다.

하지만 실제로 이 상태로 프로젝트를 생성해 컴파일하면 Cannot run program "make": Launching failed 메시지가 console창에 보이면서 컴파일이 불가능하다. 이 메시지는 컴파일을 수행할 수 있는 make 명령어를 찾지 못한다는 것을 의미한다. Eclipse에서는 기본적으로 make가 지정되어 있지만 MinGW의 make 명령은 mingw32-make로 다르다. 그러므로 이 문제를 해결하기 위해 mingw32-make.exe를 복사해 같은 폴더에 make.exe로 이름을 바꿔주면 된다. (물론 찾아보면 다른 방법도 있지만 이 방법이 가장 빠르고 쉽다.)

makefile을 이용해 개발해보는 간단한 예제는 다음글을 참고한다.
http://kkamagui.springnote.com/pages/446531


5. 간단한 win32용 프로그램 개발하기
지금까지의 예제는 단순히 Console창에 결과가 나온다. MinGW는 Windows API를 이용한 개발도 지원하므로 앞서 설명한 같은 C++ 프로젝트로 만들고 아래 코드로 변경해도 컴파일이 가능하다.


#include <windows.h>

int WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
        MessageBox(NULL, "Hello in EclipseCDT", "EclipseCDT", MB_OK);
        return 0;
}


실행해 보면 아래와 같은 다이얼로그 창이 나온다.

경험자들에 따르면 Win32 로 개발하는 경우에는 한가지 중요한 C++ 링커 설정을 해야한다고 한다.

먼저 해당 Win32 프로젝트를 선택후(필자의 경우 Test3) 이클립스 메뉴에서 Project > Properies로 들어갑니다. 아래처럼 창이 뜨면 C/C++ Build > Setting으로 들어가 Tool Setting 탭을 선택합니다. 거기에 MinGW C++ Linker>Miscellaneous를 선택해 Linker flags에 -mwindows를 입력한다. 완료하면 Apply버튼이나 OK 버튼을 누르면 적용된다.

이 설정을 해야 군데 군데 "undefined reference"와 같은 에러를 발생시키지 않는다고 한다. 수정했는데도 에러를 띄우면 File->Save all 또는 이클립스 종료후 다시 시작하면 된다.

Windows API 학습을 위해 다음 링크를 참고한다.
http://www.winapi.co.kr/

6. 디버깅 환경 만들기
지금까지 개발환경으로 개발자체는 문제 없지만 디버깅은 할 수 없다. MinGW에 디버깅을 하기 위한 도구가 설치되어야 하는데 이를 가능하게 하는 것이 gdb이다.

먼저 gdb를 다운로드 받자.
http://downloads.sourceforge.net/project/mingw/GNU%20Source-Level%20Debugger/GDB-7.1/gdb-7.1-2-mingw32-bin.tar.gz

압축을 풀고 bin과 share 폴더를 그대로 설치된 MinGW 폴더에 복사해준다. 이것으로 MinGW의 C/C++ 디버깅 환경이 구축되었다.

이제 Eclipse에서 F11 누르거나 아래처럼 벌레모양의 아이콘을 눌러 Debugging을 할 수 있게 된다.


디버깅을 하게 되면 Debug Perspective창으로 바뀌면서 아래처럼 디버깅이 가능해진다.

참고로, 이클립스에서 디버깅 설정은 Run > Debug Configurations에서 할 수 있다. 좌측 리스트에서 C/C++ Application을 선택해 해당 exe를 선택한뒤 우측에서 Debugger 탭을 선택하면 Debugger를 선택할 수 있게 된다.

만약 gdb를 설치했음에도 불구하고 에러를 내면서 디버깅을 할 수 없는 경우 CMD창에서 다음과 같이 입력해보길 바란다.



만약 위처럼 메시지가 나오지 않고 libexpat-1.dll이 없다고 경고창이 나오면 현재 설치한 gdb버전이 libexpat-1.dll과 의존성이 있는 것이다. 이때는 libexpat-1.dll도 함께 다운로드 받아 MingGW/bin에 복사해줘야 한다.

libexpat-1.dll은 아래 링크에서 다운로드 받는다.
http://downloads.sourceforge.net/project/mingw/MinGW%20expat/expat-2.0.1-1/libexpat-2.0.1-1-mingw32-dll-1.tar.gz

다음 글을 참고한다.
http://forums.codeblocks.org/index.php/topic,11301.msg77273.html

7. 정리하며
프로젝트 중에 C와 MySQL을 연동하여 개발할 경우가 있어서 전체 환경설정을 정리한다는 마음으로 글을 적었다. 기존에 나와 있는 많은 글들이 너무 옛날 버전이라서 그런지 지금과 맞지 않는 부분도 있었다. 앞으로 MySQL과 연동하게 되면 관련 내용도 소개해 볼까 한다. 개발에 있어서 환경을 구축하고 적응하는게 반인 것 같다. ^^;


8. 참고글

이클립스(Eclipse) CDT 설치
(Eclipse Galileo) C/C++ Developments User Guide
CDT를 이용한 Windows C/C++ 개발 환경 만들기
Eclipse/CDT
Eclipse Project CDT (C/C++) Plugin Tutorial 1, 2By Brian Lee
이클립스(eclipse)를 이용해서 C/C++ 프로그래밍 환경설정
MinGW + Eclipse 를 이용한 Windows 개발 환경 구현

글쓴이 : 지돌스타(http://blog.jidolstar.com/677)
파워플 킥오프 모임을 합니다.
http://blog.jidolstar.com/680


파워플(Powerfl) 프로젝트 1차 모임을 진행하려고 합니다.

아직 장소와 날짜는 미정이지만 서울 강남이나 신사동, 4월 19일~4월 23일 사이가 될 것 같습니다. 일단 복잡하게 할 것 없이 파워플에 대한 설명과 로드맵을 들어보고 역할을 정해보는 시간이 될 겁니다. 

제 블로그와 히카님의 블로그를 통해 많이들 신청해 주셨습니다. 하지만 연락처가 제대로 남겨져 있지 않아서 통일된 연락을 하기가 곤란하네요. 일단 이번 프로젝트에 참여 의사를 밝혀주신 분들입니다.

히카, 니케, 지돌스타, 정태현(아라리요), 김현우(marines), 전은정, 오현식(오군), 사광호(쿠로), 성우, 고재연, 웹눈, 유지남, 성규, 박준우, blackiz, 윤종호(초로기), 박동준 (이상 임의순)

참여하실 분은 이름, 별명, 홈페이지(블로그), 이메일, 전화, 참여분야 및 특이사항에 대해서 제 블로그에 비밀댓글로 적어주세요.

그럼 제가 전화나 이메일로 연락을 드리도록 하겠습니다. 

파워플 프로젝트에 대해 : http://blog.jidolstar.com/673




Flash는 다양한 보안모델을 가진다. 보안모델에 따라 동작하지 않는 경우에는 Flash Player는 여지없이 Security Error를 발생시킨다. 이러한 보안모델이 있으면 개발할 때는 난해하지만 사용자 입장 및 서버 관리자 입장에서 보안문제를 적절히 처리할 수 있기 때문에 안심하고 Flash 어플리케이션을 사용할 수 있는 것이다.

Flash Player 보안에 관련된 것은 다음 글을 보면 자세히 나온다.

Flash Player 보안


각설하고...
SWF파일과 그것을 감싸는 HTML이 동일한 도메인에 위치하는 경우에는 <object>및 <embed> 태그에 allowScriptAccess 매개변수값을 sameDomain이나 always를 지정하는 것만으로 SWF에 ExternalInterface.addCallback() 함수로 자신내부의 메소드를 호출할 수 있게끔 되어 있는 경우 보안상 아무 문제없이 HTML에서 SWF의 메소드를 호출할 수 있다. 다음글을 참고해 보자.

아웃바운드 URL 액세스 제어

하지만 SWF파일과 HTML이 동일한 도메인에 위치하지 않은 경우에는 Flash Player 보안이 더욱 강화된다. allowScriptAccess 매개변수를 기본적으로 allowScriptAccess="always"로 지정해야 하는 것 외에 다른 보안 설정이 필요하다. 그것은 SWF에 Security.allowDomain()  메소드가 가능하게 한다.

SWF가 domain1.com에 있고 HTML이 domain2.com에 있다고 할 때 HTML에서 SWF내에서 ExternalInterface.addCallback()으로 외부접근을 허락한 메소드를 접근하려면 SWF에  Security.allowDomain("domain2.com");을 지정해 주어야 한다. 이 말은 이 SWF는 domain2.com에 있는 HTML에서 접근하는 것을 허락한다는 것을 의미한다.

어떤 도메인에 있던지 SWF 내부의 메소드 접근을 항상 허용하려면 Security.allowDomain("*") 로 지정하면 된다. 하지만 이것은 보안상 좋지 않는 방법이다.

서로 다른 도메인에 있는 SWF와 HTML에 대해서 이런 보안이 있는 이유는 명료하다. 내가 배포한 SWF에 정의된 함수를 HTML에서 접근할 수 있도록 ExternalInterface.addCallback()을 정의했는데, 다른 사람이 다른 도메인에서 이것을 악용할 소지가 있을 수 있기 때문이다.

일반서비스인 domain.com과 파일서버인 file.domain.com을 보자. 두개다 domain.com을 사용하지만 domain.com은 file.domain.com의 슈퍼도메인이다.  이는 Flash Player 6 이전에는 같은 것으로 인식했으나 Flash Player 7이상으로 되면서 다른 도메인으로 인식하게 되었다. 이렇게 된 것은 보안상 문제로 보안정책의 변경이 필요했기 때문이다. file.domain.com에 어떤 사용자가 악성 스크립트를 심은 swf를 업로드 했다고 가정하자. 이때 domain.com과 file.domain.com을 동일하게 인식해버리면 이 swf는 domain.com에 마음대로 접근이 가능해진다. 그래서 flash player는 file.domain.com과 domain.com을 다르게 인식하는 것이다. 이것은 crossdomain정책과 같다.

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

Security.allowDomain()에 대해

아쉬운 것은 Security.allowDomain("*.domain.com") 식의 지정이 안된다는 것이다.  crossdomain.xml 설정시에는 와일드카드(*)를 *.domain.com 형태로 사용할 수 있지만 Security.allowDomain()에서 안되는 것은 이외다.

개인적으로 개발환경과 테스트환경, 배포환경이 모두 다르기 때문에 와일드카드 지원이 있으면 좀 편할텐데 하는 아쉬움이 남는다.

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





Flex 4까지 공식 배포되기 시작했지만 Flex 역사가 수년이 지났음에도 불구하고 버젓한 한글문서라고는 flexdocs.kr 가 다였습니다. 일본어를 어렵게 번역하고 수번이나 수정하면서 한 것들이라 중간중간 필요한 내용이 누락되거나 단어가 이상한 것도 있었지만 그동안 Flex 학습에 큰 도움을 준 것은 사실입니다. 아쉽게도 Flex 3에 대한 한글문서가 잠깐 선보였다가 갑자기 사라진 뒤로는 이제 더이상 Flex관련 한글 문서는 기대하기 힘들겠다 생각했습니다.

그러나 열이아빠님께서 이러한 분위기를 깨뜨려주셨습니다.

개인적으로 진행하시던 Flex 4 한글 문서화를 공개하신겁니다. Flex 4 한글 문서화 프로젝트는 단순히 한글 문서를 만드는데만 그친다고 보지 않습니다. 개발자들끼리 뭔가 새로운 네트워크를 만들어주는 좋은 장치일 수 있습니다. 그래서 저도 동참하기로 마음먹었고 미약하게나마 지원해 드릴 수 있는 부분은 적극적으로 하려고 합니다.

Flex 2, 3에 대한 개발경험이 있으신 분이시고 번역작업에 동참하시고 싶으신 분이라면 언제든지 참여하실 수 있습니다. 자세한 내용은 다음 글을 참고하세요.

어도비에 한글 문서가 없다고 불만만 가지지 말고 이런 상황을 잘 이용할 필요가 있습니다. 단순한 작업 같지만 매우 중요하고, 그러면서 뭔가 다른 개발자들에게 도움이 될 수 있는 일이면서 자신의 인맥을 넓힐 수 있는 중요한 계기가 될 수 있습니다.

많은 Flex 개발자 분들의 참여를 부탁드려요.

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



공지 : 파워플 킥오프 모임을 참고해 주세요. http://blog.jidolstar.com/680


이전부터 많은 지면을 할당하여 원더플(wonderfl.net)에 관련된 글을 많이 적었습니다. 제 블로그에 꽤 많은 Flash 개발자들이 다녀가시기 때문에 이제 원더플을 모르시는 분은 거의 없을 듯 합니다.

아시다시피 원더플은 웹에서 직접 코딩하고 컴파일되어 결과를 사이트에서 바로 확인할 수 있다는 장점이 있습니다. 여기에 SNS기능도 곁들여서 세계적으로 많이 애용되고 있는 사이트입니다. 저는 이 때문에 칭찬을 아끼지 않았죠.

하지만 원더플은 중요한 한계점이 있습니다. Flex든 ActionScript든 딱 한개의 문서만 만들 수 있습니다.  현업에서 실제로 더 중요시 되는 것은 아키텍쳐와 설계 부분에 대한 부분입니다. 그러므로 원더플의 이런 구조로는 이런 요구를 수용하기에는 부족할 수 밖에 없지요. 가령, 제 경우만 하더라도 이전에 Flex로 제작해 놓은 3D 게시판이나 상용에서 사용할 만한 이미지 에디터, 천문프로그램 정도의 코드를 원더플에 개시하기에는 너무 역부족이였습니다.

파워플(Powderfl)이란?!


지금 소개해드리고자 하는 것은 원더플의 한계를 극복하면서도 더욱 파워풀하게 Flex, ActionScript 3.0 코드를 공유할 수 있는 파워플(powderfl) 프로젝트입니다.

파워플은 ActionScript 코드를 웹에 개시하고 보여주며 소스를 공유하는 것까지는 원더플과 비슷한 컨셉입니다. 하지만 파워플은 원더플처럼 웹에서 직접 코딩할 수 있는 조악한(?) 에디터를 대신 Flash Builder를 에디터로 삼고 그 결과를 SVN으로 전송해주면 원더플과 똑같이 SWF 애플리케이션을 웹에서 볼 수 있도록 합니다.

이 아이디어는 애초부터 hika님으부터 나온 것이고, 저는 프로젝트 명만 정했습니다. ^^
Powerfl(Power + Flash) 프로젝트 명은 제가 생각해도 너무 잘지은듯.... ^^ 마침 도메인도 다 비어 있었다는 ㅋㅋ


파워플 개요
앞서 말씀드렸듯이 파워플은 원더플의 한계점을 극복하기 위해 Flash Builder를 에디터 삼는데서 시작합니다. 이에 대한 대략적인 개요는 다음과 같습니다.

  1. 서버에 계정을 만들면 svn레포지토리를 할당받는다.
  2. 로컬에서 레포지토리 싱크를 하면 서버측 swc를 전부 내려받고 기본적인 폴더 구조를 내려받는다.
  3. 로컬에서 개발한 후 커밋하면 훅스크립트( http://www.javajigi.net/pages/viewpage.action?pageId=110395394 )를 이용하여 자동으로 서버측에서 컴파일을 일으킨 후
  4. 원더플과 마찬가지로 swf를 웹에 게시한다.
  5. 커밋로그를 작성할 때 다양한 매크로를 지원하여 swf에 대한 기술을 손쉽게 하고 서버가 swf를 컴파일한 후 swf에 대한 자세한 분석레포트를 같이 올린다.
  6. svn에 커밋된 내용은 웹에서 완전히 보고, 수정하고, 삭제할 수 있으며 기존 원더플 방식을 웹에서 파일을 새로 만들어 편집하는 것도 가능하게 한다.
  7. 상대방의 svn 중 원하는 패키지를 import할 수 있게 되어 유저가 작성한 라이브러리를 캡슐화한 상태에서 공유할 수 있다.
  8. 유저는 자신의 swf를 구동하는 도중에 몇개라고 썸네일을 생성해낼 수 있다.
  9. 서버에서 생성된 swf 및 썸네일은 전부 로컬로 다운로드 받을 수 있으며, 특정 유저의 레포지토리를 zip파일로 내려받을 수 있다.

대략적으로 보면 에디터를 Flash Builder 뿐 아니라 웹에도 에디팅이 되게 하고 SVN이 되기 때문에 어떤 에디터를 써도 무방하다는 것을 알 수 있습니다. 그만큼 파워플! 한거죠 ㅋ


파워플은 왜 필요한가?
일단 한국에서 뭔가 설계, 아키텍쳐, 기술을 공유하는 것은 너무 어렵습니다. 딱히 그 이유에 대해서 말씀드릴 필요는 없고, 이런 개발 환경에서 개발자들이 힘을 합해 파워플과 같은 사이트를 만들어 운영하기 시작하고 활성화 되면 어도비에서도 한국에 대한 관심을 가지기 시작할 겁니다. 뭔가 시장이 활발한 것을 보여줘야 어도비도 반응할 것이고 '어도비는 일본만 좋아해'라며 부러운 시선을 가지지 않아도 될겁니다. 

파워플은 아마도 개발자들이 참여하는 만큼 초반부터 영어로된 사이트가 되지 않을까 생각합니다. 정상적으로 만들어져서 운영이 된다면 한국개발자뿐 아니라 일본, 미국 개발자들도 유용하게 쓸 수 있는 사이트가 되지 않을까요? 만약 그렇게 정말루~ 된다고 생각해보세요.

그리고... 파워플이 활성화 되서 어도비 공식 기술 사이트로 선정되었다고 생각해보세요. 정말 뿌듯할 겁니다.

카페나 블로그에서 정보 공유하는 것을 뛰어 넘어서 우리도 뭔가 이런 일을 누군가는 해야만 한국 Flash 발전이 있을 것이고 이러한 활동이 Flash에만 국한되는 것이 아니라 관련 업종에도 영향을 주어 여러 벤치마킹한 사이트들이 많아진다면 그 또한 즐거운 일입니다.

파워플의 특성상 유용한 공개 프레임워크들이 많이 올라올 수도 있을 겁니다. 특이하고 멋진 아키텍쳐로 이루어진 애플리케이션도 경험할 수 있을 겁니다. 다른 개발자들과 함께 협업을 통해 함께 쓸 수 있는 결과물을 도출할 수 있을 겁니다. 그리고 우리도 Flex 5 만들 수 있지 않을까요?


파워플 개발 팀원 모집

파워플 서비스가 오픈되려면 다음과 같은 분야의 다양한 노가다를 필요로 합니다.
  • 리눅스 서버 셋팅 (아마도 데비안이 후보)
  • 아파치를 다양한 mod와 합체하여 최적화 컴파일설치
  • 서비스는 php로 작성
  • db는 mySQL..오라클 꼴보기 싫으면 pSQL이나 mSQL 쓸지도..
  • svn설치 및 아파치 연동
  • svn 훅스크립트 작성을 위한 리눅스 스크립트…냐 아니면 펄이나 파이썬이냐..아니면 php냐..
  • mxmlc와 mod연동
  • 웹사이트 제작
  • 위의 모든 서비스를 웹사이트에 인테그레이션
  • rss 및 다양한 웹배포수단 연동모듈작성
  • 예상컨데 자바나 c계열 소켓서버의 브릿지를 통해 as3 socket에서 인증을 처리해주고 아파치웹서버에 접근할 수 있게 해줄 예정(귀찮은데 펄데몬으로?)
  • 기타 워드프레스 인테그레이션도 하고 싶은…ㅋㅋㅋ

어떤 분야든 좋습니다. 함께 하실 분을 찾습니다.


현재 맴버


제작일정
  • 모집마감 : 없음(언제라도 들어와만 주신다면 감사할 따름 ^^;)
  • 프로젝트 런칭 및 킥오프미팅 : 아직은 미정이지만 4월 20일정도
  • 오픈 예정 : 6월? ㅎㅎ 과연

관련글
신규 프로젝트 팀원 모집


어쨌거나~~ 한번 정도 만나서 이와 관련된 이야기를 해보고 진행하는 것은 꽤 생산적인 일이 될겁니다.
우리도 원더플과 같이 멋진 국제 기술 사이트를 만들어 보자구요!
참여하실 분은 주저말고 신청해주세요. 단, 어느 부분에 있어서 하고 싶다는 것을 적어주시면 더욱 좋습니다.
여러분의 참여가 한국 개발 현실을 바꿀 수도 있습니다. ^^

이미 사비 들여서 도메인도 구입해 두었습니다.
powerfl.com, powerfl.net, powerfl.co.kr

도메인도 샀으니 해야만 합니다. ^^

많은 참여 부탁합니다.

글쓴이 : 지돌스타(http://blog.jidolstar.com/673)
게임에서 키보드 제어는 가장 기본이면서 중요하다. 요즘 Flash 관련 게임을 제작해보겠다는 열의를 가지고 이것저것 테스트 해보고 있는데 게임 분야는 워낙 방대하고 알아야할 것도 많아서 계속 도전하고 싶은 충동을 강하게 만든다.

일단 가장 기본적인 슈팅게임을 만들어보겠다는 목표를 세웠다.

원더플(http://wonderfl.net)에 올라오는 다양한 완성된 게임들을 보면서 나름대로 코드를 분석하고 정리하면서 하나씩 완성해 나가고자 한다.

키보드 제어에 대해서는 Hika님의 글이 매우 유용하다.

마우스/키보드의 인터렉션 최적화 2/2

나는 키보드 제어에 대한 기본 지식을 위 글을 통해서 공부했고 뭔가 슈팅게임을 위한 적당한 클래스를 만들어봤야겠다는 생각을 했다. 좀 더 범용적인 클래스가 좋겠다고 했는데.. 마침 원더플에 올라온 KeyMapper 클래스가 눈길을 끌었다.


// Key mapper
//----------------------------------------------------------------------------------------------------
class KeyMapper {
	public var flag:int=0;
	private var _mask:Vector.=new Vector.(256, true);

	function KeyMapper(stage:Stage):void {
		stage.addEventListener("keyDown", function(e:KeyboardEvent):void { flag|=_mask[e.keyCode]; });
		stage.addEventListener("keyUp", function(e:KeyboardEvent):void { flag&=~(_mask[e.keyCode]); });
	}

	public function map(bit:int, ... args):KeyMapper {
		for (var i:int=0; i < args.length; i++) {
			_mask[args[i]]=1 << bit;
		}
		return this;
	}
}

매우 단순하고 아름답기 까지 하다. ㅎㅎ
사용법은 꽤 간단하다. 아래는 사용할 키를 등록하는 방법이다.

// keyboard mapper
_key=new KeyMapper(stage);
_key.map(0, 37, 65).map(1, 38, 87).map(2,39,68); //좌 : Left(37), A(65); 위:Up(38),W(87); 우:Right(39),D(68)

_key.map() 메소드의 첫번째 인자값은 해당 키가 눌려졌을때 참조할 bit위치이다. 2번째 부터는 이 bit위치가 변경되기 위한 키코드를 넣을 수 있다. 가령 0번 bit에 Left키 또는 A키를 눌렀을때 bit가 on될 수 있도록 한다.

사용된 키코드는 KeyboardEvent가 발생할때 이벤트의 keyCode속성에 나오는 코드이다. 이 값은 범용적으로 약속된 값으로 다음 글에서 참조하길 바란다.

키코드값 표

KeyMapper에 등록된 키들은 EnterFrame 이벤트 발생되는 시점에서 다음과 같이 사용하면 된다.

var inkey:int = _key.flag;
//회전
ar=(((inkey & 4)>>2) - (inkey & 1))*0.5; //회전가속
vr += ar; //회전속도
r += vr; //회전값
ar *= .96; //계속 증가 못하도록 방지
vr *= .92; //계속 증가 못하도록 방지
( r < 0 ) ? r += 360 : ( r > 360 ) ? r-=360 : 0; //0~360으로 Normalization

//이동
a = -((inkey & 2)>>1)*1.2; //가속
ax = a * Math.sin((360-r)*D2R); //가속 x성분
ay = a * Math.cos((360-r)*D2R); //가속 y성분 
vx += ax; //속도 x성분
vy += ay; //속도 y성분
x += vx; //위치 x성분
y += vy; //위치 y성분 
ax *= .96; //계속 증가 못하도록 방지
ay *= .96;
vx *= .90;
vy *= .90;



즉 inkey & 4(10진수)는 inkey & 100(2진수)과 같다. 즉 3번째 bit의 값이 on이냐 off이냐에 따라서 3번째으로 지정된(여기에서는 우측이동키 또는 D)키가 눌려졌는지 알 수 있다. 이 값에 shift연산자로 >>2를 하면 눌려졌으면 1, 안눌려졌으면 0을 참조할 수 있게 된다.

이처럼 키보드를 참조하기 위해 if문이 아닌 bit연산으로 참조함으로 이 부분에 있어서 더 빠른 참조가 가능해진다고 본다.

단지 현재 이 KeyMapper 클래스는 키동작의 순서를 담지 않기 때문에 액션대전게임등과 같은 것을 만드려면 그와 관련된 Key동작을 mapping할 수 있는 것이 필요할 것이다.

필자는 이것으로 한대의 비행기를 회전, 이동하는 Flash 애플리케이션을 간단하게 만들어봤다.


보러가기 : http://wonderfl.net/code/53e6c9b9ed9a776dd45b529304937d13ea9398ac

참고로 비행기는 아래 이미지를 참고했으며 출처는 다음 링크에 있다.
http://www.brighthub.com/internet/web-development/articles/11010.aspx



위 이미지를 회전시켜 BitmapData를 만드는 방법은 다음글을 참고한다.
Flash 속도 개선을 위한 실험 - 10만개 입자 유체 시뮬레이션 연장전!


추가사항

hika님께서 댓글로 다음처럼 지적해주셨습니다.

결사반대!
그러니까 80년대에 사용하던 비트or연산 합체는 제발 지양해주세요.
그 원조인 c업계에서도 철폐를 위해 노력하고 있습니다.
코드는 점점 암호화 되고 그걸 다시 가시화하기 위해 엄청난 const를 잡게 됩니다. 나락으로 빠지는 길이죠.
빠른 벡터를 컨테이너로 사용하세요 ^^

var keyDown:Vector.<Boolean> = new Vector.<Boolean>();

stage.addEventListener("keyDown", function(e:KeyboardEvent):void { keyDown[e.keyCode] = true; });
stage.addEventListener("keyUp", function(e:KeyboardEvent):void { keyDown[e.keyCode] = false; });

if( keyDown[Keyboard.UP] ){

if( keyDown[Keyboard.SHIFT] ){
   if( keyDown[Keyboard.SPACE] ){

이러한 코드는 가독성만 좋은게 아닙니다.
http://help.adobe.com/ko_KR/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7d01.html
이 문서를 보세요.

키와 키 코드의 매핑은 장치 및 운영 체제에 따라 다릅니다. 따라서 키 매핑을 사용하여 액션을 트리거해서는 안 됩니다. 대신에 Keyboard 클래스에서 제공하는 미리 정의된 상수 값을 사용하여 적당한 keyCode 속성을 참조해야 합니다. 예를 들어, Shift 키의 키 매핑 대신 Keyboard.SHIFT 상수를 사용하십시오

이러한 멘트가 나옵니다. 플래시플레이어는 운영체제 버전에 따라 키코드값을 적절하게 매핑해주기 때문에 개발한 게임이 맥에서 안도는 사태를 막아줍니다 ^^

여하튼 그 모든걸 떠나서 결국 38, 39를 외우는 것도 무리고 코드를 해석하기 위해 맨날 키코드표를 참조한다는것도 말도 안되고 그 결과 const를 도입해서 느려지는 식입니다.

저 방식은 원더플에 올라오는 수준이거나 데모를 만드는 수준에서만 키셋이 몇개 없을때 통용되는거지 실제 게임에서 유저가 키셋팅을 커스터마이즈하는 것도 흔하고 플래시 특성상 이기종에서 실행되는것도 흔하기 때문에 아니되옵니다 ^^


네.. 중요한건 Adobe문서에 잘 나와있었다는 거죠 ^^;
그래서 기존 KeyMapper를 버리고 새로운 형태로 만들어봤습니다.


class Key {
	static private var _down:Vector.=new Vector.(256, true);
	static private var _stage:Stage;

	static public function start($stage:Stage):void {
		stop();
		if (Capabilities.hasIME) {
			IME.enabled=false;
		}
		if ($stage === null) {
			throw new Error("인자값이 null이면 안됩니다.");
		}
		_stage=$stage;
		_stage.addEventListener("keyDown", KEY_DOWN);
		_stage.addEventListener("keyUp", KEY_UP);
	}

	static public function stop():void {
		if (Capabilities.hasIME) {
			IME.enabled=true;
		}
		if (_stage) {
			_stage.removeEventListener("keyDown", KEY_DOWN);
			_stage.removeEventListener("keyUp", KEY_UP);
		}
	}

	static private function KEY_DOWN($e:KeyboardEvent):void {
		_down[$e.keyCode]=1;
	}

	static private function KEY_UP($e:KeyboardEvent):void {
		_down[$e.keyCode]=0;
	}

	static public function isDown($keyCode:int):int {
		return _down[$keyCode];
	}
}


IME 부분은 한글로 입력되어 제대로된 키코드가 들어오는 것을 방지하기 위함입니다.(이 부분은 좀더 생각해볼 필요가 있을듯..) Vector를 Boolean말고 int로 한 것은 간단한 연산처리가 필요한 경우에 활용하기 위함입니다. 이렇게 해두면 호스트쪽은 아래처럼 참조하면 됩니다.
var a:Number; //inkey:int = _key.flag;
var right:int=Key.isDown(KEY_RIGHT) | Key.isDown(KEY_D);
var left:int=Key.isDown(KEY_LEFT) | Key.isDown(KEY_A);
var up:int=Key.isDown(KEY_UP) | Key.isDown(KEY_W);

// rotation
ar=Number(right - left) * 0.5; //ar=(((inkey & 4)>>2) - (inkey & 1))*0.5;
vr+=ar;
r+=vr;
ar*=.96;
vr*=.92;
(r < 0) ? r+=360 : (r > 360) ? r-=360 : 0;

// move
a=-Number(up) * 1.2; //a = -((inkey & 2)>>1)*1.2;
ax=a * Math.sin((360 - r) * D2R);
ay=a * Math.cos((360 - r) * D2R);
vx+=ax;
vy+=ay;
x+=vx;
y+=vy;
ax*=.96;
ay*=.96;
vx*=.90;
vy*=.90;


이렇게 함으로써 가독성 향상도 되고 운영체제에 따른 다른 키코드에 대한 대응도 되겠네요. 

물론 KEY_DOWN, KEY_UP등은 Keyboard.DOWN, Keyboard.UP값으로 정의했습니다. 

사실... Key.isDown($keyCode:int) 대신 Key.isDown(...args)로 하려고 했는데... 이게 문제인것이 |연산인지 &연산인지 호스트 코드를 작성하는 입장에서는 저 함수명으로는 알기 힘들어지고... 사실 이런 요구가 계속 넘쳐날것 같아서 결국 가장 간단하게 만들어놓고 필요하면 그때그때 대응하는 편이 훨씬 좋다는 판단을 했습니다.

아무튼 좋은거 배웠습니다. 감사합니다.

결과 : http://wonderfl.net/code/23e1ea553ffa7994af88933059c1a55b04e683eb


참고
원더플(wonderfl.net)
마우스/키보드의 인터렉션 최적화 2/2
키코드값 표
Flash 속도 개선을 위한 실험 - 10만개 입자 유체 시뮬레이션 연장전!
원더플(Wonderfl)을 이용해 ActionScript 3.0을 공부하자. - 자동 테스트 환경 구축 소개

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

인터넷 상에 멋진 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)
이전에 "10만개 입자를 이용한 유체 시뮬레이션 실험"이라는 제목으로 글을 적었다. 이 글로 10만개의 1x1 픽셀의 입자를 되도록 빠르게 렌더링하기 위한 기술을 습득할 수 있었다.

이번에는 조금 더 실용적으로 접근한다. 이 실험은 입자 유체 시뮬레이션의 연장전으로 입자대신 화살표를 이용한다. 소개하고자 하는 것은 원래 일본의 Yasu 아이디를 가진 개발자가  Wonderfl에 단순한 학습자료 용도로 올린 소스였다. 이 소스는 계속 Fork(원본소스를 퍼가서 수정하는 작업)가 거듭됨에 따라 매우 흥미롭고 재미있는 실험으로 탈바꿈하기 시작했다. 수십번의 Fork와 수백번의 favorite로 지정된 이 실험은 필자로 하여금 Flash 속도 개선에 대한 아이디어를 얻을 수 있다는 기대를 가지게 하기에 충분했다.


최종 실험 결과 화면



이 글은 Yasu님의 블로그에 소개된 글을 다시 한번 검토해보고 분석하는 정도이다.
(참고로 Yasu님은 Wonderfl과 블로그에서 clockmaker 아이디로 활동중이며 꽤 재미있는 컨텐츠를 많이 생산하고 있다.)

앞으로 소개할 내용은 아래 첨부파일내 ActionScript 3.0 코드를  참고하길 바란다.  Flash Builder 또는 Flash CS4에서 Flash Player 10기반으로  테스트하면 되겠다. 





BitmapData 배열을 활용한 속도 증가

Yasu님은 그의 블로그를 통해 Flash 속도 개선을 위한 매우 좋은 아이디어를 알려주었다.

[다음 Flash의 속도를 체험] BitmapData를 배열에 저장하면 2 ~ 3 배 속도 - 일본어 번역기를 통해 보세요.


이 실험은 2가지 경우의 예를 들고 있다. (첨부파일의 bitmap_arrow_01.as, bitmap_arrow_02.as를 참고)

  1. 화살표를 정해진 수만큼 Sprite로 생성하고 회전/이동 처리 
  2. 화살표의 모양을 회전값에 따라 BitmapData로 만들어 배열에 저장한 뒤, 정해진 수만큼 화살표를 렌더링할 Bitmap을 만든다. 1번과 같이 Bitmap의 위치를 지정하되 화살표의 방향은 이미 만든 BitmapData를 이용하여 회전된 모양 처리

(Wonderfl이 아닌 첨부파일 소스에서)
1번 예제는 화살표 500개로 FPS(frame/seconds)가 50정도의 속도가 나온다. 2번예제는 화살표 3000개로 FPS 50정도 나온다. 같은 FPS이지만 화살표를 더 렌더링할 수 있는 2번 예제의 경우가 훨씬 빠르다는 것을 알 수 있다. (물론 FPS는 개인 PC사양에 따라 다르게 나온다.)

일반적으로 많은 Flash 입문자가 처음 접하는 예제는 1번 일것이다. 하지만 화살표 모양의 Vector 데이터가 500개나 되기 때문에 회전, 이동때 마다 많은 수의 Vector정보를 렌더링하기 위해 계산이 많아질 수 밖에 없다. 2번 예제의 경우에는 이 문제를 해결하기 위해 화살표 회전값에 따라 미리 BitmapData를 만들어 놓고 실제 렌더링시에는 회전각도에 따라 선별적으로 미리 만든 해당 화살표 BitmapData를 가져와 사용할 수 있도록 만들었다. 일단 이렇게 하면 렌더링을 위한 계산량이 급격하게 떨어지므로 FPS 증가를 도모할 수 있게 된다.

이것만 보더라도 1번 예제처럼 만들고 Flash는 너무 느려요 라고 말하시는 분들이 있다면 반성해야한다. 어떤 언어로 만들던지 1번처럼 만들면 당연히 느려진다. 이것은 필요하다면 속도 개선을 위한 노력이 필요하다는 것을 말해주는 단적인 아주 좋은 예제이다. ^^


Fork! Fork!

Wonderfl 사이트의 강점 중에 하나가 바로 Fork 기능이다. 이를 이용해 다른 사람이 만들어 놓은 ActionScript 코드를 더욱 개선하거나 발전시킨 코드로 만들 수 있고 공유할 수 있다. 실제로 이 기능을 통해 Yasu님이 올려놓은 코드는 바로 다른 개발자들의 입맛에 맛게 다음과 같은 암묵적인 약속에 의해 변경되기 시작했다. 

  1. 속도에 따라 화살표 색이 달라진다.(빠를수록 빨강, 느릴수록 파랑)
  2. 속도가 빠른 화살표가 가장 화면 상단에 배치된다. 
  3. 위 조건으로 했을 때 최대 속도를 내도록 코드를 갱신한다.

즉, 위 조건은 보는 이로 하여금 더욱 예쁘고 멋진 효과를 보여주도록 하는 구체적인 목표인 것이다. 

이 시발점은 Wonderfl 아이디 keno42 님으로부터 시작한다.

속도개선 전, 색깔을 입힌 화살표 모습


- 화살표의 속도에 따라 색과 레이어를 달리 지정하도록 수정 (첨부파일의 bitmap_arrow_04.as 참고)

확실히 밋밋했던 화살표가 색이 들어가니 보기가 좋아졌다. 하지만 이에 따라 FPS가 급격하게 떨어지게 되었다. 가장 큰 이유는 2번 조건때문에 화살표의 레이어 위치를 수시로 변동시켜 주는 아래와 같은 코드가 추가되었기 때문이다.

arrow.parent.removeChild(arrow);
worldAlphaChildren[speed].addChild(arrow);	

이 때문에 FPS가 급격히 줄어들어 앞선 실험에서 사용된 화살표 3000개를 1000개로 줄일 수 밖에 없었다. 이렇게 해서야 FPS 57정도 나왔다. 


(참고)Fork된 코드는 diff기능으로 비교하자.


Wonderfl의 강력한 기능중에 하나는 Fork된 원본코드와 Fork를 통해 수정된 코드를 비교할 수 있다는 것이다. 위 Wonderl의 캡쳐 화면에서 볼 수 있듯이 Fork한 코드는 forked from과 원래 제작자 및 코드의 제목이 나와 있고 그 옆에 diff(104)가 표시되어 있다. 이 말은 이 소스가 원본 소스와 104개 줄이 추가/삭제/수정 되었다는 것을 의미한다. 이것을 클릭하면 다음과 같은 화면이 나온다. 


이것을 사용하면 어느부분이 수정되었는지 바로 알 수 있기 때문에 매우 유용하다.


속도개선 1 (bitmap_arrow_05.as 참고)

첫번째로 생각한 것은 마우스 이벤트 전파부분이였다. 빠른 속도를 가진 화살표가 위로 올라오게 하는 조건을 만들기 위해 앞선 코드에서는 단계별 레이어를 만들었다. 이 레이어는  Sprite로 만들어졌다. Sprite는 DisplayObjectContainer를 확장한 클래스로 자식을 가질 수 있는 시각객체를 렌더링하는데 기본이 되는 클래스중 하나이다. 시각객체들은 자식과 부모관계(addChild()에 의해)를 가지게 되면 이벤트 전파 메커니즘에 따라 이벤트가 전파된다. 그중에 마우스 이벤트가 대표적이다. 하지만 이 이벤트 메커니즘은 Flash 속도 저하에 큰 요소가 되기도 한다. 그러므로 이벤트 전파가 필요 없는 곳에는 사용하지 않도록 강제로 설정해줄 필요가 있다. 그 역할을 하는 것은 Sprite속성중에 mouseChildren, mouseEnabled 이다. 화살표가 올라갈 부모로서의 Sprite 레이어들은 어떠한 마우스 이벤트를 받을 필요가 없기 때문에 이들 속성을 모두 false로 지정하도록 한다. 이 설정으로 약간의 FPS개선이 있었다. 

두번째로 arrow.parent.removeChild(arrow); 부분을 삭제하는 것이였다. 시각객체는 2개 이상의 부모가 존재할 수 없다. 그러므로 otherParent.addChild(arrow); 하는 것만으로 arrow.parent.removeChild(arrow)가 이미 실행된 것이다. 이는 비싼 실행 비용을 지불하므로 제거하면 확실히 속도 개선이 된다. 실제로 FPS가 70이상으로 개선되며 거의 20이상 증가하게 되었다. 


속도개선 2 (bitmap_arrow_06.as 참고)
아직도 비싼 실행 비용을 지불하는 것이 있다. 바로 addChild()이다. 속도에 따라 레이어를 변경하더라도 굳이 변경할 필요가 없는 경우도 있다. 이러한 경우에는 선별적으로 변경되도록 해야한다. 아래 코드처럼 단순한 조건문 하나 넣어 쓸데없이 addChild를 실행하지 않게 하는 것만으로 속도 개선이 된다. 

if (arrow.parent != world.getChildAt(speed)) {
	Sprite(world.getChildAt(speed)).addChild(arrow);
}
이 작업으로 무려 FPS가 90 이상으로 상승했다. 속도개선 1 보다 약 20정도가 증가했다. 


속도개선 3 (bitmap_arrow_07.as 참고) 
속도개선 2에서 getChildAt()하는 것은 그리 빠른 방법이 아니다. 그래서 Array를 이용해 아래와 같이 바꾸게 된다.
if( arrow.parent != childrenArr[speed] )
	childrenArr[speed].addChild(arrow);	
원래 실험자는 개선이 있었다고 하나 필자는 그리 큰 개선은 못보았다.

속도개선 4 (bitmap_arrow_08.as 참고)
화살표는 정사각형 영역이 아닌 직사각형 영역에 그려진다. 무슨말인가? 회전된 화살표가 그려지는 영역은 항상 작아졌다가 커졌다가 한다. 이는 화살표의 영역을 담은 BitmapData가 실제 화살표 크기보다 클 수 있다는 것을 의미한다. 실제 화살표 크기보다 큰 BitmapData에서 쓸데 없는 부분을 제거하는 작업을 트리밍(trimming)이라고 한다. 이 작업으로 실제로 렌더링하는 화살표의 적당한 크기의 영역만을 가진 BitmapData를 만들기 때문에 물리적인 렌더링 시간 향상에 도움을 줄 수 있다.

while (i--) { //각도에 따라 화살표 비트맵 생성 
	matrix=new Matrix();
	matrix.translate(-11, -11);
	matrix.rotate((360 / ROT_STEPS * i) * Math.PI / 180);
	matrix.translate(11, 11);
	
	temp = new BitmapData(22,22,true,0x0);
	temp.draw(dummyHolder, matrix);

	//트리밍 처리(중심점의 조정은 하지 않는다)
	rect = temp.getColorBoundsRect(0xff000000, 0x00000000); //알파채널이 0이 아닌 사각형 이미지 경계 
	rotArr[i + k]=new BitmapData(rect.width, rect.height, true, 0x0);
	rotArr[i + k].copyPixels(temp, rect, new Point(0,0));
}

위 코드에서 보는데로 BitmapData의 getColorBoundsRect() 메소드를 이용해 트리밍 처리를 하고 있다. 이 작업으로 FPS를 거의 100까지 올릴 수 있었다.


속도개선 5 (bitmap_arrow_09.as 참고)
지금까지의 방식은 화살표 하나를 표현하는 도구로 Bitmap을 사용했다. 이것은 시각객체(DisplayObject)의 하나이다. 이 방식은 화살표 1000개를 그릴려면 시각객체 1000개가 필요하다는 말과 같다. 속도개선 1~4까지 하면서 사실 거의 최대한으로 속도개선했다. 이제 속도개선에 기대할 수 있는 마지막 단계는 바로 화살표를 렌더링하는 방법 조차도 BitmapData 를 이용하는 것이다. 이때는 1000개의 시각객체가 아니라 1000개의 위치/방향 정보를 담는 객체가 필요하므로 일단 상대적인 메모리 부담이 줄어든다. 그리고 기존 방식과 거의 동일하나 그려지는 Canvas가 이제는 Sprite를 이용한 레이어가 아니라 BitmapData 하나가 된다. 그러므로 화살표는 BitmapData의 copyPixels()메소드를 이용해 그리게 된다. 또한 화살표의 속도에 따른 위치를 정렬하기 위해서 Array.sortOn()함수를 이용한다. 

결과적으로 FPS가 120까지 상승했으며 초반에 60미만이였던 것에 비해서 거의 2배이상 빨라졌다. 그뿐인가? ColorTransform적용으로 화살표 궤적도 보여줄 수 있게 되었다. 

속도개선 최종화면



실행 : http://wonderfl.net/code/834201fba6c3562ca6fd7a0f1f229138b263b446 


조금더 예쁘게!!!!
지금까지 속도개선은 모두 Wonderfl의 Fork기능을 통해 각각 다른 사람들에 의해 구현된 것들이다. 최초에 화살표를 이용한 예제를 만든 Yasu님은 자신의 블로그에 지금까지 필자가 다루웠던 내용을 요약하면서 마지막으로 한번더 Fork를해 더 예쁘게 보여주기 위한 코드로 수정했다. 

마지막으로 더 멋진 화면을 만들기 위한 작업 결과




분명히 이전 코드와 속도상에 차이는 없으면서 더욱 아름다운(?) 결과물을 도출해냈다. 

그리고 1000개의 화살표가 아닌 3000개로 바꾸어도 FPS가 40이상 나왔다. 


정리하며
1픽셀짜리 데이터를 다룰때는 10만개나 다루었다가 그와는 상대적으로 너무 적은 3000개 화살표 렌더링하는 것으로 전환되면서 너무 숫자가 급감하는 것처럼 보일 수 있다. 하지만 실제로 렌더링하는 데이터 요소는 10만픽셀 이상으로 많은 데이터를 다루는 것이다.(조금만 생각하면 감이 올거다.)  렌더링 자원 숫자를 줄이는 것도 중요하지만, 그 자원이 어떤 조건의 것인가와 어떻게 렌더링 할 것인가도 역시 중요하다. 결국, 화면 렌더링을 어떻게 하느냐에 따라 Flash 애플리케이션의 속도와 밀접한 관계가 있는 것이다. 실무에서 제약된 환경의 디바이스나 서비스의 경우라면 이러한 노력은 필수이다. 

이 글에 소개된 아이디어는 정답은 아니다. 더 분석하고 더 최적화할 여지가 있다. 

Wonderfl은 아주 고급의 스킬을 다루지는 않지만 적어도 중고급 실력에 도달하기 위한 예제를 찾는데는 이만한 곳이 없다고 생각한다. 필자는 이곳에 올라온 코드들을 보면서 수없이 감격하고 있다. 

마지막으로 라이브 코딩을  SNS 영역으로까지 승격한 Wonderfl을 이용해 다양한 실험들을 하는 일본의 개발 문화를 보면서 항상 감탄한다. 아무쪼록 이러한 일본의 개발 문화가 한국에도 정착되길 희망한다. 


글쓴이 : 지돌스타(http://blog.jidolstar.com/671)
2009년 봄, 일본에서 ActionScript 개발자들간에 재미있는 실험이 있었다. 그것은 "Flash속도를 극대화하기" 실험중 하나로 수만개의 입자를 가지고 가상의 유체 시뮬레이션을 보여주는 예제를 만드는 것이였다.

수만개의 입자를 이용한 유체 시뮬레이션 실행


위 화면은 최초실험대상이 된 1만개 입자 유체 시뮬레이션 코드의 실행화면이다.

아래 소스는 위에서 소개된 소스를 10만개 입자로 수정하고 최대 frameRate를 110으로 조정한 것이다. 또한 마우스 클릭으로 초기화 하는 대신 500ms단위로 유체의 이동패턴과 전체색이 조금씩 변하도록 수정했다.

package {
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	import flash.utils.*;
	
	import net.hires.debug.Stats;
	
	[SWF(width="465", height="465", backgroundColor="0x000000", frameRate="110")];
	/**
	 * BitmapData를 이용한 파티클 렌더링 속도 테스트 
	 * @see http://clockmaker.jp/blog/2009/04/particle/
	 */ 
	public class bitmap_liquid100000 extends Sprite {
		
		private const nums:uint=100000;
		private var bmpDat:BitmapData;
		private var vectorDat:BitmapData;
		private var randomSeed:uint;
		private var bmp:Bitmap;
		private var vectorList:Array;
		private var rect:Rectangle;
		private var cTra:ColorTransform;
		private var vR:Number;
		private var vG:Number;		
		private var timer:Timer;
		
		public function bitmap_liquid100000() {
			initialize();
		}
		
		private function initialize():void {
			//stage 관련의 설정
			stage.align=StageAlign.TOP_LEFT;
			stage.scaleMode=StageScaleMode.NO_SCALE;
			stage.frameRate=110;
			
			//파티클이 렌더링 되는 메인 Bitmap. 
			bmpDat=new BitmapData(465, 465, false, 0x000000);
			bmp=new Bitmap(bmpDat);
			addChild(bmp);
			
			//파티클의 가속도를 계산하기 위한 도움 BitmapData로서 perlinNoise가 적용된다. 
			vectorDat=new BitmapData(465, 465, false, 0x000000);
			randomSeed=Math.floor(Math.random() * 0xFFFF);
			vectorDat.perlinNoise(230, 230, 4, randomSeed, false, true, 1 | 2 | 0 | 0);
			//addChild(new Bitmap(vectorDat)); //만약 이 perlinNoise가 적용된 Bitmap을 보고 싶다면 주석을 풀자 
			
			//화면크기 
			rect=new Rectangle(0, 0, 465, 465);
			
			//파티클 궤적을 그리기 위함 
			cTra=new ColorTransform(.8, .8, .9, 1.0);
			vR = 0;
			vG = 0;
			
			//파티클을 넣기 위한 List
			vectorList=new Array();
			for (var i:uint=0; i < nums; i++) {
				//파티클 위치
				var px:Number=Math.random() * 465;
				var py:Number=Math.random() * 465;
				var pv:Point=new Point(px, py);
				//파티클 가속도
				var av:Point=new Point(0, 0);
				//파티클 속도 
				var vv:Point=new Point(0, 0);
				//파티클 위치,가속도,속도 정보를 List에 저장 
				var hoge:VectorDat=new VectorDat(av, vv, pv);
				vectorList.push(hoge);
			}
			
			//지속적인 파티클 렌더링을 위한 loop 함수 호출 
			addEventListener(Event.ENTER_FRAME, loop);
			
			//500ms마다 
			timer = new Timer(500, 0);
			timer.addEventListener(TimerEvent.TIMER, resetFunc);
			timer.start();
			
			//통계 
			addChild(new Stats);
		}
		
		private function loop(e:Event):void {
			//렌더링용 BitmapData를 colorTransform로 어둡게 하여 기존에 그려진 파티클의 궤적을 보이도록 함 
			bmpDat.colorTransform(rect, cTra);
			
			//파티클의 위치를 재계산하여 렌더링한다.
			var list:Array=vectorList;
			var len:uint=list.length;
			for (var i:uint=0; i < len; i++) {
				var dots:VectorDat=list[i];
				var col:Number=vectorDat.getPixel(dots.pv.x, dots.pv.y);
				var r:uint=col >> 16 & 0xff;
				var g:uint=col >> 8 & 0xff;
				dots.av.x+=(r - 128) * .0005; //적색을 x축 가속도로 사용
				dots.av.y+=(g - 128) * .0005; //녹색을 y축 가속도로 사용
				dots.vv.x+=dots.av.x;
				dots.vv.y+=dots.av.y;
				dots.pv.x+=dots.vv.x;
				dots.pv.y+=dots.vv.y;
				
				var _posX:Number=dots.pv.x;
				var _posY:Number=dots.pv.y;
				
				dots.av.x*=.96;
				dots.av.y*=.96;
				dots.vv.x*=.92;
				dots.vv.y*=.92;
				
				//stage 밖으로 이동했을 경우 처리. 3항 연산자 처리함 
				(_posX > 465) ? dots.pv.x=0 : (_posX < 0) ? dots.pv.x=465 : 0;
				(_posY > 465) ? dots.pv.y=0 : (_posY < 0) ? dots.pv.y=465 : 0;
				
				//1*1 pixel을 bitmapData에 렌더링 
				bmpDat.fillRect(new Rectangle(dots.pv.x, dots.pv.y, 1, 1), 0xFFFFFF);
			}
		}
		
		private var seed:Number = Math.floor( Math.random() * 0xFFFF );
		private var offset:Array = [new Point(), new Point()];
		private function resetFunc(e:Event) :void{
			//파티클의 가속도를 계산하기 위한 도움 BitmapData로서 perlinNoise를 변경 
			vectorDat.perlinNoise( 230, 230, 3, seed, false, true, 1|2|0|0, false, offset );
			offset[0].x += 20;
			offset[1].y += 20;
			
			//파티클 궤적을 표시하기 위한 부분을 변경  (조금씩 색변동이 일어난다)
			var dots:VectorDat = vectorList[0];
			vR += .001 * (dots.pv.x-232)/465;
			vG += .001 * (dots.pv.y-232)/465;
			( vR > .01 ) ? vR = .01:
				( vR < -.01 ) ? vR = -.01:0;
			( vG > .01 ) ? vG = .01:
				( vG < -.01 ) ? vG = -.01:0;
			
			cTra.redMultiplier += vR;
			cTra.blueMultiplier += vG;
			( cTra.redMultiplier > .9 ) ? cTra.redMultiplier = .9:
				( cTra.redMultiplier < .5 ) ? cTra.redMultiplier = .5:cTra.redMultiplier;
			( cTra.blueMultiplier > .9 ) ? cTra.blueMultiplier = .9:
				( cTra.blueMultiplier < .5 ) ? cTra.blueMultiplier = .5:cTra.blueMultiplier;         
		}
	}
}

import flash.geom.Point;

class VectorDat {
	public var vv:Point;
	public var av:Point;
	public var pv:Point;
	
	function VectorDat(_av:Point, _vv:Point, _pv:Point) {
		vv=_vv;
		av=_av;
		pv=_pv;
	}
}

처음 이 소스를 본다면 뭔가 굉장히 복잡할 것이라고 생각할지 모른다. 지금껏 이런 것을 구현하기 위해 ActionScript 3.0의 DisplayObject객체 기반으로 입자를 구현하는 것을 먼저 생각한 분이라면 위 소스가 더 어렵게 느껴질 것이다. 참고로 만약 10만개의 입자를 전부 DisplayObject 객체로 구현하면 Flash Player는 제대로 작동도 못하고 중지될 것이다. DisplayObject와 그것을 확장한 화면 표시 객체는 다소 무겁다. Flash Player가 이런 무거운 객체를 10만개나 frameRate가 24 수준으로 그리는 것은 일반 보급 컴퓨터에서는 불가능하다.

위 소스를 잘보면 알겠지만 입자를 표현하기 위해 BitmapData(bmpDat변수 참고)를 이용한다. BitmapData 하나에 모든 입자 정보가 표현되도록 하는 것이 첫번째 아이디어이다. 이렇게 함으로써 10만개 입자 렌더링이 가능해진다.

처음 실행하면 무작위로 지정된 위치에 입자들이 배치되었다가 무작위 궤적을 따라 이동하는 것을 볼 수 있다. 이러한 이동이 뭔가 엄청나게 고난이도 물리엔진  알고리즘이 들어간 것 같지만 실상은 전혀 아니다. 입자의 위치를 이용해 가속도와 속도를 계산하는데 사용된 것은 다름 아닌 BitmapData(vectorDat변수 참고)이다. 이 BitmapData는 실제화면에는 눈에 보이지 않는다.  대신 가속도와 속도를 계산하기 위해 BitmapData에 PerlinNoise를 주어 입자의 위치정보에 해당하는 색정보를 얻어 색의 RGB값중 R값은 x축 가속도값으로 G값은 y축 가속도 값으로 참조하도록 구현되어 있다. PerlinNoise가 적용된 BitmapData는 아래와 같다. 실제로 이 화면을 보고 싶다면 위 코드의  "//addChild(new Bitmap(vectorDat))" 부분을 찾아 주석처리를 삭제하고 실행해 보면 된다.  

필자는 개인적으로 PerlinNoise 대신이 뭔가 물리학적 정보가 들어간다면 훨씬더 실용성 있는 재미난 교육용 자재로 만들 수 있지 않을까 생각했다.

입자의 가속도 계산을 위한 PerlinNoise가 적용된 BitmapData의 모습. 500ms단위로 계속 변한다.


BitmapData에 입자들의 위치를 갱신하는 것까지는 알겠는데... 입자들이 특정한 궤적을 남기도록 하는 효과는 도데체 어떻게 하는 것일가? 따로 궤적을 다루는 Array나 List가 있는 것일까? 아니다. ColorTransform 클래스에 비밀이 숨겨져 있다. loop() 함수를 보면 bmpDat.colorTransform(rect, cTra); 부분이 있다 .rect은 colorTransform을 적용할 범위이고 cTra가 적용할 ColorTransform객체이다. 이 객체에는 cTra.redMultiplier, cTra.greenMultiplier, cTra.blueMultiplier 속성등이 있다. 이 값은 기존 BitmapData에 RGB값을 해당값을 정해진 값으로 곱해주는 역할을 하는데 가령 cTra.redMultiplier가 0.8이면 loop를 돌때마가 Red값이 0.8배로 감소하게 된다. 결국 입자의 흰색(0xffffff)로 BitmapData에 찍히면 cTra.redMultiplier에 의해 계속 어두워지다가 결국 검은색이 된다. 이러한 원리로 궤적이 남게 되는거다.

하지만 위 코드는 문제가 있다. 실제로 실행해보면 필자의 컴퓨터에서는 frameRate가 10도 안나왔다. 최대치 110을 주었음에도 이 정도 밖에 안나왔다는 것에 대해 개발자로서 계산 및 렌더링 최적화에 관심을 자연스럽게 가지게 된다.

일본 개발자들은 Wonderfl의 "Fork"기능을 이용해 서로 소스를 공유하며 위 코드의 문제를 계속 개선해 나갔다. 

아래 압축파일은 필자가 여러 단계를 거쳐서 실험했던 코드를 압축한 것이다.



개선사항 1. BitmapData의 lock()과 unlock() 함수 추가 (bitmap_liquid100000_01.as)
BitmapData가 Bitmap을 통해 화면에 이미 렌더링 자원으로 활용되고 있다면 BitmapData가 수정될 때 마다 Bitmap에도 갱신한다.(즉, addChild(new Bitmap(bitmapData))일때를 의미한다.)  구체적으로 설명하자면 입자가 10만개 이므로 입자의 위치가 바뀔때마다 10만번의 BitmapData를 수정하면 그때마다 Bitmap에도 영향을 준다. 만약 BitmapData을 수정하고 난 다음에 마지막에 Bitmap에 적용될 수 있도록 하면 속도개선에 도움을 줄 것이다. 이것을 가능하게 하는 함수가 BitmapData의 lock(), unlock()함수이다. 위 코드에서 loop()함수에 맨처음과 맨끝부분에 각각 bmpDat.lock(), bmpDat.unlock()을 추가하자. 이 작업으로 전체적으로 2~3 frameRate 개선을 볼 수 있었다. frameRate가 이 실험에서는 크게 개선되지 않았지만 만약 BitmapData에 이보다 더욱 복잡한 수정이 있거나 더 크기가 크면 클수록 그 차이가 더 할 것이다.
private function loop(e:Event):void {
	bmpDat.lock();
	(생략)
	bmpDat.unlock();
}



개선사항 2. fillRect을 setPixel로 변경 (bitmap_liquid100000_02.as)
위 코드에서 가장 잘못된 부분중에 하나가 1px짜리 입자를 렌더링하기 위해 fillRect()을 이용했다는 것이다. 이 함수 대신 setPixel()로 수정한다. 이 작업으로 25~30 frameRate 개선을 했다. 여기까지만 하더라도 상당한 진전이다.

개선사항 3. point 대신 number로 (bitmap_liquid100000_03.as)
위 코드에서 비효율적인 코드부분이 있다. 바로 입자의 정보를 담은 VectorDat부분이다.

import flash.geom.Point;

class VectorDat {
	public var vv:Point; //속도
	public var av:Point; //가속도
	public var pv:Point; //위치 
	
	function VectorDat(_av:Point, _vv:Point, _pv:Point) {
		vv=_vv;
		av=_av;
		pv=_pv;
	}
}

위 클래스는 입자의 위치,속도,가속도 정보를 Point를 이용해 만들었다. 이렇게 하게 되면 10만개나 되는 입자의 위치를 vectorDat.pv.x, vectorDat.pv.y 형태로 접근해야 한다. 이것 대신 vectorDat.px, vectorDat.py로 하면 훨씬 빠른 접근이 가능해질 것이다. 반복되는 로직에 깊은 DOM을 접근을 사용하면 그만큼 느려진다는 것을 항상 염두할 필요가 있다. 다음 코드는 위 클래스를 개선한 것인다.

class VectorDat {
	public var vx:Number = 0; //x축 속도
	public var vy:Number = 0; //y축 속도
	public var ax:Number = 0; //x축 가속도
	public var ay:Number = 0; //y축 가속도
	public var px:Number; //x축 위치 
	public var py:Number; //y축 위치 
	
	function VectorDat( px:Number, py:Number ) {
		this.px = px;
		this.py = py;
	}
}

위 클래스에 맞게 전체 소스를 수정한 뒤 실행하면 frameRate가 10이상 개선되는 것을 확인할 수 있었다. 꽤 큰 진전이다.


개선사항 4. 가속도 계산을 위한 bitmapData 크기 조정 (bitmap_liquid100000_06.as)

BitmapData의 getPixel은 BitmapData의 크기가 크면 클수록 그 효율이 떨어진다. 위 코드에서 입자의 가속도를 계산하기 위해 perlinNoise를 적용한 BitmapData(vectorDat)는 렌더링하기 위한 BitmapData(bmpDat)와 크기가 동일하다. 이 크기를 465x465 대신 28x28로 줄이고 loop()함수내에 col=vectorDat.getPixel(dots.px, dots.py)대신 col=vectorDat.getPixel(dots.px>>4, dots.py>>4)로 수정해서 테스트하면 frameRate가 위 개선사항을 모두 적용한 것보다 약 10이상 증가한다. 



지금까지 개선사항 4가지를 통해 최초 코드의 frameRate를 12에서 60~70까지 끌어올릴 수 있었다. 아래는 실행화면이다.

실행 화면


결과 보러 가기 : http://wonderfl.net/code/2e15897ec742a2e2c09255bcba35c2b20a026356


다른 실험들...

필자는 VectorDat를 담는 List의 형태를 Array에서 Vector로 바꿔서 실험을 해봤다. 이 부분에 있어서는 거의 frameRate변화가 없었다. (bitmap_liquid100000_04.as참고)

또한 List형태를 Linked List로 바꿔봤는데.. 오히려 frameRate가 감소했다.  (bitmap_liquid100000_05.as참고)

마지막으로 BitmapData의 setPixel()보다 setVector()가 더 빠르다기에 그것으로 실험해 보았다. 하지만 이 방법은 위 실험에 쓰기에 적합하지 않았다. 왜냐하면 단순히 setPixel()과 setVector()만 놓고 보았을때 setVector()가 더 빠르지만 setVector()를 사용하기 위해서는 getVector()와  같은 부가적인 코드가 더 들어가게 되었고 그 코드들로 인해 오히려 frameRate가 더 줄어들었다. 그러므로 뭔가 도입할때는 그 상황에 맞게 도입해야한다. (bitmap_liquid100000_07.as참고)


추가사항 



웹눈님께서 이글을 보고 위 프로그램을 만들었는데 mouseX, mouseY 엑세스 비용문제를 해결하지 못해 속도 저하가 일어났었다.

while(pt.next) {
   var dx:Number = pt.x - mouseX;
   var dy:Number = pt.y - mouseY;
   (중간생략)
   pt = pt.next;
}


위처럼 만드셨는데...이렇게 하면 입자(파티클)이 10만개면 10번 mouseX, mouseY를 참조하게 된다. 이 속성을 엑세스 하는 것은 꽤 비싼 자원이 소비되므로 아래처럼 하면 속도개선 된다.

var mx:Number, my:Number;
mx = mouseX;
my = mouseY;
while(pt.next) {
   var dx:Number = pt.x - mx;
   var dy:Number = pt.y - my;
   (중간생략)
   pt = pt.next;
}


테스트 결과, 저렇게 바꾸는 것만으로 FPS(frame per seconds)가 무려 40에서 90까지 개선되었다.
별거 없는데도 엄청난 속도 개선이다! 


정리하며

일본에서 진행된 이 입자 유체 시뮬레이션 실험은 꽤 인기를 얻었으며 Wonderfl 사이트의 favorite와 fork기능을 통해 계속 퍼져나갔다. 필자도 예전부터 봐왔던 실험이였으나 BitmapData 활용을  이미지 에디터 정도의 프로젝트 외에는 거의 사용을 하지 않아서 깊숙히 파고들지 않았다. 

이런 단순한 실험이 일본의 여러 Flash 개발자들 사이에 인기를 끌고 함께 코드를 수정하는 모습을 보면서 그들의 개발분위기가 무척 부러웠다. 한국에도 이런 분위기가 정착되길 바란다. 

필자는 지금도 Wonderfl을 하루에도 수십번 방문한다.


참고글
Wonderfl - build Flash online
빠른 flash 연구회를 wonderfl에서
Flash 10, Massive amounts of 3D particles with Alchemy (source included).
Massive amounts of 3D particles without Alchemy and PixelBender
Flash 10, Massive amounts of 3D Particles (with haXe)
Using BitmapData.setVector for better performance


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


Flash Player 10.1을 설치하려고 기존 Flash Player를 전부 uninstaller로 삭제한다음 Flash Player 10.1을 다운로드 받아 설치하려는데 다음과 같은 에러를 발생시키며 설치할 수 없었습니다.

설치하려고 하는 Adobe Flash Player 버전은 최신 버전이 아닙니다.

Windows 7 환경에서 인터넷 익스플로러(IE8)에 Active-x debug(개발자용) 버전을 설치가 되지 않아 하루 종일 이것까지고 씨름했습죠. 

  1. Adobe 이와 관련된 기술노트가 있어서 따라해 봤지만 안되었습니다.(관리자 권한으로 실행해도 마찬가지였습니다.)
  2. 또한 무식하게 Windows까지 새로 깔았지만 안되더군요.

그래서 마지막으로 관련 레지스트리를 삭제했습니다. 됩니다 ㅡㅡ;;;

  1. 시작 > 실행 > regedit 입력
  2. HKEY_LOCAL_MACHINE\SOFTWARE\Macromedia\FlashPlayer\SafeVersions 를 찾아 전부삭제

흑.... 어제부터 이것때문에 완전 생쑈를 했습니다. 시간 아까워.....

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


오랜만에 Adobe Labs를 가봤는데 마침 Flash Builder 4, ColdFusion Builder, Flex 4 SDK가 베타 딱지를 떼고 정식으로 배포되기 시작했네요.

  • Adobe ColdFusion Builder
  • Adobe Flash Builder™ 4
  • Adobe Flex® 4 SDK

    위 링크를 통해 자세한 정보를 볼 수 있습니다.
  • 추가사항 1
    정영훈님께서 유용한 댓글을 달아주셨습니다. 이클립스에 Flash Builder를 Plug-in 형태로 설치할 때 멈추는 현상이 있는데 아래 내용으로 문제를 해결할 수 있습니다.

    설치할때 우선 압축을 풀게 되는데 그 경로를 지정할 수가 있습니다.
    그 압축을 푸는 경로에 하글이 섞여 있으면 설치가 안됩니다.

    xp에서는 바탕화면 에 압축을 풀게 딜포트 설정으로 나옵니다
    그 경로를 한글이 없는 경로로 수정하면 설치가 가능합니다.


     

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


    Flash 애플리케이션을 제작할때 항상 고민되는 것은 메모리와 속도일 것이다. 이 문서는 이러한 고민을 했던 분들을 위해 본인의 경험을 반영하여 작성한 것이다. 주의할 것은 이 문서에서 다루는 Object Pool은 메모리, 속도 개선을 위한 하나의 방법일 뿐이지 전부는 아니다라는 것이다. Object Pool이 어떻게 당신의 애플리케이션의 속도와 메모리를 개선시켜줄 것인가 전달하는 것이 문서의 목표이다.

    목차

    1. 객체 관리에 대한 질문!
    2. new 연산자는 비싸다!
    3. Object Pool이란?
    4. Object Pool을 도입할 때 고려사항
    5. Object Pool을 사용해보자.
    6. Object Pool과 new 연산자 사용 비교
    7. 메모리 사용의 폭이 급격한 애플리케이션은 좋지 못하다.
    8. 재사용할 수 있는 클래스 제작
    9. 정리하며
    10. 참고글



    1. 객체 관리에 대한 질문!
    제작된 애플리케이션에 필요한 최소한의 메모리에 대해서 생각해 본적이 있는가? 하나의 애플리케이션에 들어가는 최소 자원(resource)은 얼마나 될까? 그 자원은 얼마나 자주 사용하고 버리는 것일까?

    슈팅 게임을 보면 동일한 적군전투기가 많이 나온다. 그 중에 적군전투기1이 있다고 하자. 적군전투기1이 한 화면에 5개가 나왔다. 아군전투기에 의해 죽든 화면에서 사라지든 어쨌든지 언젠간 화면에서 사라지고 쓸모없게 된다. 좀 있다가 또 적군전투기1이 3개가 나온다. 또 반복해서 사라진다. 당신이 이 슈팅 게임을 만든다면 적군전투기1 관리를 어떻게 할 것인가? 즉, 화면에 나타날때 new로 생성하고 사라지면 delete로 메모리에게 해지요청할 것인가? 아니면 어느 특정 영역에 화면에 나타날 전투기1을 미리 5개에서 10개 생성해놓고 필요할 때마다 재사용할 것인가?

    2. new 연산자는 비싸다!
    new는 클래스를 통해 객체를 생성하는 연산자이다. 이는 비교적 비용이 비싼편이다. 비용이 비싸다는 말을 구체적으로 설명하자면 new를 통해 객체를 생성하여 메모리에 올리고 다 사용했다고 판단될때 delete이나 null등을 이용해 객체참조를 제거하여 가비지 컬렉터에 맡기게 되며 결과적으로 가비지 컬렉터는 이 객체를 어느 특정시점에 제거하는 일련의 객체 생성과 삭제까지 동작이 꽤 복잡하여 메모리, CPU연산등의 자원소비가 크다라는 것을 의미한다. 특히나 클래스의 크기가 크고 복잡하다면 그 비용은 더욱 갑절로 들게 된다. new 연산 사용은 이처럼 비싼 비용을 지불해야 하기 때문에 할 수 있다면 최소한으로 사용하도록 하는 것이 속도와 메모리 관리에 도움을 준다.

    앞서 설명한 슈팅 게임을 보자면 적군전투기1은 수시로 보였다가 안보였다가 한다. 이 경우 new, delete를 반복한다면 경우에 따라서 엄청난(?) 비싼 비용을 지불하게 된다.

    다른 예를 들어보겠다. 본인은 얼마전에 스타플(http://starpl.com)이라는 사이트에서 제공하는 타임라인 애플리케이션을 Flash로 개발했다. 타임라인은 사용자의 기록, 앨범등을 시간순으로 보여주게 되며 좌우측으로 이동해서 내 인생의 기록을 보는듯한 느낌을 주도록 만들었다. 이때 화면에 보여지는 사용자의 기록들이 수시로 보여졌다가 사라진다. 이때 사용된 기록을 보여주는 시각객체(DisplayObject)를 new와 delete를 반복해서 생성 및 삭제한다면 너무 비싼 비용을 들이는 것과 같다.

    스타플의 타임라인

    참고글 : 스타플 타임라인 업그레이드 및 앨범 기능 추가 소식

    앞의 2가지 예에서 언급했듯이 자주 보여지고 사라지는 객체를 필요할 때마다 new연산자를 사용하는 것보다는 어느 특정 영역에 미리 생성해놓고 재사용을 반복하는 구조를 도입한다면 메모리와 CPU연산에 있어서 큰 비용절감을 기대할 수 있게 된다.

    3. Object Pool이란?
    Object Pool(객체 풀)은 객체를 미리 만들어놓고 담아놓는 메모리 영역이다. 개발자는 이 Object Pool에 미리 만들어진 객체를 사용하고 다 사용한 뒤에는 다시 반환시킴으로서 재사용(또는 대여)을 하도록 한다. Object Pool을 사용하면 처음 객체를 생성할때와 더 필요한 객체가 있는 경우를 제외하고 new 연산을 최소화 할 수 있다.

    4. Object Pool을 도입할 때 고려사항
    Object Pool은 미리 객체를 생성하고 지속적으로 재사용하기 위해 사용한다. 이때 미리 객체를 생성한다는 것은 초반 애플리케이션 동작 퍼포먼스에 영향을 미칠 수 있다.

    Object Pool을 사용했을때 다음과 같은 장점을 얻을 수 있다.

    1. new 연산자를 자주 사용해야하는 경우에 도입하면 애플리케이션 속도 향상을 기대할 수 있다.
    2. 가비지 컬렉터에 의존하지 않는 메모리 관리를 꾀할 수 있다.

    하지만 일반적으로 10개가 필요한데 미리 100개를 만들어 항상 10% 미만 정도의 재사용률을 보인다면 그것은 메모리 낭비이다. 그러므로 다음과 같은 조건에서 Object Pool을 사용하고 관리하는 것이 좋겠다.

    1. 비교적 큰 용량의 객체를 자주 사용되거나 반환해야하는 경우에 사용한다.
    2. 미리 생성할 객체를 적당하게 정하도록 한다.

    반대로 위 경우가 아니라면 쓸데없이 Object Pool을 사용하지 말자.


    5. Object Pool을 사용해보자.

    폴리고널 lab에서 간단한 Object Pool을 공개했다. 아래 링크에서 다운로드 받을 수 있다.

    Download: ObjectPool_v1.1.zip

    사용 방법은 매우 간단하다. ObjectPool 클래스는 아래 코드처럼 특정 클래스를 할당(allocate)하면 된다.

    var isDynamic:Boolean = true;

    var size:int = 100;

    var pool:ObjectPool = new ObjectPool(isDynamic);

    pool.allocate(MyClass, size);


    isDynamic 플래그는 Object Pool내의 객체가 비어있는가 체크하기 위해 사용한다. 즉, true이면 Pool은 자동적으로 정해진 크기(size)를 넘어서더라도 Pool의 크기를 확장하여 새로운 객체를 줄 수 있으나 false이면 정해진 크기만큼 객체를 이미 준 경우에는 Pool이 비게 되므로 Error를 발생시킨다. 

    크기(size)를 100으로 지정했기 때문에 미리 Object Pool에 100개의 MyClass의 객체를 생성해 두도록 한다.

    이제부터 MyClass는 아래와 같은 방식으로 사용하면 되겠다.

    myObjectArray[i] = new MyClass();

    // 코드 대신 아래를 사용한다.

    myObjectArray[i] = pool.instance;


    다 사용한 MyClass객체는 Object Pool에 반환해줘야 한다. 이때 가비지 컬렉터의 기능을 이용하는게 아니라 재사용(recycle)을 위해서 다음과 같이 코딩하면 되겠다.

    pool.instance = myObjectArray[i];

    사용방법은 이게 전부다.

    원한다면 자신의 애플리케이션에 맞게 이 공개된 ObjectPool을 개선시킬 수 있다. 가령, 100개의 Pool을 만들어놓지만 처음부터 100개를 생성해놓지는 않겠고 필요할 때마다 생성해주는 구조인 게으른(lazy) 생성을 기대할 수 있을 것이다. 또는 DisplayObject의 경우라면 사용한다는 것을 DisplayObject.parent 속성으로 판단할 수 있다. 위 처럼 pool.instance = '다 사용한 객체'와 같은 형태로 반환하는 것보다 자신의 부모가 존재하지 않는다면 재사용할 객체로 판단하도록 만들면 일종의 자동 Object Pool을 만들 수 있다. 자동 Object Pool은 다음 링크를 참고하면 좋겠다.

    자동 풀링(auto pooling)을 구현해보자.



    6. Object Pool과 new 연산자 사용 비교

    Object Pool을 사용해야하는 타당성은 퍼포먼스 실험을 통해 쉽게 알 수 있다.  이러한 점에서 폴리고널 랩에서 제공하는 실험은 매우 유용한 자료이다.

    실험 방법은 아래와 같다. (폴리고널 랩에서는 이 실험을 Window Vista, Flash Player 9.0.124에서 진행했다.) 그리고 Pool크기는 100으로 지정했다.

    첫번째로 아래 코드처럼 k수 만큼 Object Pool로 부터 객체를 get만 한다.

    for (var i:int = 0; i < k; i++) instances[i] = p.instance;


    두번째로 아래 코드처럼 k수 만큼 Object Pool로부터 객체를 get/set 한다.

    for (i = 0; i < k; i++) instances[i] = p.instance;

    for (i = 0; i < k; i++) p.instance = instances[i];


    세번째로 아래 코드처럼 k수 만큼 Object Pool대신 new 연산자를 사용한다.

    for (i = 0; i < k; i++) instances[i] = new MyClass();



    결과는 다음과 같다.


    네이티브 Object 클래스의 경우 Object Pool을 사용하는 것이 new 연산자를 사용한 것보다 5배 정도 빨랐다.


    Object 보다 더 크고 복잡한 flash.geom.Point의 경우는 약간더 빠른 결과를 보인다.


     flash.display.Sprite 객체를 가지고 하면 무려 80배나 속도 개선이 되었다. 이는 복잡하고 큰 클래스에 Object Pool을 도입하면 효과적일 수 있다고 판단할 수 있다.

    이 실험을 통해 적절한 방법으로 Object Pool을 사용하는 것은 애플리케이션의 퍼포먼스 증가에 지대한 영향을 줄 수 있다는 것을 깨달을 수 있다.


    7. 메모리 사용의 폭이 급격한 애플리케이션은 좋지 못하다.
    본인은 어떤 애플리케이션(Flash가 아니더라도)이든지 그 애플리케이션에 필요한 최소한의 메모리는 항상 존재한다고 생각한다. 무조건 메모리 사용을 줄여보겠다고 new, delete를 반복하는 것은 잘못된 것이다. new, delete를 반복하면 오히려 CPU 및 메모리 사용의 반복을 부축이며 애플리케이션의 전체적인 퍼포먼스를 저하시키는 경우가 발생할 수 있다. 실제로 new,delete를 반복하는 구조로 개발한 애플리케이션을 Flash Builder의 프로파일링 기능을 통해 메모리 사용을 살펴보면 큰폭으로 메모리 사용/해지가 일어난다는 것을 확인할 수 있다. 그러므로 몇몇 사람들이 Object Pool과 같은 개념과 같이 퍼포먼스 향상에 도움이 되는 개념을 전혀 사용하지 않고 Flash는 느리고 가비지 컬렉터 동작은 비정상적이다라고 말하는 것은 섣부른 판단이다라고 생각한다. 얼마든지 메모리를 효율적으로 사용하고 속도 개선을 할 수 있음에도 불구하고 잘못된 개발 방식때문에 전체적인 퍼포먼스가 나빠지는 것은 결국 개발자의 잘못인 것이다. 이는 Flash든 어떤 언어든 동일한 생각으로 접근해야 한다.

    Flash Builder의 프로파일링 기능을 이용해 Object Pool과 new 연산자간의 메모리 변화를 확인할 수 있는 간단한 실험을 해보겠다. (프로파일링 기능을 사용하는 방법은 주제에서 벗어나므로 제외하겠다.)

    먼저 Shape객체를 확장해서 크기와 색이 렌덤하게 그려지는 클래스를 만든다.

    //화면에 출력할 Shape

    class RandomShape extends Shape {

            static private var colorPool:Array = [0xff0000,0x00ffff,0x0000ff,0x00ff00,0x0f0f0f,0xf0f0f0,0xffff00,0xf00cf0,0x00fcaa,0xff0c9a];

            public function RandomShape():void {

            }

            public function draw():void {

                   var radius:Number = Math.random() * 40;

                   var color:uint = colorPool[ Math.floor(Math.random()*9) ];

                   graphics.clear();

                   graphics.beginFill(color,1.0);

                   graphics.drawCircle( 0, 0, radius );

                   graphics.endFill();

            }

    }


    위 클래스를 가지고 자동 Pool을 구현한 클래스를 만든다.(참고 : http://www.diebuster.com/?p=1000)

    //Shape 자동 객체 Pool

    class RandomShapeAutoPool {

            private var _pool:Vector.<RandomShape>;

            public function RandomShapeAutoPool() {

                   //적당히 수로 초기화한다.

                   _pool=new Vector.<RandomShape>();

            }

     

            public function getShape():RandomShape {

                   var key:*, result:RandomShape;

                   //먼저 기존의 pool에서 찾아본다.

                   for (key in _pool) {

                           //만약 해당 객체가 null이라면 new 통해 생성한다.

                           if (_pool[key] === null) {

                                  _pool[key]=new RandomShape;

                                  result=_pool[key];

                                  break;

                           //null 아니라면 parent 조사하여 사용가능한지 판단한다.

                           } else if (_pool[key].parent === null) {

                                  result=_pool[key];

                                  break;

                           }

                   }

                   //기존의 pool안에서 쓸만한 찾지 못했다면

                   if (result === null) {

                           result=new RandomShape;

                           _pool[_pool.length]=result;

                   }

                   //인스턴스를 반환한다.

                   return result;

            }

    }


    이제 테스트할 수 있는 호스트코드를 제작해보겠다.

    package {

            import flash.display.*;

            import flash.events.*;

            import flash.text.*;

            import flash.utils.*;

     

            [SWF(backgroundColor="#ffffff", frameRate="60", width="400", height="400")]

            public class ObjectPoolTest extends Sprite {

                   private var pool:RandomShapeAutoPool=new RandomShapeAutoPool;

                   private var poolFlag:Boolean = false;

                   private var textPoolFlag:TextField;

                   private var shapeCanvas:Sprite;

     

                   public function ObjectPoolTest() {

                           addEventListener(Event.ADDED_TO_STAGE, ADDED_TO_STAGE);

                   }

     

                   private function ADDED_TO_STAGE($e:Event):void {

                           removeEventListener(Event.ADDED_TO_STAGE, ADDED_TO_STAGE);

                           stage.scaleMode=StageScaleMode.NO_SCALE;

                           stage.align=StageAlign.TOP_LEFT;

                           //rf)http://blog.jidolstar.com/656

                           if (stage.stageWidth === 0 && stage.stageHeight === 0) {

                                  stage.addEventListener(Event.ENTER_FRAME, function($e:Event):void {

                                                 if (stage.stageWidth > 0 || stage.stageHeight > 0) {

                                                         stage.removeEventListener($e.type, arguments.callee);

                                                         init();

                                                 }

                                          });

                           } else {

                                  init();

                           }

                   }

     

                   private function init():void {

                           //shape 부모객체

                           shapeCanvas = new Sprite;

                           addChild( shapeCanvas );

                           //PoolFlag 상태표시

                           textPoolFlag = new TextField();

                           textPoolFlag.text = "poolFlag=" + poolFlag;

                           addChild(textPoolFlag);

                           //마우스 클릭 영역

                           graphics.beginFill(0x000000,0);

                           graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);

                           graphics.endFill();

                           addEventListener(MouseEvent.CLICK, MOUSE_CLICK);

                           //반복동작     

                           addEventListener(Event.ENTER_FRAME, ENTER_FRAME);

                   }

     

                   private function MOUSE_CLICK($e:MouseEvent):void {

                           //마우스 클릭때마다 poolFlag 상태를 전환

                           poolFlag = !poolFlag;

                           textPoolFlag.text = "poolFlag=" + poolFlag;

                   }

                  

                   private function ENTER_FRAME($e:Event):void {

                           var shape:RandomShape, i:int, numChildren:int;

                           //poolFlag 따라서 new연산자 또는 Object Pool에서 객체 참조

                           shape = poolFlag ? pool.getShape() : new RandomShape;

                           shape.draw();

                           shape.x = Math.random() * stage.stageWidth;

                           shape.y = 0;

                           shapeCanvas.addChild( shape );

                           //계속 아래로 떨어뜨림. 화면에서 벗어나면 removeChild시킴

                           numChildren = shapeCanvas.numChildren;

                           for( i=0; i<numChildren;i++) {

                                  shape = shapeCanvas.getChildAt(i) as RandomShape;

                                  shape.y += 5;

                                  if( shape.y > stage.stageHeight ) {

                                          shapeCanvas.removeChild( shape );

                                          i--;

                                          numChildren--;

                                  }

                           }

                   }

            }

    }


    동작은 매우 단순하다. poolFlag가 true이면 제작한 자동 Object Pool을 사용하겠다는 것이고 false이면 new 연산자를 사용해 RandomShape객체를 생성하겠다는 것이다. poolFlag 전환은 마우스 클릭으로 할 수 있다. 그리고 비가 내리는 것처럼 Shape 객체는 위에서 부터 아래로 떨어지며 맨 아래에 닿으면 removeChild 시킨다.

    테스트 화면


    실행 코드와 결과물은 http://wonderfl.net/code/805dc65d1f7800b7bee5dc3cd72ca21c0a87c2d6 에서도 볼 수 있다.

    이제 Flash Builder의 프로파일링 기능을 이용해 메모리 변화 및 객체 생성빈도를 살펴보자.

    먼저 new 연산자를 사용했을때 메모리 변화를 보자.

    new 연산자로 객체생성시 Memory Usage


    new 연산자를 사용해 객체를 생성하고 필요없을때 removeChild를 하게 되면 가비지 컬렉션 처리가 되기 때문에 일정한 시점에 메모리가 해지되는 것을 반복하는 것을 확인할 수 있다.

    new 연산자로 객체생성시 Live Object


    위 표는 축적된 객체수(Cumulative Instances), 현재 객체수(Instances), 축적된 메모리(Cumulative Memory), 현재 메모리(Memory)를 나타낸다. 변화 무쌍하며 실제로 보여지지 않는 객체도 가비지 컬렉션 처리가 일어날때까지 메모리에 남아 있다. 분명 비효율적으로 보인다.

    반대로 Object Pool을 이용했을때 메모리 변화이다.

    Object Pool로 객체관리시 Memory Usage

    new 연산자를 사용했을때와 비교가 안될 정도로 고요한 느낌이 든다. 거의 일직선의 메모리 사용율을 보인다.

    Object Pool로 객체관리시 Live Object


    축적되는 객체도 81개를 넘지 않는다. 그만큼 객체 생성과 삭제에 민감하지 않게 되고 가비지 컬렉터 도움을 받지 않고 객체관리가 되는 것을 확인할 수 있다.


    8. 재사용할 수 있는 클래스 제작

    Object Pool을 사용시 한가지 고려할 사항은 재사용할 수 있는 클래스를 만들어야 한다는 점이다. 왜냐하면 Object Pool은 단순히 재사용을 위한 공간만 제공하는 것이지 재사용을 하기 위한 어떤 기능을 클래스에게 줄 수 없기 때문이다. 그래서 재사용이 가능한 클래스를 설계하는 것은 하나의 기술적 이슈가 될 수 있다. 

    재사용할 수 있는 클래스를 어떻게 만드는지 간단한 예를 들어보겠다.

    final public class MyData {

            public var type:String;

            public var id:int;

            public var name:String;

    }


    위 클래스는 데이터를 담는 클래스이다. id, name은 실제 사용하는 데이타 값들이고 이 데이타의 종류는 type으로 구분한다. 이 데이타를 사용하는 인터페이스를 제작해보겠다.

    public interface IMyClass {

            function init($data:MyData):void;

            function clear():void;

            function get data():MyData;

    }


    이 인터페이스는 앞으로 만들 MyClass01, MyClass02... 등을 구현하기 위한 것이다. 초기화 하기 위해 init()함수가 있고 인자로 위에서 제작한 MyData 객체를 받는다. 또한 사용할 필요가 없을때 내부 청소를 위해 clear()함수를 추가했다. 게다가 가지고 있는 data를 참고하기 위해 get data()도 만들었다. 이제 이 인터페이스를 구현한 클래스는 아래처럼 만든다.

    final internal class MyClass01 extends Sprite implements IMyClass {

            private var _data:MyData;

            public function MyClass01() {

            }

            public function init($data:Mydata):void {

                   _data = $data;

                   //구현

            }

            public function clear():void {

                   _data = null;

            }

            public function get data():MyData {

                   return _data;

            }

    }

     

    final internal class MyClass02 extends Sprite implements IMyClass {

            private var _data:MyData;

            public function MyClass02() {

            }

            public function init($data:Mydata):void {

                   _data = $data;

                   //구현

            }

            public function clear():void {

                   _data = null;

            }

            public function get data():MyData {

                   return _data;

            }

    }

     

    final internal class MyClass03 extends Sprite implements IMyClass {

            private var _data:MyData;

            public function MyClass03() {

            }

            public function init($data:Mydata):void {

                   _data = $data;

                   //구현

            }

            public function clear():void {

                   _data = null;

            }

            public function get data():MyData {

                   return _data;

            }

    }



    코드가 길어보이지만 3개 클래스 모두 IMyClass를 구현했고 Sprite를 확장했다. 단지 클래스 이름만 다르며 실제 구현부는 알아서 구현하면 된다.

    개발자는 MyClass01, MyClass02, MyClass03은 매우 자주 new 연산자로부터 생성되는 클래스로 판단했고 그래서 Object Pool을 도입하겠다고 결정했다고 하자. 그럼 다음과 같이 시도해 볼 수 있다.

    import de.polygonal.core.ObjectPool;

    import flash.display.DisplayObject;

     

    public class MyPool {

            private static var poolList:Object;

           

            static public function init():void {

                   poolList = {

                           'type01':new ObjectPool(true),

                           'type02':new ObjectPool(true),

                           'type03':new ObjectPool(true),

                   };

                   poolList.type01.allocate(20, MyClass01);

                   poolList.type02.allocate(10, MyClass02);

                   poolList.type03.allocate(100, MyClass03);

            }

           

            static public function getObject($data:MyData):IMyClass {

                   if( $data === null ) {

                           throw new Error('인자값은 null이면 안됩니다.');

                   }

                   try {

                           var object:IMyClass = poolList[$data.type].object;

                           object.init($data);

                   } catch {

                           throw new Error('데이타의 type값이 잘못된 값입니다.');

                   }

                   return object;

            }

           

            static public function returnObject($object:IMyClass):void {

                   if( $object === null ) {

                           throw new Error('인자값은 null이면 안됩니다.');

                   }

                   $object.clear();

                   if( DisplayObject($object).parent ) {

                           DisplayObject($object).parent.removeChild( DisplayObject($object) );

                   }

                   poolList[$object.data.type].object = $object;

            }

    }



    위에서 제작된 MyPool은 static클래스이며 ObjectPool을 이용해 IMyClass 인터페이스를 구현한 클래스를 init()함수에서 적당하게 할당하는 것을 확인할 수 있다. 또한 getObject()를 통해 인자값 data를 참고하여 참조할 Object Pool을 찾아 IMyClass를 구현한 각각의 클래스의 객체를 받아올 수 있으며 returnObject를 통해 다 사용한 객체를 반환한다.

    실제로 실무에서 이런 형태로 개발했고 이는 매우 유용했다. MyPool 클래스를 더 개선해서 더 많은 수 Object Pool을 감당할 수 있도록 확장할 수 있다면 더욱 유용해질 것이라 생각한다.


    9. 정리하며

    Object Pool 개념은 Flash에만 국한되는 유용한 개념이 아니다. 이런 개념들에 대한 노하우를 계속 쌓아간다면 당신의 애플리케이션은 더욱 좋은 작품으로 탈바꿈할 수 있을 것이다.

    Flash Player가 느리고 메모리 관리가 안된다고 생각하지는 말자. Flash Player 태생자체가 그런것을 어찌하겠는가? 제작한 애플리케이션이 느리다면 그것은 결국 개발자가 잘못한 것임을 항상 인지하자. 중요한 것은 여전히 Flash Player는 유용하고 많이 사용되고 있으며 지금도 나날이 발전하여 Cross OS, Cross Browser를 넘어 Cross Device 세계로 뻗어가고 있다는 점이다.


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

    Frame 메타데이터 태그는 Flex 기반에서 만든 Flash Application에 Preloader를 구현하기 위해서 가장 많이 사용합니다. 실제로 Flex를 뜯어보면 [Frame(factoryClass="mx.managers.SystemManager")] 부분이 존재합니다. 이때 이 SystemManager가 바로 Preloader 역할을 합니다. 재미있게도 이렇게 유용한 Frame 메타데이터 태그에 대한 어떠한 공식문서를 저는 본적이 없다는 겁니다. Flex기반이지만 ActionScript 3.0으로만 개발할때 꼭 필요한 요소인데도 말이죠.

    각설하고,
    지금 말하고자 하는 것은 Flash Builder 4, Flex 4 기반에서 ActionScript 3.0 기반 프로젝트를 만들어 Frame 메타데이터 태그를 사용할때 다음과 같은 에러메시지가 난다는 겁니다.(에러 안날 수 있습니다. 하지만 제 프로젝트에서는 나버리네요.)

    1172: mx.skins.spark:BorderSkin 정의를 찾을 수 없습니다.
    1202: 정의되지 않은 속성 BorderSkin(mx.skins.spark 패키지)에 액세스했습니다.


    이쯤되면 엄청 짜증나죠. 

    ActionScript 3.0기반에서는 chart.swc, spark.swc 같은 것은 사용하지 않죠? 여기서 해결 아이디어가 나옵니다. Flash Builder나 Flex Builder에서 프로젝트를 생성하면 .actionScriptProperties 파일이 생성됩니다. 이 안에는 Flex 컴파일러인 mxmlc를 통해 컴파일할때 필요한 설정이 들어가 있지요. 내부를 살펴보면 <excludedEntries>부분이 보입니다. 이 부분은 기본 Flex SDK에 있는 SWC나 SWZ중에 ActionScript 3.0 기반에서 제작시에 필요없는 SWC, SWZ를 포함해서 컴파일하지 않겠다는 것을 명시하는 겁니다. 그런데 Frame 메타데이터 태그를 사용하면 mxmlc컴파일러가 mx.skins.spark:BorderSkin가 들어가는 코드를 만들어주는데 그게 없어서 에러를 던지는 겁니다. 이 클래스는 sparkskin.swc에 포함되어 있는데 이게 <excludeEntries>에 포함되어 있습니다. 그러므로 <excludedEntries>부분을 주석처리를 하거나 삭제하면 sparkskin.swc를 컴파일시 참조할 수 있습니다.  결국 이렇게 하면 깔끔하게 에러가 없어집니다. 명확히 그 이유를 확인하고 싶으시다면 -keep-generated-actionscript 컴파일 옵션을 사용해보세요.

    참고로 Flash Builder에서 .actionScriptProperties 파일은 숨겨져 있습니다. 숨김설정을 푸는 방법을 간단히 소개하자면...

    1. Package Explorer에서 Filters를 선택합니다.


    2. Flex Package Explorer Filters 창이 뜨면 체크되어 있는 모든 항목 풉니다. .actionScriptProperties만 풀어도 됩니다.


    3. 이제 자신의 프로젝트에 .actionScriptProperties 파일을 볼 수 있습니다.


    추가사항 1
    댓글로 hika님께서 Embed 태그문제를 거론해주셨습니다. mxmlc 컴파일러는 Embed 메타데이터 태그를
    사용하면 중간에 mx.core.BitmapAsset, mx.core.MovieClipAsset, mx.core.FontAsset과 같은 클래스를 확장한 클래스를 만들어줍니다. 그렇기 때문에 이들 클래스가 있는 swc를 프로젝트 내에 포함해야겠지요. 정확히 알고 싶다면-keep-generated-actionscript 컴파일 옵션을 이용해 bin-debug에 들어간 generated 폴더를 확인해 보시면 됩니다.

    Flash 애플리케이션을 만드는데 사용되는 SWC는 playerglobal.swc 뿐입니다. 하지만 mxmlc를 사용해 Embed, Frame 메타데이터와 같은 편리한 기능을 사용하려면 그외에 swc를 포함해줘야합니다. Flash Builder에서 ActionScript 3.0 프로젝트를 만들면 playerglobal.swc, textLayout.swc, flex.swc, utilities.swc를 포함되는 것은 다 이런 이유 때문입니다. 나머지 swc는 모두 <excludedEntries>로 제외해버리죠. 하지만 Frame을 썼을때 위에서 언급한 에러가 발목을 잡으므로 <excludedEntries>를 제거함으로써 문제를 해결할 수 있었다는 것을 언급한겁니다. 그때그때 상황에 맞게 .actionScriptProperties를 가지고 조절하시면 된다는 말씀~ ^^; (간단한건데.. 왜이렇게 설명을 장황하게 했는지 저도 모르겠습니다. ㅎㅎ)

    추가사항 2
    CSS파일이 없습니다라는 에러도 <excludedEntries>를 제거하면 없어집니다. -defaults-css-url 컴파일 옵션이나 .actionScriptProperties내에 <!-- --> 빈 주석을 달아 없애는 것 외에 또 하나의 팁!

    참고글
    Flex에서 순수 AS3로 Preloader 구현하기
    AS3로 SystemManager 의 Preloading을 흉내내기
    ActionScript 3.0에서 Preloader 구현 및 Default css file not found 경고 메시지 없애기
    Preloader의 기본 css 파일이 없습니다(Default css file not found) 경고 문제
    프로젝트 템플릿 ant
    Preloaders in AS3

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


    2009년 한국천문연구원 아마추어 천문학 발전을 위한 위탁과제로 만든 대한민국 인터넷 천문대(http://realsky.org) 입니다.

    이 사업은 언제 어디서나 누구나 인터넷을 통해 쉽게 밤하늘을 관측하고 정보를 얻을 수 있는 것을 목표로 최초 제작되었습니다. 2009년 6월 부터 12월까지 프로젝트가 진행되었으며 이를 위해 미국 우주망원경연구소의 디지털하늘탐사(Digitized Sky Survey, DSS) 데이터를 이용해 전천의 밤하늘 이미지를 가공했고 이것을 보여주기 위해 Adobe Flash 기술을 이용했습니다.

    이미지의 편집 및 이용권한은 천문노트(http://astronote.org) 에게만 있습니다.

    클라이언트 엔진은 ActionScript 3.0이며 일부 Flex 4를 이용해 개발했습니다. 서버측 통신은 ZendAMF를 이용했습니다. 최초 개발한 만큼 아직 부족하나 앞으로 유용하고 다양한 형태로 개발할 계획입니다.

    http://realsky.org

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

    나는 Flex나 기타 무거운 프레임워크를 사용하지 않고 ActionScript 3.0 API 수준으로 개발을 한지 이제 겨우 1년이 넘은 것 같다. Flex로 개발할 것과 ActionScript로 개발해야할 것에 대한 구분은 항상 명확하다. 자주 새로 빠르게 로드되어야 하는 애플리케이션 제작은 무조건 ActionScript 기반으로 제작한다. 하지만 가끔 사용하며 Flex 컴포넌트를 잘 활용해야하는 애플리케이션이라면 Flex로 개발한다.

    ActionScript 3.0기반으로 개발하고 완성하고 나서는 뭔가 꼬여있는듯한 느낌, 유지보수가 안될 것 같은 느낌을 항상 받아왔다. 또한 깨끗하지 못한 객체관리가 항상 걸렸고 너무 길어져서 지저분해지는 코드 또한 마음에 들지 않았다. 그리고 클래스간 역할 분담이 분명하지 않는다는 느낌을 받았다. 동작은 뭔가 석연치 않은 느낌을 계속 받았다. 어떤 개발의 스킬이나 전문적인 지식에 대해서 학습을 해보지 못해봤던 나로써는 설계때부터 항상 고민거리가 생긴다.

    나는 근 한달동안 블로그 활동 및 외부 활동을 거의 안하면서 개발에만 매진하면서 지냈다. 바로 스타플의 타임라인 개발때문이였다. 타임라인이 궁금하시다면 아래 링크로 접속해보면 된다.

    스타플 타임라인 업그레이드 및 앨범 기능 추가 소식


    개발하면서 위에서 제시한 문제점을 어느정도 해소시킬 수 있는 몇가지 개념들이 있었다. 여기에 간단하게 적어두도록 하겠다.


    1. 객체의 재사용 Object Pool
    Flash 개발하면서 항상 걸리는 문제중에 하나가 메모리이다. Flash Player가 정확한 가비지 컬렉터(GC)를 가진다고 하지만 지금도 그다지 명확한 신뢰를 받지 못하고 있다. 하지만 분명 GC는 동작한다. 중요한 것은 Flash Player의 GC를 너무 맹신하지는 말자는 것이다.

    new를 통한 객체 생성은 항상 지양하는 것이 가장 중요한 메모리 관리의 첫걸음이다. MyClass.Factory()와 같은 static 클래스를 통해 내부적으로 new를 사용하고 각종 처리는 factory가 담당하는 것이 각종 초기화 및 객체관리에 도움이 된다.

    더불어 매우 자주 사용하다가 없어지는 객체를 계속 new를 통해 생성하고 GC객체화 시키는 것은 메모리 관리에 도움이 안될뿐 더러 퍼포먼스에도 문제가 있다. 실제로 비교적 큰 디스플레이 객체인 Sprite를 new를 자주하면 전체적인 퍼포먼스에 좋지 않은 영향을 줄 수 있다. 이런 경우에 퍼포먼스와 메모리를 동시에 해결할 수 있는 방법은 바로 Object Pool을 이용하는 것이다.

    Using object pools : http://lab.polygonal.de/2008/06/18/using-object-pools/ 

    위의 글은 Object Pool의 강력함을 시각적으로 말해주고 있다. 특히나 Object나 Point와 같은 작은 클래스가 아닌 Sprite와 같은 큰 클래스의 경우 new 연산자 사용보다 Object Pool의 사용은 큰 이점을 주고 있다는 것을 알 수 있다.

    사용한 객체를 그냥 버리지 말자. 잘 보관했다가 다시 사용할 수 있게 만들고 사용하자... 특히나 시각객체(Sprite기반)이 자주 생성,소멸해야하는 경우라면 강력히 Object Pool을 사용하라고 주장한다.


    2. Linked List 와 Dictionary
    컴퓨터를 전공한 사람이라면(본인은 아님) 자료구조를 배우면서 Linked List는 반드시 이해해야할 필수 항목이다. 나는 이번 타임라인을 제작하면서 Linked List를 활용해 중간중간에 얻어진 시간순서 데이터를 엮어주는데 요긴하게 사용했다. 일반적인 Array형태의 데이터는 중간에 데이터 삽입하는 것은 효율적이지 못하지만 Linked List는 연결하고자 하는 지점의 연결부의를 끊고 그 중간에 삽입하여 앞뒤로 연결만 하면 되기 때문에 훨씬 효율적이다.

    Data Structures example : linked lists http://lab.polygonal.de/2007/08/13/data-structures-example-linked-lists/

    또한 특정 key의 자료검색에 Dictionary를 사용했다. Dictionary를 직접 사용하는 것보다는 한번 감싸서 클래스를 만들어 추가/삭제/편집/데이터접근등을 처리하기 쉽게 하면 정말 요긴해진다. 나는 Linked List와 더불어 Dictionary를 통해 key값을 통한 데이터 검색에 사용했다. 이것을 이용하면 아무래도 index 순으로 찾는 것보다 훨씬 빠른 검색을 할 수 있다.


    3. MVC
    개발자라면 Model-View-Controller에 대한 개념을 잘 알고 있을 것이다. 이러한 개념은 Flex 수준의 개발이라면 Flex가 어느정도 지원해주는 편이기 때문에 그나마 괜찮지만 ActionScript 수준에서 개발하려면 이 점을 고려하는 것이 좋다. 기본적으로 Model의 데이터 변화는 View에 영향을 미치고 View의 변화는 Controller에 영향을 준다. Controller는 View와 Model을 통제하며 서로의 상호작용에 조절한다. 그리고 View가 Model을 절대 변경하지 않는다. 이런 약속으로 MVC 구조를 설계하면 View와 View관계도 명확해지고 아직 로드되지 않는 View(모듈)에게도 도움이 된다.

    타임라인의 경우 최초 구동될때 달력과 보기설정은 아에 모듈로 만들어 버린다. 그리고 보여달라고 요청할때만 모듈을 로드해 시각화시킨다. 이때 모듈은 부모 애플리케이션에 Model에 있는 데이터를 사용한다. 이 데이터는 View(모듈)의 존재를 모르기 때문에 어떤 상황이 되든 사용할 수 있다. 또 달력과 보기설정은 같이 보여질 수 없다. 각각 show()와 hide()함수가 있는데 달력이 show하면 보기설정은 hide가 호출되어야하는 식이다. 그런데 View에서 이런것을 컨트롤해버리면 View간에 상호 간섭이 생긴다. 이런 경우 Controller가 중재하도록 하면 훨씬 쉽게 이런 문제를 해결할 수 있다.


    4. 인자객체를 이용하자.
    TextField를 이용해서 객체를 생성해본적이 있을 것이다. 1개 Text를 출력하기 위해서 얼마나 많은 설정을 해야하고 또한 수십줄이 넘는 코딩을 해야하는가? 타임라인의 달력 기능에는 한개 한개가 다 TextField 객체들이다. 물론 반복적으로 생성해주면 되지만 이 마저도 마우스오버,클릭,선택등과 같은 동작에 대응해야하기 때문에 단 몇줄 코드로는 이런 기능을 구현하기가 어렵다.

    나는 이러한 문제를 인자객체를 이용해 해결했다.
    이에 대해서는 더이상 말이 필요없다. 다음 글을 보자.
    인자객체 : http://diebuster.com/flash/?s=인자객체


    5. Helper 수준의 프레임워크
    이번 타임라인을 제작하면서 가장 도움이 되었다고 생각하는 사람은 hika님이다. 물론 같은 회사도 아니고 자주 만나본 것도 아니지만 hika님의 블로그에 정리해놓은 내용은 나에게 큰 자극제가 되었다. 그중에 hika님이 만들어놓은 ActionScript 3.0 프레임워크는 나에게 큰 도움이 되었다. 이는 흔히 말하는 무거운 프레임워크 아니다. 정확히 말하자면 Helper 수준이다. Helper 수준이라는 것은 Flex처럼 Button, Label, DataGrid와 같은 컴포넌트를 가지는 것이 아니라 Sprite, Shape, TextField, Loader등의 기본 ActionScript 3.0 기반의 클래스를 더욱 효율적으로 사용하게 하기 위한 것이라는 것이다.

    나는 이 프레임워크를 이용해서 시각객체관리, 레이어관리, Embed자원관리, 원격자원관리, 이벤트관리, 데이터관리등 전반적으로 큰 도움이 되었다. 나는 hika님이 만들어놓은 프레임워크를 분석하면서 큰 구조의 변경없이 나만의 프레임워크로 변경하고 이번 타임라인 개발에 적극적으로 사용했다. 만약 프레임워크의 도움이 아니였다면 역시나 어려운 코딩을 감당해야할지도 모르겠다.


    6. EnterFrame 수준의 동작 관리
    MouseMove 이벤트를 시각객체를 이동하는데 사용하지 말자. 대신 EnterFrame 이벤트시에 stage.mouseX, stage.mouseY등을 사용해서 시각객체를 이동시키자. 실제로 MouseMove 이벤트는 그 동작이 매우 느리다. 그러나 EnterFrame 이벤트는 최소한 stage.frameRate에 지정된 수준에서 발생하기 때문에 느린 마우스 동작에 걱정하지 않아도 된다.

    나는 타임라인을 마우스로 움직일 때 이 방법을 사용했다.

    마우스 이벤트 최적화 : http://diebuster.com/flash/category/algorithm/io


    7. Event 사용의 최소화
    나는 Event 사용을 최소화 하려고 많이 노력했다. 정말 필요한 곳에만 사용해도 개발에 무리가 없었다. 시각객체를 통한 이벤트 전파는 생각보다 매우 느린 편이기 때문에 되도록이면 사용을 지양해야한다. 이것도 정말 필요하고 요긴하다고 판단할때만 사용하는 것이 옳다. 사용한 이벤트는 반드시 remove시키는 것이 현명하다. useWeakReference는 정말 필요할때만 사용하는 것이 좋다. 되도록이면 명시적으로 remove 시키는 것이 메모리 관리에 도움이 된다.


    8. arguments.callee 활용
    arguments.callee는 함수 자신을 말한다. 이는 꽤 유용한데 특히나 이벤트 리스너 함수에서 자신을 호출한 이벤트를 삭제할때 사용될 수 있다.

    스스로 삭제되는 이벤트 리스너 : http://diebuster.com/flash/110


    9. BitmapData.clone() 사용 지양
    BitmapData 클래스는 비트맵 데이터를 저장하는 클래스이다. 이 데이터를 시각화 하기 위해 사용하는 클래스는 Bitmap이다. new Bitmap( bitmapData ) 시에 첫번째 인자가 BitmapData 객체이다.  같은 bitmapData가 자주 사용되어져야 하는 경우에 bitmapData.clone() 을 통해 계속 생성하지 말고 그냥 원본 bitmapData를 이용해 Bitmap객체를 생성해서 시각화 처리하자.

    나는 타임라인에 들어가는 각종 비트맵 객체에서 bitmapData를 뽑아내어 static으로 참조해 계속 사용하도록 했고 Bitmap 또한 Object Pool 관리를 통해 최소한의 객체 생성을 유도시켜 메모리 관리를 하도록 했다.


    10. mouseChildren, mouseEnabled, tabChildren과 같은 속성은 왠만하면 false로 지정하자.
    이러한 속성들이 필요없는 곳에 사용되면 오히려 퍼포먼스에 악영향을 준다. 정말 필요한곳에만 사용하는 것이 좋다. 이들 속성의 의미를 파악해두는 것이 중요하다.


    11. cacheAsBitmap은 이동에만 사용하자.
    cacheAsBitmap은 확대,축소,회전,Alpha값 변경되는 곳에는 사용하지 않는 편이 좋다.
    다음 글을 참고하자.

    왜 cacheAsBitmap은 나쁜가? http://www.bytearray.org/?p=290


    12. 동적언어의 장점을 충분히 살리자.
    동적언어와 정적언어의 차이점은 메모리를 사용하는 차이점에서 비롯된다. 실행하는 시점에 초기에 메모리를 확보해서 사용하는 언어는 정적이다라고 하며 거대한 메모리를 먼저 잡아놓고 실행하는 시점에 메모리를 자유롭게 사용하는 것은 동적언어이다. ActionScript 는 동적언어이기 때문에 중간중간에 실행도중 클래스 정의, 함수를 추가/수정/삭제가 가능하다. 이에서 나온 개념이 클로저라는 것인데 이 개념을 잘 이용하면 코딩이 훨씬 간편하고 편리해질 경우가 많다. Flex의 대부분 코딩 방법은 정적언어처럼 되어 있는데 사실 ActionScript는 동적언어이므로 이를 더 잘 사용해서 만들어져야 한다고 생각한다. 

    Closure에 대해 : http://diebuster.com/flash/82


    추가사항

    13. package 격리원칙
    클래스 격리에 대해서는 들어봤을 것이다. 클래스내 함수에 public, private를 적절하게 잘 사용해서 외부에 노출되는 메소드는 3~4개정도로 하고 내부 동작방식을 몰라도 클래스를 쉽게 이해할 수 있도록 하는데 필요한 개념이다. 이는 사용하는 관점도 그렇고 상속받는 관점에서도 마찬가지로 적용되도록 한다.

    패키지 격리라는 것은 internal을 적극활용한다는 의미이다. 아마도 많은 개발자들이 internal의 강력함을 잘 인지하지 못할것이다. 이에 대해서는 내가 굳이 설명할 필요 없다. 다음글을 보자.

    플래시 애플리케이션 개발하기 : http://diebuster.com/flash/86


    14. 적절한 코딩규칙
    개발하는데 코딩규칙은 무엇보다 중요하다. 가령 클래스 private 변수에는 _을 붙힌다던지 const값은 대문자로 표시한다던지 그런것 말이다. ActionScript 3.0에서 코딩규칙은 더욱 상세화 되면 좋다. 왜냐하면 워낙 동적인데다가 다른 언어에 비해서 명확한 컴파일 단계의 문법체크가 약하기 때문이다.

    hika님의 2009년도 코딩 규칙을 잠깐 보자. http://diebuster.com/flash/3 

    나는 처음에 클래스에 C, 정적클래스에 CS를 붙이는 의도에 대해서 잘 이해할 수 없었다. 하지만 이에 대한 강력함은 개발해보면 안다. 나는 클래스에 C, 정적클래스에 S, 인자객체는 A, internal 클래스는 앞에 _, 어떤 클래스의 Part라면 클래스명뒤에 _파트명 이런식으로 클래스 이름 규칙을 정했다. 실제로 이렇게 관리하면 클래스가 정확히 어떤 역할을 하는지 눈에 팍 들어온다. 함수를 인자로 받아야하는 경우 FN_intNumberString_Boolean:Function 과 같이 쓰면 이 인자로 들어올 함수는 반드시 function( a:int, b:Number, c:String):Boolean 형태로 만들어야 된다. 이런 규칙은 코딩시 코드힌트기능에도 표시되기 때문에 사용하면 정말 편리하다.

    반복구문에 대해 : http://diebuster.com/flash/?s=%EB%B0%98%EB%B3%B5%EA%B5%AC%EB%AC%B8&submit=

    결국 코딩규칙을 사용하는 명확한 의미는 한눈에 코드를 파악하고자 하는 의도에서 찾아야 한다.


    정리하며
    앞서 말했지만 타임라인 개발하는데 많은 도움을 주신 분을 꼽자면 hika님이다. 부족하지만 이 분의 도움으로 나는 스킬업을 할 수 있었다. 사실 hika님 입장에서는 이런 외부활동과 저술활동이 없어도 부족함이 없어보인다. 하지만 그의 공유정신 때문에 개발에 입문하는 많은 사람들에게 큰 도움이 되는 것은 사실이다. 이 글을 통해 감사하다고 전하고 싶다.

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

    이번에 리뉴얼된 스타플(http://starpl.com)에서 새롭게 업그레이드된 타임라인과 추가된 앨범 기능을 소개할께요.  

    제 별(http://starpl.com/jidolstar)에 놀러가시면 지금 보실 수 있습니다.

    A. 타임라인

    1. 더욱 보기 편해진 디자인


    나의 일대기를 시간순으로 보여주기 위한 타임라인으로 다시 태어났습니다. 크게 키워드/앨범/중요기록/기록 으로 구분되어 보기 쉽고 더욱 이쁘게 볼 수 있도록 했습니다.


    2. 부드러운 움직임
    이번 타임라인은 어떻게 하면 사용자들에게 만족감을 줄 수 있을까 많은 고민을 하면서 제작했습니다. 마우스 드래그나 점프버튼등을 이용해 부드러운 움직임을 통한 이용의 즐거움을 줄 수 있도록 노력했습니다.

    3. 강력한 보기 설정


    스타플의 기록은 타임라인에서 모두 볼 수 있습니다. 대신 세분화된 보기설정으로 원하는 타임라인을 만들 수 있습니다.
    게다가, 내가 설정한 보기설정을 저장하여 계속 유지할 수 있고 저장한 설정은 다른 방문자에게도 똑같이 보입니다. 

    특별히 중요기록은 내 인생중에서 꼭 기억하고 싶은 것만 모아서 아주 쉽게 올릴 수 있도록 스타플 기능에 포함되어 있고 이것이 타임라인과 연동되도록 되어 있습니다.

    아래처럼 자신의 게시물에 중요기록 아이콘을 클릭해 아주 간단하게 ON/OFF 할 수 있습니다.



    4. 달력보기로 원하는 시간으로 한번에 이동하기


    타임라인의 또 다른 강력한 기능으로 달력을 이용해 원하는 날짜로 점프해서 이동해 볼 수 있습니다. 내 결혼식이 2008년 6월 14일이 였다면 그때 올렸던 글이나 사진을 보고 싶을때 이 달력을 이용하면 좋겠죠?


    5. 하루하루 감정까지 담아내는 타임라인



    이번 스타플 리뉴얼에서 기록하기 시, 기존보다 더욱 다양해진 감정을 선택할 수 있게 되었습니다. 이 감정은 타임라인에도 그대로 반영이 됩니다. 크게 긍정,보통,부정의 3가지로 분류가 되고 보기설정의 감정높이정렬을 통해 3단계로 높이 조절이 됩니다.



    높이의 흐름을 보면 그날 그날 감정의 기복을 볼 수 있겠죠?


    6. 취향에 맞게 스킨 변경



    자신의 별뿐 아니라 타임라인도 취향에 맞게 이미지와 스킨을 아주 쉽게 변경할 수 있습니다.

    특별히 이미지는 추억으로 기억할 만한 사진을 등록해서 관리할 수 있습니다.


    7. 과거시간을 담는 타임라인
    스타플은 기록을 과거글로 지정해 쓸 수 있습니다.
    타임라인 시간설정 기능을 이용하면 쉽게 할 수 있습니다.


    또한 위 방법 외에 기록시 이미지 편집기를 이용하면 사진을 찍은 날짜로 쉽게 과거 시간을 설정할 수 있답니다. ^^


    B. 앨범기록
     
    나의 일대기를 담는 타임라인에 사진이 빠질 수 없겠죠?
    타임라인을 업그레이드 하면서 사진에 최적화된 앨범기록도 함께 추가했습니다. 
    일반기록을 통해 사진을 올릴때보다 더욱 쉽고 많은 사진을 아주 쉽게 올릴 수 있게 되었어요.

    기록하기 상단에 "앨범" 탭을 선택합니다.


    사진을 최대 100장까지 한번에 올릴 수 있습니다. 마우스로 위치이동, 회전, 대표사진 설정등 다양하게 이용할 수 있습니다.

    스타플에 놀러오세요. ^^
    http://starpl.com



    지난 2월 2일 AIR 2.0 beta 2가 배포된데 이어 오늘(한국날짜기준 2010.02.24) Adobe Flash Player 10.1 beta 3가 배포되었습니다. Adobe Labs에 가면 상세한 정보를 보실 수 있습니다. 


    릴리즈 노트의 Fixed Issues에 별표(*) 표시된 것이 이번 배포의 수정사항들입니다. 보면 기존에 있었던 Fix된 버그와 개선사항을 확인할 수 있습니다.

    주의사항은 이것은 말그대로 beta라는 점입니다. beta는 정식버전이 아니기 때문에 Flash Player 10.1 API를 이용해서 개발하셔도 실제로 서비스하는 것은 안된다는 것을 의미합니다. 테스트 용도로만 설치하셔서 사용하시기 바랍니다.

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

    애플이 아이패드를 발표하면서 Flash Player 지원을 공식적으로 거부했습니다.
    그 이후로 엄청난 논란이 있는 가운데...
    Adobe CTO 케빈 린치는 다음 처럼 의견을 제시했습니다.

    Adobe 케빈 린치의 '컨텐츠 및 애플리케이션에 대한 오픈 액세스

    아무튼 이런 상황에서 아래와 같은 기사도 올라왔네요.
    한번 읽어보시고 각자의 의견을 들어보는 것도 좋을 것 같아요. ^^

    ---------------------------------------------------------------------

    [원문]
    http://online.wsj.com/article/SB20001424052748703546004575055184080144688.html


    [월 스트리트 저널(Wall Street Journal)]


    애플(Apple)의 마이크로소프트(Microsoft)화?


    2010.02.09 / 홀먼 W. 제킨스 주니어(Holman W. Jenkins, Jr.)


    애플은 증오적 라이벌 관계로 인해 제로섬 전략에 집착하는 회사로 전락할 위험에 놓여있습니다.

    현재를 언급하는 것이 아니라 애플의 시가총액이 상상할 수 없는 규모에 달하여 마이크로소프트의 시가총액을 뛰어넘는 해의 이야기가 될 수 있습니다. 당연히 축하 인사가 따르겠지만 위로의 말도 함께 따를 것입니다. 제품 개발에만 전념하는 회사의 경우 전략에 집착하는 회사가 될 위험의 소지가 있기 때문입니다. 그리고 여기서 “전략”이란 증오적 라이벌 관계로 인한 제로섬 전략을 의미합니다.

    안타깝게도 현재 우리는 신뢰를 찾기 힘든 타락한 세상에 살고 있습니다.

    아이패드(iPad)의 예를 들어보겠습니다. 아이패드는 세상에 공개되자마자 “구세주 태블릿”이라는 별명을 얻었습니다. 아이패드는 상상하지 못할 만큼 탁월한 제품이 아닌 단지 애플이 넷북 경쟁에 뛰어들기 위해 출시한 제품으로, 아이팟 터치(iPod Touch)를 확대해 놓은 버전에 불과합니다. 아이패드는 최상의 웹 브라우징 시스템으로 부각될 수 없을 수도 있습니다. 왜냐하면 애플이 웹 상에서 비디오를 전달하는 데 75% 가량 사용되고 있는 플래시(Flash) 지원을 거부했기 때문입니다. 그러나 아이패드(iPad)(‘지불’이라는 영어 PAID의 철자 순서를 바꾸어 만든 말)는 애플의 온라인 서비스를 통해 판매되고 있는 e북, 음악 및 비디오를 사용하기에는 적합한 디바이스로 보입니다. 단도직입적으로 말하자면 아이패드는 마치 아이튠즈(iTunes) 스토어를 후원하기 위해 최적화된 디바이스로 보입니다.


    그렇다면 왜 애플은 플래시를 제외시키기로 결정했는지 궁금하지 않을 수 없습니다. 애플과 애플의 후원업체들은 플래시가 짜증나는 웹 광고를 만드는데 사용되는 제품이라는 미학적이고 철학적인 이유를 내세우고 있습니다.


    애플이 플래시를 거부하는 이유는 바로 여기에 있습니다. 플래시를 사용하면 아이폰(iPhone) 및 아이패드 사용자는 아이튠즈를 통하지 않고 비디오 및 기타 엔터테인먼트를 사용할 수 있을 것이고 애플 앱 스토어(Apple App Store)에서만 현재 구입할 수 있는 다양한 기능을 무료로 얻을 수 있게 됩니다.


    네트 중립성 옹호자들이나 독점 금지법 집행자들이 스티브 잡스(Steve Jobs)를 연행해가는 오류를 범하지 않도록, 한 가지 덧붙이자면 애플은 플래시를 거부할 수 있는 적법한 권리를 가지고 있습니다. 그러나 한 가지 짚고 넘어가야 할 것은 애플은 엄청난 양의 웹 컨텐츠와 사용자를 분리시키려는 전략적 선택을 내세우고 있다는 점입니다. 플래시가 버그를 부른다는 주장 등에 대해 플래시 옹호자의 시점에서 잠시 벗어나 설명해 보겠습니다. 플래시는 다른 비디오 플레이어와의 시장 경쟁에서 우위를 점하고 있으며 10억 명에 달하는 PC 사용자가 정기적인 업데이트를 다운로드하고 있다는 사실은 엄청난 성공이라고 해도 과언이 아닐 것입니다. Hulu.com에서 TV 프로그램을 시청하거나 MLB.com에서 야구 경기를 보거나 Facebook을 통해 친구와 커뮤니케이션할 때에도 플래시가 필요합니다.


    현재로선 플래시를 소유하고 있는 어도비는 최소한 플래시 프로그래머가 애플의 앱 스토어를 통해 자신이 개발한 컨텐츠나 애플리케이션을 제공할 수 있도록 몇 가지 툴을 출시하고 있다고 말하고 있습니다. 이것도 애플의 축복이 있어야 가능할 것입니다. 그러나 애플은 미래의 웹 표준은 독점적인 플래시를 대체하게 될 것이라고 주장하고 있습니다. 이것은 지켜봐야 할 일입니다. 플래시는 현재 전세계 95%의 PC에 설치되어 있어 하루 아침에 웹 표준이 바뀔 가능성은 극히 희박합니다. 또한 파이어폭스(Firefox) 같은 브라우저 제작업체 모두가 애플이 말한 새로운 표준과 생각을 함께 하고 있는 것도 아닙니다.


    더 크게 우려되는 바는 여기에 있습니다. 애플이 이러한 무모한 목표로 인해 자사의 모바일 디바이스 사용자층을 확대하여 단지 더 많은 사용자가 아이튠즈만 이용하도록 사용자를 가두는 “네트워크 효과”의 매혹적인 유혹에 무릎을 꿇을 수 있다는 점입니다. 애플은 최근까지 제휴 관계를 유지했던 구글(Google)과 전면전에 돌입했습니다.


    지난달 말  애플 직원과의 미팅에서 스티브 잡스가  “지금까지 애플은 검색 시장에서 구글과의  경쟁을 피하기 위해 노력했지만 ‘아이폰  타도’ 를 위해 자사의 모바일 디바이스르  출시했다.” 면서 “사악해지지 말자(Don't be evil) " 라는 구글의 모토를 폄하한 발언이 일파 만파 퍼졌습니다.


    구글폰으로 인해 아이폰이 없어지지는 않을 것입니다. 시장은 수많은 모바일 디바이스를 수용할 수 있는 여건을 충분히 갖추고 있습니다. 그러나 실제 위협이 되는 것은 수천만 명의 소비자를 앱 스토어인 아이튠즈만 이용할 것을 주입시킬 수 있는 애플의 능력입니다. 구글이 아이패드가 공개되기 며칠 전 자사의 슬레이트 모양의 디바이스 모델을 발표한 것은 의미 있는 행보였습니다. 구글의 모바일 디바이스는 플래시를 지원하고 있습니다. 애플 사용자가 사용할 수 없는 비디오 및 기타 웹 기능을 사용자가 구매하거나 사용할 수 있도록 했습니다.


    애플이 아이폰에서 구글을 대체하기 위해 마이크로소프트의 검색 엔진인 빙(Bing)과 거래를 고려하고 있다는 소문이 돌고 있습니다. 또한 애플이 광고 사업에도 뛰어들어 구글의 서비스와 경쟁하기 위해 클라우드 서비스를 확대할 것이라는 소문도 들리고 있습니다. 어디서 많이 들어본 스토리 아닌가요?


    네트워크 효과는 권력과 부를 가져오는 방법이 될 수는 있지만 마이크로소프트의 사례에서 볼 수 있듯이 너무 많은 성과는 특권의 지위를 유지하기 위한 방어적이고 망상적 시도로 인해 물거품이 될 수 있습니다. 회사의 최고 심미가이자 완벽주의자가 더 이상 정상적인 의사 결정을 내리지 못하게 되면 과연 애플은 어떤 회사가 될 것인지 많은 전문가들은 의문을 가지고 바라보고 있습니다. 애플이 점점 더 많은 사용자들에게 아이튠즈 앱 스토어만 사용할 수 있는 질이 나쁜 디바이스를 출시하는 회사로 전락되지 않을까 우려되는 바입니다.
     



    ACC의 오창훈님과 이문국님이 직접 제작한 Adobe Flash Platform 탐구생활 동영상입니다.

    동영상의 좌측 상단을 누르시면 Flash Builder와 Flash Catalyst의 베타버전을 무료로 다운로드 받을 수 있습니다. ^^

    Flash Builder 100배 즐기기



    Flash Catalyst 100배 즐기기


    출처 : http://www.adoberia.co.kr/trial_down/main.html

    stageWidthstageHeight 속성은 Stage에 정의된 속성으로 관련 디스플레이 객체가 디스플레이 리스트에 추가가 되면서 마지막에 Stage에 추가될 때 발생하는 Event.ADD_TO_STAGE 이벤트 발생 시점부터 stage.stageWidth, stage.stageHeight 형태로 참조할 수 있게 된다. 

    이 값은 ActionScript 3.0 프로젝트를 진행할 시에 정해진 Stage의 기본 폭과 높이를 알려주게 된다. 아래 블로그 글을 통해서 이 속성이 어떤 의미를 가지는지 쉽게 파악할 수 있을 것이다.

    AS3.0에서 stage.stageWidth 와 stage.width 의 차이

    아래 코드처럼 작성한다면 이 값들을 쉽게 참고가 가능하다. SWF 메타데이터로 100,100으로 지정했기 때문에 모든 경우가 100,100으로 나와야 하는 것이 정상이다.
    package {
    	import flash.display.Sprite;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	import flash.events.Event;
    	import flash.text.TextField;
    
    	[SWF(width="100",height="100")]
    	public class MainApp extends Sprite {
    		public function MainApp() {
    			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    		}
    
    		private function onAddedToStage($e:Event):void {
    			var textField:TextField;
    			textField = new TextField();
    			textField.text = stage.stageWidth + "," + stage.stageHeight;
    			addChild( textField );
    			
    			stage.align = StageAlign.TOP_LEFT;
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			
    			textField = new TextField();
    			textField.text = stage.stageWidth + "," + stage.stageHeight;
    			textField.y = 30;
    			addChild( textField );
    		}
    	}
    }
    

    Flash Player 에서 직접 실행시키면 모두 100으로 나온다. 하지만 IE8에서는 처음에는 100이지만 다음에는 0이 나왔다. IE외에 다른 브라우져는 정상적으로 100이 나온다. 재미있는 것은 IE8에서 stage.scaleMode를 지정하면서 이 값이 리셋되어 버린다는 것이다. 이게 Mac의 FF3.0에서도 이런 현상이 있다고 한다.

    내가 만든 Flash 애플리케이션이 모든 운영체제(Cross OS)의 모든 브라우져(Cross Browser)에서 동작하도록 하기 위해서 다음과 같은 처리가 필요하게 되었다.
    package {
    	import flash.display.Sprite;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	import flash.events.Event;
    	import flash.text.TextField;
    
    	[SWF(width="100",height="100")]
    	public class MainApp extends Sprite {
    		public function MainApp() {
    			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    		}
    
    		private function onAddedToStage($e:Event):void {
    			$e.target.removeEventListener( $e.type, arguments.callee );
    			stage.align = StageAlign.TOP_LEFT;
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			var count:int = 0;
    			if( stage.stageWidth === 0 && stage.stageHeight === 0 ) {
    				stage.addEventListener( Event.ENTER_FRAME, function ($e:Event):void {
    					if( stage.stageWidth > 0 || stage.stageHeight > 0 ) {
    						stage.removeEventListener( $e.type, arguments.callee );
    						_init();
    					}
    					trace( ++count ); //IE8에서 두번 들어왔다.
    				});
    			} else {
    				_init();	
    			}			
    		}
    		
    		protected function _init():void {
    			var textField:TextField;
    			textField = new TextField();
    			textField.text = stage.stageWidth + "," + stage.stageHeight;
    			addChild( textField );
    		}
    	}
    }
    


    Flex의 경우라면 아마도 이런 처리가 다 되어 있기 때문에 신경쓰지 않고 개발하면 될 것이다. 하지만 ActionScript 3.0으로 개발할 때에는 이런 문제를 간과하면 안될 것 같다.

    참고글
    Firefox 3 Mac Flash Bug stageWidth and stageHeight are 0
    stageWidth is zero in IE
    flash.display.Stage 클래스
    flash.display.DisplayObject 클래스


    글쓴이 : 지돌스타(http://blog.jidolstar.com/656)
    최근에 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)

    근래 스티브 잡스의 Adobe Flash에 대한 자극적인 발언으로 시끄러운 가운데 Adobe CTO인 케린 빈치가 의견을 게시했습니다. 아래는 그 내용의 번역본입니다.


    원본글 : http://blogs.adobe.com/conversations/2010/02/open_access_to_content_and_app.html
    게시자: Kevin Lynch, CTO

    최근 출시되고 있는 우수한 디바이스에 Flash Player가 탑재되어 있지 않다는 사실에 적잖이 놀랐을 것입니다.


    본래 Flash는 시장이 형성되지 않았던 약 15년 전에 펜(Pen) 컴퓨팅 태블릿을 위해 고안되었습니다. 초창기에는 낮은 대역폭의 벡터 그래픽을 지원했지만 지난 십여 년간 새로운 기능을 빠르게 추가해 나가면서, 웹의 틈새 시장을 공략했습니다. 현재는 새로운 활용 방안을 찾는 데 노력을 기울이고 있습니다. 일례로 웹상에서의 애니메이션, 스트리밍 오디오, 풍부한 인터랙션, 임의의 폰트, 양자간 오디오/비디오 커뮤니케이션, 로컬 저장소, 혁신적인 비디오 전달 등이 있습니다.
     

    HTML 기능이 추가된 Flash는 상당히 높은 채택율을 기록하고 있는 가운데, 상위 웹 사이트의 85% 이상에서 Flash 컨텐츠를 사용하고 있으며, 인터넷이 연결된 컴퓨터의 98% 이상에서 Flash가 실행되고 있습니다. Flash는 웹상의 대부분의 캐주얼 게임, 비디오 및 애니메이션에 사용되고 있으며 Nike, Hulu, BBC, Major League Baseball 등 유명 브랜드에서 Flash를 사용하여 10억 이상에 달하는 전세계 사용자에게 매력적인 경험을 전달하고 있습니다.


    Flash의 미래에 있어서 지금이 가장 중요한 시기입니다. 현재 PC 외에도 다양한 디바이스들이 하루가 다르게 출시되고 있으며, 많은 수의 디바이스가 인터넷 검색에 사용될 것입니다. 따라서 컨텐츠 및 애플리케이션 제작자와 사용자는 PC에서 Flash를 통해 얻어질 것으로 기대되는 경험, 즉 원활하고 일관되며 풍부한 경험을 디바이스에도 동일하게 전달하기 위해 많은 과제들을 해결해야 할 것입니다. Flash 엔지니어링 팀은 이를 실현하기 위해 다양한 디바이스에서 Flash Player를 철저히 분석해 왔습니다.


    이러한 노력의 결과로 Adobe는 시장 선도적인 제조업체를 대상으로 한 스마트폰용 Flash Player 10.1을 선보이려고 합니다. 이러한 제조업체에는 스마트폰 뿐만 아니라 태블릿, 넷북, 인터넷 TV 등 시장을 형성하고 있는 Google의 Android, RIM의 Blackberry, Nokia, Palm Pre, 기타 업체들이 있습니다. Flash를 통해 고객은 전체 웹을 검색할 수 있으므로 브라우저에서 Flash를 사용하고 있는 이러한 디바이스는 경쟁력을 갖추게 됩니다. 이는 사실상 오픈 스크린 프로젝트(Open Screen Project)를 통해 이루어지고 있으며, Adobe는 50여 이상의 파트너와 협력하면서 다양한 디바이스에서 이를 실현하기 위해 노력하고 있습니다. 예를 들어, 최근 선보인 구글 넥서스원(Nexus One)은 Flash Player 10.1을 통해 탁월한 브라우저 경험을 선사할 것입니다.


    그렇다면 Apple 디바이스에서 실행 중인 Flash는 어떨까요? Adobe는 Flash 기반으로 iPhone용 독립 실행형 애플리케이션을 구축할 수 있게 함으로써, Flash 기술은 이러한 디바이스에서의 사용을 넓혀 나가기 시작했습니다. 실제로 이러한 애플리케이션 중 일부는 FickleBlox, Chroma Circuit과 같은 Apple App Store(앱스토어)에서 이미 제공되고 있습니다. 이 동일한 솔루션은 iPad에서도 작동될 것입니다. Adobe는 이러한 디바이스의 브라우저에서 Flash를 지원하기 위한 준비를 마친 상태이며 이제 Apple에서 사용자를 위해 이를 허용하는 것만이 남아 있습니다. 그러나 Apple은 지금까지도 어도비의 이러한 협력 요청을 받아들이지 않고 있습니다.


    장기적으로 볼 때, Flash에 대한 요구 사항을 대신하게 될 것으로 HTML이 꼽히고 있는 데, 특히 최근 개발된 HTML 5 버전이 출시되면 그 움직임은 본격화될 것으로 전망하는 사람들이 많아지고 있습니다. 그러나 오늘날은 물론이거니와 앞으로도 한 기술이 다른 한 기술을 대체하게 되지는 않을 것으로 예상됩니다.


    Adobe는 HTML의 발전을 지원하고 있습니다. 앞으로 HTML이 진화를 거듭할수록 Adobe 소프트웨어에 더 많은 기능이 추가될 것으로 기대하고 있습니다. HTML이 Flash의 기능을 안정적으로 수행할 수 있다면, Adobe는 많은 시간과 수고를 덜 수 있지만, 사실상 그렇게 될 가능성은 거의 없습니다. 비디오 부문의 경우 현재 웹상에 있는 비디오의 75% 이상에서 Flash가 사용되고 있는 데도 불구하고, 브라우저 간 포맷 통일이 이루어질 수 없으므로 사용자와 컨텐츠 제작자는 비호환성이라는 문제를 안게 되고 결국 HTML 비디오 구현은 어두운 뒤안길에 남겨질 것입니다.


    HTML의 진보에도 불구하고 Flash의 생산성과 표현 기능은 웹 커뮤니티에서 가희 독보적이라고 할 수 있습니다. Flash 팀은 지난 십여 년간 불가능하다고 여겼던 경험을 구현해 왔던 것처럼 앞으로도 더욱 혁신에 박차를 가할 것입니다. 1년도 채 안 되는 짧은 시간 동안 대다수의 웹 클라이언트를 업데이트할 수 있었던 Flash는 다양한 브라우저 전반에 걸쳐 HTML이 수행하는 것보다 훨씬 빠른 시간 내에 고객에게 이러한 혁신 기술을 선보일 수 있을 것으로 기대를 모으고 있습니다.


    Adobe는 시간, 장소, 매체에 구애 받지 않고 아이디어와 정보를 생성하고 전달할 수 있는 방식을 혁신하고 있으며 디자이너와 개발자가 자신의 독창적인 아이디어를 마음껏 펼칠 수 있는 방법을 고안해 내는 데 전력을 다하고 있습니다. 또한 가장 생산성이 우수한 툴과 컨텐츠 및 애플리케이션을 배포할 수 있는 광범위한 기능을 창의적인 관리 방법과 접목시키는 것 또한 Adobe의 미션이라고 할 수 있습니다. Adobe는 고객이 목표를 달성하고 기술 격차를 극복할 수 있는 기술 혁신을 이뤄내는 데 필요한 모든 기술과 포맷을 지원하고 있습니다. Flash와 HTML이 결합되면 최고의 기술이 탄생하게 될 것입니다. 그렇게 되면 누구나 웹상에서 이 기술을 사용하여 최상의 경험을 제공할 수 있게 될 것입니다.


    아이디어와 정보를 활용하는 것은 사용자가 선택한 컨텐츠와 애플리케이션을 보고 서로 인터랙션할 수 있는 개방된 에코시스템과 자유가 존재한다는 것을 의미합니다. 이 오픈 액세스 모델은 가장 효율적인 모델로 그간의 여러 시행착오를 거쳐 그 효과가 입증되었습니다. 이와 반대로 폐쇄 모델에서는 사용자가 개별 컨텐츠와 애플리케이션을 보거나 승인 및 거부할 수 있는 사항을 제조업체에서 결정하려고 했습니다. 웹은 디바이스에 상관없이 컨텐츠와 애플리케이션을 일관되게 액세스할 수 있는 개방된 환경으로 존재해야 한다는 데는 이견이 없을 것입니다.


    Adobe는 고객들이 최상의 업무 성과를 달성하고 운영 체제, 브라우저 및 다양한 디바이스 전반에 걸쳐 효과적이고 안정적으로 전세계 모든 사용자에게 다가갈 수 있도록 지속적으로 지원해 나갈 것입니다.




    원래 이런 문제가 발생하면 안되는건데... 현재 구글 크롬에서 Flash Player를 설치해도 설치되지 않은 것처럼 보여주는 버그가 있습니다. 제 컴퓨터만 그렇겠지 했는데... 회사 동료도 이런 문제가 있더군요. 허허허....

    회사 동료로부터 좋은 정보를 얻게 되어 이 문제를 해결하는 방법을 소개합니다. ^^

    http://blog.still.pe.kr/550

    Window 7에서 하실때 권한 문제같은거 발생할 수 있는데요.
    C:\Documents and Settings\[사용자 이름]\Local Settings\Application Data\Google\Chrome\Application
    까지만 들어가셔서 거기에 Plugins를 폴더를 만드시면 될 것 같습니다. ^^

    최근에 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


    "Flash로 못할 것이 없다."라는 말을 들어보셨나요? 이번에 소개하는 것은 beautifl.net에 올라온 Flash 코드중 개인적으로 자극을 받은 것만 선정해본 것들입니다. wonderfl.net에 올라온 것들이기 때문에 코드도 공개되어 있어 언제든지 분석도 할 수 있습니다. Flash의 세계를 만끽해보세요.

    보러가기 : http://opencast.naver.com/FL188/16

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

    나는 올해 멋진 밤하늘을 보여줄 수 있는 모바일 애플리케이션 및 Flash 애플리케이션을 만드는 것을 목표로 삼았다. 모바일이나 Flash와 같이 렌더링하는데 어려운 환경에서 어떻게 최적화 하는가가 큰 이슈가 되지 않을까 생각이 든다. 아래는 간단하게 만들어본 밤하늘의 모습이다. Flash Player에서 거의 40~50 FPS로 나오는것으로 봐서 이것 조차도 느리다.



    각종 천체목록과 연결되고 이미지 서비스도 된다. 천체에 대한 정보도 보여주고 태양계(행성,혜성,위성) 정보도 볼 수 있다. 각 지역에서의 밤하늘 정보를 보여주며 각종 천문현상도 볼 수 있다.

    아무튼 일단 너무 큰거 말고 작은것부터 차근차근 만들어 봐야겠다.

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

    + Recent posts