저는 귓속말 서비스의 매쉬업이 필요해서 귓속말 서비스가 제공하는 OAuth인증방법에 대해서 삽질을 하고 있었습니다. 근데, 잘 안되는겁니다 ㅠ 그래서 다른 서비스에서도 안되는건가 해서 OAuth를 지원하는 다른 서비스를 찾아보고 있었는데, 마침 구글에서 스토리큐가 OAuth를 지원하고 있었습니다 ^^

그래서 스토리큐도 OAuth했을 때 잘 안됐었는데, aproxacs님의 도움으로 스토리큐에서는 해결했습니다 ^^
귓속말도 rath님의 도움으로 해결을 했습니다 ㅠ

왜....얘기가 딴데로 흘러가지....-_-; 암튼 얘기가 삼천포로 빠졌네요.

우선 이번에 소개드릴 서비스는 스토리큐라는 서비스입니다.

http://www.storyq.net/

우리가 가지고 있는 ppt파일이나 이미지파일로 쉽게 슬라이드쇼를 만들 수가 있습니다.
ppt파일을 스토리큐에 올리게 되면 스토리큐는 해당 ppt파일을 슬라이드쇼로 만들어줍니다. 우리는 이것을 파워포인트가 없이 슬라이드쇼를 볼 수가 있게 됩니다.

가장 큰 장점은 웹에서 파워포인트프로그램 없이 슬라이드쇼를 감상할 수 있죠 ^^
아주좋아요~ ^^ 제가 가지고 있는 문서를 올려봤는데, 잘 보이게 변환이 되더라구요.
해당 슬라이드를 비공개로 올려서 공개되면 안되는 자료들도 보관해놓고 있다가 나중에 사내에서 발표할 때에도 유용하게 쓰이겠네요 ^^

지원하는 파일이 PPT, PPTX, PPS, PPSX, PDF라고 되어있길래 제가 가지고 있는 500페이지짜리 책을 한번 올려봤습니다-_-; ActionScript3 DesignPattern-_-;

오.....놀랍습니다^^ 올라갑니다 ^^

534페이지입니다. 좀 아쉬운 점이 있다면 페이지를 직접 입력해서 갈 수 있었으면 합니디만 ^^
저기 숫자있는 부분에 직접 입력하면 안되네요 ^^

전체화면으로 봐도 잘 보이는군요.


게다가 해당 큐를 자동으로 북마크를 할 수 있도록 북마크 서비스를 하는 곳에 원클릭으로 북마크를 추가할 수 있습니다. 와 놀랍네요. 이런 것 까지 세심하게 배려한...^^

파이어폭스용 부가기능인 파이어큐라는 것도 있습니다 ^^
오른쪽 하단에 fireQ라는 로고가 있네요.
저같은 경우는 파이어폭스에서 파이어버그랑 IE TAB을 같이 쓰는데 고 사이에 있네요 ^^

클릭하면 왼쪽에 새로운 탭이 뜨네요. 인증을 하면 업로드도 하고 자신이 가지고 있는 큐를 볼 수 있고, 다른 사람이 올린 인기있는 큐도 볼 수 있네요.

이런 유용한 웹서비스가 앞으로도 많이 나왔으면 하네요 ^^

 
Posted by 머드초보
,
 
두번째글입니다.
프로그램 설치 및 체험(?)은 아래의 INSTALL NOW을 클릭해주세요 ^^

Alternative content

Get Adobe Flash player


이제 air프로젝트를 하나 생성합니다.
[code]
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical" width="300" height="300"
    horizontalAlign="center" verticalAlign="middle"
    verticalScrollPolicy="off" horizontalScrollPolicy="off">
   
    <mx:Script>
        <![CDATA[
            import flash.net.navigateToURL;
            import mx.rpc.events.FaultEvent;
            import mx.controls.Alert;
            import mx.rpc.events.ResultEvent;
            import com.mudchobo.MudchoboOAuth;
           
            private var mudchoboOAuth:MudchoboOAuth;
            private var consumerToken:String = "자신의 Consumer Token";
            private var consumerSecret:String = "자신의 Consumer Secret";
            private var requestTokenURL:String = "http://www.storyq.net/oauth/request_token";
            private var accesstokenURL:String = "http://www.storyq.net/oauth/access_token";
            private var authorizeURL:String = "http://www.storyq.net/oauth/authorize";
           
            private function login():void
            {
                // RequestToken 요청
                mudchoboOAuth = new MudchoboOAuth(consumerToken, consumerSecret);
                mudchoboOAuth.requestOAuth(new Object(), requestTokenURL, "POST",
                    requestTokenResultHandler, requestTokenFaultHandler);
                currentState = "stateRequestToken";
            }
           
            private function logout():void
            {
                mudchoboOAuth = null;
                currentState = "";   
            }
           
            private function loginConfirm():void
            {
                // AccessToken요청
                mudchoboOAuth.requestOAuth(new Object(), accesstokenURL, "POST",
                    accessTokenResultHandler, accessTokenFaultHandler);
            }
           
            private function loginCancel():void
            {
                Alert.show("로그인에 실패했습니다.");   
                currentState = "stateStart";
            }
           
            // RequestToken 요청 성공 시
            private function requestTokenResultHandler(event:ResultEvent):void
            {
                currentState = "stateLogin";
                mudchoboOAuth.parseData(event.result.toString());
                authorizeHTML.location = mudchoboOAuth.authorizeSite(authorizeURL);
            }
           
            // RequestToken 요청 실패 시
            private function requestTokenFaultHandler(event:FaultEvent):void
            {
                Alert.show(event.fault.toString() + "요청이 잘못 되었습니다.");
                currentState = "stateStart";
            }
           
            // AccessToken 요청 성공 시
            private function accessTokenResultHandler(event:ResultEvent):void
            {
                currentState = "stateShowMyQ";
                mudchoboOAuth.parseData(event.result.toString());
               
                // 스토리큐에서 자신의 큐리스트를 가져온다.
                mudchoboOAuth.requestOAuth(new Object(), "http://www.storyq.net/boxes/mine.xml", "GET",
                    getQResultHandler, getQFaultHandler);
            }
           
            // AccessToken 요청 실패 시
            private function accessTokenFaultHandler(event:FaultEvent):void
            {
                Alert.show(event.fault.toString() + "요청이 잘못 되었습니다.");
                currentState = "stateStart";
            }
           
            // 자신의 큐리스트 가져오기 요청 성공 시
            private function getQResultHandler(event:ResultEvent):void
            {
                dgQ.dataProvider = event.result.boxes.box;
            }
           
            // 자신의 큐리스트 가져오기 요청 실패 시
            private function getQFaultHandler(event:FaultEvent):void
            {
                Alert.show(event.fault.toString() + "가져오는 도중 실패했습니다.");
            }
           
            // 버튼으로 다시 요청하기
            private function clickReplyHandler():void
            {
                mudchoboOAuth.requestOAuth(new Object(), "http://www.storyq.net/boxes/mine.xml", "GET",
                    getQResultHandler, getQFaultHandler);
            }
           
            // 더블클릭 시 해당 큐로 이동
            private function doubleClickDgQ():void
            {
                if (dgQ.selectedItem.uri != null)
                {
                    navigateToURL(new URLRequest(dgQ.selectedItem.uri), "_blank");
                }   
            }
        ]]>
    </mx:Script>
   
    <mx:states>
        <mx:State name="stateStart">
            <mx:SetProperty name="width" value="300"/>
            <mx:SetProperty name="height" value="300"/>
        </mx:State>
       
        <mx:State name="stateRequestToken">
            <mx:AddChild position="lastChild">
                <mx:Label text="잠시만 기다려주세요..."/>
            </mx:AddChild>
            <mx:SetProperty target="{btnLogin}" name="enabled" value="false"/>
        </mx:State>
       
        <mx:State name="stateLogin">
            <mx:SetProperty name="width" value="1024"/>
            <mx:SetProperty name="height" value="768"/>
            <mx:AddChild position="lastChild">
                <mx:HTML width="1024" height="642" id="authorizeHTML"/>
            </mx:AddChild>
            <mx:RemoveChild target="{btnLogin}"/>
            <mx:AddChild relativeTo="{authorizeHTML}" position="before">
                <mx:HBox id="hbox1">
                    <mx:Button label="확인" click="loginConfirm()"/>
                    <mx:Button label="취소" click="loginCancel()"/>
                </mx:HBox>
            </mx:AddChild>
            <mx:AddChild relativeTo="{hbox1}" position="before">
                <mx:Text text="아래 창에서 StoryQ사이트 로그인페이지로 이동합니다. &#xa;로그인 완료 후, 확인 버튼을 클릭하주세요." height="40"/>
            </mx:AddChild>
        </mx:State>
       
        <mx:State name="stateShowMyQ">
            <mx:RemoveChild target="{btnLogin}"/>
            <mx:AddChild position="lastChild">
                <mx:Button label="로그아웃" id="btnLogout" click="logout()"/>
                <mx:Button label="다시 가져오기" id="btnReply" click="clickReplyHandler()"/>
            </mx:AddChild>
            <mx:SetProperty name="width" value="550"/>
            <mx:SetProperty name="height" value="422"/>
            <mx:AddChild position="lastChild">
                <mx:DataGrid width="522" height="328" id="dgQ"
                    doubleClickEnabled="true" doubleClick="doubleClickDgQ()">
                    <mx:columns>
                        <mx:DataGridColumn headerText="제목" dataField="title"/>
                        <mx:DataGridColumn headerText="주소" dataField="uri"/>
                    </mx:columns>
                </mx:DataGrid>
            </mx:AddChild>
        </mx:State>
       
    </mx:states>
    <mx:Button label="스토리큐에 로그인" id="btnLogin" click="login()"/>
   
</mx:WindowedApplication>
[/code]
대충만들어서 코드가 지저분합니다만-_-;
아마 알아보실 수 있을듯(응?-_-)합니다.

프로그램을 실행하면, 로그인버튼이 있는데, 로그인 버튼을 클릭하면, 위에서 말한 RequestToken절차가 실행됩니다. RequestToken을 얻어오게 되면, AIR프로그램은 자신의 브라우저를 하나 열어서 로그인 RequestToken을 파라미터로 한 Authorize사이트로 이동합니다.
여기서 사용자는 자신의 아이디로 로그인을 합니다. 로그인이 완료되면, 서버쪽에서 AccessToken을 만들어놓습니다.
로그인 후 위에 확인버튼을 클릭하게 되면, 클라이언트에서는 AccessToken절차가 시작됩니다. AccessToken을 가져온 뒤 그 해당 AccessToken을 이용해 보호된 자원에 접근하는 과정입니다.

접근해서 가져온 데이터는 자신의 큐리스트를 보여주는 겁니다.
자신의 큐들을 데이터그리드에 넣었으며, 그 데이터그리드의 해당 칼럼을 더블클릭하게 되면 해당 큐로 이동하게 됩니다. 정말 별거없네-_-;

위에 코드는 위와 같은 절차로 되어있습니다.
혹시...Oauth로 삽질하시는 분은 이 글로 도움이 되었으면 하네요 ^^

이전 글 링크 - http://mudchobo.tomeii.com/tt/323
PS. 이 짓으로 추석을 날렸습니다-_-;
 
Posted by 머드초보
,
 
우선 OAuth인증에 대해서.....
잘 모르겠지만, 오픈되어있는 인증인 듯 합니다-_-;(응?)
어쨌든, 이걸 이용해서 인증을 하고, 인증이 되었으면, 보호된 자원으로부터 접근을 할 수 있습니다.
절차는 이렇습니다.

1. RequestToken얻어오기.
우선 매쉬업애플리케이션을 등록하면, consumer key와 consumer Secret을 받게 됩니다.
이것과 requestToken요청할 수 있는 주소를 알면 RequestToken을 얻을 수 있습니다.

2. Authorize하기.
RequestToken을 가지고, 인증페이지로 가서 로그인을 합니다. 로그인이 완료되면, 서버프로바이더에서는 해당 RequestToken에 대해서 AccessToken을 준비해놨을겁니다.

3. AccessToken얻어오기.
서버프로바이더가 준비한 AccessToken을 요청합니다. 이건 consumer key, consumer secret, requestToken, requestTokenSecret, AccessToken요청할 수 있는 주소를 알면 얻어올 수 있습니다.

4. 해당 AccessToken을 이용해서 자원에 접근하기.
이 AccessToken이 있으면 해당 자원에 접근할 수 있습니다. 이 토큰으로 보호된 자원에 접근할 수 있는 것이죠.

이런 절차의 인증방식을 사용합니다. 너무 간소화했지만, 여기에 가면 자세히 설명이 있습니다.
http://aproxacs.springnote.com/pages/1279246 (aproxacs님 감사해요~)

AIR에서도 OAuth인증을 사용하는 곳에 인증을 할 수 있습니다.
사실 오픈마루가 제공하는 귓속말을 하려고 했는데, 이건 잘 안되더라구요 ㅠ 운영자님께 문의해놨습니다 ㅠ

스토리큐로 해봅시다 ^^ 이 서비스 정말 획기적인데요? OAuth때문에 처음알았습니다 ^^

여기서 사용한 라이브러리는 as3용 oauth라이브러리 http://code.google.com/p/oauth-as3/
암호화 라이브러리 http://code.google.com/p/as3crypto/ 를 사용했습니다.

아...소스코드가 너무 기네-_-;
우선 제가 만든 OAuth요청 클래스입니다.
[code]
package com.mudchobo
{
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;
    import mx.rpc.http.HTTPService;
   
    import org.iotashan.oauth.OAuthConsumer;
    import org.iotashan.oauth.OAuthRequest;
    import org.iotashan.oauth.OAuthSignatureMethod_HMAC_SHA1;
    import org.iotashan.oauth.OAuthToken;
   
    public class MudchoboOAuth
    {
        private var consumer_key:String;
        private var consumer_secret:String;
        private var oauth_token:String = "";
        private var oauth_token_secret:String = "";
       
        public function MudchoboOAuth(consumer_key:String, consumer_secret:String)
        {
            this.consumer_key = consumer_key;
            this.consumer_secret = consumer_secret;
        }
       
        // OAuth요청
        public function requestOAuth(params:Object, url:String, method:String,
            resultHandler:Function, faultHandler:Function):void
        {
            var consumerToken:OAuthConsumer = new OAuthConsumer(consumer_key, consumer_secret);
            var oauthToken:OAuthToken = null;
            if (oauth_token != "")
            {
                oauthToken = new OAuthToken(oauth_token, oauth_token_secret);
            }
            var request:OAuthRequest = new OAuthRequest(
                method, url, params, consumerToken, oauthToken);
            var builtUrl:String = request.buildRequest(new OAuthSignatureMethod_HMAC_SHA1());
            trace(builtUrl);
           
            var requestHTTPService:HTTPService = new HTTPService();
            requestHTTPService.url = url;
            requestHTTPService.method = method;
            requestHTTPService.addEventListener(ResultEvent.RESULT, resultHandler);
            requestHTTPService.addEventListener(FaultEvent.FAULT, faultHandler);
            requestHTTPService.send(params);
        }
       
        // Authorize를 위한 로그인 사이트  주소 리턴.
        public function authorizeSite(authrizeURL:String):String
        {
            return authrizeURL + "?oauth_token=" + oauth_token;
        }
       
        // Data파싱 후 oauthToken과 oauthTokenSecret추출
        public function parseData(data:String):void
        {
            var tokenStart:Number = data.indexOf("oauth_token");
            var tokenEnd:Number = data.indexOf("&oauth_token_secret");
            this.oauth_token = data.substring(tokenStart + 12, tokenEnd);
            this.oauth_token_secret = data.substring(tokenEnd + 20);
        }
    }
}
[/code]
메소드중에 requestOAuth메소드가 있는데, 이것은 parameter와 url, method, resultHandler, faultHandler만 지정해주면 자동으로 OAuth에 필요한 파라메터를 만들어서 요청해주는 메소드입니다.
이걸로 RequestToken, AccessToken, 그 외 보호된 자원에 접근하기 위한 요청으로 쓸 수 있습니다.

authorizeSite메소드는 로그인 해주는 사이트로 이동할 때, requestToken을 파라메터로 붙여주는 주소를 리턴해줍니다.

parseData메소드는 요청 후 리턴받은 데이터가 oauth_token=~~~~&oauth_token_secret=~~~ 라고 되어있는 것을 단순히 파싱해서 변수에 저장하는 역할을 합니다.(참고로, 오픈마루의 귓속말서비스 같은 경우 AccessToken을 요청할 때 뒤에 오픈아이디를 같이 붙여줘서 리턴값이 틀립니다. 그걸 참고해서 저걸 사용해야할 듯 싶습니다.)

글이 너무 길어져서 다음글로 패스-_-;
http://mudchobo.tomeii.com/tt/324
 
Posted by 머드초보
,