Adobe MAX 2009는 미국 LA에서 현지 날짜 2009년 10월 5일부터 7일까지 열립니다. 이 행사를 통해서 Adobe에 추진하는 가장 새로운 소식을 접할 수 있습니다. 저처럼 직접 참석해서 들을 수 있지만 참석하지 못한 사람도 현장 분위기를 느낄 수 있는 방법이 있습니다. 바로 Adobe TV를 이용하는 방법입니다.
주요행사인 키노트 및 각종 세션을 이곳을 통해 들을 수 있습니다.(아직 행사 진행중이므로 지금도 계속 업데이트되고 있습니다.) 꼭 한번 참조하셔서 관심있는 분야를 들어보세요. 들으시고 나름대로 정리하셔서 블로깅도 해주시면 좋겠어요. 저기 있는 내용 다 블로깅하고 싶지만... 어휴~ 정말 너무 많아요 ㅎㅎ
제 생각에 세세한 조절을 위해 코드를 수정할 때일겁니다. 가령, 100px 움직여야하는 디스플레이 객체를 90,80,70씩 움직여가며 가장 좋은 결과를 보여준다던가 하는 일을 해야할 경우 코딩후 실행, 종료후 코드 수정다음 다시 실행하는 작업을 반복해야할 겁니다.
Adobe MAX 2009 둘째날 저녁에 열린 MAX Bash@LA Live 행사에서 공개한 Flash Builder는 위의 문제를 해결하기 위해 Edit/Continue 기능을 소개했스니다. 듣기로 이 기능은 이미 일본의 Spark 프로젝트 팀에서 먼저 한걸로 알고 있는데요. Anyway....
Edit/Continue 기능은 디버깅 중간에 코드를 수정하면 바로 실행중간에 결과가 반영된다는 겁니다! olleh!!!
Flash Platform 개발자들의 수고를 한층 덜어주는 기능이군요. 제 생각에 이 기능은 Flash Builder 정식버전 및 Flash CS5에서 적용되지 않을까 생각됩니다. 아주아주 기대가 큰 기능입니다.
이 기능이 가능한 것은 어짜피 컴파일 결과물이 AVM2상 메모리에 올라가고 변경된 코드와 대응하는 메모리상의 위치의 값을 변경하는 일이기 때문입니다.
미국 LA에서 열리는 Adobe MAX 2009 행사 키노트에서 요즘 한국에서도 급부상하고 있는 아이폰(iPhone) 애플리케이션을 Flash로 제작할 수 있게 되었다고 최초로 공개했다. Flash CS5부터 Flash IDE에서 제작한 결과물을 iPhone용으로 컴파일하는 기능이 추가될 예정이다.
아래 그림 하나로 모든게 설명되는.... ^^
아래는 키노트 시에 공개된 동영상이다.
Adobe 사장, 부사장 둘이서 아이폰을 어떻게 구워삼을까 고민하다가 결국 개발자에게 넘긴다. 한방에 해결된다는... 뭐... 그런거?
Adobe MAX 행사는 키노트 외에도 각종 세션이 있습니다. 행사 참여자는 자신이 듣고 싶은 세션을 선택해서 언제든지 들을 수 있지만 다른 세션과 중복되는 경우에 각 세션에서 진행한 내용을 Adobe TV를 통해 못봤던 세션을 볼 수 있습니다.
Building Applications for iPhone with Flash Professional CS5
Flex for mobile devices
아래는 위 동영상에서 Flex로 만든 애플리케이션을 iPhone에서 구동 시연을 캡쳐한 사진입니다.
결론 : Flash Platform 개발자들이여! 이것저것 다하려 하지 말고 Flash하나만 잘해서 향후 10년 밥줄이어갑시다. ㅋ
호텔에서 Adobe에서 제공하는 셔틀버스를 타고 Adobe MAX 행사장인 LA 컨벤션 센터에 왔습니다. 오늘부터 3일간 행사에 참가하게 됩니다. 등록을 마치고 제공해주는 간단한 아침식사(빵과 커피)를 먹고 첫날 키노트 행사에 가기 위해 근처 Nokia Theater에 갔습니다. 전 ACC라 Adobe Community Leader라 태그가 붙은 명찰을 받았습니다. 이 마크가 달려있으면 주요인사, 기자단, 주요발표자 다음으로 자리배치 해줍니다.
이 키노트 행사에 무려 3500명 정도가 왔다고 하네요. 다들 무슨 이야기가 올까 궁금해하는 것 같았습니다.
마이크 챔버스 아저씨도 왔네요. 초상권 침해나? ^^
어도비의 윤미선님과 우야꼬와 나~~ 행사중에 있을 3D영화를 보기 위해 준 안경을 쓰고 함께 찰칵
그나마 잘나온 야꼬와 내 사진.. ㅋ
오픈 공연을 시작했네요.... 아무튼 이렇게 Adobe MAX가 시작되었습니다. ^^
이날 Adobe MAX에서 Flash Player 10.1과 AIR 2.0등 각종 최신정보를 들을 수 있었고 기억에 남는 것은 플래시가 iPhone 애플리케이션으로 컨버팅해주는 기능을 CS5 부터 넣겠다는 것과 Flash Player가 오픈 스크린 프로젝트하에 많은 회사에서 제작하는 모바일등 다양한 플랫폼에서 동작하도록 지원한다는 것, 그리고 더욱 강력해지는 AIR 2.0에서 USB 지원 및 소켓서버, 멀티터치 지원등입니다.
키노트 마지막쯤에 AVATAR라는 3D영화 제작과정과 각종 씬을 보여주었는데....3D안경을 끼고 보는 이 영화는 정말 실세계 3D에 빠지는 듯 했다. 영화내용도 너무 실감나더군요. 이 영화가 아마도 올해말에 미국에 개봉할 터인데 한국에서도 크게 인기를 끌지 않을까 생각합니다.
Flex 4 컴포넌트와 AIR 2.0에 대한 세션을 듣고 저녁에는 Adobe 관계자 및 기자들과 만나 식사를 같이 했습니다. 시차적응 못해서 전날 1시간 밖에 못잤는데 오늘은 푹 자야겠네요.
이번 Adobe MAX 2009에서 Flash Player 10.1을 공개했습니다. 이번 키노트를 통해 더욱 최적화되고 강력해진 Flash player에 대해 설명을 들을 수 있었는데요, 주목할 점은 다양한 모바일등의 다양한 플렛폼 지원과 최적화 부분이겠네요.
MAX현장에서 직접 찍은 사진입니다. ^^
위 화면에서 Flash Player 10.1의 추가 및 향상된 기능을 보실 수 있습니다. 스마트 폰을 지원하며 멀티터치 기능이 가능해집니다. 또한 메모리, 전력, 하드웨어 가속에 대해서 최적화 했으며 Http 비디오 스트리밍등을 지원하게 됩니다.
각종 스마트폰에 지원이 되는 만큼 애플리케이션의 저전력 구동은 핵심이 될겁니다. 일반 비디오는 3.4시간, 활동적인 에니메이션이 들어간 애플리케이션인 경우 6.5시간, 저전력 모드에서는 14.5시간 볼 수 있다고 언급하고 있습니다.
Flash Player 10.1과 기존 Flash Player 10의 메모리 사용양을 나타냅니다. 같은 자원을 쓰고서도 거의 1/2~2/3가량 용량이 줄어들었습니다. Flash Player 10.1이 모바일에 지원되기 위한 최적화의 노력의 성과겠네요.
Adobe는 이번 키노트를 통해 Flash Player가 더이상 데스크탑이 아닌 모바일에서도 같은 동작을 한다는 것을 강조하고 있습니다. 지금까지 Flash Lite로만 모바일 기기 애플리케이션을 만들었지만 Flash Player 10.1부터는 일반 Flash로도 충분히 모바일에서 돌아가는 플래시 컨텐츠를 생산할 수 있게 되었습니다. 이렇게 Flash Player가 다양한 모바일에 지원된데에는 오픈 스크린 프로젝트(Open Screen Project)의 추진과 관련 있습니다. 흥미로운 점은 이 프로젝트에 구글이 동참했다는 점입니다. 오픈 스크린 프로젝트는 더이상 PC, 모바일만을 뜻하지 않으며 다양한 플렛폼에서 다양한 경험을 제공하는 것을 목표로 합니다. 이 오픈 스크린 프로젝트는 구글, 삼성, LG를 비롯해 노키아, NVIDIA등 수많은 회사가 참여하고 있으며, 앞으로 이들이 내높는 다양한 플랫폼에 Flash Player를 지원하게 되어 어디서든 Flash 컨텐츠를 접할 수 있게 됩니다. 하지만 아쉽게도 아이폰의 제작 회사인 Apple은 이 프로젝트에 포함되지 않았군요.
하지만 가만히 있을 Adobe가 아니죠.
위 그림 하나로 다들 쓰러졌다는....
그러니깐 Flash IDE에서 Flash컨텐츠를 개발해서 그대로 iPhone용으로 익스포트 시켜주는 기능이 CS5부터 추가된다고 합니다. 멀티터치 기능도 추가되겠다. 결국 이 기능도 iPhone을 겨냥한듯합니다. ㅎㅎ
저는 새로 리뉴얼 되는 스타플(http://starpl.com)의 타임라인과 키워드에 대해서 서비스차원에서 접근을 하려고 합니다. 저는 스타플을 만든 회사 위콘 직원입니다. 단순히 회사차원에서만 보는게 아니라 사용자 입장과 또 서비스적인 측면에서 바라보겠습니다. (결국 이런 말의 의미는 스타플이 활성화 되는 마음에서 왔다는 겁니다.)
지금 타임라인은 시간순별로 글의 제목이 나열되는 형태입니다. 물론 스타플 초기의 타임라인은 다른 어느 서비스보다 먼저한 형태입니다. 하지만 지금의 타임라인은 스타플 서비스에 잘 융합시켜놓은 것은 사실이지만 스타플이 최초가 아니며 비슷한 타임라인이 다른 서비스에도 존재합니다. 즉, 지금 타임라인은 스타플 고유의 것은 아니라는 것이지요. 그래서 스타플의 가장 독특하고 스타플 서비스의 중심이 타임라인이다라는 말은 지금의 타임라인으로서는 적절치 못합니다. 여기서는 스타플 만의 경쟁력을 찾기 힘들어지죠.
지금 타임라인은 카테고리별 시간순에 대한 글의 나열형태로 되어 있는 형태이기 때문에 서비스 차원의 SNS는 기대하기 힘듭니다. 지금 형태의 타임라인이 좋다고 하시는 분도 계시지만 시간순으로 가시화 시킨것 외에는 개인블로그와는 다를바 없다고 생각합니다. 나쁘지 않지만 더이상 확장하기 힘든 형태는 서비스의 정체를 일으키는 주요인이라고 생각합니다. 저는 타임라인이 SNS로의 확장이 반드시 필요하다고 생각합니다.
스타플은 SNS가 되길 바랍니다. SNS는 사람들과 사람들의 관계를 엮어주는 것을 기본으로 합니다. 왜 엮어줄까요? 그것은 사람은 기본적으로 사회적 동물이기 때문입니다. SNS는 바로 그 사회성을 최대한 극대화 시키는 것을 목표로 한다고 생각해요. 그래서 저는 SNS는 기본적으로 정보중심이 되어야한다고 생각합니다. 특별히 내가 원하는 정보를 매우 쉽게 찾을 수 있고 공유할 수 있어야한다고 생각해요. 결국 관심분야를 가진 사람들과 묶어줄 수 있어야 한다고 생각합니다. 하지만 지금의 타임라인은 그것을 해주는데 역부족입니다. 추억공유기능이라는 색다르고 재미있는 기능이 있지만(저도 실제로 잘 사용했습니다.) 그것만 가지고는 SNS라고 하기에는 부족하지요.
SNS가 나왔으니 알림이를 빼놓을 수 없겠군요. 알림이도 스타플의 참 좋은 서비스임에 틀림없다고 생각합니다. 하지만 알림이는 SNS가 되기위한 도구로는 부족합니다. 친구맺고 친구들의 소식을 들 수 있는 좋은 도구이긴 하지만 진정으로 정보 및 관심분야의 공유는 안되죠. 결국 개인적인 관심에 어긋나는 내용은 더 보고 싶지 않게 될 것이고 결국 서비스를 이용하지 않게 됩니다. 사실 친구도 지인과 그냥 서비스 상에서 만난 친구등으로 나눠서 내가 진정으로 소식을 듣고 싶은 친구만 알림이를 통해 봤으면 하는 소망이 있습니다.
사람들마다 차이가 있습니다. 내 정보가 마구 공유되었으면 하는 사람, 그리고 공유가 되지만 내별에 방문자나 친구만 볼 수 있도록 하면 좋겠다는 사람, 마지막으로 혼자만의 타임라인을 구성하고 싶은 사람... 네! 서비스라면 이런 모든 사람을 다 수용하면 좋습니다. 하지만 서비스가 서비스의 가장 독특하면서 경쟁력을 갖추지 않는 상태에서는 무조건 다 수용하는 것은 바람직하지 않다고 생각합니다. 타임라인도 마찬가지 입니다. 지금의 타임라인이 근본적인 경쟁력을 갖추지 않은 상태에서 모든 사람의 요구는 들어주기는 힘들다고 생각해요.
진정으로 사용자들을 위한다는 것은 무엇일까요? 서비스는 기본적으로 사용자의 의견을 듣는 것이 중요하다고 생각합니다. 하지만 무조건 수용은 안됩니다. 서비스를 만드는 것은 사용자들이 아닙니다. 새로운 것을 내고 새로운 패러다임을 구축하는 일은 결국 서비스를 만드는 회사가 될 것이라 생각해요. 회사는 새롭고 경쟁력있는 SNS 를 만들면서 사용자의 요구를 받아야 한다고 생각합니다. 즉 결국 서비스는 고객만족을 주요목표로 하며 실천하는 겁니다. 이것이 바로 회사가 선택할 수 있는 사용자를 위한 길입니다.
새 타임라인에 대해서 언급하겠습니다.
새 타임라인의 중요한 컨셉은 바로 키워드입니다. 이것은 타임라인뿐 아니라 스타플 서비스 전체에 걸쳐서 키워드가 도입되었습니다. 키워드는 글안으로 숨어있는 태그를 더욱 극대화 시키기 위한 겁니다. 태그가 글에 속하는 반면 키워드는 글을 포함합니다. 단순한 차이지만 SNS로 확대를 위해 필요한 선택이라고 생각합니다. 키워드는 SNS를 만들기 위한 스타플의 주요 컨셉이 된 겁니다. 스타플의 별이 감성적으로 접근하여 관계를 만들어준다면 키워드는 정보성을 극대화 시키기 위한 겁니다. 그 키워드를 표현하기 위해 타임라인을 사용한 것입니다. 사람은 인생을 살고 인생은 시간개념이 들어갑니다. 시간과 인생은 별개가 아닙니다. 그리고 사람은 어떤 사람을 만나고 정보공유를 합니다. 시간과 정보(키워드)를 하나의 시각화 객체에 담은 것이 바로 타임라인이지요. 개인의 시간과 키워드를 표현하기 위해 타임라인은 아주 적절한 도구인겁니다.
새로운 타임라인의 키워드 도입으로 내 주요 관심사, 학교, 직장, 사는곳등의 다양한 주제로 키워드를 만들고 다른 사람과 공유할 수 있게 됩니다. 키워드 도입은 SNS로서의 도약을 위해 스타플 서비스의 선택인것입니다. 기존의 타임라인이라면 사실 이런 표현이 매우 어려워진다는 것을 이해할 필요가 있을 것 같아요.
카테고리와 키워드는 다릅니다. 카테고리는 블로그 카테고리라고 생각하면됩니다. 키워드는 말한것처럼 태그의 개념을 좀더 다른 방식으로 확대한 겁니다. 카테고리, 태그, 키워드 모두 무엇을 위해 존재하는 것일까요? 그것은 분류입니다. 나름대로 글에 대해서 분류하기 위해 사용하는 것이지요. 태그와 키워드의 비교는 이미 언급했으니 생략하고 카테고리와 키워드의 차이점을 말해야겠네요. 일반적으로 카테고리는 자기 자신만의 분류입니다. 이것으로 다른 사람과 공유는 안하지요. 하지만 키워드는 자신의 키워드이면서도 다른사람과의 연결 통로입니다. 카테고리는 누구나 알겠지만 분류로 인해 고민이 많게됩니다. 하지만 키워드는 그럴 걱정이 별로 없습니다. 어짜피 태그처럼 사용해도 됩니다. 더 한가지... 스타플의 키워드는 시간개념이 도입되었습니다. 하지만 카테고리는 시간개념이 없습니다. 어떠신가요? 완전 다르죠? 다르다 못해 무한한 확장이 가능해집니다. 카테고리는 이미 많은 서비스에서 사용합니다. 새롭고 참신한 서비스를 원하는 스타플 사용자들에게 키워드는 이러한 의미에서 좋은 경험을 선사할 것이라고 생각해요.
이것저것 타임라인과 키워드 이야기를 했지만 이러한 모든 것이 사용자들에게 쉽고 재미있게 다가가지 않으면 안된다고 생각합니다. 사용자들이 사용할 수 있는 환경을 제공하기 위해 이제 무단히 노력해야하고 첫단추를 이번 리뉴얼을 통해 끼었다고 생각합니다. 이번 리뉴얼이 100% 맞다라는 보장은 못하지만 방향성은 옳다라고 감히 주장합니다.
스타플은 앞으로 국내 대표 SNS뿐 아니라 국외 대표 SNS를 꿈꾸고 있습니다. 별이라는 색다르고 감성적인 메타포와 나의 시간을 담은 타임라인, 그리고 사람들과의 관계를 엮어주는 키워드로 스타플은 그 꿈을 이룰거라 의심하지 않습니다. 그래서 국제적으로 대한민국의 자랑스런 대표 서비스가 되길 희망합니다.
구를 표현하는데 있어서 많은 3D 예제들이 그냥 경도, 위도로 나눠서 텍스쳐 입히는 것이 대부분입니다. 사실 그게 가장 쉬운 방법이고 완성된 구를 표현할 것 같으면 그편이 좋습니다.
Geodesic Sphere에 대한 프로그램 코드는 그리 많지 않은 것 같더군요. 대신 건축물은 왜 이렇게 많은지.. ㅎㅎ 이 형태의 구로 만든 건축물이 매우 이쁘고 독특해보이죠. 디즈니랜드의 Epcot Dom이 매우 유명한 Geodesic Sphere의 한종류이죠.
응용하면 아래처럼 의자도 만들 수 있네요. 왠지 세련되어 보입니다.
Geodesic이라는 말은 두점간에 최단선(측지선)을 의미합니다. 이 원리를 이용해 만든 구가 바로 Geodesic 구라는 것이지요.
구글 어스(Google Earth)와 같은 3D 프로그램에서 구의 형태를 어떻게 표현했다고 생각하시나요? 정확히는 모르겠지만 Geodesic Sphere 기법을 이용했을 거라 추측합니다. 구글어스에 보이는 지도는 타일 이미지들의 모임입니다. 만약 경도,위도로 그 타일 이미지를 나눠버리면 결국 극쪽(북극, 남극)쪽에 불러와야할 타일 이미지는 엄청 많아지겠죠. 확대할수록 더할 겁니다. 이런 점에 비춰볼때 3D에서 Geodesic Sphere의 선택이 필요할 것 같아집니다.
Geodesic Sphere를 표현하는 방법은 다양합니다. TETRAHEDRON, OCTAHEDRON, ICOSAHEDRON 기반등을 이용해 그려나갈 수 있지요. 아래처럼요.
아직 3D 지도에 어떤 방식이 좋을지 명확히 판단하지 못했지만 OCTAHEDRON 또는 ICOSAHEDRON 기반이 아닐까 생각합니다.
회전 : 마우스 드래그
폴리곤수 조절 : 방향키 Up/Down
Flash 3D API의 drawTriangle 메소드 기반으로 그려본겁니다. 일반 경도, 위도만을 가지고 그릴때보다 복잡한 편이라 애를 먹었습니다. 극부분이 이미지가 찌그러져보이는 것은 UV설정이 잘못되었기 때문입니다. 이부분에 대한 보완이 필요합니다. 어쨌든 3D 지도 기반을 만들기 위한 첫단추를 꽨 셈이군요. ^^
Flash에서 이미지 처리는 지금까지 BitmapData 조작이였다. 싱글스레드 기반의 Flash Player는 이미지 조작을 하기에 좋은 퍼포먼스를 가지기 어려웠다. 하지만 Flash Player 10부터 멀티스레드를 기반으로하는 Adobe Pixel Bender를 지원함에 따라 이제 이미지 프로세싱에 Flash의 막강화력을 보여줄 수 있게 되었다. Adobe Pixel Bender에 대한 국내 문서는 그다지 많지 않은 편이지만 앞으로 Flash/Flex/ActionScript 개발자들에게 각광받는 기술이 될 것임에 틀림없겠다.
한글 에러 : ArgumentError: Error #2180: AVM1 내용(AS1 또는 AS2)이 AVM2(AS3) 내용으로 로드된 경우 AVM1 내용을 displayLis의 다른 부분으로 이동할 수 없습니다."
영문 에러 : ArgumentError: Error #2180: It is illegal to move AVM1 content (AS1 or AS2) to a different part of the displayList when it has been loaded into AVM2 (AS3) content.
위와 같은 에러를 본 적이 있는가?
이것은 Flash Player 9 버전으로 만들어진 애플리케이션에서는 발생하지 않는다. Flash Player 10 버전으로 만들어진 애플리케이션에서 위와 같은 문제가 발생한다.
위 에러가 발생하는 조건은 다음과 같다.
AVM2(ActionScript Virtual Machine 2)기반으로 만들어진 Flash 애플리케이션 AVM2 기반이라는 것은 ActionScript 3.0 기반으로 만들어진 애플리케이션을 의미한다.
Flash Player 10 기반으로 컴파일한다. 컴파일 옵션에서 Flash Player 버전을 9.0.124 등이 아닌 10.0.0을 기반으로 한다.
위 두 조건하에서 AVM1기반으로 만들어진 SWF파일을 flash.display.Loader를 이용해 로드한다. AVM1 기반이라는 것은 ActionScript 1.0 또는 ActionScript 2.0 기반으로 만든 SWF파일을 의미한다.
Loader.contentLoaderInfo.content를 addChild() 한다. Loader는 외부의 이미지, 플래시 파일등을 로드해서 디스플레이 객체로 등록하는 일종의 wrapper역할을 담당한다. 이들을 로드완료하면 Loader.contentLoaderInfo.content에 해당 객체의 참조를 얻을 수 있다. 일반적으로 화면에 출력하기 위해 addChild( Loader ) 식으로 하면 되는데 이렇게 안하고 addChild( Loader.contentLoaderInfo.content ) 를 하는 경우다. 이것이 가능한 이유는 Loader.contentLoaderInfo.content 도 DisplayObject이기 때문이다.
위 조건에 맞춰서 ActionScript 3.0 기반으로 코딩을 아래와 같이 해보겠다.
package
{
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
public class AVM1LoadTest extends Sprite
{
private var loader:Loader;
public function AVM1LoadTest()
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,onLoadComplete);
loader.load( new URLRequest("avm1asset.swf") );
}
private function onLoadComplete( event:Event ):void
{
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
trace( loader.numChildren );
trace( loaderInfo.content.parent );
var avm1asset:DisplayObject = loaderInfo.content;
addChild( avm1asset );
trace( loader.numChildren );
trace( loaderInfo.content.parent );
}
}
}
위 프로그램을 Flash Player 9 버전으로 컴파일하고 실행해보자. (실행되는 Flash Player 버전은 10이어야 한다. 당연히 실험 애플리케이션이 Flash Player 9버전 뿐 아니라 10버전도 만들 것이기 때문이다.)
아래 화면과 같이 AVM1 기반 SWF를 예쁘게 출력해준다.
onLoadComplete() 안을 살펴보면 addChild() 앞뒤로 똑같은 trace()문이 있다. 이것은 addChild() 전후로 loader.numChildren과 loaderInfo.content.parent(이것은 loader.content.parent와 같다)의 변화를 살펴보기 위함이다. loader.numChildren은 Loader 객체에 로드된 AVM1객체가 자식으로 등록되어 있는 경우에는 1이 출력되고 그 반대는 0이 된다. loaderInfo.content.parent는 로드된 AVM1객체의 부모가 무엇인지 가리킨다.
결과는 다음과 같다.
1 [object Loader] 0 [object AVM1LoadTest]
무엇을 의미하는가? addChild()를 하기 전에는 AVM1객체는 Loader의 자식이지만 addChild() 후로는 AVM1LoadTest객체의 자식이 된다. AVM1LoadTest는 위 프로그램의 메인 클래스이다. 이것은 당연한 결과로 자식은 두개 이상의 부모를 가질 수 없는 것을 의미한다. Flash Player 9버전으로 만든 경우에는 이러한 코딩이 가능했다.
조건을 바꿔보자. 이제 위 프로그램을 Flash Player 10 버전으로 컴파일하고 실행해보자. 언급했던 에러가 발생한다.
"ArgumentError: Error #2180: AVM1 내용(AS1 또는 AS2)이 AVM2(AS3) 내용으로 로드된 경우 AVM1 내용을 displayLis의 다른 부분으로 이동할 수 없습니다."
무엇을 의미하는가? Flash Player 10 기반으로 만들어진 애플리케이션은 AVM1 객체를 AVM2 기반의 디스플레이 객체로의 이동을 불허한다. 즉 Loader에서 떠나지 못함을 의미한다.
Flash Player 10기반에서 AVM2 SWF를 로드한 경우는 addChild( loader.contentLoaderInfo.content ) 가 가능할까? 이것은 된다!
Flash Player 10에서 AVM1기반의 SWF는 부모로 Loader이어야만 하는 이유는 무엇일까? 사실 여기에 대한 답변을 나는 아직 찾지 못했다. 혹시 아시는 분은 댓글 및 트랙백을 부탁한다.
일본에서는 이미 이 문제로 고민한 사람이 꽤 있었다. 재미있게도 AVM1을 AVM2로 강제로 바이트 단위로 버전을 수정해 버려 로드해버리는 커스텀 Loader까지 만들어 배포하는 사람이 있었다. 좋은 방법인지는 모르겠지만 아무튼 이런 연구가 활발히 진행되는 일본이 부럽당.. ㅡㅡ
Flash/Flex 개발에 있어서 데이타와 UI의 의존성을 줄여주어 장기적으로 유지보수 및 효율적 관리를 하기 위해 MVC 디자인 패턴이 기본적으로 적용된 프레임워크를 직접 제작하거나 무료로 공개된 것들을 사용할 수 있다. 여기서 소개하는 프레임워크들은 Flex, Flash 개발하는데 MVC를 적절하게 활용해볼 수 있는 것들이다. 유용한 정보가 되길 바라며 관련된 많은 글들이 올라왔으면 한다.
무려 200개의 자리를 꽉 채울 정도로 많은 분들이 밤 늦은 시간까지 경청하시고 가셨습니다. 정말 수고 많으셨어요. ^^
세미나에서 옥상훈님께서는 UX(User Experience)에 대해서 매우 자세히 잘 말씀해주셨습니다. UX에 대해 다시 한번 생각해보는 계기가 되었구요. 또 배준균님께서 BlazeDS에 대해 현업에서 어떻게 쓰일지에 대해서 자세히 말씀해 주셨습니다. 아쉽게도 인터넷이 잘 안되어서 난감해 하셨는데... 알고보니 그거 유동IP가 아니라 고정IP였다는... ^^
저는 Flex 4에 대해 설명드렸습니다. 사실 저도 Flex 4를 공부한다는 마음을 가지고 이번 세미나 주제로 다룬겁니다. 덕분에 저도 열심히 공부할 수 있었고 많은 분들에게 Flex 4에 대해서 처음으로 소개할 수 있게 되어서 기분도 좋았답니다. 하지만 시간이 촉박해서 마지막에 질의시간을 가지지 못한 것 죄송합니다.
Flex 4에 대해 잠깐 언급했고 Flex 4를 이용해서 개발하는 툴인 Flash Builder 4와 Flash Catalyst에 대해서 소개했습니다. CSS, FXG, Namespace, State, Data Binding, Effect, None Visual Component 다루는 법, Spark Component와 Spark Layout 등에 대해서 약간 맛배기 정도로 다뤘습니다. 사실 소개드린 하나하나가 무거운 주제로 다루어도 몇시간씩은 해야하는 것들입니다. 그러므로 더 깊이 있는 공부를 위해서 관련 내용을 프리젠테이션에 있는 링크를 보시고 습득하시길 바래요.
개인적인 소망이 있습니다. 많은 분들이 제 블로그를 통해 Flex 관련자료를 접하고 계신것 잘 알고 있습니다. 하지만 저도 다른 사람들을 통해서 많이 배우고 싶은 사람입니다. 서로 소통이 되어야 진정으로 정보공유라고 생각하고 바람직한 방향이라고 생각해요. 앞으로 Flex 4든 Flash Platform에 대한 어떤 자료든지 아시는 것있다면 함께 공유해서 함께 커갔으면 합니다. 어짜피 기술이라는 것은 시간이 지나면 또 바뀌고 사라질 수도 있잖아요? 자기만 알고 있다고 해서 다 좋은 것은 아니라는 겁니다. Flex 5, Flex 6가 나올때도 한국에서 한국 블로거 또는 카페등을 통해 매일 피상적인 정보만 얻어가는 정도나 질문에만 답해야하는 그런 상황이 반복된다면 정말 암울할 것 같아요. 정보생산의 주체가 적어도 어제 참석한 분들의 수만 되도 한국의 IT 미래는 매우 밝아질 것입니다.
■ 주제: 1. Flex 활용 UX 구현 테크닉 2. BlazeDS 활용 3. Flex 4의 새로운 기능
■ 대상 : 웹전산학관련 학과생 및 졸업생, IT분야 종사자, 플렉스 개발자
■ 내용 1. Flex 활용 UX 구현 테크닉 - UX 개요 - Flex UX 기획 및 설계 - UX를 위한 Flex 테크닉 2. BlazeDS 활용 - BlazeDS의 개요 - BlazeDS의 주요 기능 - LCDS와 BlazeDS의 차이 - Flex 연동 방법 3. Flex 4의 새로운 기능 - Flex 3와의 차이점 - Flex 4의 새로운 기능 - Flex의 비전
■ 일시 : 2009년 8월 13일(목요일) 19:00~ 22:00
■ 장소 : 비트교육센터 지하 2층 멀티미디어관(서울 서초구 서초 2동 1327-15 비트아카데미빌딩)
주차지원안됨
지하철 2호선 강남역 3번 출구 삼성화재 건물까지 직진 후, 오른쪽 골목길로 50M 가량 직진하면 우측에 일식집 “일조수사” 건물
■ 형식 : 전문강사의 세미나와 질의 응답
■ 시간별 진행일정 오프닝: 19:00 ~ 19:10 세션1: 19:10~20:00 Flex를 활용한 UX 구현 테크닉 - 옥상훈 세션2: 20:10~21:00 BlazeDS 활용 - 배준균 세션3: 21:10~22:00 Flex 4 새로운 기능 - 지용호
■ 강사소개
옥상훈 OkGosu.Net 운영자 한국SW아키텍트 연합회장 예제로 배우는 플렉스 저자 전 한국 Adobe 컨설턴트
배준균 Adobe Community Champion 한국 키스코 플렉스 개발팀장
지용호 Adobe Community Champion flexdocs.kr 운영자 위콘 커뮤니케이션 UI 개발팀장
Flash가 Cross OS, Cross Browser를 지향하지만 한글문제를 비롯해 각종 몇가지 기능을 제대로 수행하지 못하는 경우가 있다. 마우스 휠(Mouse Wheel)도 그와 같은 맥락이다.
Mac 운영체제에서는 마우스 휠 기능이 전혀 먹지 않는다. MS Window에서 wmode가 transparent일때 Internet Explorer를 제외하고 다른 브라우저에서는 마우스 휠 기능을 사용할 수 없다.
이 문제를 해결할 수 있는 유일한 방법은 Javascript를 이용하는 방법이다. ActionScript3의 ExternalInterface를 이용해 Javascript와 통신해서 마우스 휠 이벤트를 사용하는 것이다. 자바스크립트를 이용하는 방법은 내 블로그에도 몇번 글을 올렸다.
단, 위 방법이 잘될 수 있도록 하기 위해 allowScripAccess는 같은 도메인의 swf인 경우 sameDomain, 다른 도메인의 swf인 경우 always로 지정해야한다. allowNetworking은 항상 all로 설정해야한다. 이에 대한 자세한 내용은 다음글을 참고한다.
SWFWheel 클래스는 browserScroll 속성이 있다. 이것을 이용해면 Flash 위에서 MouseWheel을 이용할 때 Flash를 담은 브라우저의 스크롤링을 허용할 것인가 설정할 수 있다. 기본값은 false이다.
네이버 오픈캐스트의 메인 프로그램이 Flash로 만들어져 있는데 마우스 휠 기능이 어떤 운영체제든 브라우져든 잘 동작한다. 아마도 이 SWFWheel을 사용하지 않았나 생각된다.
이렇게 꽁수 안부리고 마우스 휠이든 한글문제든 Flash Player가 문제 없이 잘 동작하면 얼마나 좋을까?
[생각더하기]일본의 Spark 프로젝트의 이름에 눈길이 간다. Flex 4의 새로운 컴포넌트는 Spark로 명명했다. 그럼 Flex 4가 만들어질때 일본의 Spark 프로젝트의 역할이 컷던 것 아닐까? 실제로 Spark 프로젝트는 일본내에서 매우 활발하고 유명한 Flash 관련 프로젝트이다. 그 유명한 증강현실(FLARToolKit)도 이 프로젝트에서 만들어진 것이다. 우리 나라에는 왜 이런 멋진 프로젝트 팀이 없는 것일까? 안타깝다.
3D 환경맵핑(Environment Mapping)의 한 기법으로 Cube Maps가 있다. 말그대로 직육면체의 각면에 대한 텍스쳐 이미지로 3D 환경을 조성하는 방법인데 아래 이미지를 보면 바로 이해할 수 있다.
출처 : developer.com
Cube Maps는 환경 맵핑 방법중 가장 빠르고 매우 사실적으로 묘사할 수 있는 방법으로 내가 알기로는 게임등에 자주 사용하는 것으로 알고 있다. 단점은 모서리 부분에서 약간 티가 난다는 점이다.
Flash Player 10부터 3D API를 제공한다. 이 API들을 적절히 활용하면 Papervision3D나 Away3D와 같은 라이브러리를 이용하지 않고도 어려움 없이 Cube Maps을 구현할 수 있다.
아래 프로그램은 Flash Player 10에서 제공하는 3D API만 이용해서 Cube Map을 구현한 것이다. 마우스 드래그로 회전이 가능하다.
마우스로 Drag하면 회전이 됩니다. (Mac에서 화면이 작게 나오는데 아직 이유를 모르겠네요 ^^;)
위와 같은 프로그램을 만들기 위해 6면을 가지는 텍스쳐 이미지가 필요하다. 인터넷에 많이 있는데 아래처럼 Layout이 여러종류가 있다.
Cube Map Layout 출처 : cgtextures.com
나는 가장 일반적이고 자주 쓰이는 Horizontal Cross방식 대신에 Horizontal Strip(NVidia DDS Exporter Layout)을 이용했다. 이 방식은 아래같이 구성된다.
Cube Map Layout 출처 : cgtextures.com
이러한 이미지가 어떻게 Cube Map을 만들 수 있는지 아래 화면을 보면 쉽게 알 수 있다.
마우스로 Drag하면 회전이 됩니다.
아래는 위 프로그램에 대한 소스이다.
package
{
import flash.display.*;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.*;
import flash.utils.getTimer;
[SWF(frameRate=60, backgroundColor=0x000000)]
/**
* Cube Map 예제
* @author Yongho, Ji (http://blog.jidolstar.com/574)
* @since 2009.08.05
*/
public class CubeSky extends Sprite
{
[Embed(source="sky.jpg")]
private const SKY:Class;
//투영된 Vectex 정보
private var projected:Vector.<Number>;
//World 변환 행렬
private var world:Matrix3D;
//투영을 위한 변환행렬
private var projection:Matrix3D;
//Mesh 데이터
private var mesh:GraphicsTrianglePath
//Texture
private var texture:BitmapData = (new SKY as Bitmap).bitmapData;
//Viewport (3D 렌더링 대상)
private var viewport:Shape;
//Viewport의 Z축 위치
private var viewPortZAxis:Number = 0;
public function CubeSky()
{
//Viewport
viewport = new Shape;
addChild( viewport );
//투영 변환 행렬
var p:PerspectiveProjection = new PerspectiveProjection()
p.fieldOfView = 30;
projection = p.toMatrix3D();
//World 변환 행렬
world = new Matrix3D();
var check3D:Vector3D = Utils3D.projectVector(
projection,
new Vector3D(1.0,0,1.0)
);
//Mesh 데이타
mesh = createCubeMesh( check3D.x );
projected = new Vector.<Number>(0,false);
viewPortZAxis = check3D.x;// * 4;
//이벤트 핸들러 등록
stage.addEventListener( Event.RESIZE, resize );
stage.addEventListener( Event.ENTER_FRAME, render );
//stage.addEventListener( MouseEvent.MOUSE_WHEEL, onMouseWheel );
stage.addEventListener( MouseEvent.MOUSE_DOWN, onMouseEvent );
resize();
}
private function resize( event:Event = null ):void
{
viewport.x = stage.stageWidth/2;
viewport.y = stage.stageHeight/2;
}
private function render( event:Event = null ):void
{
world.identity(); //단위행렬로 전환
world.appendRotation( zRotation, Vector3D.Z_AXIS );
world.appendRotation( 90+xRotation, Vector3D.X_AXIS );
world.appendTranslation(0, 0, viewPortZAxis); //이동
world.append(projection); //투영 변환 적용
// mesh 데이터를 투영하여 projected 생성
// uvtData도 갱신된다. 갱신되는 데이터는 T값이다.
Utils3D.projectVectors( world, mesh.vertices, projected, mesh.uvtData );
// texture를 이용해 렌더링
viewport.graphics.clear();
//viewport.graphics.lineStyle( 1, 0xff0000 );
viewport.graphics.beginBitmapFill( texture, null, false, true );
viewport.graphics.drawTriangles( projected, mesh.indices, mesh.uvtData, mesh.culling );
//Cube는 z축을 굳이 정렬할 필요 없다.
//viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), mesh.uvtData, mesh.culling );
}
/**
* 마우스 휠 처리
*/
private function onMouseWheel( event:MouseEvent ):void
{
viewPortZAxis += event.delta * 10;
}
private var xRotation:Number = 0;
private var zRotation:Number = 0;
private var prevX:Number;
private var prevY:Number;
/**
* 마우스 이벤트 - x,y축 회전
*/
private function onMouseEvent( event:MouseEvent ):void
{
switch( event.type )
{
case MouseEvent.MOUSE_DOWN:
stage.addEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
stage.addEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
prevX = mouseX;
prevY = mouseY;
break;
case MouseEvent.MOUSE_MOVE:
if( event.buttonDown == false )
{
stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
break;
}
var dx:Number = mouseX - prevX;
var dy:Number = mouseY - prevY;
zRotation += dx;
xRotation += dy;
if( xRotation > 90 ) xRotation = 90;
if( xRotation < -90 ) xRotation = -90;
prevX = mouseX;
prevY = mouseY;
break;
case MouseEvent.MOUSE_UP:
stage.removeEventListener( MouseEvent.MOUSE_MOVE, onMouseEvent );
stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseEvent );
break;
}
}
}
}
import flash.display.GraphicsTrianglePath;
import flash.display.TriangleCulling;
/**
* Cube Mesh 데이타 작성
* @param size 한변의 사이즈
* @return mesh 데이터
*/
function createCubeMesh( size:Number = 512 ):GraphicsTrianglePath
{
var vertices:Vector.<Number> = new Vector.<Number>( 0, false );
var indices:Vector.<int> = new Vector.<int>( 0, false );
var uvtData:Vector.<Number> = new Vector.<Number>( 0, false );
var mesh:GraphicsTrianglePath = new GraphicsTrianglePath( vertices, indices, uvtData, TriangleCulling.POSITIVE );
var s:Number = size/2;
var w:Number = size * 6;
var u00:Number = 0;
var u01:Number = (size-1)/w;
var u10:Number = size/w;
var u11:Number = (size*2-1)/w;
var u20:Number = (size*2)/w;
var u21:Number = (size*3-1)/w;
var u30:Number = (size*3)/w;
var u31:Number = (size*4-1)/w;
var u40:Number = (size*4)/w;
var u41:Number = (size*5-1)/w;
var u50:Number = (size*5)/w;
var u51:Number = (size*6-1)/w;
mesh.vertices.push(
//Right
s,-s,-s, //0
s,-s,s, //1
s,s,s, //2
s,s,-s, //3
//Left
-s,s,-s, //4
-s,s,s, //5
-s,-s,s, //6
-s,-s,-s, //7
//Top
-s,-s,s, //8
-s,s,s, //9
s,s,s, //10
s,-s,s, //11
//Bottom
-s,s,-s, //12
-s,-s,-s, //13
s,-s,-s, //14
s,s,-s, //15
//Front
-s,-s,-s, //16
-s,-s,s, //17
s,-s,s, //18
s,-s,-s, //19
//Back
s,s,-s, //20
s,s,s, //21
-s,s,s, //22
-s,s,-s //23
);
mesh.indices.push(
0,1,2, 0,2,3, //Right
4,5,6, 4,6,7, //Left
8,9,10, 8,10,11, //Top
12,13,14, 12,14,15, //Bottom
16,17,18, 16,18,19, //Front
20,21,22, 20,22,23 //Back
);
mesh.uvtData.push(
//Right
u00, 1, 1,
u00, 0, 1,
u01, 0, 1,
u01, 1, 1,
//Left
u10, 1, 1,
u10, 0, 1,
u11, 0, 1,
u11, 1, 1,
//Top
u20, 1, 1,
u20, 0, 1,
u21, 0, 1,
u21, 1, 1,
//Bottom
u30, 1, 1,
u30, 0, 1,
u31, 0, 1,
u31, 1, 1,
//Front
u40, 1, 1,
u40, 0, 1,
u41, 0, 1,
u41, 1, 1,
//Back
u50, 1, 1,
u50, 0, 1,
u51, 0, 1,
u51, 1, 1
);
return mesh;
}
/**
* GraphicsTrianglePath를 기반으로, Z축으로 sort된 인덱스를 돌려준다.
* 이 작업을 해주어야 z축 깊이에 따라 Triangle이 제대로 그려진다.
* @param mesh 정보
* @return sort된 index 데이터
*/
function getSortedIndices( mesh:GraphicsTrianglePath ):Vector.<int>
{
var triangles:Array = [];
var length:uint = mesh.indices.length;
//z축 sort를 위한 기반 제작
for ( var i:uint=0; i < length; i += 3 )
{
var i1:uint = mesh.indices[ i+0 ];
var i2:uint = mesh.indices[ i+1 ];
var i3:uint = mesh.indices[ i+2 ];
var z:Number = Math.min( mesh.uvtData[i1 * 3 + 2], mesh.uvtData[i2 * 3 + 2], mesh.uvtData[i3 * 3 + 2] );
if (z > 0)
{
triangles.push({i1:i1, i2:i2, i3:i3, z:z});
}
}
//z축으로 sort
triangles = triangles.sortOn("z", Array.NUMERIC);
//sort된 값을 이용해 Vector값 만듬
var sortedIndices:Vector.<int> = new Vector.<int>(0, false);
for each (var triangle:Object in triangles)
{
sortedIndices.push(triangle.i1, triangle.i2, triangle.i3);
}
return sortedIndices;
}
아래 이미지는 위 프로그램에서 사용한 이미지이다.
위 프로그램에서는 Graphics.drawTriangle() 메소드를 이용했다. 하지만 이 함수를 사용하지 않고도 만들수 있다. 왜냐하면 Flash Player 10부터는 DisplayObject에 대해 x,y,z축 회전 및 이동 API가 추가되었기 때문이다. 그러면 위 프로그램 소스처럼 힘들게 vertex, index, uvt 데이타를 만들지 않아도 될 것이다.
내 생각에 Flash에서 3D 개발을 위해 Graphics.drawTriangle()와 DisplayObject의 3D API, Matrix3D 등을 서로 섞어가면서 만드는 것이 좋을 것 같다.
Flex Explorer만 선별하여 네이버 오픈케스트에 발행했습니다. Flex, AIR 개발자라면 꼭 들어가서 볼 필요있는 것만 나름대로 엄선했으니 참고 바랍니다. Flex를 처음 공부하는 사람에게는 더욱 필요한 정보가 될 것입니다. 현재 Flex 4 Beta가 나와 있는 상태이지만 Flex 2, Flex 3에 대한 Explorer로 포함합니다. 이는 Flex SDK 버전에 상관없이 유용할 것이 판단했기 때문에 넣었습니다.
이 사람 블로그에 가면 Flex 4기반 커스텀 컴포넌트를 만든 예제들이 정말 많다. 한동안 이 사람 블로그와 친해져야 겠다는 생각이 들었다. ㅎㅎ
올라온 자료중에 Gumbo(4.0.0.4932) 버전으로 만든 Knob Button 예제가 있었는데 Flex 4 SDK Beta버전이 정식으로 나오기 전이라 바로 실행할 수 없었다. 그래서 소스를 보면서 마이그레이션 작업을 해보았다. 아래 실행 예제는 바로 이 작업의 결과물이다.
위 프로그램은 Knob Button에 대한 데모이다. 마우스로 돌려볼 수 있다.
마이그레이션 하면서 Spark 기반 커스텀 컴포넌트를 제작하는 법에 더욱 익숙해질 수 있었다.
Flex 3의 Halo 기반 컴포넌트는 스킨이 그래픽 기반만 지원했는데 Flex 4의 Spark기반 컴포넌트들은 스킨이 그래픽적 요소 뿐 아니라, 상태변화(state), 다른 컴포넌트 배치, 데이타 표현등이 된다. 또한 FXG를 지원하고 MXML형태로 스킨을 만들 수 있기 때문에 그 확장력이 무궁무진하게 되었다. Halo 컴포넌트의 경우에는 상태변화 바꾸거나 내부 컴포넌트 배치만 달라져도 컴포넌트 자체를 확장해서 다시 만들어야 하는 불편함이 있었지만 Spark컴포넌트는 큰 변경없이 커스텀 스킨만 제작하는 것만으로도 충분히 해결할 수 있게 되었다. 이 내용은 여기에 올려놓은 소스 또는 "Spark DropDownList 사용하기"를 보면 알 수 있을 것이다.
Spark 컨테이너의 경우에는 Layout까지 동적으로 변경할 수 있도록 만들어져서 언제든지 다른 Layout으로 바꿀 수 있고 또는 커스텀 Layout를 만들어 쓸 수도 있다. 반면 Halo 기반 컨테이너는 컨테이너의 Layout을 바꾸기 위해 기존 컨테이너를 다시 확장하거나 새로 만들어야만 했다. 이 내용에 대해서는 "Spark 컨테이너의 Layout, Scrolling, Viewport 소개"를 읽어보길 바란다.
CSS는 정말 획기적으로 많이 추가 되었다. 기존 Class, Type Selector밖에 없었는데 Flex 4로 넘어오면서 ID, Descendant, Pseudo, Mutiple Class등 다양한 Selector가 추가되었다. 이에 대한 글은 "Flex 4의 CSS"를 참고하길 바란다.
Flex 4는 Flex 3의 컨테이너의 Scrolling 기능보다 더욱 강화되고 직관적으로 바뀌었다. 여기에 Layout, Viewport라는 개념까지 포함되어 더욱 다루기 편하도록 만들어졌다. 이 글은 Flex 4 Spark 컨테이너의 Layout, Scrolling, Viewport에 대해서 소개한다.
아래는 Flash Builder 4 환경에서 작업했다. Flash Builder 4는 아래 링크를 통해 다운받을 수 있다.
Flex 3에서 Halo 기반 컨테이너는 Canvas, Box, HBox, VBox, Tile등이 있었지만 Flex 4의 Spark 기반 컨테이너는 모두 Group 하나로 통일되었고 layout 속성을 통해 BasicLayout, HorizontalLayout, VerticalLayout, TileLayout을 설정하여 Flex 3의 Canvas, HBox, VBox, Tile역할을 할 수 있도록 했다. Flex 4 Spark 기반 컨테이너는 이와 같이 동적으로 Layout을 바꿀 수 있도록 만들어졌기 때문에 좀 더 유연한 컨테이너 환경을 만들 수 있다.
Flex 3의 모든 컨테이너는 Scroller기능이 내장되어 있었는데 반해 Flex 4 Spark 기반 컨테이너는 기본적으로 Scroller 기능이 없다. 그래서 컨테이너는 더욱 가벼워졌고 필요할 때 ScrollBar를 Group 태그 밖으로 감싸주기만 하는 것으로 스크롤을 추가할 수 있도록 되었다.
Flex 4 Spark 컨테이너(List, Group등)는 이렇게 쉽게 Layout을 임의대로 바꿀 수 있고 Scroller도 쉽게 배치할 수 있도록 되어 있다. Flex 4 Spark 기반 컨테이너는 Flex 3의 Halo 컨테이너보다 더욱 사용하기 간편하고 직관적이며 활용도가 높다는 것을 해본 사람은 알 수 있을 것이다.
Spark 컨테이너의 Viewports
Spark 컨테이너에서 들어간 재미있으면서 유용한 개념이 들어갔는데 Viewport 라는 것이다. Viewport는 말그대로 보여지는 영역이다. 아래 그림만 봐도 Viewport의 의미를 쉽게 이해할 수 있을 것이다.
방금 설명했던 Scroller는 시각적으로 보이는 horizontalScrollbar, verticalScrollbar와 ViewPort의 horizontalScrollPosition, verticalScrollPosition 속성의 값을 묶어(Bind)준다. 그러므로 어느 한쪽이 조절되면 다른 한쪽이 변경되어지게 된다. 백문의 불여일타.... 아래 예제만 봐도 이게 무슨 말인지 바로 알게 된다.
Spark 컨테이너인 Group은 GroupBase를 상속받고 GroupBase는 IViewport 인터페이스를 구현하고 있다. Scroller는 이 IViewport과 바인딩되어 있다는 것을 이해하는 것이 중요하다.
public interface IViewport extends IVisualElement
{
function get width():Number;
function get height():Number;
function get contentWidth():Number;
function get contentHeight():Number;
function get horizontalScrollPosition():Number;
function set horizontalScrollPosition(value:Number):void;
function get verticalScrollPosition():Number;
function set verticalScrollPosition(value:Number):void;
function getHorizontalScrollPositionDelta(scrollUnit:uint):Number;
function getVerticalScrollPositionDelta(scrollUnit:uint):Number;
function get clipAndEnableScrolling():Boolean;
function set clipAndEnableScrolling(value:Boolean):void;
}
그러므로 IViewport 인터페이스를 구현한 GroupBase, Group, DataGroup, RichEditableText, SkinnableContainer, SkinnableDataContainer 등은 모두 Scroller를 사용할 수 있게 되는 것이다.
위 소스에서 사용자가 Scroller를 컨트롤 하지 않고 Viewport를 컨트롤하여 스크롤바를 움직이게 할 수 있다.
Flex 4의 Spark 계열 DropDownList는 List를 확장한 컴포넌트이다. Flex 3에서 ComboBox와 유사하게 사용할 수 있다.
DropDownList는 DropDownBase대신 List를 서브클래스로 한 것이 커다란 변화이다. 이렇게 된데에는 Iwo Banas라는 사람이 기존 Gumbo의 FxComboBox을 더욱 확장이 가능하고 내부적으로 독립화된 구조의 필요성에 대한 제안을 반영한 결과이다. 이에 따라 DropDownList는 dropDown 스킨 부분을 더이상 가지지 않고 List의 dataGroup 스킨을 사용하게 되었다.
DropDownList 컴포넌트의 기능 및 디자인 스펙을 보고 싶다면 아래 글을 보기 바란다. 이 글만 잘봐도 DropDownList에 내부구현 및 사용방법에 대해 어느정도 이해할 수 있을 것이라 생각한다.
이 예제는 DropDownList의 커스텀 스킨을 적용하는 방법을 보여주고 있다. 블로그에 있는 커스텀 스킨은 Gumbo버전이라 작동을 잘 안하는데 스킨은 아래 것을 받아 해보길 바란다. Peter deHaan의 예제에서 PopUp을 사용했는데 Flex 4부터는 PopUpAnchor로 바뀌었다.
List에 보여지는 아이템이 원래 DropDownList의 기본 Skin인 spark.skins.DropDownListSkin과 달라진 점은 PopupAnchor의 DataGroup를 VerticalLayout으로 지정하는데 여기서 사용한 Layout은 HorizontalLayout이라는 점이 다르다. 게다가 위치도 아래표시가 아니라 우측에 표시되도록 했다. 이처럼 스킨부분에서 모든 Layout을 수정할 수 있도록 한 것이 Flex 4 컴포넌트의 컨셉이다. 이것은 Flex 3의 단점을 잘 극복한 부분중에 하나이다.
조금 더 재미있게 바꿔보자. Application과 DropDownSkin을 아래 코드로 바꿔보자.
방금 예제에서 DropDownList의 스킨을 바꾸는 것을 보여주었다. 그럼 DropDownList에 이미지도 보여줄 수 있을까? 당근 아주아주 쉽게 할 수 있다.
여기에서 사용된 BitmapImage는 spark.primitives.* 패키지에 포함되는 것으로 Group을 확장한 Graphic내에 넣을 수 있다. Graphic에 넣을 수 있는 것중에는 <Rect>, <Path>, <Ellipse>등도 포함한다. 관련 내용은 아래 링크를 참고한다.
지금까지 Flex 4의 Spark DropDownList에 대해서 살펴보았다. Flex 3와 달리 Flex 4에서는 스킨 적용시 ActionScript 3.0 기반이 아닌 MXML이라 직관적이고 편리하게 할 수 있다는 것이 큰 매력이였다. 또한 기능도 매우 확대되었다. Spark 컴포넌트라면 이와 같은 방법으로 스킨을 적용할 수 있다는 것을 이해하는 것이 중요하겠다.