2010년 1월 11일 월요일

Flex BlazeDS 세팅

방장은 누규? 채팅사이트 조이팅스토리를 운영하며 마케팅, 사업 등에 관심이 많은 철이

 

 

Flex 게시판을 위한 간단한 세팅...

 

[환  경]

OS : WinXP

DB : MySql( oracle 좋은지 이제야 느낀다..;; )

Server : Tomcat 6.X

FF : Eclipse(Flex-plug-in)

DS : BlazeDS ( 후...;;; 저 단어만 보면.. 아직도.. 화가나 .. -_- )

 

 

1단계

BlazeDS 다운 받기

http://opensource.adobe.com/wiki/display/blazeds/Release+Builds 에서 Turnkey 버전으로

다운받는다.. 예제소스 및 BlazeDS.war, tomcat이 포함되어있어 샘플을 보며 확인하기 좋다.

 

다운 완료후 압축을 풀면 BlazeDS.war 파일이 보일것이다.. (일단 세팅이 먼저이므로 세팅)

 

꼭 넣어야하는지는 아직 잘 모르겠으나... BlazeDS.war 를 tomcat 의 webapps에 넣고 실행해준다.

 

그리고 이클립스 실행~!

 

new -> Flex Project 클릭~!

 

이미지를 클릭하시면 원본크기로 보실수 있습니다.

 

 

프로젝트 이름 설정, 프로젝트 위치는 개인별 취향대로

Server technology는 J2EE 나머지는 아래와같이 한후.. next

이미지를 클릭하시면 원본크기로 보실수 있습니다.

 

 

Target runtime 은 아파치 톰캣선택

Context root는 짧게 써주자.. (단, 기억해둘것)

Flex WAR file 은 사용자 마다 경로가 틀릴수 있으니 BlazeDS.war가 있는 경로면 ok!

next -> Finish 하면 프로젝트 완성

이미지를 클릭하시면 원본크기로 보실수 있습니다.

 

프로젝트에서 오른쪽키 Properties 에서 Flex Build Path 의 아래부분이 처음에 세팅해준 Context root

와 같은지 확인

 

이미지를 클릭하시면 원본크기로 보실수 있습니다.

 

Flex Server 메뉴에서도 아래와 같이 URL 마지막고 Context Root를 일치시켜주고 Validate Location

을 클릭

이미지를 클릭하시면 원본크기로 보실수 있습니다.

 

이렇게 확인한뒤 ok 해주면 기본 프로젝트 세팅은 끝~!

 

현재 프로젝트를 톰캐서 서버에 Add 시켜준뒤 서버 구동 그리고 FlexBoard.mxml 선택후 Flex Server

로 실행.. 했을때 플렉스 고유의 녹색화면이 나오면 1단계성공

 

[2단계]

다음으로는 BlazeDS 나머지세팅..할차례

 

FlexBoard -> WebContent -> WEB-INF -> Flex 폴더의 services-config.xml 의

<service> 태그 안쪽에 아래처럼 코드를 추가

이미지를 클릭하시면 원본크기로 보실수 있습니다.

 

여기까지가 프로젝트 기본 세팅이 되겠다.. 다음 포스팅에서는 RemoteObject 세팅과 DAO, VO,

그리고 mxml 작성을 보도록 하겠심다..;; 아.. 왜케 힘들지... -_-;;;

 

 

출처: http://blog.naver.com/roadmn

 

 

 

 

 

 



P 이경철님의 파란블로그에서 발행된 글입니다.

2010년 1월 10일 일요일

Flash Player에서 P2P를? Adobe Stratus에 대해

방장은 누규? 채팅사이트 조이팅스토리를 운영하며 마케팅, 사업 등에 관심이 많은 철이

 

 

Adobe Stratus는 Flash Player 10이상 또는 AIR 1.5 이상에서 RTMFP(Real Time Media Flow Protocol)이라 불리우는 통신프로토콜을 이용하여 클라이언트끼리 Peer to Peer(P2P)가 가능하게한 기술이다. TCP가 아닌 UTP를 기반으로 하기 때문에 통신 속도에는 좋지만 데이타 전송에 대한 신뢰도는 떨어지지 않을까 판단한다. 이러한 이유로 Stratus는 채팅, 각종 미디어 통신, 멀티 게임등을 만드는데 적합하다고 할 수 있다. 

 

위 그림에서 볼 수 있듯이 기존 RTMP는 사용자가 서버를 통해서만 다른 사용자에게 접근이 가능했다. 하지만 Adobe Stratus에서 제공하는 RTMFP를 이용하면 처음 Staratus 서버와 접속하여 인증절차만 완료하면 Flash Player 끼리 P2P통신이 가능해진다. 이렇게 함으로서 서버의 부하를 줄일 수 있게 되었다.

필자는 Adobe에 올라온 예제를 가지고 테스트를 해봤다. 이 예제는 Adobe Stratus를 가지고 어떤 기능까지 가능한가 총체적으로 보여주는 예제이다. 단순한 채팅뿐 아니라 마이크, 스피커, 비디오 예제가 한 소스에 들어가 있다. 예제는 [여기]에서 실행해볼 수 있고 또한 다운로드 받을 수 있다. 

필자는 제공된 Stratus 예제 Flex 애플리케이션을 수정하지 않고 서버측 사용자 등록하는 코드인 reg.cgi를 PHP로만 포팅해서 테스트 해봤다. 

이것을 테스트 하기 위해 다음단계를 거친다. 

  1. Flash Player 10 이상 설치한다.
  2. Flex SDK 3.2 또는 Flash Builder 3.0.2 를 설치한다.
  3. Stratus beta 개발 키를 받는다. Adobe에 가입해야한다.
  4. 참고로 Flash Player 10 API 문서를 본다. (한글은 여기)
  5. “Stratus service for developing end-to-end applications using RTMFP in Flash Player 10” 문서를 본다. 우야꼬님이 번역한 문서는 [여기]를 참고
  6. 예제 프로그램 다운로드
  7. 질문 및 답변은 여기로

 

Stratus는 현재 Adobe측에 서버를 두고 있고 공개하고 있진 않다. 그러므로 자신의 서버에 Stratus를 설치하지는 못한다. 하지만 개발키를 부여하고 있기 때문에 “rtmfp://stratus.adobe.com/개발키” 에 접속해서 언제든지 개발은 가능하다. 아래는 개발키를 가지고 Adobe의 Stratus서버에 접속인증을 하는 예제이다.

 

1.private const StratusAddress:String = "rtmfp://stratus.adobe.com";
2.private const DeveloperKey:String = "your-developer-key";
3.private var netConnection:NetConnection;
4.  
5.netConnection = new NetConnection();
6.netConnection.addEventListener(NetStatusEvent.NET_STATUS,
7.netConnectionHandler);
8.netConnection.connect(StratusAddress + "/" + DeveloperKey);

 

다운로드 받은 예제에서 사용자 등록 웹서비스를 위한 reg.cgi는 python으로 만들어졌다. python에 대해서 아는바가 별로 없어서 이 코드를 PHP에서 동일하게 동작하도록 만들었다. 또한 DB는 간편하게 만들기 위해 SQLite를 이용한다. 

먼저 개인 리눅스 서버에 sqlite를 설치했다.(안녕리눅스 pkgadd를 이용해서 간편하게 설치함 ^^). 다른 환경이라면 그 환경에 맞게 설치하면 되겠다.(http://www.sqlite.org/ 에서 다운로드 받아 설치한다.)

 

# pkgadd sqlite

Zend Optimizer requires Zend Engine API version 220051025.

The Zend Engine API version 220060519 which is installed, is newer.

Contact Zend Technologies at http://www.zend.com/ for a later version of Zend Optimizer.

sqlite          : Install 성공

 

sqlite는 아래처럼 mysql과 비슷하게 사용이 가능하다.

# sqlite test.db

SQLite version 2.8.17

Enter ".help" for instructions

sqlite> create table test(idx integer);

sqlite> .table

test

sqlite> insert into test(idx) values(1);

sqlite> insert into test(idx) values(2);

sqlite> insert into test(idx) values(3);
sqlite> select * from test;

1

2

3

sqlite> .quit

 

여기서 사용할 DB를 만들기 위해 아래와 같이 원하는 폴더에 staratus_registration.db를 만들자. 만들고 그냥 저장하면 된다.

 

vi staratus_registration.db

 

아래는 만들어진 SQLite DB(staratus_registration.db )를 이용해 사용자 접속 처리를 하는 PHP(reg.php) 코드이다. 참고로 PHP 5.2 이상 환경이고 이 환경에서는 SQLite에 대한 API를 제공하고 있다.

 

001.<?php
002.    $_METHOD = $_GET; // $_GET 또는 $_POST;
003.    $username = $_METHOD['username'];
004.    $identity = $_METHOD['identity'];
005.    $friends = $_METHOD['friends'];
006.  
007.    header("Content-type:text/plain;charset=utf-8");
008.    header("Cache-Control: no-cache, must-revalidate");
009.    header("Pragma: no-cache");
010.  
011.    echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
012.    echo "<result>";
013.  
014.    //DB 열기
015.    $db = @sqlite_open('stratus_registrations.db', 0777, $error);
016.    if( $db )
017.    {
018.        //Table 존재 확인
019.        $result = @sqlite_query($db, "SELECT name FROM sqlite_master WHERE type='table' AND name='registrations'");
020.        $tables = @sqlite_fetch_all( $result );
021.  
022.        $exist_table = true;
023.  
024.        //테이블이 존재하지 않는다면 테이블을 생성함
025.        if( count($tables) == 0 )
026.        {
027.            //Table 생성
028.            $query = "CREATE TABLE registrations (
029.                            m_username VARCHAR COLLATE NOCASE,
030.                            m_identity VARCHAR,
031.                            m_updatetime DATETIME,
032.                            PRIMARY KEY (m_username) );
033.                    CREATE INDEX registrations_updatetime ON registrations (m_updatetime ASC);";
034.            if( !@sqlite_exec($db, $query, $error) )
035.            {
036.                echo "<error>$error</error>";
037.                $exist_table = false;
038.            }
039.        }
040.  
041.        //Table이 존재하는 경우만 실행
042.        if( $exist_table )
043.        {
044.            //개인접속
045.            if( $username )
046.            {
047.                //기존 사용자 있는지 확인
048.                $select_query = "SELECT m_username, m_identity FROM registrations WHERE m_username = '$username'";
049.                $result = @sqlite_query($db, $select_query);
050.                $array = @sqlite_fetch_array($result);
051.  
052.                //사용자 추가 및 업데이트
053.                if( $array )
054.                {
055.                    $update_query = "update registrations SET m_identity='$identity', m_updatetime=datetime('now') where m_username='$username';";
056.                    //echo "<update_query>$update_query</update_query>";
057.                    if( sqlite_exec($db, $update_query$error) )
058.                    {
059.                        echo "<update>true</update>";
060.                    }
061.                    else
062.                    {
063.                        echo "<update>false</update>";
064.                        echo "<error>$error</error>";
065.                    }
066.                }
067.                else
068.                {
069.                    $insert_query = "insert into registrations (m_username, m_identity, m_updatetime) values ( '$username', '$identity', datetime('now'))";
070.                    //echo "<insert_query>$insert_query</insert_query>";
071.                    if( sqlite_exec($db, $insert_query, $error) )
072.                    {
073.                        echo "<update>true</update>";
074.                    }
075.                    else
076.                    {
077.                        echo "<update>false</update>";
078.                        echo "<error>$error</error>";
079.                    }
080.                }
081.            }
082.            //친구연결요청(1시간 동안 접속을 하지 않은 사람은 무시)
083.            else if( $friends )
084.            {
085.                $f = trim($friends);
086.                echo "<friend><user>$f</user>";
087.                $select_query = "SELECT m_identity from registrations where m_username = '$f' and m_updatetime > datetime('now', '-1 hour')";
088.                $result = @sqlite_query( $db, $select_query );
089.                $array = @sqlite_fetch_array($result);
090.                if( $array )
091.                {
092.                    echo "<identity>$array[m_identity]</identity>";
093.                }
094.                echo "</friend>";
095.  
096.            }
097.  
098.        }
099.    }
100.    else
101.    {
102.        echo "<error>$error</error>";
103.    }
104.  
105.    echo "</result>";
106.?>

 

“http://당신의 Domain/경로/reg.php?user=jidolstar&identity=발급받은개발키” 로 접속해보자. 아래와 같이 나오면 정상이다. 이것은 처음 사용자가 접속했을때 인증하기 위한 것이다.

 

에러가 발생하면 PHP파일과 DB가 같은 폴더에 있는지 확인하고 그래도 문제가 있다면 DB를 포함하는 폴더의 권한을 777로 바꿔보자.

 

 

이번에는 http://당신의 Domain/경로/reg.php?friends=jidolstar 로 접속해보자. 아래와 같이 나오면 정상이다. 이것은 상대방이 접속요청을 할때 인증하기 위한 것이다.

 

 

다운 받은 Flex코드를 새 Flex 프로젝트를 만들어 복사한뒤 Application내에 개발키와 웹서비스 URL를 설정한뒤 실행한다.

 

1.// stratus address, hosted by Adobe
2.[Bindable] private var connectUrl:String = "rtmfp://stratus.adobe.com"
3.  
4.// developer key, please insert your developer key here
5.private const DeveloperKey:String = "Adobe에서 발급받은 개발키"
6.  
7.// please insert your webservice URL here for exchanging
8.private const WebServiceUrl:String = "http://당신의 URL/설치디렉토리/reg.php";

 

성공한다면 아래처럼 시원하게 채팅이 가능할거다. 물론 웹캠과 마이크가 있다면 더욱 완벽한 테스트가 가능하다.

 

 

필자가 못생겨서 더이상 읽고 싶지 않나? 할 수 없다. 이미 더 이상 쓸 말도 없다. ㅋ

 

출처: http://blog.jidolstar.com/498?expandComment=1

 

 

 

 

 



P 이경철님의 파란블로그에서 발행된 글입니다.

[FLEX] Struts + iBATIS + FLEX 초간단게시판-_-;

아...계속 게시판만 만드네요-_-; 그래도 데이터연동의 가장 기본이 되는것을 해야하니깐-_-;

테스트환경은 TOMCAT6.0 + JDK 6.0 + Struts 1.3.8 + iBATIS 2.3.0 + Flex3 Beta 2 에서 했습니다.

저번엔 RemoteObject로 삽질했는데 이제 HTTPService로 삽질하네요.
우선 RemoteObject는 해당클래스를 직접 호출해서 가져다가 쓰는 것이구요.
HTTPService는 말그대로 http를 통해 주소값을 넣으면 그것을 실행하게 되는 것인데요.
그 실행해서 나오는 값이 xml형태이면 xml태그에 있는 값을 가져올 수 있습니다.

그렇다는 얘기는 서버단에서는 Struts와 iBATIS를 통해서 DB와 연동해서 데이터를 xml형태로 리턴해주면 그것을 FLEX에서 값을 가져다가 DataGrid에 넣든 쇼를 하든 할 수 있다는 얘기죠.

그리 대단한 건 아니지만, HTTPService로 삽질하시는 분들께 도움이 되고자-_-;

FlexBoard.war
Struts + iBATIS를 이용한 xml을 리턴하는 게시판입니다.
즉 http://localhost:8080/FlexBoard/list.do 을 실행하게 되면 결과값이 xml코드를 리턴한다는 얘기죠.
그런식으로 나타낸 xml코드를 flex에서 가져다가 DataGrid에 넣도록 되어있습니다.

ClientFlexBoard.zip
Flex Project Archive로 export한 프로젝트입니다.
이거는 저 위에 있는 FlexBoard.war에 포함되어있는 swf를 만들어주는 프로젝트입니다.
즉, 그냥 서버단에서 리턴하는 xml을 가져와서 DataGrid에 넣기도하고, http서비스를 이용하여 파라메터를 던져서 해당 DB를 컨트롤하기도 하고 그런거하는 겁니다.

예제주소입니다.

DB구조는.....
CREATE TABLE `Board` (
  `b_id` int(11) NOT NULL auto_increment,
  `b_name` varchar(10) NOT NULL,
  `b_pwd` varchar(10) NOT NULL,
  `b_title` varchar(30) NOT NULL,
  `b_content` mediumtext NOT NULL,
  PRIMARY KEY  (`b_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=euckr AUTO_INCREMENT=115 ;
입니다-_-;

설명은 나중에-_-; 아.....졸려-_-;


출처: http://mudchobo.tomeii.com/tt/163


http://www.dude.co.kr


P 이경철님의 파란블로그에서 발행된 글입니다.

[Flex] Mate Flex Framework로 만들어본 HelloWor...

방장은 누규? 채팅사이트 조이팅스토리를 운영하며 마케팅, 사업 등에 관심이 많은 철이

 

 

열이아빠님 블로그를 구독하고 있는데요. 플렉스에서 좋은 프레임워크가 없을까 하는 생각에 갑자기 열이아빠님의 블로그에 포스팅한 '10 가지 플렉스 디버깅 도구 및 프레임워크'가 생각이 났습니다.
여기서 나온게 Mate Framework라는 것인데, 발음은 메이트가 아닌 마떼라고 발음하는 것이라고 열이아빠님께서 말씀하셨습니다. 조또마떼! 일본애니메이션보면 디게 많이 나오는데...잠만 기댕겨?라는 뜻인듯?-_-;
Mate Flex Framework 공식홈페이지 http://mate.asfusion.com/
Mate Flex Framework 공식홈페이지에 가보면,

Mate is a tag-based, event-driven Flex framework.
라고 되어있습니다. tag기반에 event driven 플렉스 프레임워크라는 겁니다.
개발 방식도 우선 Service와 Event를 정의한 EventMap, 그리고, View로 나눠져 있습니다. MVC랑 비슷한 것 같은데, View에서 이벤트가 발생하면 이벤트를 정의해놓은 EventMap에서 찾아서 Event를 발생하죠. 이 EventMap에서는 해당 Service를 호출하는 등의 구조로 되어있어요. 제가 설명하는게 맞는지는 모르겠지만, 대충 저런 구조인 듯하네요. '서비스는 WAS없이도 돌아간다라고 생각하고 짜야된다가 현재의 결론입니다'라고 허광남씨가 말했던 것 처럼 여기서도 Service는 다른 것에 영향없이 돌아가야하는 겁니다.
이 Service는 BlazeDS를 이용한 RemoteObject가 될 수 있고, HTTPService를 이용한 서비스가 될 수 있고, WebService가 될 수 있습니다. 물론, Flex에서 만든 어떤 Method가 될 수도 있습니다.

그러면 해당 서비스를 EventMap에 정의를 해놓고, View에서는 Event를 발생시키기만 하면 되는 겁니다.
백날 말로 하는 것보다 코드로 보는게 빠릅니다-_-;

New -> Project -> Flex Project 를 선택!
Project Name은 HelloMate라고 하고, Web application을 선택하고, Application Server는 NONE으로 Finish고고싱!
Mate Flex Framework공식 홈페이지에 가면 downloads를 클릭하면, swc파일이 있는데요. 이것을 libs폴더에 복사합니다.

우선 패키지를 만들어봅시다.
src에 New -> Folder를 선택해서 events, maps, services, views를 만듭니다.
우선 events에 Event클래스를 하나 만들어봅시다.
New -> ActionScript Class선택 후 Name은 HelloEvent로 하고, Superclass는 flash.events.Event를 선택!
events/HelloEvent.as

package events
{
   
import flash.events.Event;

   
public class HelloEvent extends Event
   
{
       
/* Constants */
       
public static const SAY:String = "sayHelloEvent";
       
       
/* Properties */
       
public var name:String;
       
       
public function HelloEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
       
{
           
super(type, bubbles, cancelable);
       
}
   
}
}

SAY라는 상수를 하나 만들어서 sayHelloEvent라고 합시다. 이 이름을 통해 이벤트를 구분합니다.
Properties는 event를 발생할 때 properties값을 지정해서 EventMap에 정의한 메소드에 파라메터로 넣기 위해서 사용합니다.

이제 service를 만들어봅시다.
New -> ActionScript Class선택 후 name은 HelloService라고 합시다.
services/HelloService.as

package services
{
   
public class HelloService
   
{
       
public function sayHello(name:String):String
       
{
           
return "Hello! " + name + ", I'm so sorry but I love dagujitmal";
       
}
   
}
}

단순히 name을 파라메터로 받아서 Hello!를 붙여줘서 리턴해주는 메소드입니다.

이제 view를 만들어봅시다.
New -> File -> MXML Component선택! Filename은 HelloBox로 하고, Based on은 조낸 귀찮으니까 VBox로 하고, 사이즈도 그냥 그대로 갑시다-_-;
views/HelloBox.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
   
xmlns:mate="http://mate.asfusion.com/"
   
width="400" height="300"
   
horizontalAlign="center"
   
verticalAlign="middle">
   
<mx:Script>
        <![CDATA[
            import events.HelloEvent;
           
            private function sayHello():void
            {
                helloDispatcher.generateEvent();
            }
           
            private function helloResult(result:String):void
            {
                myLabel.text = result;
            }
        ]]>
   
</mx:Script>
   
   
<!-- Dispatcher -->
   
<mate:Dispatcher id="helloDispatcher" generator="{HelloEvent}" type="{HelloEvent.SAY}">
       
<mate:ResponseHandler  type="helloResultResponse" response="helloResult(event.helloResult)" />
       
<mate:eventProperties>
           
<mate:EventProperties name="{input.text}" />
       
</mate:eventProperties>
   
</mate:Dispatcher>
   
   
<mx:TextInput id="input" enter="sayHello();"/>
   
<mx:Button id="button" label="hello" click="sayHello();"/>
   
<mx:Label id="myLabel" text="이름을 입력하세요" width="295"/>
</mx:VBox>

mate에서 제공하는 Dispatcher기능을 쓰기 위해 네임스페이스를 지정해줍니다.
그리고, <mate:Dispatcher />태그가 있는데요. 여기서 자세히 보면, HelloEvent를 발생하고, 이벤트 종류는 HelloEvent.SAY인 것이죠. 이 이름은 단순 EventMap에 있는 것이랑 매핑하기위해서 쓰인 것 같구요.
이 이벤트를 발생할 때 Properties값은 input에 있는 text값을 넘깁니다. 방금 위에서 HelloEvent를 정의할 때 Properties값 name을 선언했는데, 저렇게 EventProperties에 지정을 해주면 저 값을 물고 들어갑니다.
그다음 그 응답값을 받을 ResponseHandler를 정의해주는데, helloResultResponse는 EventMap에 있는 것이랑 매핑하기 위한 이름이구요, response는 실제 메소드를 호출해줍니다. event.helloResult값은 EventMap에서 정의한 Properties값이구요. 여기서 설명하는 것보다 EventMap을 실제로 보면 이해가 훨씬 빠릅니다.

New -> MXML Component를 선택, Filename은 MainEventMap 그냥 Finish.
<mx:Canvas를 EventMap으로 고칩니다.
maps/MainEventMap.mxml

<?xml version="1.0" encoding="utf-8"?>
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml"
   
xmlns="http://mate.asfusion.com/">
   
<mx:Script>
        <![CDATA[
            import services.HelloService;
            import events.HelloEvent;
        ]]>
   
</mx:Script>
   
   
<EventHandlers type="{HelloEvent.SAY}">
       
<MethodInvoker generator="{HelloService}" method="sayHello" arguments="{event.name}" />
       
<ResponseAnnouncer type="helloResultResponse">
           
<Properties helloResult="{lastReturn}" />
       
</ResponseAnnouncer>
   
</EventHandlers>
   
</EventMap>

EventHandlers를 정의해줬는데 여기서 type은 아까 dispatcher에서 정의한 type과 같은 HelloEvent.SAY가 되겠죠. 그다음 MethodInvoker는 메소드를 호출하는 태그입니다. 위에서 언급했듯이 메소드 말고, RemoteObject나 HTTPService, WebService도 다 됩니다.
generator는 클래스를 적어주고, method에 메소드 이름을 적습니다. arguments에는 파라메터값인데 event.name은 해당 event에서 받은 name을 말하는겁니다. 우리가 Dispatcher에 정의한 name에 input.text값을 넣었으니 그게 들어가는 겁니다. 그다으메 ResponseAnnouncer는 응답을 정의하는 건데, 그 type이 아까 위에서 정의한 그 타입과 일치해야합니다. Properties의 helloResult도 위에 Dispatcher에서 정의한 것과 동일한 이름이여야겠죠. 그러면 저 Properties에 값이 들어가는데 여기서 보면 lastReturn이라고 써있는데, 위에 메소드에서 리턴한 값을 말하는 겁니다.
저렇게 해주면 끝납니다.

이제 마지막으로 main을 작성해봅시다.
HelloMate.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
   
layout="absolute"
   
xmlns:view="views.*"
   
xmlns:maps="maps.*">
   
   
<!-- Event Maps -->
   
<maps:MainEventMap />
   
   
<!-- views -->
   
<view:HelloBox />
   
</mx:Application>

EventMap을 추가하고, View만 추가해주면 끝납니다.
실행해봅시다.
사용자 삽입 이미지

 

 

출처: http://mudchobo.tomeii.com/tt/tag/HelloWorld#entry_300

 

 

 

 

 



P 이경철님의 파란블로그에서 발행된 글입니다.

플래시 플랫폼으로 만들어 보는 P2P 채팅 애플리케...

방장은 누규? 채팅사이트 조이팅스토리를 운영하며 마케팅, 사업 등에 관심이 많은 철이


플래시 플랫폼으로 만들어 보는 P2P 채팅 애플리케이션

플래시 플레이어 10이 출시되면서 가장 주목 받는 점을 꼽자면, RTMFP(Real Time Media Flow Protocol)라는 UDP 프로토콜 기반 위에서 통신하는 새로운 프로토콜이 제시되면서 플래시 플레이어간의 Peer to Peer(P2P)가 가능해진 것이다.
P2P 기능 구현이 가능해진 것은 플래시 플레이어에 고유 아이디를 부여해, IP 기반이 아닌 플래시 플레이어의 고유 아이디를 기반으로 서로 통신할 수 있도록 됐기 때문이다. 플래시 플레이어간의 연결은 NetConnection 객체를 이용해 할 수 있으며, 플래시 플레이어간의 데이터 전송은 NetStream 객체를 이용하면 된다. 예제는 텍스트 기반의 통신을 하는 간단한 채팅 애플리케이션을 구현하겠지만, NetConnection이란 객체가 오디오나, 비디오 데이터로 전송 가능하므로, 화상 채팅 같은 애플리케이션으로 충분히 발전시킬 수 있을 것이다. 그럼 Peer to Peer의 기본 원리부터 알아보자.

간단한 채팅 서버 기능 구현하기
NetConnetion 객체를 이용해 플래시 플레이어간 통신을 하려면, 일단 고유 아이디가 필요하다. 플래시 플레이어의 고유 아이디 생성 방법은 FMS(Flash Media Server 3.x) 이상을 활용하거나, 어도비에서 제공하는 Stratus(http://labs.adobe.com/technologies/stratus) 서비스를 이용하면 고유 아이디를 생성할 수 있다. FMS는 비용적인 측면이 있으니, Stratus 서비스를 이용해 고유 아이디를 생성해 보자. <그림 1-1>에서 보이는 것처럼 Developer Key Registration 메뉴를 클릭하고 개발자 키를 발급받으면, Stratus 서비스를 이용할 수 있다.

<그림 1-1> Stratus 서비스의 Developer Key를 발급받는 화면
 

개발자 키를 발급받았다면, 채팅 서버를 구현해 보자. 구현할 사항은 다음과 같다.

l        Stratus 서비스에서 고유 키 발행 받기
l        클라이언트의 접속을 감시할 채널 스트림 객체 생성하기
l        클라이언트의 데이터를 청취할 스트림 객체 생성하기
l        클라이언트의 텍스트 데이터를 전송 받을 메쏘드 등록하기

가장 처음으로 해야 할 것은 Stratus 서비스에서 고유 키를 발행 받는 일이다. Stratus 서비스에서 고유 키를 발행 받아 보자.

[Stratus 서비스에서 고유 키 발행 받기]

//Stratus 개발자 키
private const DEVELOPER_KEY:String = "Stratus Developer Key";
//Stratus의 기본 경로
private const STRATUS_URL:String = "rtmfp://stratus.adobe.com";
//Stratus 서비스에 연결할 NetConnection 객체
private var netConnection:NetConnection;

private function onConnect():void
{                                         
    //NetConnection 객체 생성
netConnection = new NetConnection();

//NetConnection 객체의 연결 상태를 확인할 이벤트 등록
netConnection.addEventListener(
NetStatusEvent.NET_STATUS, netConnectionHandler);
  
    //Stratus 서비스에 NetConnection 객체 연결
netConnection.connect(STRATUS_URL + "/" + DEVELOPER_KEY);
}

private function netConnectionHandler(event:NetStatusEvent):void             
{
    //Stratus 서비스에 연결이 되면 neared 속성에 고유 아이디가 발급된다.
trace(netConnection.nearID);
//output
//9951da938d8db2204b401e780252fbcb157281cf0048a09e9881030afbc0d5ec
}

onConnect();


여기서 발행된 고유키는 클라이언트가 접속할 주소라고 생각하면 된다. 고유 키를 발급 받았으니, 이번엔 고유 키로 접속하는 클라이언트를 감시하는 기능을 구현해 보자. 클라이언트를 감시하는 기능은 Stratus 서비스에 연결된 netConnection 객체를 그대로 사용하며, NetStream 객체를 이용해 클라이언트가 접속할 수 있는 채널을 생성하고, 해당 채널에 접속하는 클라이언트를 감시하는 방식이다. LocalConnection 객체 사용법과 상당히 비슷한 부분이다. 구현방식을 살펴보자.


[클라이언트 감시 채널 구현하기]

//플래시 플레이어의 NetStream이 직접 접근 가능한 NetStream 객체 생성

listenerStream = new NetStream(netConnection
,NetStream.DIRECT_CONNECTIONS);

//NetStream 객체의 상태를 볼 수 있는 이벤트 등록
listenerStream.addEventListener(NetStatusEvent.NET_STATUS
, listenerHandler);

//listenTest라는 수신 채널 생성
listenerStream.publish("listenTest");

//채널에 접속한 클라이언트의 기능 정의                                                                   
listenerStream.client = {
        //플래시 플레이어에서 직접적으로 연결됐을 때, 메쏘드 정의
           onPeerConnect : function(client:NetStream):Boolean
           {
           //현재 접속한 클라이언트의 아이디 출력
              trace(client.farID + "가 접속했습니다. n");         
              return true;                                                                                        }          
}

                                                    

클라이언트가 해당 채널로 접속하게 되면 onPeerConnect() 메소드가 실행되며, 연결된 클라이언트가 보낸 NetStream 객체의 farID 속성을 이용해 접속한 클라이언트의 고유 키를 알 수 있게 된다.
이제 간단한 채팅 서버 구현의 마지막 단계다. 클라이언트의 고유 키를 알게 됐으므로, 클라이언트가 데이터를 전달할 수 있도록 데이터를 전달할 메쏘드를 만들어 줘야 한다. 클라이언트가 데이터를 전달할 수 있도록 하기 위해 클라이언트의 데이터를 청취할 스트림 객체를 생성하고, 스트림 객체에 클라이언트가 호출할 수 있는 메쏘드를 등록해보자. 이번엔 간단한 채팅 서버가 구현된 전체 소스를 보도록 하겠다.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
           layout="absolute" applicationComplete="init()">
           <mx:Script>
           <![CDATA[                             
             private const DEVELOPER_KEY:String = "";
             private const STRATUS_URL:String = "rtmfp://stratus.adobe.com";
       private var netConnection:NetConnection;
             private var listenerStream:NetStream;
             private var receiveStream:NetStream;
            
private function init():void{
               onConnect();
             }
                               
             private function onConnect():void
             {                                      
                netConnection = new NetConnection();
                netConnection.addEventListener(
NetStatusEvent.NET_STATUS, netConnectionHandler);
                netConnection.connect(STRATUS_URL + "/" + DEVELOPER_KEY);
             }
                               
             private function netConnectionHandler(event:NetStatusEvent):void
             {
                trace("NetConnection event: " + event.info.code );
                trace(netConnection.nearID);
                result.text += netConnection.nearID+  "n";
          
             //접속이 완료 되었다면
                if(event.info.code == 'NetConnection.Connect.Success'){
                 
               //플래시 플레이어의 NetStream이 직접 접근 가능한 NetStream 객체 생성
 listenerStream = new NetStream(
netConnection, NetStream.DIRECT_CONNECTIONS);

                listenerStream.addEventListener(
NetStatusEvent.NET_STATUS, listenerHandler);

                listenerStream.publish("listenTest");
                                                                                                                               
               listenerStream.client = {
                   onPeerConnect : function(caller:NetStream):Boolean
                   {
                      trace("저는 " + caller.farID + "입니다n");         

                   //클라이언트의 데이터를 청취할 NetStream
                      receiveStream = new NetStream(netConnection, caller.farID);

                   //클라이언트의 스트림 상태를 확인할 이벤트 등록
               receiveStream.addEventListener(
NetStatusEvent.NET_STATUS, receiveHandler);
  
   //클라이언트가 데이터를 전달할 메쏘드 등록
 //(서버가 클라이언트가 되는 격)
                      receiveStream.client = {
                      
                              test : function(msg:String):void{
                                 result.text += msg +"n";
                              }
                      }

                   //클라이언트가 데이터를 전달하기 위해 발행한 채널 감시
   receiveStream.play("testChannel");
                      return true;                                                                                          }        
}                                          
                }//END IF
          }
                  
          private function receiveHandler(evt:NetStatusEvent):void{
            trace("클라이언트가 보낸 NetStream상태: " + evt.info.code);
          }
                  
           private function listenerHandler(evt:NetStatusEvent):void{
             result.text +="클라이언트의 접속 상태 " + evt.info.code+"n";                        
           }
           ]]>
</mx:Script>
<mx:TextArea x="10" y="10" width="659" height="429" id="result"/>
</mx:WindowedApplication>


이렇게 구현된 채팅 서버는 [그림1-2] [그림1-3]처럼 간단한 로그를 찍는 수준으로 구현했다.

<그림 1-2> 간단한 채팅 서버가 구현된 화면
 

<
그림 1-3> 채팅 서버가 실행되면서 찍힌 로그
 

간단한 채팅 클라이언트 구현하기
서버는 구현됐고, 클라이언트에서 데이터가 전달되기만을 기다리고 있다. 간단하게 클라이언트도 구현해서 서버로 전달해 보자. 클라이언트의 구현은 서버처럼 Stratus 서비스를 이용해야 고유 키를 발급받을 수 있으므로, 처음 Stratus 서비스에 접속하는 부분은 동일하지만, 그 이후부터는 반대 되는 개념으로 구현된 것을 알 수 있을 것이다. 참고로 클라이언트는 서버를 구동하면 발행되어 나오는 고유 키를 활용하게 되므로, 클라이언트의 기능을 구현할 때는 서버가 실행되어 고유 키를 발급받은 상황이어야 한다.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
           layout="absolute" applicationComplete="init()">
           <mx:Script>
           <![CDATA[
          
import flash.utils.setInterval;                            
           private const DEVELOPER_KEY:String = "Developer Key";
           private const STRATUS_URL:String = "rtmfp://stratus.adobe.com";
           private var netConnection:NetConnection;
           private var listenerStream:NetStream;
           private var sendStream:NetStream;
                               
           private function init():void{
                     onConnect();
           }
                               
           private function onConnect():void
           {                                         
          netConnection = new NetConnection();
             netConnection.addEventListener(
NetStatusEvent.NET_STATUS, netConnectionHandler);
             netConnection.connect(STRATUS_URL + "/" + DEVELOPER_KEY);
}
                               
           private function netConnectionHandler(event:NetStatusEvent):void
           {
                     trace("NetConnection event: " + event.info.code );
                     trace(netConnection.nearID);
                     result.text += netConnection.nearID+  "n";
                    
                  if(event.info.code == 'NetConnection.Connect.Success'){

                   //서버가 발행 받은 서버의 고유 키를 이용해 서버에 접근
                        listenerStream = new NetStream(
netConnection, "서버의 고유키");

                        listenerStream.addEventListener(
NetStatusEvent.NET_STATUS, listenerHandler);

                   //서버 고유키의 listenTest채널로 접속
                        listenerStream.play("listenTest");                                        
                       

                   //서버로 데이터를 보낼 NetStream객체 생성
                        sendStream = new NetStream(
netConnection, NetStream.DIRECT_CONNECTIONS);

                        sendStream.addEventListener(
NetStatusEvent.NET_STATUS, listenerHandler2);

                   //서버가 청취하고 있는 채널 발행
                        sendStream.publish("testChannel");
                 
                   //1초마다 서버에 등록된 test메소드를 호출한다.
                        setInterval(function():void{
                                sendStream.send("test","테스트");
                        },1000);                                                  
                     }
         }
                  
         private function listenerHandler(evt:NetStatusEvent):void{
              result.text +="서버 상태: " + evt.info.code+"n";                             
         }
                  
        private function listenerHandler2(evt:NetStatusEvent):void{
               result.text +="서버에 데이터를 보내는 상태: " + evt.info.code+"n";                          
         }
           ]]>
           </mx:Script>
<mx:TextArea x="10" y="10" width="659" height="429" id="result"/>      
</mx:WindowedApplication>


<그림 1-4>는 예제로 구현한 간단한 채팅 서버와 클라이언트이며, 서버에 테스트라는 문자열이 1초마다 한번씩 찍힌걸 확인할 수 있다.

 
우리가 구현한 예제는 이렇다. 클라이언트에서 서버의 고유 키를 갖고 서버로 접근하게 되면, 서버는 클라이언트의 고유 키를 알아내고 클라이언트가 수신할 수 있는 test() 메쏘드를 등록해 주고, 클라이언트의 고유 키에 등록된 채널을 청취한다. 클라이언트는 1초마다 지정된 채널에 등록된 서버의 메쏘드를 호출해 인자 값으로 데이터를 전달하면, 서버는 화면에 출력하는 매우 간단한 예제다. 하지만 이 간단한 예제가 기본 원리다. 예제를 구현하면서 다음과 같은 규칙을 발견 했을 것이다. NetStream 객체의 publish() 메쏘드는 채널을 발행하고, play() 메쏘드는 채널에 접근하는 것이며, 발행된 채널에 플래시 플레이어가 직접 통신할 수 있도록 하기 위해서는 NetStream객체를 생성할 때 NetStream.DIRECT_CONNECTIONS 상수를 이용하는 점이다. 그리고 LocalConnection 객체처럼 채널을 발행한 쪽에서 채널을 듣고 있는 쪽에 등록된 메쏘드를 호출할 수 있다는 점을 알았을 것이다.

일단 이렇게 해서 서버와 클라이언트의 동작원리를 알 수 있는 애플리케이션을 구현했다. 그런데 우리가 원하는 P2P는 서버와 클라이언트의 분리적인 개념을 넘어서 서버의 역할과 클라이언트의 역할을 동시에 할 수 있어야 한다. 따라서 예제로 만든 서버와 클라이언트의 기능을 합쳐야 진정한 P2P라 할 수 있다. 두 기능을 합쳐 <그림 1-5>처럼 양방향으로 채팅할 수 있는 애플리케이션을 만들어 보자.

<그림 1-5> Strarus 서비스를 이용해 간단히 구현한 양방향 채팅 애플리케이션
 
  
일단 <그림 1-5>에서 보는 StratusP2P testBed는 같은 내용의 애플리케이션이며, 빌더에서 두 개의 애플리케이션을 실행하기 위해 별도의 프로젝트로 만든 것이다. 일단 두 애플리케이션을 실행한 후 발행된 Net ID가 화면에 출력되면, 고유 키를 복사해 연결할 애플리케이션에 붙여 넣고 Connect peer 버튼을 클릭한다. 그러면 <그림 1-5>처럼 연결되었습니다라는 메시지가 출력된 것을 볼 수 있다. 그 후부터 하단의 입력 창에 텍스트를 입력하고 send 버튼을 클릭하면 연결된 애플리케이션에 텍스트 데이터가 전달된다. <그림 1-5>에서 구현한 소스의 내용은 다음과 같으며, 최대한 간단하게 구현했다. 기본 원리에서 이미 설명된 부분이므로 내용을 이해하는데 크게 무리는 없을 것이다. 


<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
           layout="absolute" applicationComplete="init()" height="340"
width="400"
           verticalScrollPolicy="off"
           horizontalScrollPolicy="off">

           <mx:Script>
           <![CDATA[                             
           private const DEVELOPER_KEY:String = "Stratus develop Key";
           private const STRATUS_URL:String = "rtmfp://stratus.adobe.com";
           private var netConnection:NetConnection;
           private var listenerStream:NetStream;
           private var sendStream:NetStream;
           private var isPeerConnected:Boolean = false;
                               
           private function init():void
{
          //Stratus 서비스에 연결
             onConnect();

connectPeerBtn
.addEventListener(MouseEvent.CLICK, connectPeerHandler);
}
                               
           private function connectPeerHandler(e:MouseEvent):void
{
          //고유키를 이용해 플레이어와 연결
             connectPeer(peerId.text);                                         

           }
                               
           private function connectPeer(targetId:String):void
{
          //이미 다른 플레이어와 연결되어 있다면 더 이상 연결하지 않는다.
             if(isPeerConnected) return;
             isPeerConnected = true;

             //연결된 아이디를 입력 창에 기입하고 연결 액션을 할 수 없게 한다.
             peerId.text = targetId;
             peerId.editable = false;
             peerId.enabled = false;
             connectPeerBtn.enabled = false;
            
          //고유 키를 가진 플레이어에 연결한다.
             sendStream = new NetStream( netConnection, targetId );           
             sendStream.addEventListener(
NetStatusEvent.NET_STATUS, peerListenerHandler );

          //연결된 플레이어에서 호출할 수 있는 메쏘드를 등록
             sendStream.client =
             {
                 sendText : function(msg:String):void{
                   result.text += msg +"n";
               }
              }

              //연결된 플레이어의 채널을 청취.
              sendStream.play("listenChannel");
           }
                               
           private function peerListenerHandler(event:NetStatusEvent):void
{
  //플레이어와 연결하고 플에이어의  NetStream을 청취할 수 있는 상태라면
             if(event.info.code == 'NetStream.Play.Start')
{
               isPeerConnected = true;
             }
                                         
           }
                                         
           private function onConnect():void
  {
               //Stratus 서비스에 연결
               netConnection = new NetConnection();                                          
netConnection.addEventListener(
NetStatusEvent.NET_STATUS, netConnectionHandler);
netConnection.connect(STRATUS_URL + "/" + DEVELOPER_KEY);

           }
                               
           private function netConnectionHandler(event:NetStatusEvent):void
           {
               //Stratus 서비스와 연결이 성공했다면
               if(event.info.code == 'NetConnection.Connect.Success')
{
   //발급된 고유 키를 출력한다.
   result.text += "Net ID -> " + netConnection.nearID+  "nn";
  
   //발급된 고유 키에 해당되는 채널을 생성한다.
                  listenerStream = new NetStream(
netConnection, NetStream.DIRECT_CONNECTIONS);
listenerStream.addEventListener(
NetStatusEvent.NET_STATUS, listenerHandler);

               //채널 생성
                  listenerStream.publish("listenChannel");                                      

                  listenerStream.client = {

                   //채널에 다른 플레이어의 NetStream이 연결되면 실행될 메소드
                      onPeerConnect : function(caller:NetStream):Boolean
                        {
                       //연결된 채널을 청취한다.
                            connectPeer(caller.farID);                                                                           return true;                                        
                        }     
                  }
                }

        }
                  
         private function listenerHandler(evt:NetStatusEvent):void
{
    //발행한 채널에서 상태 변화가 발생하면 출력한다.       
            result.text += evt.info.code+"n";                            

         }
                  
         private function sendMsg():void
{
    //발행한 채널에 연결된 클라이언트에 등록된 sendText 메쏘드를 호출한다.
            listenerStream.send("sendText",msgText.text);
            msgText.text = "";

         }

           ]]>
           </mx:Script>

           <mx:TextArea x="10" y="40"
width="378" height="207" id="result"
wordWrap="true" fontSize="11" fontFamily="돋움"/>

           <mx:TextInput x="10" y="10" width="246" id="peerId"/>

           <mx:TextArea x="10" y="255" width="315" height="53" id="msgText"/>

           <mx:Button x="333" y="254" label="send"
width="55" height="54" id="msgSendBtn" click="sendMsg()"/>

           <mx:Button x="264" y="9" label="Connect peer" id="connectPeerBtn"/>

</mx:WindowedApplication>


NetConnection 객체와 NetStream 객체의 API 문서를 보면, 더 많은 기능을 활용할 수 있는 부분을 찾아 볼 수 있을 것이며, 간단한 예제지만 구현해보고 나면 이런 막강한 기능을 어디에 써야 할지 고민에 빠지게 될 것이다.
사실 스킨도 입혀서 좀 더 멋지고, 화려하게 할 수 있는 기술 문서를 작성하고 싶었지만, 여러분에게 이런 좋은 기능을 하루 빨리 소개하고 싶었다. 기본 원리를 이해하고 P2P 통신을 하는 애플리케이션까지 구현해봤다면, 파일 전송 기능도 구현해 보고, 비디오, 오디오를 활용한 화상 채팅 구현에 도전해 보길 바란다. 플래시로 구현하는 P2P에 대한 반응이 좋다면, 다음 기술 문서는 매쉬업과 P2P의 활용 예제를 다뤄볼까 한다.



http://www.dude.co.kr

P 이경철님의 파란블로그에서 발행된 글입니다.