젠장... Flash Player 10에서 생기는 버그를 발견했습니다.
TextInput에서 enter이벤트에 자신의 TextInput의 초기화하는 코드가 있으면 초기화가 안됩니다 ㅠ 9버전으로 컴파일하면 되는데, 10으로 컴파일하니 안되네요 ㅠ 몇시간 잡아먹었어요 ㅠ 하소연은 여기까지....-_-

우선 Stratus라는 기술은 Flash간에 통신을 가능하게 해주는 기술인 듯합니다.
자세한 내용은 아래에......-_-(영어는 잘 못하니....)
http://labs.adobe.com/technologies/stratus/

Adobe RIA공식사이트에서 오창훈님께서 올리신 기술문서 "플래시 플랫폼으로 만들어 보는 P2P 채팅 애플리케이션"를 보고 휠받아서 한번 만들어봤습니다. 채팅이 가능하다면, 오목게임도 가능할꺼라는 생각에.....-_- 참 쓸데없는거 잘만든다-_-

http://mudchobo.tomeii.com/flexomok/
위 사이트에 접속하면 서버랑 클라이언트를 고를 수 있어요.
닉네임을 입력하세요 한명은 서버를 한명은 클라이언트를 해야해요.

서버측
서버를 클릭하고, 생성되는 ID를 클라이언트에게 알려주세요.
클라이언트측
클라이언트를 클릭하고, 서버측의 ID를 입력해서 접속하세요~

오목판이 나오면 접속 성공~ 같이 오목을 즐기면 돼요-_-
사용자 삽입 이미지
소스는 여기에 있어요.
http://my-svn.assembla.com/svn/omok/
Mate Flex Framework를 사용했고(이걸 사용해야 스피드한 개발이.....-_-), 소스도 개판이라 보시는데에는 매우 힘드실겁니다. 저도 참 만든게 신기하군요-_-

참고자료

플래시 플랫폼으로 만들어 보는 P2P 채팅 애플리케이션(로그인 후 볼 수 있습니다)
http://www.adoberia.co.kr/iwt/board/board.php?tn=pds_tech&id=244&mode=view

플래시 P2P RTMFP에 대해(예제 파일 첨부)
http://lovedev.tistory.com/entry/%ED%94%8C%EB%9E%98%EC%8B%9C-P2P-RTMFP%EC%97%90-%EB%8C%80%ED%95%B4


PS1. 버그가 조낸 많습니다. 한명이 나가도 뭔가 반응이 없습니다-_- 채팅에서 글을 쓰고 엔터를 쳐도 TextInput에서 지워지지 않습니다. 하지만, 그냥 한게임 즐기기에는 충분합니다 -_-b
PS2. 구글에서 Stratus를 치시면.....이미지검색에 여자 프로레슬러 트리쉬 스트레터스가 나옵니다-_-; 그냥 그렇다구요-_-
 
Posted by 머드초보
,
 
PHP는 HTTPService말고 방법이 없을까 하고 찾아보니, Zend에서 AMF를 이용하여 개발한게 있군요. Adobe의 공식 지원으로 만들어진거라 빠르리라고 생각합니다^^ (적어도 HTTPService보단 빠르겠죠!)

암튼, BlazeDS를 하셨다면 그리 어렵지 않군요.
이번에도 간단하게 소녀시대예제를 통해...접근해봅시다-_-;

간단히 데이터를 불러오고, 저장도 해보는 그런 애플리케이션을 만들어봅시다.

우선 Eclipse에서 php가 가능하도록 해야하는데요. 뭐 그냥 editplus에서 해도 상관없습니다. Zend홈페이지에 가면 자기네들 IDE깔라고 합니다-_-; Zend Studio라는 게 있네요. 뭐 php단은 netbeans를 이용해서 개발하셔도 되고, 이클립스에 있는 PDT를 추가하셔서 해도 상관없어요~ ^^ 전 NetBeans를 선호합니다. 좋아요!

DB입니다.
[code]DROP TABLE IF EXISTS `sosi`.`sosi`;
CREATE TABLE  `sosi`.`sosi` (
  `idx` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `sosiname` varchar(45) NOT NULL,
  `height` int(10) unsigned NOT NULL,
  `blood` varchar(45) NOT NULL,
  PRIMARY KEY (`idx`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

INSERT INTO `sosi` (`idx`,`sosiname`,`height`,`blood`) VALUES
 (1,'윤아',166,'B'),
 (2,'수영',170,'O'),
 (3,'효연',160,'AB'),
 (4,'유리',167,'AB'),
 (5,'태연',162,'O'),
 (6,'제시카',163,'B'),
 (7,'티파니',162,'O'),
 (8,'써니',158,'B'),
 (9,'서현',168,'A');[/code]
우선 Flex Project를 만듭니다.
여기서 Application server type을 php로 하세요~ php로 하게 되면 설정된 폴더로 swf를 바로 위치시킬 수 있어요!
Project name은 ZendAmfTest -> Web root는 아파치가 돌아가는 폴더를 지정하면 됩니다.
저는 C:\Program Files\Apache Software Foundation\Apache2.2\htdocs여기군요.
Root URL은 http://localhost/ Finish때려주면 ZendAmfTest-debug폴더에 swf가 생기죠.

요 아래 주소에서 ZendAMF를 받을 수 있습니다.
http://framework.zend.com/download/amf
받은 다음에 압축을 풀면 library폴더 안에 Zend라는 폴더가 있는데 통채로 복사해서 src폴더 아래에 놓습니다.

그리고 php파일을 하나 만드세요. ValueObject입니다. Java에서 bean같은..-_-;
SosiVo.php
[code]<?php
class SosiVo {
    public $idx;
    public $sosiname;
    public $height;
    public $blood;
}
?>[/code]
이제 서비스를 하나 만들어봅시다. SosiService입니다.
SosiService.php
[code]<?php
require_once 'SosiVo.php';

//conection info
define("DATABASE_SERVER", "localhost");
define("DATABASE_USERNAME", "root");
define("DATABASE_PASSWORD", "mudchobo");
define("DATABASE_NAME", "sosi");

class SosiService {
    public function getData() {
        $mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME,
            DATABASE_PASSWORD);
        mysql_select_db(DATABASE_NAME);
        $query = "SELECT idx, sosiname, height, blood FROM sosi";
        $result = mysql_query($query);

        $ret = array();
        while ($row = mysql_fetch_object($result)) {
            $tmp = new SosiVo();
            $tmp->idx = $row->idx;
            $tmp->sosiname = $row->sosiname;
            $tmp->height = $row->height;
            $tmp->blood = $row->blood;
            $ret[] = $tmp;
        }
        mysql_free_result($result);
        return $ret;
    }
}
?>[/code]
php의 array는 ActionScript3의 array와 호환됩니다.
서비스를 만들었으니 EndPoint페이지를 만들어줘야합니다.
amf.php
[code]<?php
require_once 'Zend/Amf/Server.php';
require_once 'SosiService.php';

$server = new Zend_Amf_Server();
$server->setClass("SosiService");
$server->setClassMap("SosiVo", "SosiVo");
echo($server -> handle());
?>[/code]
이제 이것을 연결시켜줄 service-config.xml파일을 만들어야합니다.
[code]<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <services>
        <service id="amfphp-flashremoting-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <default-channels>
                   <channel ref="my-zend"/>
            </default-channels>
            <destination id="zend">
                <properties>
                    <source>*</source>
                   </properties>
               </destination>
        </service>
    </services>
    <channels>
        <channel-definition id="my-zend"
            class="mx.messaging.channels.AMFChannel">
            <endpoint uri="http://localhost/ZendAmfTest-debug/amf.php"
                class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>
</services-config>[/code]
우선 default로 my-zend라는 채널을 잡았는데, my-zend는 endpoint uri가 ~~/amf.php입니다. 우리가 좀전에 작성했던 endpoint죠. 새로운 서비스가 추가되어 새로운 endpoint를 작성하게 될 때 endpoint파일을 amf1.php라고 했을 때 이곳에 추가해서 channel에 기입할 수도 있고, <mx:RemoteObject>에서 endpoint를 해당 uri로 바꿔주는 방법 2가지가 있습니다.

그럼 이제 Flex쪽으로 넘어가봅시다.
ZendAmfTest.mxml
[code]<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical"
    creationComplete="creationCompleteHandler()">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.events.FaultEvent;
           
            private function creationCompleteHandler():void
            {
                roSosi.getData();   
            }
           
            private function faultHandler(event:FaultEvent):void
            {
                Alert.show(event.message.toString());
            }
           
            private function resultGetDataHandler(event:ResultEvent):void
            {
                dgSosi.dataProvider = event.result as Array;
            }
        ]]>
    </mx:Script>
   
    <mx:RemoteObject id="roSosi" fault="faultHandler(event)" destination="zend" source="SosiService">
        <mx:method name="getData" result="resultGetDataHandler(event)" />
    </mx:RemoteObject>
  
    <mx:DataGrid id="dgSosi" width="100%" height="100%">
        <mx:columns>
            <mx:DataGridColumn headerText="아이디" dataField="idx"/>
            <mx:DataGridColumn headerText="소녀시대이름" dataField="sosiname"/>
            <mx:DataGridColumn headerText="신장" dataField="height" />
            <mx:DataGridColumn headerText="혈액형" dataField="blood" />
        </mx:columns>
    </mx:DataGrid>
   
</mx:Application>[/code]
만약 다른 서비스를 추가하게 된다면, <mx:RemoteObject>에서 endpoint를 다른 url로 잡아주면 됩니다.
[code]<mx:RemoteObject id="roAnother" fault="faultHandler(event)"
        destination="zend" source="AnothorService"
        endpoint="http://localhost/ZendAmfTest-debug/another_amf.php">[/code]
아님 service-config.xml에서 channel을 다르게 해서 destination을 다르게 설정해도 되구요.
사용자 삽입 이미지

ps1. 다른 예제에서는 2개의 service를 이용한 예제가 없더라구요. 그래서 사실 저렇게 하는 게 맞는지 잘 모르겠습니다. 다른 방법이 있을 수도 있구요^^ 암튼, 제가 삽질한 바로는 서비스당 1개의 php파일을 만들어야 하더라구요.

ps2. PHP를 시작한지 얼마 안되었는데, DB연동 시 한글이 깨지더라구요. 인코딩을 my.ini과 php.ini에 utf-8로 맞췄는데, ???로 인식해서 나오는 경우가 있더라구요. php파일에서 db정보를 출력하니 이상하게 그냥 로컬에서 쳤을 때에는 utf-8로 나오는데, php파일이 출력한 db정보는 latin으로 나올 때가 있더라구요. 이 경우 my.ini파일에 이 부분을 추가해주면 됩니다.
[mysqld] 아래에
character-set-client-handshake = FALSE
client 인코딩으로 강제 셋팅하는거라고 하더라구요. 저는 처음에 ??? 뜨길래 이걸로 해결봤습니다-_-;
아 또 다른 방법으로 쿼리날리기 전에 set names 'utf8'이라고 먼저 날려줘도 됩니다.
[code]mysql_query("SET NAMES 'utf8'");[/code]
ps3. 이런...생각해보니 client에서 VO를 안만들어줬군요-_-;

참고자료
http://corlan.org/2008/11/13/flex-and-php-remoting-with-zend-amf/

덧. 채널 못찾는 에러는 아래 주너니님의 댓글을 참조하세요~ 컴파일옵션을 추가해야합니다^^
주너니님 감사합니다 ^^
 
Posted by 머드초보
,
 
플렉스로 만들기는 했지만, 뭐 모든 클라이언트 플랫폼에서 쉽게 만들 수 있습니다.

요아래에 있는 RESTful Web Service를 구축하셨다면 그것을 이용한 클라이언트가 필요할 것입니다.

게시판용 RESTful WebService만들기
http://mudchobo.tomeii.com/tt/345
http://mudchobo.tomeii.com/tt/346

그래서 구현해봤습니다.
우선 5가지 요청을 합니다.

1. 게시물 리스트를 받아오는 것(GET)
2. 게시물 1개의 데이터를 받아오는 것(GET)
3. 게시물 쓰기(POST)
4. 게시물 삭제(DELETE)
5. 게시물 수정(PUT)

핵심은 RESTful요청이 가능한 라이브러리입니다. 요아래를 참고하세요~

[Flex/AIR] RESTful WebService를 위한 Flex RestService Library!


이 라이브러리를 통해 RESTService요청을 합니다. (기존 Flex에 있는 HTTPService는 DELETE와 PUT이 안됩니다.)
[code]
<rest:RESTService id="boardService" method="GET"
        rootURL="http://localhost/BoardWS/resources/wsboards/" port="9080"
        result="resultBoardHandler(event)" fault="faultHandler(event)"/>
[/code]
이런식으로 요청하면 되겠죠? ^^

아래는 구현체 소스입니다.
허접해서 도움이 될지는 모르겠지만....-_-; 어쨌든 저의 삽질의 결정체니....-_-;

저 위에서 구현한 WebService를 띄워놓고, 이 클라이언트로 접속하면 잘 될겁니다.

사용자 삽입 이미지

 
Posted by 머드초보
,
 
우선 RESTFul로 구현된 WebService를 Flex에서 호출하기 위해서는 클라이언트에서 method방식이 GET, POST, DELETE, PUT을 지원을 해야합니다. 근데, 제가 RESTful WebService를 만들었는데요. 만들어놓고, 자체 GlassFish에서 제공하는 RESTful WebService Test로는 무쟈게 잘 돌아갔습니다.
근데, Flex에서 제공하는 HTTPService를 이용해서 method는 DELETE로 놓은다음에 http요청을 해봤습니다. 이게 왠걸......POST로 요청이 되는 듯해서 데이터를 가져오고 있습니다-_-; 지워야하는데!

그래서 검색을 해보니.......
http://verveguy.blogspot.com/2008/07/truth-about-flex-httpservice.html
저의 짧은 영어실력으로 보니.......
4/ All HTTP PUT and HTTP DELETE requests are turned into POST requests. This appears to be a browser limitation that the Flash player is stuck with.
라고 되어있군요. PUT과 DELETE는 POST로 요청이 된다고 하는 것 같습니다. 플래쉬플레이어가 브라우저 제한에 뭐 걸린 것으로 보인다고 말하는데요. 해석이 잘 안되네요-_-;

그래서! 검색해본 결과 REST요청을 위한 라이브러리를 누가 만들었더군요. 대단합니다 ^^ 어차피 HTTP요청도 해당 포트로 뭐 데이터를 주고 받는 것이다 보니, 이것을 소켓으로 구현을 했더군요.

http://lab.arc90.com/2008/03/restservice.php

기존 HTTPService랑 틀린 것이...얘는 소켓을 통해서 쏘기 때문에 포트를 지정해줘야합니다. 그리고 arc90에서 만든 ResultEvent랑 FaultEvent를 사용해야합니다.

서버측 코드를 보면
[code]
@DELETE
    public Response delete() {
        PersistenceService persistenceSvc = PersistenceService.getInstance();
        try {
            Wsboard entity = getEntity();
            if (entity.getPwd().equals(pwd))
            {
                persistenceSvc.beginTx();
                persistenceSvc.removeEntity(entity);
                persistenceSvc.commitTx();
               
                return Response.ok("success").build();
            }
        } finally {
            persistenceSvc.close();
        }
        return Response.ok("fail").build();
    }
[/code]
그냥 단순히 비밀번호가 같으면 지워서 응답을 success로 주고, 틀리면 fail로 줍니다.

이제 플렉스에서 봅시다.
[code]
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="absolute"
    creationComplete="init()" xmlns:rest="com.arc90.rpc.rest.*">
   
    <mx:Script>
        <![CDATA[
            import com.arc90.rpc.events.FaultEvent;
            import com.arc90.rpc.events.ResultEvent;
            import mx.controls.Alert;
           
            private function init():void
            {       
                service.send();               
            }

            private function result(event:ResultEvent):void
            {
                Alert.show(event.result.toString());
            }   
           
            private function fault(event:FaultEvent):void
            {
                Alert.show(event.fault.toString());
            }       
        ]]>
    </mx:Script>
    <rest:RESTService id="service"
        port="9080" url="http://localhost/BoardWS/resources/wsboards/22/1"
        result="result(event)" fault="fault(event)" method="DELETE">
    </rest:RESTService>
   
</mx:WindowedApplication>
[/code]
포트를 port라고 해서 따로 지정해줍니다.
저 웹서비스가 {idx}/{pwd} 형식의 url을 요청합니다. DB에는 22번의 idx를 가지고 pwd가 1인 데이터가 있습니다. 저렇게 요청하면 지워집니다.

사용자 삽입 이미지
ps. 정말 적절하지 못한 웹서비스군-_-;
 
Posted by 머드초보
,
 
이건뭐 설명이 필요없네요.
아까 전시간에 만든 WebService를 호출해봅시다.

전시간에 만들어 놓은 웹서비스
http://mudchobo.tomeii.com/tt/328

플렉스빌더를 실행합니다.
Air로 프로젝트를 하나 만듭니다.
[code]
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical">
   
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
           
            private function callWebService():void
            {
                helloWebService.sayHello(input.text);
            }
           
            private function resultHandler(event:ResultEvent):void
            {
                Alert.show(event.result.toString());   
            }
           
            private function faultHandler(event:FaultEvent):void
            {
                Alert.show(event.fault.message);   
            }
        ]]>
    </mx:Script>
   
    <mx:WebService id="helloWebService"
        wsdl="http://localhost:9080/HelloWebApp/HelloService?WSDL"
        useProxy="false"
        result="resultHandler(event)"
        fault="faultHandler(event)" />
       
    <mx:TextInput id="input" enter="callWebService()"/>
    <mx:Button id="button" label="Button" click="callWebService()"/>
       
</mx:WindowedApplication>
[/code]
wsdl부분에서만 주소를 WSDL파일 주소로 써주세요.
그 외에는 RemoteObject나 HTTPService와 같이 같은 방식으로 해주면 돼요.

다음엔 RESTful이 뭔지 좀 알아봐야겠습니다.

 
Posted by 머드초보
,