2011. 9. 9. 17:39

K플레이어 베타 오픈!



K플레이어 프로젝트로 수 많은 날들을 야근으로 보내던 끝에 9월 3일이 되던 그날밤 K플레이어 서비스 베타오픈을 하게 되었습니다.
KBS에서 1M/bps급 실시간 동영상을 스트리밍으로 쏴주는 이 프로젝트는 Web, iOS, Android, Pad, Tab 까지 개발하고 있습니다. 일단 베타 오픈은 Web이었고 9월 중에 Phone, Pad 어플리케이션을 오픈할 예정에 있습니다.
그런 이유로 어플리케이션 개발자들은 피를 토하며 개발하고 있지요. ㅠㅠ( 힘내세요! 화이팅! )

이렇게 오픈한 서비스를 바로 포스팅하지 못한 까닭은 안정화 작업을 하느라 정신이 없었던 까닭인데 지금 포스팅하는 이유는 서비스가 어느정도 궤도에 오르고 서비스가 안정화되자 조금 여유가 생긴 까닭이겠지요.
하지만 바로 2차 개발을 위한 준비도 시작되서 지금이 아니면 포스팅이 힘들어지겠구나 싶어서 어서 작성해 봅니다.

아무래도 "대국민" 서비스다 보니 사용자들이 불특정 다수고 온갖 버그 트래킹이 발생할 것 같아서 매일매일 구글과 네이버, 다음을 검색하며 누가 욕이라도 써놓지 않았나.. 하고 걱정을 하다가 이런 글들을 보니 눈물이 흐르더군요.. ㅠㅠ

(스샷위치 : DC인사이드 - 이승기 갤러리 - http://gall.dcinside.com/list.php?id=leeseunggi&no=199369 )

엉엉.. DC 갤러 여러분들이 사실 제일 무서웠어요 ㅠㅠ 열심히 만들었는데 욕이라도 있었으면 어떻게 될까 ㅠ
하지만 칭찬해주시는 여러분들 감사합니다. 그 외에도 네이버, 다음에 계신 블로거 님들의 리뷰들이 하나 둘 나오면서 칭찬과 격려의 글들에 정말 깜짝 놀랬습니다. 감사합니다.ㅠㅠ

그래서 K플레이어를 좀더 잘 이용할 수 있도록 기능들에 대해서 좀 설명 드릴까 합니다.

1번 메인페이지.

일단 로그인 없이 바로 http://k.kbs.co.kr 에 들어가시게 되면 위와 같은 모습의 페이지를 만나보실 수 있으실 겁니다. 이래서는 영상을 볼 수 없죠. 로그인 하셔야겠습니다. 우측 상단의 "로그인" 버튼을 꾹 눌러주세요.

KBS 아이디를 갖고 계신분은 아이디와 비밀번호 입력하시면 되고 없으신 분을은 송구스럽습니다만 회원가입해주셔야합니다.

로그인을 하고나면 바로 실시간 스트리밍을 보실 수 있습니다.

맨 처음으로 들어오신분들은 기본적으로 KBS 1TV를 보시게 됩니다. 그 다음부터는 마지막에 보셨던 채널로 셋팅되서 나오게 되어있어요.
혹시 "로그인" 하셨지만 화면이 안나오시는 분들이 계실거라고 생각합니다.
화면이 안나오는 가장큰 원인은 Flash Player가 설치되어있지 않거나 Flash Player의 버젼이 10.1 이전 버젼이라서 안나오실 수 있습니다.
현재 만들어진 K플레이어는 10.1이후 버젼의 Flash Player를 설치가 되어있으셔야 정상적으로 동작하는데 그 이전 버젼의 Flash Player일 경우 자동으로 업데이트 해주게 됩니다.

현재 보고 계시는 화면에 설치되어있는 버젼은 Flash Player 9.277버젼입니다. 저기서 "예"를 클릭해주면 알아서 최신 Flash Player를 Adobe 서버에서 자동으로 내려받게 해줍니다. 설치도 자동! 그리고 브라우져도 알아서 껐다가 켜주기까지 합니다.
한 1분정도만 투자해주시면 됩니다. 하지만 9.16 이하의 버젼을 사용하시게 되면 아래와 같은 화면이 나옵니다.

하지만 이정도로 옛날 버젼의 Flash Player를 사용하시게 되면 Naver 같은 포털 사이트도 제대로 동작하지 않으니 속히 Flash Player 다운로드 센터로 이동해서 최신버젼을 다운로드 해주세요.
( 최신 버젼 다운로드 받기 : http://get.adobe.com/kr/flashplayer )

그런데 간혹 최신버젼을 받아서 설치했는데도 업데이트가 안되는 경우가 있습니다. 이 경우에는 모든 Flash Player를 지우고 다시 설치해야합니다. Flash Player Uninstaller를 다운 받아 실행시키고 Flash Player를 다시 설치하시면 됩니다.
( Flash Player Uninstaller 다운로드 받기 : http://kb2.adobe.com/cps/141/tn_14157.html )

그럼 이제 플레이어의 컨트롤 바에 대해서 설명해 드리겠습니다.


왼쪽부터 설명하겠습니다.
1번 - 재생, 정지 버튼입니다. 실시간 방송이다보니 일시정지 기능은 없어요.
2번 - 프로그래스 바 입니다. 현재 방송되는 프로그램의 편성정보를 기준으로 얼마나 봤는지 프로그래스 바에서 표현됩니다. 그래서 실시간 방송중에는 컨트롤 할 수 없습니다.( 당연하게도 VOD를 재생할 때에는 Seek 기능을 지원하게 됩니다.)
3번 - 현재 방송중인 프로그램의 편성시간을 알려줍니다.
4번 - 볼륨 조절입니다. 클릭을 하면 음소거가 되고 마우스를 위에 올리면 볼륨을 조절할 수 있게 패널이 나오게 됩니다.
5번 - 이름을 짓기가 애매해서 저는 Extender 버튼이라고 이름 지었습니다. 저 버튼을 클릭하면 플레이어의 사이즈가 줄어듭니다.


6번 - 전체화면 버튼입니다.

이와는 별개로 눈에 보이지 않는 기능들을 설명 드리겠습니다.
  • 동영상 화면을 더블 클릭 - 전체화면기능 
  • Enter키 - 전체화면(ESC 키를 누르면 원상 복귀)
  • 위아래 키 - 볼륨 조절( 동영상 영역에 포커싱이 되어있을 때 )
  • 마우스 휠 - 볼륨 조절( 동영상 영역에 마우스 커서가 위치되어야 함)
키보드나 마우스 컨트롤은 아무래도 전체화면에서 컨트롤 하기 쉽습니다. 작은 화면일때는 마우스 휠이나 키보드 상하 키로 컨트롤 하면 브라우져의 스크롤도 같이 동작하기 때문에 컨트롤 하시기 힘들거라고 생각합니다.
VOD를 재생중에는 좌우키로 5초 앞으로 / 뒤로 기능을 제공하고 스페이스바로 일시정지를 할 수 있습니다. 
( 제가 테스트를 하다가 불편하길래 추가한 기능들 입니다. ㅎㅎ ) 

그 외에 왼편에 Remote Control 패널을 이용해 채널 변경 및 볼륨제어가 가능합니다.

그럼 편성표 메뉴로 넘어가보겠습니다.

10개 채널에 대한 편성정보를 확인할 수 있습니다. 기본적으로 상단에 좌우로 배치된 버튼을 이용해 2시간 단위로 이동시켜 보실 수 있습니다. 그리고 하단의 스크롤 버튼을 드래그해서 시간을 이동시킬수도 있습니다.

그리고 가운데 상단의 "On Air" 버튼을 클릭하시면 다른 시간을 보시다가도 현재 날짜, 현재 시간으로 위치하게 됩니다.
우측 상단의  이 두개의 버튼을 설명하겠습니다.
버튼의 모양대로 "달력" 버튼과 "새로고침" 버튼입니다. 새로고침은 말 그대로 현재 보고있는 편성표를 새로 고침해서 확인할 수 있게 하고 달력 버튼은 과거나 앞으로의 편성정보를 확인 할 수 있도록 달력을 제공하게 됩니다.


여기서 활성화 되어있는 날짜를 클릭하면 해당 날짜의 편성표를 보게 됩니다. 그리고 각 셀을 클릭하게되면 해당 프로그램의 상세정보를 확인할 수 있습니다.


 그리고 오늘 날짜의 현재 방송되고 있는 프로그램을 확인하게 되면 "시청하기"버튼이 나타나게 됩니다. 시청하기 버튼을 클릭하게 되면 "라이브" 메뉴로 넘어가서 해당 채널을 재생하게 됩니다.


KBS K플레이어를 개발하면서 항상 "대국민"서비스라는 점에서 책임감이 무거웠습니다. 그것이 아마도 그것이 이번 1차 오픈에 크게 작용했던것 같습니다. 크로스 브라우징, 크로스 플랫폼, 모바일에서도 동작할 수 있는 웹컨텐츠를 설계해야하고 모든 브라우져에서 동일한 동작을 할 수 있도록 개발했어야 하니깐요. 그리고 이달 중에 런칭할 폰, 패드의 어플리케이션까지 개발중에 있습니다.(물론 저는 플래시 부분만 개발했어요. 어플리케이션은 다른 분들이 만드시고 계십니다.)  이 모든것들은 결국 모든 사람들이 좀 더 자유롭게 정보를 공유하고 컨텐츠를 공유할 수 있는 획을 그었길 기도합니다.

칭찬과 격려와 관심을 가져주셨던 모든 분들께 감사드립니다. 

K플레이어 바로가기 : http://k.kbs.co.kr

p.s :
그리고 지금 K플레이어 베타 런칭기념 이벤트가 진행중입니다!


많은 참여 부탁드려요!
(이벤트 페이지 : http://k.kbs.co.kr/Home/KEvent)
 

2011. 7. 19. 19:27

OSMF로 플레이어 만들기 - 2탄 - 영상재생하기


컴파일도 잘 되는거 같으니 이제 코드를 좀 적어줘야겠다. OSMF에서 가장 중요한 녀석은 MediaPlayer라는 클래스다. 이녀석이 재생 컨트롤도 맡고 있고 시간도 뱉어낸다. 온갖 이벤트를 발생시켜서 플레이어의 상태도 알려준다. API를 살펴보자.
어도비가 다른건 몰라도 Language Reference를 기똥차게 만든다고 생각한다. 어쩜 이렇게 잘 만드는지. MediaPlayer를 찾아들어가서 보니 Example이 떡하니 있다.


요 예제를 그대로 Main 클래스에 옮겨서 컴파일 해보도록 하자. 아, 물론 package 경로랑 constructor 이름은 Main 클래스에 맞게 고쳐야겠지.
그래서 전에 만든 명령어를 이용해 컴파일을 한 다음 swf를 실행해 보자.


예제를 만들고 나서 알았다. 파일의 확장자가 mp3 였다는 사실을!! 이런!! 그러고 보니 AudioElement라는 클래스를 사용한걸 보니 영상쪽이 아니라 mp3 재생기였다.
생각해보니 OSMF Feature를 보면 재생할 수 있는 포멧들이 쭉 나와있는데 mov, mp4, flv, swf, mp3... 등등이라고 나와있다.


잘 읽어보니 OSMF에서 재생되는 포멧이 아니라 Flash Player 10.0 이후 버젼에선 저런 포멧들을 지원한다는 이야기인거다. 어쨌든 지금 뭘 재생할 수 있느냐가 중요한게 아니라 영상을 재생하고 싶은거니깐 일단 영상부터 나오게 해보자.

예제 코드를 아래와 같이 살짝 수정해 보자.
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;

import org.osmf.containers.MediaContainer;
import org.osmf.elements.VideoElement;
import org.osmf.events.TimeEvent;
import org.osmf.media.MediaPlayer;
import org.osmf.media.URLResource;
import org.osmf.media.MediaPlayerSprite;

public class Main extends Sprite
{
    private var mediaPlayer:MediaPlayer;

    public function Main()
    { 
        super();

        stage.scaleMode = StageScaleMode.NO_SCALE;
        stage.align = StageAlign.TOP_LEFT;

        var mediaPlayerSprite:MediaPlayerSprite = new MediaPlayerSprite();
        var videoElement:VideoElement = new VideoElement();

        videoElement.resource = new URLResource("rtmp://cp67126.edgefcs.net/ondemand/mediapm/strobe/content/
  test/SpaceAloneHD_sounas_640_500_short
"
);

        mediaPlayer = mediaPlayerSprite.mediaPlayer;

        addChild( mediaPlayerSprite );

        mediaPlayer.volume = .5;
        mediaPlayer.loop = true;
        mediaPlayer.addEventListener(TimeEvent.CURRENT_TIME_CHANGE, onTimeUpdated); 
        mediaPlayer.addEventListener(TimeEvent.DURATION_CHANGE, onTimeUpdated);
        mediaPlayer.autoPlay = true;
        mediaPlayer.media = videoElement; 
    }

    private function onTimeUpdated(event:TimeEvent):void
    {
        trace('time: ' + mediaPlayer.currentTime + ' duration: ' + mediaPlayer.duration);
    }
}
}

이렇게 수정하고 컴파일을 하면 아래와 같은 에러메시지를 확인 할 수 있는데

사실 이 내용은 "RSL 컴파일 안되니까 옵션에서 빼라"는 말입니다. 저 메시지를 보게되면 명령어를 다음과 같이 바꿔서 컴파일 해주면 된다. RSL 컴파일에 대해서는 여러 블로그에서 설명을 하고 있으므로 한번 검색해서 스스로 공부해 보길 바란다.



위에 빨간줄 쳐진 부분을 컴파일 옵션에 포함시키면 위와 같은 현상은 없어지지만 swf 파일의 용량이 129kb로 뻥튀기 된다. 그 이유는 RSL 컴파일에 대해서 공부하면 자연스럽게 나오므로 따로 설명하진 않겠다.

컴파일도 되었겠다. 한번 swf를 실행해보자.


오오오 동영상이 재생된다. 어디나라 언어인진 못알아 보겠지만; 파일명에 확장자가 없는데도 잘 재생되는 걸 보니 꽤나 똑똑하게 만들어졌나 보다.( flash.media.Video 객체가 꽤나 똑똑한 모양 )
다음 3탄에서는 간단한 컨트롤러를 이용해서 동영상을 컨트롤 하는 것을 만들어볼 예정이지만 언제 만들어질지는 알기 힘들다. 왜냐하면 꽤나 큰 프로젝트를 맡아서 진행하게 되어 시간이 부족해졌기 때문인데... 언제든 짬짬히 다음 3탄을 만들어 올리도록 하겠다. 에헴.
2011. 6. 17. 12:42

OSMF로 플레이어 만들기 - 1.5탄 - 컴파일 명령어 만들기

일단 OSMF로 플레이어를 만드려면 준비물이 필요하다. 최근에 나온 1.6 Sprint 5를 기준으로 샘플을 만들어볼텐데 그걸 어디서 구하냐... 하면 http://opensource.adobe.com/wiki/display/osmf/Downloads 여기서 구하면 된다.

이번에도 저렴하게 Edit Plus로 코딩할텐데 swc 라이브러리 파일을 complie 옵션에 포함시킬지 명령어를 만들어보자.

일단 http://livedocs.adobe.com/flex/3/html/compilers_06.html#149503 여기에 가보면 Command-line에서 사용되는 문법들을 정리해 뒀다. Builder에서 컴파일을 못할경우. 음. 그러니까. 예를 들어 리눅스 환경에서 개발할 경우라던가 하는 경우에 용이하게 사용되거나 나처럼 Builder의 정품 인증 30일을 넘어서 코딩하기 힘들때 용이하게 사용할 수 있겠다.


라이브러리 경로를 추가할 수 있게 되었다. 그렇다면 나는 libs라는 디렉토리를 만들고 거기에 OSMF.swc를 넣어준담에 컴파일을 해보면.. 아래와 같은 현상을 관찰 할 수 있다.

Main.as

 package
{
//-----------------------------------------------------------------------------
//
//  Imports
//
//-----------------------------------------------------------------------------
import flash.display.Sprite;

/**
 *  Main Class
 */
public class Main extends Sprite
{
    //-----------------------------------------------------------------------------
    // Constructor
    //-----------------------------------------------------------------------------
    /**
     *  Constructor.
     */
    public function Main()
    {
        super();
    }

    //-----------------------------------------------------------------------------
    // Variables
    //-----------------------------------------------------------------------------
    //-----------------------------------------------------------------------------
    // Properties
    //-----------------------------------------------------------------------------
    //-----------------------------------------------------------------------------
    // Methods
    //-----------------------------------------------------------------------------
    //-----------------------------------------------------------------------------
    // Events
    //-----------------------------------------------------------------------------
}
}

이렇게 코드를 짜놓고..

libs 폴더를 만들고

OSMF.swc를 복사해 붙여넣고


컴파일 명령어에 -library-path+=libs 명령어를 추가해주면

swf 파일이 생겨납니다.

매우 간단한 과정들이 진행되어 일단 1단계를 마무리 할 수 있게 되었습니다.
"난 Builder를 사용해서 저런거 몰라도 됨"이라고 한다면 뭐 크게 할 말은 없지만 Builder 뒤에서 저런 명령어들이 오고 가는거 조금 알고 있어도 괜찮지 않을까요.
2011. 6. 16. 16:16

OSMF로 플레이어 만들기 - 1탄 -


Flash라는 것을 조금 하다 보면 이런 요구사항들을 들어볼 수 있는데 "동영상 플레이어 만들어주삼" 인거심.
동영상 플레이어를 만드는 방법은 생각보다 많은 이벤트를 이해해야하고
NetStream 머시깽이랑 Video 클래스랑 NetConnection이랑 이것저것 많이 쉐킷쉐킷 섞어서 만들어야 하기 때문에 다들 꺼려하는 장르중에 하나다. 그도 그럴게 일단 Flash에서 동영상 플레이어란 미디어 서버에 붙어서 스트리밍 방식의 동영상 재생이 기본기능으로 자리잡고 있으므로 NetConnection으로 일단 커넥션을 열어두고 그 정보를 NetStream 클래스에 넘겨준다음에 NetStream에서 데이터를 스트리밍으로 받아서 Video에 넘겨주면 그 데이터를 재생시켜주는 순서를 갖게 된다. 뭔가 복잡한가??!!!

그래서 어도비에서 Open Framework 로 OSMF(Open Source Media Framework)라는 것을 만들어 냈다.


이게 뭐냐 하면 Flash로 할 수 있는 거의 모든 미디어 기능을 요녀석이 아주아주 간편하게 만들어주는 것. 이라고 생각하면 된다. 그와 동시에 해결해주는 것들이

1. HTTP Dynamic Streaming
2. RTMP Dynamic Streaming
3. Stage Video
4. Streaming Video Play
등등등..

미디어 서버로 동영상 재생하는건 다된다. 심지어 Live 방송도 재생한다.
그냥 알아서 다 해결해준다. 멋지지 않은가.
하지만 코드양은 좀 많다. 물론 예전보다야 많이 줄었다.
그래서 동영상 플레이어를 사용하는데 큰 어려움이 없어졌다.
그럼 이제 한번 만들어볼까? 다음 이시간에 계속.
2011. 6. 13. 20:30

야훔의 저렴한 개발 시즌2 - 싱글톤 패턴


예전에 시즌 1에서 이야기 했는지 모르겠지만 디자인 패턴이라는 부분에 대해서 좀 짚고 넘어가야할 부분이 있는것 같다. 요즘 플래시 모임이나 어디 게시판에 가보면 디자인 패턴 좀 안다면서 코드를 주르륵 흘려놓고 그걸 어떻게 치워야 할지도 모른 채 지나가버리는 사람들을 종종 보게 되는데 여기서 문제는 그 주르륵 흘려놓고 뒷 수습을 못한다는 부분이다.
디자인 패턴이라고 흘려놓은 코드들이 잘못되었다는게 아니라 디자인 패턴이라는 것이 도데체 왜 생겨났고 그걸 어떻게 이용하는 것인지 그리고 그걸 왜 이용하는 것인지에 대한 이해 없이 슥 코드만 써놓고 "이게 싱글톤 패턴임"이라고 적어놓는다는데 있다. 그러 식의 글을 보면서 느끼는건 "나는 싱글톤을 알고있다. 그건 이런 코드로 작성하면 됨"하고 자랑하는 것으로 밖에 보이지 않는다. 그런 사람들하고 대화를 하다보면 디자인 패턴이라는게 왜 있고 그걸 왜 쓰고 도데체 그걸 어디다 어떻게 적용하는 것인지에 대해 전혀 이해없이 그저 코드만 외워서 쓰는 경우가 많다.
본문으로 들어가기 전에 미리 이야기하는데 디자인 패턴을 설명하는 것은 "방법론을 공유하자" 라는 부분과 "방법론을 좀 더 간결하게 만들자"하는 개발자 적 욕구로 시작되었음을 이해해야한다. 그래서 최초의 디자인 패턴을 서술해 놓은 책을 보면 코드는 단 한줄도 없었다고 한다. 그저 그 책은 "이런 문제가 있을 땐 이런식으로 해결하자"는 식의 풀이집에 더 가까웠다는 것이다. 그 이후 그 책을 기반으로 각각의 언어로 구현하는 방법들을 소개하는 책들과 글들이 쏟아져 나왔다. 그러니 "자바에서 싱글톤 패턴이 구현되나요?"라던가 "자바에서 MVC 패턴을 구현하는 방법이 있나요?" 같은 바보같은 질문은 삼가하도록 하자. 자바나 플래시, C나 C++과 같은 언어들의 종류를 불문하고 디자인 패턴이라는 것은 구현된다. 서로서로 방법이 다를 뿐 디자인 패턴이라는 것은 결국 하나의 흐름에서 각각의 문제를 해결하기 위한 방법들을 만들어가는 가이드 라인일 뿐이니.
서론이 길었다. 본론으로가자.

이번 포스팅은 싱글톤 패턴에 대해서 써놓으려고 한다. 싱글톤을 처음 이야기 하는 이유는 디자인 패턴 중에서 가장 쉽고 설명하기도 간편하며 가장 많이 사용되는 패턴이라서 그렇다. 그래서 이번 포스팅에서는 Actionscript 3.0으로 싱글톤을 구현하는 방법에 대해 설명할텐데 그전에 싱글톤이 뭔지부터 좀 알아보자.


위키엔 없는게 없다. 역시 우리 모두의 백과사전. 싱글톤 패턴의 기본적인 정의를 잘 설명해 놨다. 싱글톤 패턴의 구현여부는 인스턴스가 하나만 있어야한다는 무결성을 보장해야하며 이 인스턴스에 접근 할 수 있는 전역적인 접촉점을 제공하면 된다. 음. 데이터의 무결성. "단 하나만의" 객체를 생성하는 약속이 보장되어야 한다는게 바로 싱글톤 패턴이다. 그럼 이걸 언제 사용하지?

싱글턴 패턴이나 팩토리 패턴, 옵져버 패턴, MVC패턴. 이런게 다 어디에 사용하느냐. 언제 사용하느냐. 에 대한 질문을 받을때면 대답하기가 굉장히 애매해진다. "싱글톤 패턴이 뭔가요?"라고 질문하면 "그건 ....."이라면서 설명이라도 해줄 텐데 "싱글톤 패턴은 언제 사용해요?"라는 질문에는 "적절하게 필요할 때..."라고 밖에 대답을 못해주겠다. 데이터가 두번 생성되지 말아야하는 경우가 어디 한두가지여야 말이지. 뭐. 싱글턴 패턴의 용도는 일단 앞에서 말했다 시피 데이터가 두번 생성되지 말아야하는 상황에서 사용된다. 무결성이 보장되어야하고. 한번만 생성되어야하며 그 데이터의 변경 값에 따라 다른 객체들도 그 변경값이 동일하게 적용되어야하는 상황에서 싱글턴 패턴을 사용한다. 간단한 예제를 들어서 설명해볼까. 일단 Flash에서 빈번하게 사용되는 싱글턴 패턴 클래스를 만들어 보자. 워낙 많은 사람들이 알고 있는 코드이기도 하고 코드를 좀 살펴보면 이해하기도 간편하니 설명없이 코드를 써놓을 텐데 코드가 이해가 안간다고 슬퍼하지 말 것. 필요한 때가 오면 다 이해가 되니깐.

classes/UserInfo.as
package classes
{
   public class UserInfo
   {
       public function UserInfo(creator:Creator = null):void
       {
           if( null != instance )
           {
               throw new Error(“이미 객체가 생성되어 있음”);
           }

           if( null == creator )
           {
               throw new Error(“정상적인 방법으로 생성하지 않았음”);
           }
       }

       public static var instance:UserInfo;
public var isLogin:Boolean = false; 

       public static function getInstance():UserInfo
       {
           if( null == instance )
           {
               instance = new UserInfo( new Creator() );
           }

           return instance;
       }
   }
}

final class Creator
{} 

위의 코드가 Flash에서 흔히 사용하는 싱글톤 패턴의 정형화된 구현인데 위와 같은 방법으로 코드를 작성할 경우 getInstance() 라는 static 메서드를 통하지 않는다면 다른 방법으로 instance를 생성할 방법이 없도록 구현된다. 결국 instance는 한개 밖에 생성되지 않기 때문에 그 클래스 안의 내용들은 한번만 생성되고 무결성을 유지할 수 있게 된다. 저렇게 코드를 하나 두고 샘플클래스 몇몇개를 작성해보도록 하자.

Main.as
package
{
import flash.display.Sprite;
import flash.display.Graphics;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;

import classes.LoginCheck;
import classes.UserInfo;

public class Main extends Sprite
{
    public function Main()
    {
        super();

        addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
    }

    private var btn_login:Sprite;
    private var btn_logout:Sprite;
    private var txt_output:TextField;
    private var loginCheck:LoginCheck

    /**
     *  @private
     *  Create Buttons
     */
    private function createButtons():void
    {
        var g:Graphics;

        if( null == btn_login )
        {
            btn_login = new Sprite();
            
            g = btn_login.graphics;
            g.clear();
            g.beginFill( 0xFF0000, 1 );
            g.drawRect( 0, 0, 100, 50 );
            g.endFill();

            btn_login.buttonMode = true;
            btn_login.useHandCursor = true;

            btn_login.addEventListener( MouseEvent.CLICK, onLoginClick );

            addChild( btn_login );
        }

        if( null == btn_logout )
        {
            btn_logout = new Sprite();

            g = btn_logout.graphics;
            g.clear();
            g.beginFill( 0x0000FF, 1 );
            g.drawRect( 0, 0, 100, 50 );
            g.endFill();

            btn_logout.buttonMode = true;
            btn_logout.useHandCursor = true;

            btn_logout.addEventListener( MouseEvent.CLICK, onLogoutClick );

            addChild( btn_logout );
        }

        if( null == txt_output )
        {
            txt_output = new TextField();
            txt_output.autoSize = TextFieldAutoSize.LEFT;
            txt_output.text = "logout";
            addChild( txt_output );

            loginCheck = new LoginCheck( txt_output );
        }
    }

    private function updateButtons():void
    {
        if( null != btn_login )
        {
            btn_login.x = 10;
            btn_login.y = 10;
        }

        if( null != btn_logout )
        {
            btn_logout.x = 115;
            btn_logout.y = 10;
        }

        if( null != txt_output )
        {
            txt_output.x = 225;
            txt_output.y = 10;
        }
    }


    /**
     *  @private
     *  Added To Stage
     */
    private function onAddedToStage( event:Event ):void
    {
        removeEventListener( Event.ADDED_TO_STAGE, onAddedToStage );

        stage.align = StageAlign.TOP_LEFT;
        stage.scaleMode = StageScaleMode.NO_SCALE;

        createButtons();

        updateButtons();
    }

    /**
     *  @private
     *  Click Login button
     */
    private function onLoginClick( event:MouseEvent ):void
    {
        UserInfo.getInstance().isLogin = true;

        if( null != loginCheck )
        {
            loginCheck.checkLogin();
        }
    }

    /**
     *  @private
     *  Click Logout button
     */
    private function onLogoutClick( event:MouseEvent ):void
    {
        UserInfo.getInstance().isLogin = false;

        if( null != loginCheck )
        {
            loginCheck.checkLogin();
        }
    }
}
}


classes/LoginCheck.as
package classes
{
import flash.text.TextField;
public class LoginCheck
{
    public function LoginCheck(targetField:TextField)
    {
        field = targetField;
    }

    private var field:TextField;

    public function checkLogin():void
    {
        if( true == UserInfo.getInstance().isLogin )
        {
            field.text = "login";
        }
        else
        {
            field.text = "logout";
        }
    }
}
}

 
위와 같이 코딩하고 나면 TextField에 Text를 입력하는 부분은 Main에서 해주는게 아니라 LoginCheck라는 클래스에서 해주게 되어있다. 이때 LoginCheck의 값에 따라서 TextField에 "login"이나 "logout"을 뿌려주는데 Main에서는 그저 UserInfo라는 싱글톤 클래스에 값을 변경해주는 것 뿐이고 그것에 대한 행동은 LoginCheck라는 클래스에서 알아서 하도록 냅두기 위함이다.
여기서 싱글톤 클래스를 사용하는 한가지 예를 살펴보았다. "이게 뭔소리여"할 수도 있는데 나중에 도움이 되리라 생각하고 읽어보길 바란다.  



p.s http://goo.gl/Ueoip 이것은 싱글톤 패턴에 대한 정리로 예전에 디자인 패턴에 대해 정리한 적이 있었는데 그때 써놓은걸 간추려놓은 것임. 싱글톤 패턴을 구현하는 기술적 방법론에 대해 서술했음. 
 
2011. 2. 11. 16:19

이벤트란 무엇인가요? #3


이벤트 이야기 세번째입니다.

오늘은 bubbles와 cancelable에 대해서 설명해보려고 합니다.

일단 bubbles를 알아야 cancelable에 대해서 알아 볼 수 있지요.

bubbles라는 것에 대해 Language Reference에서 확인을 좀 해봤습니다.

....

무슨 소리지? -_-

해서 구글번역기로 돌려봤습니다.

영어를 한글로 바꾼다는 것이 좀 문제가 있으니 저정도의 어휘들은 이해하도록 하고.....-_-;;;

일단 거품이라는게 bubble을 직역을 하다보니 저렇게 표시가 된건데 이것은 이벤트가 어떤 거품이 터지는것과 비유하여 사용하게 되었다 합니다.(사실 어디선가 주워들은 이야기 ㅎ)

우리가 집중해서 봐야할 부분은 "이벤트 흐름의 세 단계를 통해 이동" 이라는 부분인데요.

이벤트에 대해서 이해하기 전에 사실 액션스크립트에서는 객체들을 XML로 뽑아내어줄 수 있다는 사실을 알고 있는게 좀 도움이 될 것 같습니다.(flash.utils.describeType() 메서드를 보면 도움이 될까요?) 이는 곧 무슨이야기냐 하면 어떤 객체가 있다면 그 객체 내부적으로 어떤 객체를 갖고 있을 수 있는지 XML로 파악할 수 있다는 이야기이고 그런 하이라키(Hierarchy)구조에서는 각 객체들의 계층관계를 파악할 수 있다는 이야기지요.

위에서 표시되는 "대상 노드"라는 것은 방금 이야기한 XML의 각 노드라고 이해하면 됩니다.(객체간 관계가 정리되어있는 XML의 각 노드지요. 족보라고 생각하면 이해가 빠릅니다.)

그래서 이벤트가 발생되는 단계를 세단계로 쪼개서 확인해보면

1. 대상 노드가 표시 목록 계층 구조의 상단에서 노드의 흐름을 캡처 단계직전.

- 이 이야기는 어떤 객체가 이벤트가 발생했을 때 이를 수신하는 상위 객체가 이벤트를 받기 직전의 상태를 이야기 합니다.

2. 이벤트를 받는 순간

3. 상위 노드로 이벤트를 계속적으로 발생시킬지에 대한 판단

이렇게 나눌 수 있습니다. 여기서 3번과정에서 판단의 기준이 되는 것이 bubbles 가 되겠습니다. 이해하기 어렵다면 예제를 만들어보면 쉽게 이해할 수 있습니다.

일단 클래스 구조를 이런 형태로 만들어 볼까요.

(위의 그림에서 화살표는 데이터의 흐름을 의미합니다.)

각각의 객체들은 Main 클래스 하위에 붙어있고 실질적으로 이벤트를 발생시키는 곳은 SubClass뿐입니다.

이벤트를 수신하는 곳은 Main클래스와 ExampleClass 이렇게 두 곳으로 두고 있습니다.

여기서 주의 할 점은 Main은 ExampleClass에서 이벤트를 수신한다는것을 조건으로 해야 이번에 만드는 샘플이 의미가 있습니다.

오늘 샘플은 Flex로 만들어 볼까요? (그래야 만들기 쉬우니까요...)

/Main.mxml


    
        
    





/classes/example/Example.as

package classes.example
{
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.MouseEvent;

import mx.core.UIComponent;

import classes.example.SubClass;

public class Example extends UIComponent
{
    public function Example()
    {
        super();
    }

    private var rect:Sprite;

    public var sub:SubClass;

    public var bubbles:Boolean = false;

    public var cancelable:Boolean = false;

    override protected function createChildren():void
    {
        super.createChildren();

        if(!rect)
        {
            rect = new Sprite();
            rect.addEventListener(MouseEvent.CLICK, rectClickHandler);
            addChild(rect);
        }

        if(!sub)
        {
            sub = new SubClass();
            addChild(sub);
        }
    }

    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);

        if(rect)
        {
            var g:Graphics = rect.graphics;

            g.clear();
            g.beginFill(0xFF0000, 1);
            g.drawRect(0, 0, 30, 30);
            g.endFill();
        }
    }

    private function rectClickHandler(event:MouseEvent):void
    {
        sub.test(bubbles, cancelable);
    }
}
}

/classes/example/SubClass.as

package classes.example
{
import flash.display.Sprite;

import classes.events.CustomEvent;

public class SubClass extends Sprite
{
    public function SubClass()
    {
        super();
    }

    public function test(bubbles:Boolean = false, cancelable:Boolean = false):void
    {   
        dispatchEvent(new CustomEvent(CustomEvent.SAMPLE_EVENT, bubbles, cancelable));
    }
}
}

/classes/events/CustomEvent.as

package classes.events
{
import flash.events.Event;

public class CustomEvent extends Event
{
    public static const SAMPLE_EVENT:String = "sampleEvent";
    public function CustomEvent(type:String, bubbles:Boolean = true, cancelable:Boolean = true)
    {
        super(type, bubbles, cancelable);
    }
}
}

위의 예제대로 만드셨다면 컴파일 ㄱㄱ

음. 확실히 flash 패키지만으로 코딩한 것과는 용량 차이가 많습니다. Flex는 용량이 커요. 역시. 하지만 그만큼 제공되거나 개발 속도도 flash 패키지만으로 작업한것보다 더 좋은건 사실입니다. 딜레마에요. 딜레마.

어찌되었든 컴파일이 성공적으로 이뤄졌다면 결과물을 확인해봐요.


bubbles 를 체크한것과 체크하지 않은 것에 대해 이벤트 버블링이 어떻게 처리되는지 확인 할 수 있습니다.

자신이 속해있는 디스플레이 객체에만 전달할 것인지 아니면 그보다 상위 객체에 전달하게 할것인지 결정할 수 있다는 이야기인데요. bubbles 속성이 true인 대표적인 이벤트가 MouseEvent 입니다.

자. 그럼 여기서 문제로 거론되는 건데요.

bubbles가 true인 이벤트를 받았을때 addEventListener 되는 객체가 실제로 해당 객체인지 아닌지에 대해 고민해 볼 필요가 있습니다.

위의 예제를 살펴보면 Example 클래스에서 addEventListener해도 받을 수 있고 Example클래스 안에있는 SubClass라는 클래스에서도 받을 수 있지요. 하지만 실질적으로 Example에서 받은 이벤트는 실제로 Example 클래스에서 발생시킨게 아니지요. 이벤트를 실제로 발생시킨 객체와 이벤트를 bubbles의 여부에 따라 중계해준 클래스. 이 두개의 상황에 대해 이벤트는 아주 괜찮은 방법을 제시하고 있습니다.

currentTarget과 target이라는 속성들이지요.

이것들을 레퍼런스에서 확인해보면

라고 정의 되어있습니다.

사실 저는 무슨말인지 번역기를 돌려봐도 모르겠어서 그냥 위에서 만든 샘플을 조금 손보기로 했습니다.

/Main.mxml

 <?xml version="1.0" encoding="utf-8"?>
<mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:example="classes.example.*"
    creationComplete="creationCompleteHandler(event);">
    <mx:Script>
        <![CDATA[
        import flash.events.MouseEvent;
        import mx.events.FlexEvent;
        import classes.events.CustomEvent;

        private function creationCompleteHandler(event:FlexEvent):void
        {
            exampleRect.addEventListener(CustomEvent.SAMPLE_EVENT, exampleRectSampleEventHandler);
            exampleRect.sub.addEventListener(CustomEvent.SAMPLE_EVENT, subSampleEventHandler);
        }

        private function bubblesChkClickHandler(event:MouseEvent):void
        {
            exampleRect.bubbles = bubblesChk.selected;
        }

        private function subSampleEventHandler(event:CustomEvent):void
        {
            ta.text += ("event Listen : subClass eventListen\n");
            ta.text += "sub currentTarget : " + event.currentTarget + "\n";
            ta.text += "sub target : " + event.target + "\n";
        }

        private function exampleRectSampleEventHandler(event:CustomEvent):void
        {
            ta.text += "event Listen : exampleRect event Listen\n";
            ta.text += "example currentTarget : " + event.currentTarget + "\n";
            ta.text += "example target : " + event.target + "\n";
        }
        ]]>
    </mx:Script>
<mx:TextArea id="ta" width="300" height="200" editable="false" />
<mx:CheckBox label="bubbles" id="bubblesChk" click="bubblesChkClickHandler(event);" />
<example:Example id="exampleRect" width="30" height="30" />
</mx:Application>

(코드 하이라이트가 mxml 코드는 잘 못그려내는 바람에 그냥 코드를 썼어요)

이렇게 수정하고 확인해보면 이렇습니다.

(이미지입니다. 클릭해서 낚이는 불상사는 피해주세요)

Example에서 수신된 이벤트의 currentTarget은 이벤트를 발생시킨 SubClass가 아닌 자신을 반환하고

target은 실제로 이벤트를 발생시킨 SubClass인것을 확인할 수 있습니다.

사실 이번시간에 cancelable까지 다 손을 보려고 했습니다만 너무 양이 많아졌네요.

cancelable은 다음시간에 이야기하도록 해요.

p.s 마지막에 target과 currentTarget을 확인하려고 만든 샘플은 이 밑에 있어요.

2011. 1. 20. 15:26

이벤트란 무엇인가요? #2

#1 에서 이벤트 등록하고 핸들러를 작동하는 것까지 확인해봤는데요..
어차피 이벤트라는게 다 사람이 만든건데 그거 내가 만들어서 작동 못할것도 없잖아요.
그래서 만들어봅니다. 커스텀 이벤트!

근데 커스텀 이벤트를 만들겠다고 거창하게 이야기했지만 결국 뭐 부터 시작해야할지 모르겠군요.
일단 우리가 흔히 보는 MouseEvent를 한번 확인해보도록 하지요.
Language Reference에서 보면 MouseEvent의 족보에 대해 파악해 볼수가 있습니다.
flash.events 패키지에 위치하고 있고 public class고 Event를 상속받았군요.
Subclasses들은 뭐.. MouseEvent를 상속받은 놈들을 알려주는 거니까요. 지금은 신경쓸 필요는 없어 보이네요.

일단 Event라는 놈들은 원래 죄다 flash.events.Event 클래스를 상속받습니다. 물론 Event클래스를 만들 수도 있을지 모르겠습니다만 나중에 이벤트를 발생시킬때 Event 를 상속받은 놈들만 발생시킬 수 있거든요.

그렇다면 일단 Event를 상속받아 보지요.

package classes.events
{
import flash.events.Event;

public class CustomEvent extends Event
{
    public function CustomEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
    {
        super(type, bubbles, cancelable);
    }
}
}

요렇게 하면 일단 이벤트는 상속받았고.. 일단 Event를 만들었으니 잘 작동하나 볼까?... 했지만 이것만 갖고는 뭘 어떻게 해야할지 감이 안옵니다.
좀 그럴싸하게 이벤트 상수를 만들어보도록해요.

package classes.events
{
import flash.events.Event;

public class CustomEvent extends Event
{
    public static const SAMPLE_EVENT:String = "sampleEvent";
    public function CustomEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false)
    {
        super(type, bubbles, cancelable);
    }
}
}

저렇게 해놓고 이벤트를 발생시킬 클래스가 필요하니 클래스를 하나더 만들어보아요.

package classes.example
{
import flash.events.EventDispatcher;

import classes.events.CustomEvent;

public class ExampleEventDispatcher extends EventDispatcher
{
    public function ExampleEventDispatcher()
    {
        super();
    }

    public function checkDispatch():void
    {
        dispatchEvent(new CustomEvent(CustomEvent.SAMPLE_EVENT));
    }
}
}

그럼 chkeckDispatch메서드를 호출생하기만 하면 CustomEvent.SAMPLE_EVENT가 발생하게 됩니다.
그럼 한번 테스트 해볼까요.. 테스트할 Main.as 는 이렇게 만들어 봅시다.

package
{
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.MouseEvent;
import flash.text.TextField;

import classes.events.CustomEvent;
import classes.example.ExampleEventDispatcher;
public class Main extends Sprite
{
    public function Main()
    {
        super();
        setTextField();
        setGreenButton();
        setExample();
    }

    private var textField:TextField;

    private var greenBtn:Sprite;

    private var exam:ExampleEventDispatcher;

    private function setExample():void
    {
        if(!exam)
        {
            exam = new ExampleEventDispatcher();

            exam.addEventListener(CustomEvent.SAMPLE_EVENT, examSampleEventHandler);
        }
    }

    private function setTextField():void
    {
        if(!textField)
        {
            textField = new TextField();

            textField.width = 200;
            textField.height = 300;
            textField.y = 10;
            textField.multiline = true;
            textField.background = true;
            textField.backgroundColor = 0xFFFFFF;
        }

        addChild(textField);
    }

    private function setGreenButton():void
    {
        if(!greenBtn)
        {
            greenBtn = new Sprite();

            var g:Graphics = greenBtn.graphics;

            g.clear();
            g.beginFill(0x00FF00, 1);
            g.drawRect(0, 0, 50, 50);
            g.endFill();

            greenBtn.x = 210;
            greenBtn.y = 10;

            greenBtn.buttonMode = true;
            greenBtn.useHandCursor = true;

            greenBtn.addEventListener(MouseEvent.CLICK, greenBtnClickHandler);
        }

        addChild(greenBtn);
    }

    private function greenBtnClickHandler(evnet:MouseEvent):void
    {
        exam.checkDispatch();
    }

    private function examSampleEventHandler(event:CustomEvent):void
    {
        textField.appendText("Listen from ExampleEventDispatcher\n");
    }
}
}

컴파일을 하고 실행시키면 아래처럼 나오게 되면 성공..

<


녹색버튼을 마구마구 누르면 왼쪽의 텍스트 필드에 메시지가 찍히게 됩니다. 이게 바로 사용자 정의 이벤트. 커스텀 이벤트의 기초적인 내용이에요. 좀더 복잡한 내용이 있지만 오늘은 여기까지.
궁금한게 있다면 Google을 애용해주세요. ㅎ

이벤트란 무엇인가요? #3 예고편! 두둥!

1. bubbles는 뭔가요? cancelable은요?
2. target? currentTarget? 다 거기서 거기 아닌가요?
3. 악 왜 에러가 나는거에요! clone 메서드의 존재이유.

2011. 1. 14. 11:00

저렴하게 AIR 개발하기

새벽에 잠이 들어야하는데 (아침 출근길이 걱정입니다) 잠이 안와 이벤트 시리즈와는 별도로 저렴하게 AIR를 개발하는 법을 소개하려고 합니다.

Flex Builder나 그외 IDE 툴로 개발하면 무척이나 편리하지만 기본적인 원리를 알게 되면

혹여나 다른 열악한 환경에서도 개발하게 되더라도 도움이 되지 않을까 생각되서 소개하려고 합니다.

 

일단 AIR는 Flex와는 다르게 좀 환경이 복잡합니다. 기본적으로 소개는 여기를 확인하면 됩니다.

AIR를 개발하려면 (Ajax AIR는 제 전문분야가 아니라서 생략하도록 할께요) 기본적으로 "AIR application descriptor file" 이라는 것을 작성해야합니다.

Builder에서 개발할때 기본적으로 만들어지는 코드 파일 외에 [프로젝트명_app.xml] 파일이 생겨날 것인데 이게 바로 방금 말했던 AIR application descriptor file 입니다.

이 파일에는 AIR를 실행하거나 혹은 패키징할때 필요한 정보들을 담게 되는데요. 이 파일이 없으면 아무것도 할 수 없습니다. 그러니 이 셋팅은 매우 중요합니다.(여기를 참고하세요)

 

Flex 3 Developer Guide에 명시된 내용을 한번 볼까요.



    samples.flex.HelloWorld
    0.1
    HelloWorld
    
        HelloWorld.swf
        true
        none
        true
        400
        200
    

요래 적혀있습니다. 각 노드마다 설명은 다음과 같습니다.

  • id : 어플리케이션의 기본 아이디로 어떤 어플리케이션인지 알만한 단어로 써주면 좋습니다.
  • version : 어플리케이션의 버젼인데 나중에 배포할때 이것을 기준으로 업데이트 할지 말지 결정합니다.
  • filename : 어플리케이션의 메인 파일이 무엇인지 명시합니다. 이때는 확장자는 제외합니다.
  • initialWindow : 어플리케이션 실행시 표시되는 윈도우 속성들은 제어합니다.
  • content : 어플리케이션에서 표시될 content가 무슨 파일인지 명시합니다.
  • visible : 우리가 알고 있는 일반적인 visible 속성과 같습니다.
  • systemChrome, transparent : 설명이 기니 링크를 따라가세요.
  • width, height : 어플리케이션 윈도우의 너비와 높이입니다.

일단 이만큼만으로도 샘플 파일을 컴파일하거나 실행시킬 수 있습니다.

그 외에 아이콘이라던가 하는 부분들도 있습니다만 오늘은 살짝 맛만 보도록하지요. ㅎ

 

그럼 일단 위의 모양대로 파일을 하번 만들어 보지요. 이왕에 하는거 HelloWorld.as 파일도 만들어 보아요.

HelloWorld.as 파일의 내용은 아래처럼 합니다.

 

package
{
import flash.display.Sprite;
public class HelloWorld extends Sprite
{
    public function HelloWorld()
    {
        super();
        trace("Hello World");
    }
}
}

 

컴파일을 해야겠는데 AIR 컴파일은 amxmlc 명령어로 컴파일합니다.

 

이렇게 컴파일을 하고 난 뒤에 우리 한번 실행을 해야겠는데 무턱대고 swf 를 Flash Player로 실행하면 안됩니다. 이때 명령어는 adl 이라는 것을 이용하는데요. 이것은 AIR Debug Launcher 의 약자로 디버그용으로 확인할때 사용합니다.

개발중에 패키징하고 인스톨해서 실행해보는 모든과정을 일일이 하면 얼마나 귀찮습니까. 그래서 adl을 통해 실행합니다.

 

일단 오늘은 맛만 보는거니까 이렇게만 실행해 볼까요.

 

참. 아까도 적어놨지만 실행을 할때는 AIR application descriptor file를 이용해서 실행합니다.

이렇게...

 

 

하려고 했으나 "error while loading initial content"라고 하면서 실행이 안됩니다. 왜 이러지? 라고 생각하면서 Developer Guide를 뒤지고 난리법석을 피웠었는데 이유는 의외의 곳에서 문제가 있습니다. SDK가 3.2 버젼 이후부터는 xmlns="http://ns.adobe.com/air/application/1.0"이게 아니라 xmlns="http://ns.adobe.com/air/application/1.5"로  변경되었습니다.(Adobe 이것들. Developer Guide도 수정을 안해놓다니.. 사람 뻘짓하게..)

 

게다가 systemChrome이라던가 transparent라던가 하는 옵션값을 좀 수정을 해야합니다.

왜냐하면 위에서 소개한 옵션으로는 투명한데다가 systemChrome도 없어서 어플리케이션이 실행되었어도 눈에 보이지 않기 때문이지요.

 

AIR application descriptor file을 아래와 같이 수정합니다.

 



    HelloWorld
    0.1
    HelloWorld
 Hello World
    
  
        HelloWorld.swf
        standard
        false
        true
        640
        480
    

 

그럼 좀전에 했던것 같이 컴파일 하고 adl을 이용해 실행해볼까요.

 

위와같은 빈창이 뜨게 됩니다. 와우. 성공. 그리고 프롬프트 창을 확인하면

 

 

저렇게 Hello World를 화면에 표시해줍니다.

와우.. 그냥 웹용으로 개발할때는 그럭저럭 할만하다가 AIR로 개발할라 치니까 복잡해 죽겠네요. :)

셋팅할것도 많고.. 하지만 어느날 갑자기 이클립스도 어떻게 설치할 수 없는 열악한 상황에서도

이런 방법을 통해 개발을 진행할 수 있다는 것만 알아두세요.(평소엔 그냥 Builder 개발 고고)

 

참고로 java 를 통해서도 컴파일이 가능하니 그건 Developer Guide를 참고 하시면 도움이 될거에요.

이제 슬슬 잠이 옵니다. 오늘은 이만할게요.

p.s : SyntaxHighlighter 기능중에 XML 을 처리하는 모듈에서 xmlns 로 네임스페이스를 지정할때 xmlns:xx 식으로 처리하지 않으면 자동으로 특정문자열이 들어가네요. 그걸 막으려고 xmlns: 라고 적어놨는데 xmlns 로 봐주시면 됩니다.

2011. 1. 13. 20:53

이벤트란 무엇인가요? #1


내가 처음 3.0을 접했을때가 불과 2년전이었는데 그때 가장 어렵게 느껴졌던게
도무지 "이벤트"라는 녀석과 친해지기 어렵다는 사실이었다.

도데체 왜 "이벤트"라는 놈을 갖고 코드를 작성해야만 할까.
"이벤트"없이 코딩할 수 있지 않을까?

라는 고민으로 수없이 많은 생각들을 하곤했었는데

그것에 대한 결론으로는 여기를 확인하면 좀 도움이 되리라 생각된다.
결론적으로는 ActionScript의 태생적 이유때문이라는 것인데 ECMAScript를 모태로 삼고있는
ActionScript로써는 개발자가 원하는 흐름대로 코드를 진행하기 위해서는 이벤트를 사용해야만 한다고
결론 지었다.
또다른 결론이 하나 더 있는데 이는 어플리케이션을 구성하는데 있어서 구조적으로 "약한결합"을 사용해야 유지보수, 클래스의 컨포넌트 화(化), 등이 용이하다는 결론.

위와같은 결론 덕에 결국 이벤트를 사용하긴 해야겠는데 뭘 어떻게 써야하는 것인지 당최 배우기가 마땅찮다.
그래서 일단 이벤트를 추가하고(import), 등록하고(addEventListener), 행동(Handler) 하는 3가지 단계를 마스터 해보자.

package
{
import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.MouseEvent;

import flash.text.TextField;

public class Main extends Sprite
{
    public function Main()
    {
        super();

        createSprite();
        createTextArea();
    }

    private var sp:Sprite;
    private var ta:TextField;

    private function createSprite():void
    {
        if(!sp)
        {
            sp = new Sprite();

            addChild(sp);

            drawSprite();
        }
    }

    private function drawSprite():void
    {
        if(sp)
        {
            var g:Graphics = sp.graphics;

            g.clear();
            g.beginFill(0x00FF00, 1);
            g.drawRect(0, 0, 100, 100);
            g.endFill();

            configureSpriteListeners();
        }
    }

    private function createTextArea():void
    {
        if(!ta)
        {
            ta = new TextField();
            ta.width = 200;
            ta.height = 300;
            ta.x = 110;
            ta.y = 0;
            ta.multiline = true;
            ta.background = true;
            ta.backgroundColor = 0xFFFFFF;

            addChild(ta);
        }
    }

    private function configureSpriteListeners():void
    {
        sp.addEventListener(MouseEvent.CLICK, spriteClickHandler);
    }

    private function spriteClickHandler(event:MouseEvent):void
    {
        ta.appendText("click sprite! \n");
    }
}
}
위의 코드를 한글로 번역을 하자면 이렇다.
"sp라는 Sprite객체를 만들어서 너비 100, 높이 100 사이즈의 초록색 네모를 그린다음 클릭하면 "click sprite!"라는 메시지를 ta라는 TextField객체에 출력해라!"

라는 것이다. 컴퓨터 나라말은 참 다양하지만(JAVA, C, C++, ActionScript, JavaScript.. 등등) 번역하고 보면 별거 없는 것같은 기분이다.
어쨌든 저 코드를 컴파일 해보자.
 
그럼 저 Main.swf를 실행해볼까.. (아래의 녹색 상자를 클릭해보세용)



간단하게 CLICK 이벤트를 등록하고 작동하게하는 예제를 만들어 봤다.

나름 간단하게 작성된 예제인데 여기서 드는 의문점은 해당 클래스에 등록할 수 있는 이벤트들은 어디서 확인하느냐는 의문.
그런 이벤트에 관련된 사항은 Language Reference를 확인하면 된다.
예를들어 방금 사용한 Sprite 클래스를 확인해보면

어떤 이벤트를 등록(addEventListener) 할 수 있는지 확인할 수 있다.
게다가 친절하게도 어떤 시점에서 이벤트가 발생하는지 설명도 해줬다. 좀 괜찮은데?

예전에 같이 일하던 분이 요런 말씀을 해주셨더랬다.
"궁금하면 API 뒤져봐"
그말이 진리. ㅎㅎㅎ

Sprite 클래스는 내부적으로 스스로를 클릭하면 CLICK 이벤트를 발생하게끔 설계되어있다.
그럼 우리는 그런 이벤트를 수신할 수 있도록 addEventListener를 통해 이벤트를 등록한다.
이때 같이 들어가는 Handler는 이벤트가 수신했을 때 실행하게 된다.

대략 이런 모양.
이미지 출처는 제가 그렸으므로 함부로 퍼가면 안됩니다. 말씀은 해주시고 퍼가세요.

Sprite는 addEventListener를 하든 말든 어차피 클릭을 하면 CLICK이벤트를 발생시킨다.
그럼 어떤 누군가는 addEventListener를 해서 핸들러를 실행시키겠지.
이벤트의 등록과 핸들러의 실행의 구조적인 모양새는 이런 모양이다.
그럼 클래스도 누군가가 만들어 사용하는 걸텐데 (적어도 신이 만든건 아닐거 아냐) 나도 만들어 볼 수 있지 않을까??

두둥. "이벤트란 무엇인가요? #2" 예고편.

1. 내가 만든 이벤트?
2. dispatchEvent?
3. 오오오오.....(뭐지???)
2011. 1. 13. 20:44

ActionScript Class 만들기(기본)

Actionscript 3.0 은 클래스 단위로 프로그램을 구성한다.(적어도 내 생각엔)
그럼 어떤 프로그램을 만들기 위해서 가장 먼저 선행되어야 하는 것은 클래스를 만들 줄 알아야한다는 것이니
한번 시작해보자.

일단 준비물을 살펴보자.

Flex SDK 3.5.0 : 다운로드
빌더, 혹은 에디터 툴.(난 Editplus로 개발하니깐 Editplus)

뭐 이정도로 간단하게 준비가 끝났으면 시작해보자.
일단 간단하게 소스 저장용 폴더와 파일 하나를 하나 만들자.
여기서 파일명은 Main.as 로 하자.

Main.as 파일에는 이렇게 코딩해볼까.

Main.as 파일에는 이렇게 코딩해볼까.
package
{
import flash.display.Sprite;

public class Main extends Sprite
{
    public function Main()
    {
        super();
    }
}
}
이러면 우리 클래스를 만든거다.
실행하려면 아까 우리가 준비했던 Flex SDK를 이용해 컴파일을 하면 swf 파일이 떨어진다.
아까 sdk 파일 폴더로 가자. 그 폴더안에 가보면 "bin" 이라는 폴더가 있는데
그 안에 살펴보면 mxmlc라는 파일이 있다.
저 명령어를 이용하면 된다.
아까의 작업폴더로 이동한 뒤(프롬프트 창에서) 아래와 같이 입력한다.

 [mxmlc가 있던 sdk의 bin 폴더]\mxml Main.as -o Main.swf

결과화면은 아래와 같다.

오. Main.swf 파일이 생성되었다. 556bytes 뿐이다.
폴더를 확인했더니


정상적으로 파일이 생성된것을 확인할 수 있다.
그럼 실행해볼까.

아무것도 없는 빈파일이다. 이렇게 그냥 끝내면 아쉬우니까 네모칸 하나 만들어 넣자.
아까의 Main.as 소스를 아래와 같이 수정한다.

package
{
import flash.display.Sprite;
import flash.display.Graphics;

public class Main extends Sprite
{
    public function Main()
    {
        super();

        drawRect();
    }

    private function drawRect():void
    {
        var g:Graphics = this.graphics;

        g.clear();
        g.beginFill(0xFF0000, 1);
        g.drawRect(0, 0, 100, 100);
        g.endFill();
    }
}
}

수정한뒤에 다시 첨에 했던것처럼 컴파일.
그담에 생성된 swf 파일을 확인해보자.


예상한대로 빨간 사각형이 그려졌다.
음. 좋군. 클래스 만들기 별것 아니로구나.

도움글 :