본문 바로가기

Actionscript3.0

AS3.0의 통신 - 브라우저와의 통신 (Javascript)


 Flash의 컴파일 결과물인 SWF는 그 자체로 UI 나 GAME 그리고 어플리케이션(AIR)이 될 수 있지만, 대부분은 브라우저에 Embeded 되어 사용됩니다. 배너와 같이 단순하게, 보여 주기 위해서 브라우저에 Embeded하는 경우도 있겠지만, UI 나 웹 어플리케이션을 구현할 때는 브라우져나, 서버사이드 언어와의 통신이 필요 하게 됩니다. AS3.0으로 거의 대부분의 Front-End 단의 개발작업을 모두 할 수 있긴 하지만, 퍼포먼스나 구현의 편의성을 위해 Javascript를 사용해야 하는 경우가 많습니다. 이 문서는 앞으로 몇개의 포스팅을 통해 다루게 될 AS3.0의 통신 방법에 대한 첫 문서로서 AS통신의 가장 기본이라 할 수 있는 브라우저와의 통신에 대해 다루고 있습니다.

 SWF와 브라우저와 통신 하는 방법은 크게 두가지로 구분할 수 있습니다.

첫번째, SWF 를 브라우저에 Embeded 시킬때 변수를 전달해 주는 방법
아래와 같이 main.swf 파일을 브라우저에 Embeded할때 "FlashVars" 변수를 SWF에서 접근 하는 방식입니다.

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
             id="FlashVar" width="500" height="375"
             codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
         <param name="movie" value="FlashVar.swf" />
         <param name="quality" value="high" />
         <param name="bgcolor" value="#869ca7" />
         <param name="allowScriptAccess" value="sameDomain" />
    <param name="FlashVars" value="data0=DongHyuk&data1=동혁&data2=http:www.ddongkang.com" />
         <embed src="FlashVar.swf" quality="high" bgcolor="#869ca7"
             width="500" height="375" name="FlashVar" align="middle"
             play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
             type="application/x-shockwave-flash"
             FlashVars="data0=DongHyuk&data1=동혁&data2=http:www.ddongkang.com"
             pluginspage="http://www.macromedia.com/go/getflashplayer">
         </embed>
 </object>


object 와 embed 를 함께 사용해야 하는 이유

마이크로소프트의 Internet Explore(IE) 에서는 특정한 속성과 함께 <object>를 사용해야 하고, IE 외의 다른 브라우져에서는 이와 다른 속성을 설정한 <object>태그를 사용하거나, 아예 <embed>태그를 사용해야 하기 때문입니다. 모든 브라우저에서 크로스 브라우징을 하기 위함입니다.


object 태그를 사용하여 SWF 를 브라우져에 올릴때, FlashVars 값을 지정해서 넘겨 주는 방식 입니다. 위의 코드에서 볼드로 처리해 놓은 부분을 주의 깊게 보면, 두 부분에서 정의 하고 있는 변수들이 (data0,data1,data2)과 그 값들이 똑같아야 정상적으로 SWF에 넘어가게 됩니다.

AS3.0에서 FlashVars 변수를 통해 넘어 오는 값을 받는 방법은 다음과 같습니다.

package
{
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.text.TextField;
   

    public class FlashVarsExample extends Sprite
    {
        private var field:TextField;
        public function FlashVarsExample()
        {
            field = new TextField();
            field.autoSize = "left";
            addChild(field);
           
            var keyStr:String;
              var valueStr:String;
           
            var paramObj:Object = LoaderInfo(this.root.loaderInfo).parameters;
            for (keyStr in paramObj) {
                valueStr = String(paramObj[keyStr]);
                field.appendText("\t" + keyStr + ":\t" + valueStr + "\n");
            }
        }
       
    }
}

LoaderInfo(this.root.loaderInfo).parameters 는 위의 object 태그에서 넘겨 주는 FlashVars를 받아 오고 paramObj를 for..in 구문을 사용하여 그 안에 있는 변수들을 뽑아 오는 방법 입니다. SWF 가 브라우저 또는 다른 SWF에 로드 되었을때, 자신의 로드 정보를 LoaderInfo 객체에 담고 있기 때문에 사용할 수 있는 방법 입니다. 자세한건 LoaderInfo 클래스를 참고 하세요.

플래시에서 로드 하는 xml의 url이 유동적으로 바뀌거나, 상황에 따라 다른변수를 SWF에 전달해 주고 싶을때 간단하게 사용할 수 있는 방법 입니다. 예를들어 브라우저의 현재 위치를 SWF에 전달하여 네비게이션 Depth를 구현 하는 것도 이 방법을 사용하면 됩니다. 하지만, 비교적 작은 변수만 넘길 수 있다는 점과 변수 값이 그대로 외부에 노출 된다는 단점이 있습니다.

두번째, Flash API 의 ExternalInterface 객체를 사용하는 방법

ExternalInterface는 SWF와 Flash Player컨테이너(브라우져나 Flashplayer를 소유할 수 있는 모든 것들)간에 직접 통신 할 수 있도록 도움을 주는 클래스 입니다. Javascript와의 통신을 위한 중요한 기능을 제공하고 있으며 Adobe에서는 Javascript와의 통신에 ExternalInterface를 사용하도록 권장합니다.

통신은 양방향으로 모두 가능 합니다. SWF에서 Javascript의 함수나 변수에 접근할 수 있으며 변경도 가능합니다. 반대로 Javascript에서 SWF 안에 있는 Flash 객체들도 접근 가능 합니다. 하지만 ExternalInterface는 운영체제와 브라우저가 지원 하지 않으면 사용할 수 없습니다. 지원하는 OS와 브라우저들은 다음과 같습니다.

 브라우저  운영체제  운영체제
 Internet Explorer 5.0 이상
 Windows  
 Netscape 8.0 이상
 Windows  MacOS
 Mozilla 1.7.5 이상
 Windows  MacOS
 Firefox 1.0 이상
 Windows  MacOS
 Safari 1.3 이상
   MacOS

위와 같이 사용할 수 있는 환경이 존재 하기 때문에 사용전 체크를 해 줘야 합니다.

ExternalInterface.available :Boolean = 이 값이 true를 반환하면 사용가능하고 false이면 사용 불가능 합니다.
if(ExternalInterface.available){// 통신 실행}

위와 같은 코드를 통해 사용 가능 여부를 체크 하고 사용해야만 합니다. ExternalInterface는 통신을 위한 두가지 메소드를 제공합니다.

ExternalInterface.call(functionName:String,...arguments):*
먼저 ExternalInterface.call은 SWF를 임베디드(소유)하고 있는 컨테이너의 메소드를 호출하는 함수 입니다.그함수를 수를 호출 할 수 있을 뿐만 아니라, 변수를 전달하여 실행 할 수 있습니다. 예를들어 다음과 같이 메소드를실행 하면 브라우져에서 alert()창을 띄웁니다. (여기서는 주로 사용되는 브라우저를 예를들어 설명하겠습니다.)

ExternalInterface.call("alert","alert function is called");

전달할 인수가 없을때는 빈공간으로 호출할 수 있고, 전달한 인수가 많아지면 그 갯수 만큼 arguments 자리에 넣어
주면 됩니다.

ExternalInterface.addCallback(functionName:String, closure:Function):void
addCallback의 첫번째 인수인 functionName은 Javascript에서 SWF안에 있는 Actionscript 함수를 호출할때 사용하는 이름이고, closure는 실제로 SWF 에서 호출 되는 함수명 입니다. 이 두가지 파라미터가 처음 사용할때 많이 헛깔리는 부분인데요.

예를 들어 SWF에서 addCallback 메소드를 아래와 같이 호출 하면

ExternalInterface.addCallback("sendToActionScript", receivedFromJavascript);

sendToActionScript는 Javascript에서 SWF 에 있는 receivedFromJavascript 함수를 호출 한다는 의미 입니다. 결국 sendToActionScript는 Javascript에서 사용하는 함수이고 receivedFromJavascript는 Actionscript에서 호출되는 함수 입니다. 예를 들어 보겠습니다. (Adobe 도움말에 있는 예제)

예제 샘플
if (ExternalInterface.available) {             
                output.appendText("Adding callback...\n");
                ExternalInterface.addCallback("sendToActionScript", receivedFromJavaScript);
                if (checkJavaScriptReady()) {
                    output.appendText("JavaScript is ready.\n");
                } else {
                    output.appendText("JavaScript is not ready, creating timer.\n");            
                }
               
            } else {
                output.appendText("External interface is not available for this container.");
            }
우선 ExternalInterface.available로 ExternalInterface 객체를 사용할 수 있는지 확인한 후에 addCallback 메소드를 실행 시켰습니다. addCallback 메소드를 등록 한 후에 checkJavaScriptReady() 메소드를 호출하여 javascript 호출이 가능한지 판단 합니다. 호출이 가능한지 체크 하는 이유는 브라우져가 모두 로드가 된 후에 Javascript 메소드를 호출 할 수 있기 때문 입니다. 그래서 Javascript안에 있는 isReady 메소드를 호출하여 반환 값이 true가 넘어 오면 준비가 된걸로 생각하고 스크립트가 진행 됩니다.

ExternalInterface.addCallback 을 등록했기 때문에, 해당 SWF를 임베디드한 브라우져의 Javascript에서 sendToActionScript 메소드를 호출하면 SWF 안에 있는 receivedFromJavaScript 메소드가 실행 됩니다.
private function receivedFromJavaScript(value:String):void {
            output.appendText("JavaScript says: " + value + "\n");
        }

ExternalInterface.call 메소드와 같이 Javascript에서도 SWF로 변수를 전달할 수 있기 때문에 전달된 value값을 받아서 Javascript에서 어떤 값이 넘어 왔는지, TextField에 뿌려 주는 메소드 입니다.

주의해야 할 점 브라우저에 SWF 를 object 태그나, embed 태그를 이용하여 삽입할때, name 값과 id 값을 설정해 줘야 해당 SWF에 접근 할 수 있습니다. 페이지 안에는 여러개의 SWF가 존재 할 수 있기 때문에, id 값이나 name 값을 통해 해당 SWF를 찾아 내서 그 안에 있는 메소드에 접근 할 수 있는 것 입니다. Javascript에서 SWF를 찾아 내는 메소드는 다음과 같습니다.
function thisMovie(movieName) {

         if (navigator.appName.indexOf("Microsoft") != -1) {

             return window[movieName];

         } else {

             return document[movieName];

         }

     }




navigator.appName.indexOf("Microsoft") 를 이용하여 브라우져의 종류를 판단하여, 종류에 따라 접근 방법을 다르게 하여 SWF 를 찾아 내는 메소드 입니다.

전체 브라우저 소스는 아래와 같습니다.

<!-- saved from url=(0014)about:internet -->

 <html lang="en">

 <head>

 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

 <title>ExternalInterfaceExample</title>

 <script language="JavaScript">

     var jsReady = false;

     function isReady() {

         return jsReady;

     }

     function pageInit() {

         jsReady = true;

         document.forms["form1"].output.value += "\n" + "JavaScript is ready.\n";

     }

     function thisMovie(movieName) {

         if (navigator.appName.indexOf("Microsoft") != -1) {

             return window[movieName];

         } else {

             return document[movieName];

         }

     }

     function sendToActionScript(value) {

         thisMovie("ExternalInterfaceExample").sendToActionScript(value);

     }

     function sendToJavaScript(value) {

         document.forms["form1"].output.value += "ActionScript says: " + value + "\n";

     }

 </script>

 </head>

 <body onload="pageInit();">

 

     <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"

             id="ExternalInterfaceExample" width="500" height="375"

             codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">

         <param name="movie" value="ExternalInterfaceExample.swf" />

         <param name="quality" value="high" />

         <param name="bgcolor" value="#869ca7" />

         <param name="allowScriptAccess" value="sameDomain" />

         <embed src="ExternalInterfaceExample.swf" quality="high" bgcolor="#869ca7"

             width="500" height="375" name="ExternalInterfaceExample" align="middle"

             play="true" loop="false" quality="high" allowScriptAccess="sameDomain"

             type="application/x-shockwave-flash"

             pluginspage="http://www.macromedia.com/go/getflashplayer">

         </embed>

     </object>

 

     <form name="form1" onsubmit="return false;">

         <input type="text" name="input" value="" />

         <input type="button" value="Send" onclick="sendToActionScript(this.form.input.value);" /><br />

         <textarea cols="60" rows="20" name="output" readonly="true">Initializing...</textarea>

     </form>
 </body>
 </html>


Javascript 소스를 위에서 부터 보면 Flash 에서 ExternalInterface.call 로 호출한 isReady 메소드가 정의 되어 있습니다.
<body onload="pageInit();"> 태그가 있기 때문에 페이지가 모두 로드 되면 jsReady 변수가 true가 되어 Flash 에서 Javascript를 호출할 준비가 완료 되었다는 것을 알게 됩니다. 그리고 sendToActionScript 메소드를 실행 하면 thisMovie 를 이용하여 해당 SWF를 찾아낸 후에 그 안에 있는 receivedFromJavaScript 메소드를 호출하는 것 입니다.

thisMovie("ExternalInterfaceExample").sendToActionScript(value); 를 호출했다고 해서 SWF안에 있는 sendToActionScript메소드를 호출한다고 생각하면 안됩니다. addCallback을 통해 Javascript에서 sendToActionScript를 호출하였을때 SWF에서는 receivedFromJavasript 메소드를 호출한다고 등록 했기 때문에 receivedFromJavascript 메소드가 호출 됩니다.

그리고 SWF 에서 ExternalInterface.call 메소드를 통해 Javascript의 sendToJavaScript를 호출하면 form태그에 있는 textarea에 그 값이 추가 되는 예제 입니다.

위와 같이 ExternalInterface를 이용하면, Javascript에 있는 함수나 SWF 안에 있는 함수나 쉽게 접근 할 수 있습니다. 다만,
ExternalInterface.call 이나, ExternalInterface.addCallback 으로 등록한 함수만 접근 할 수 있는 단점이 있습니다. 그리고 ExternalInterface에는 몇가지 단점이 존재 합니다.

ExternalInterface의 단점

- 액션스크립트와 자바스크립트 모두에서 별도 코드의 라이브러리를 작성하여, 플렉스 어플리케이션의기능을 자바스크립에서 사용하거나 그 반대의 경우에 대해서도, ExternalInterface 클래스가 있어야 한다.


 - ExternalInterface 클래스는 또한 제약이 있다. 기본 데이터 타입, 배열, 심플 오브젝트들은 전달할 수 있지만, 속성과 메소드를 가지는 사용자 정의 클래스를 전달하는 것은 문제가 있다.


그래서 Adobe에서는 Flex Ajax Bridge (FABridge) 라는 ExternalInterface의 단점을 보완한 Flex framework 기반의 오픈 소스 라이브러리를 제공 하고 있습니다. FABridge의 기능은 다음과 같습니다.

 

FABridge

- 리치 플렉스 컴포넌트를 에이젝트 어플리케이션 안에서 사용하고자 하지만 플렉스 코드를 엄청나게새로 쓰는 것은 원하지 않는 경우이다.  브릿지를 사용하는 일부 어플리케이션 내에 컴포넌트를 감싸려고 한다면, 자바스크립트로 전체를 스크립트할 수 있고, 이것은 서버에서 생성된 eval() 함수를 사용하는 형태의 자바스크립트를 이용한다.


 - 우리 팀에 플렉스를 하는 사람이 한 두 사람뿐인 경우이다. FABridge 라이브러리는 팀의 모든 사람이 한 두 명의 플렉스 전문가가 만든 결과물을 사용할 수 있게 해준다.


 - 플렉스와 에이젝스로 만들어진 부분을 모두 가지는 RIA를 만드는 경우이다. 우리는 ExternalInterface만을 이용하여 통합할 수도 있고, 남보다 앞서 FABridge를 사용하여 더 빠른 방법을 찾을 수도 있다.


공개된 라이브러리는 Flex 에 한정되어 만들어 졌지만, Flash platform에 모두 사용할 수 있도록 컨버팅이 가능한 구조로 되어 있습니다. 다음 문서에서는 FABridge가 어떻게 사용되며, FABridge를 기반으로한 새로운 Javascript 통신 객체에 대해 다룰 것 입니다.

관련 문서 : FABridge (Adobe Lab)

위의 예제에 대한 Actionscript 파일 전체 소스
package
{
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.external.ExternalInterface;
	import flash.text.TextField;	

	public class FlashVarsExample extends Sprite
	{
		private var field:TextField;
		public function FlashVarsExample()
		{
			field = new TextField();
			field.autoSize = "left";
			addChild(field);
			
			var keyStr:String;
  			var valueStr:String;
			
			var paramObj:Object = LoaderInfo(this.root.loaderInfo).parameters;
			for (keyStr in paramObj) {
				valueStr = String(paramObj[keyStr]);
				field.appendText("\t" + keyStr + ":\t" + valueStr + "\n");
			}			
		}		
	}
}


'Actionscript3.0' 카테고리의 다른 글

Flash player 의 보안  (17) 2009.06.24
AS2.0에서 AS3.0으로의 달라진 점은 무엇인가?  (5) 2009.05.25
AS3.0 에서의 메모리 관리  (6) 2009.03.30