Archive for January 2010

swf로 ajax를 대신하기

ajax기술은 여러 가지를 포함하고 있습니다. DHTML, DOMscript, javascript, XHR객체, XML파서, CSS 등을 버무려서 사용하는 기법을 현재의 ajax라 정의할 수 있습니다.

이 중 나머지는 알아서들 할 일이지만 XHR객체의 경우 여전히 까다롭고 브라우저간 호환성에 문제가 많이 생깁니다. 그래서 이를 중간층에서 막아주는 jquery 등을 사용합니다만, 반대로 이번엔 그 jquery를 사용하기 위한 여러 가지 문제가 추가적으로 발생합니다.

블로그 독자 여러분의 대부분이 as3쪽이 더 친하실거라 생각하여 ExternalInterface를 통해 다음과 같은 객체를 작성해봅시다.

package{

    import com.bsidesoft.BSnet.*;
    import flash.events.*
    import flash.external.*;
    import flash.utils.*;
    [SWF(backgroundColor="#ffffff", frameRate="1", width="1", height="1")]

    public class Main extends Sprite{

        private var _listener:String;
        private var _timers:Object = {};
        private var _status:Dictionary = new Dictionary( true );

        public function Main(){
            ExternalInterface.addCallback( 'AAAXload', load );
            ExternalInterface.addCallback( 'AAAXloop', loop );
            ExternalInterface.addCallback( 'AAAXstop', stop );
        }
        private function stop( $key:String ):void{
            if( _timers[$key] ){
                _timers[$key].stop();
                _timers[$key].removeEventListener( TimerEvent.TIMER, TIMER );
            }
        }
        private function loop( $key:String, $url:String, $listener:String, $times:String, $interval:String ):void{
            stop( $key );
            _timers[$key] = new Timer( parseInt( $interval), parseInt( $times ) );
            _timers[$key].addEventListener( TimerEvent.TIMER, TIMER );
            _status[ _timers[$key] ] = {isFlushed:true, url:$url, listener:$listener};
            _timers[$key].start();
        }
        private function TIMER( $e:TimerEvent ):void{
            if( _status[ $e.target ].isFlushed ){
                _status[ $e.target ].isFlushed = false;
                CSloader.LOAD( CSloader.ARG.addTEXT( 'txt', _status[ $e.target ].url ).END( function( $data:Object ):void{
                    _status[ $e.target ].isFlushed = true;
                    ExternalInterface.call( _status[ $e.target ].listener, $data.txt );
                } ) );
            }
        }
        private function load( $url:String, $listener:String ):void{
            _listener = $listener;
            CSloader.LOAD( CSloader.ARG.END( hnLoaded ).addTEXT( 'txt', $url ) );
        }
        private function hnLoaded( $data:Object ):void{
            ExternalInterface.call( _listener, $data.txt );
        }

    }
}

귀찮아서 회사 라이브러리가 있는 버전을 그냥 올렸습니다. 참고로 BSnet은 URLLoader, Sound, Loader를 전체적으로 완만하게 래핑하고 있는 데이터 로딩 클래스입니다.

이 클래스가 제공하는 서비스는 다음과 같습니다.

  1. 단순한 데이터 로딩을 지원하는 AAAload
  2. 연속으로 데이터를 로딩하여 이벤트를 발생시키는 AAAloop
  3. 진행 중인 loop를 정지시키는 stop

간단하게 이 클래스를 컴파일 한 뒤엔 아래와 같은 html을 작성하여 사용하게 됩니다.

<html>
<head>
<script language="JavaScript" type="text/javascript" src="main.js"></script>
<script language="JavaScript" type="text/javascript">
AAAX = {
	_target:'',
	load:function( $url, $listener ){
		if( this._target ){
			this._target.AAAXload( $url, $listener );
		}else{
			alert( 'setTaget을 먼저 호출하세요' );
		}
	},
	loop:function( $key, $url, $listener, $times, $interval ){
		if( this._target ){
			this._target.AAAXloop( $key, $url, $listener, $times, $interval );
		}else{
			alert( 'setTaget을 먼저 호출하세요'+this._target );
		}
	},
	stop:function( $key ){
		if( this._target ){
			this._target.AAAXstop( $key );
		}else{
			alert( 'setTaget을 먼저 호출하세요' );
		}
	},
	setTarget:function( $id ){
		if( document.getElementById( $id ) ){
			this._target =  document.getElementById( $id );
		}else{
			alert( $id + '는 존재하지 않는 객체입니다.' );
		}
	}
}
</script>

</head>

<body onload="AAAX.setTarget( 'aaax' );">
<script language="JavaScript" type="text/javascript">
AC_FL_RunContent(
	'id', 'aaax','name', 'aaax',
	'src', 'Main','movie', 'Main',
	'width', '1','height', '1',
	'wmode', 'gpu','scale', 'exactfit','loop', 'false','play', 'false','align', 'middle','quality', 'low','devicefont', 'false',
	'bgcolor', '#02182e','menu', 'false','allowFullScreen', 'false','allowScriptAccess','sameDomain',
	'codebase', 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0',
	'pluginspage', 'http://www.adobe.com/go/getflashplayer','salign', ''
);
</script>
</body>
</html>

위의 html 매우 간단히 설명하면 사실 html이라기보다 AAAX자바스크립트 객체에 관한 설명인데, body의 onload에 걸려있는 setTarget을 통해 플래시 객체를 인지시킵니다.

이후 AAAX.load(), AAAX.loop() 등과 같은 형태로 사용할 수 있게 됩니다. 매우 흔한 ajax객체의 샘플인 2단계 select박스라면 아래와 같은 html코드로 설명할 수 있습니다.

<select id="category1" onchanged="hnChanged( this.value );" >
	<option value="0">0</option>
	<option value="1">1</option>
	<option value="2">2</option>
</select>
<div id="target"></div>
<script>
function hnChanged( $value ){
	AAAX.load(  'getCat2.asp?cat1='+$value, 'hnLoaded' );
}
function hnLoaded( $data ){
	document.getElementById( 'target' ).innerHTML = $data;
}
</script>
  1. 위의 예에서 셀렉트박스의 항목을 선택하면
  2. onchanged이벤트에 걸려있는 hnChanged 함수가 호출되고
  3. 함수 내부에 있는 AAAX.load가 호출됩니다.
  4. 이때 인자로 url과 로딩완료 리스너를 지정할 수 있는데 완료리스너의 경우 함수의 이름을 문자열로 보내면 됩니다.
  5. ‘hnLoaded’ 라고 보냈으므로 이 데이터가 로딩완료되면 자동으로 hnLoaded가 호출되며 인자로 로딩해온 데이터를 넘겨줍니다.
  6. 실제 자바스크립트의 hnLoaded함수에서는 받아온 데이터를 target DIV의 내용으로 써줄 뿐입니다.
  7. 두번째 select박스를 그리는 모든 html코드 조각은 이미 getCat2.asp가 생성해서 넘겨준 것이죠.

loop나 stop로 대동소이하게 작동합니다.

Developer Book review 10.01.20

IMG_3246

1.

서비스 중인 서버에서 웹셀공격을 받았습니다. 뭔가 회사 서버스크립트를 일제히 업데이트해야겠다는 생각에 웹해킹관련 책을 한 권 사다가 직원에게 쥐어주었는데 언제 보고 반영할지..

2.

소프트웨어 비즈니스는 매우 재밌는 책입니다. 딱 저 같은 사람들을 대상으로 쓴 책이라 와닿는 것이 너무 많습니다.

3.

기획업무를 맡고 있는 녀석들에게 가르칠 교재를 찾다 주워온 책이 제안팀 이야기인데, 보다 고객에 대해서 생각해보는 자세는 곧 서비스가 유저를 바라보도록 하는 자세이기도 합니다. 내용이 좀 단적인 면으로 흐르지만 괜찮습니다.

4.

좀 실망을 많이 안겨줬지만 그럭저럭 볼만합니다. 실버라이트3의 신기능을 중심적으로 다루지 않는다는게 흠이랄까.

5.

ajax보안은 사실 ajax가 보안되는것에 관심이 있어서 산게 아닙니다. URLLoader가 웹서버와 통신할 때도 완전히 같은 조건의 보안이슈가 발생하기 때문에 이러한 보안문제를 방비하기 위한 기본을 어진이에게 가르치기 위해 샀습니다.

6.

카지노실무론…… 이건..음 영업비밀이라 해두겠습니다 ^^

ini로 view를 기술하기 프로젝트

ini는 야바위 스펙이 아니라 윈도우즈의 설정 등에 쓰이는 아주 범용적인 포멧으로 다음과 같은 기본적인 문법으로 되어있습니다.

  • []를 통해 항목을 선언한다.
  • =을 통해 항목과 값을 구분한다.
  • 개행을 통해 각 항목을 구분한다.
  • #을 통해 주석을 처리한다.

따라서 보통 아래와 같은 형태가 됩니다.

[category1] #회사부서
item1=디자인
item2=개발
item3=기획
#--------------------------------------------
[category2] #주인공수치
level=30
hp=50
power=110
speed=50

이러한 ini파일 방식의 기술은 xml에 비해 현격하게 작은 용량과 데이터 자체만 기술하기 때문에 메모장으로도 매우 손쉽게 작성할 수 있다는 잇점이 있습니다.

하지만 이보다 더 중요한 장점은 xml은 비개발자인 기획자나 컨텐츠 제작자, 디자이너에게 작성해달라고 했을 때 심한 거부반응을 불러오는 반면 같은 내용을 적당한 ini로 기술하여 채워달라고 하면 잘해준다는 점입니다.

개발적인 마인드가 전혀 없는 팀원들이 코어 개발과 상관없는 단순한  컨텐츠기술부분을 외부데이터로 처리해 줘야 하는 경우 현실적으로 디자이너나 기획자에게 xml을 가르치기보다는 그냥 ini를 전달하는 편이 훨씬 좋죠.

확장된 ini

사실 ini자체 스펙으로는 기술에 한계가 있어서 저는 자체적으로 다음과 같은 형태까지 확장을 시켰습니다.

배열을 기술하기

[data1:Array]
50
60
70

trace( $parsed.data1[0] ); //50

만약 대괄호가 한번도 나오지 않는다면 하나의 큰 배열로 인식한다.

50
60
70

trace( $parsed[0] ); //50

항목이 배열인 경우

[data1]
list1=50,60,70,80

trace( $parsed.data1.list1[0] ); //50

형을 기술할 수 있다.

[data1]
(int)list1=40
(Boolean)list2=true,false,true

trace( $parsed.data1.list2[0] === true ); //true

항목이 오브젝트인 경우

[data1]
(Object)position=x:3,y:5,z:50
trace( $parsed.data1.position.x ); //3

자식관계를 형성한다.

[item1]
list1=3

[item1.list1]
item=test1

[item1.list1.item]
item=test2

trace( $parsed.item1.list1.item ); //test1
trace( $parsed.item1.list1.item.item ); //test2

그래서 이것을 통해 간단하면서도 복잡한 데이터를 기술할 수 있게 되었습니다. 사실 그래서 저는 거의 xml과 e4x를 잘 쓰지 않고 ini를 즐겨쓰는 편입니다.

ini를 통한 designView의 기술

이걸 이용해서 완전히 view를 생성하는 부분을 외부 파일로 분리하여 런타임에 처리하려고 생각하고 있었습니다.

[login:view]
(Number)background:sprite=x:0,y:0,width:500,height:300 #간단히 기술되는경우
(Number)label1:text=x:10,y:10
(Number)input1:input=x:50,y:10,width:200,height:30,border:0xffffff
(Number)submit:sprite=x:200,y:100,width:200,height:50

[login.label1] #보다 복잡한 기술이 추가적으로 필요한 경우
(Boolean)embedFonts=true
font=go1
size=12
color=0x000000
text=아이디

[login.submit]
click=instance1.hnClick
add:bitmap=bitmapData:button1
add:text=text:아이디,color:0xffffff

물론 사람이 이걸 생성하게 할 생각은 없고 전용 툴을 하나 만들고 있는데 궁극적으로는 PSD를 저 코드로 자동변환해주는 거죠.
해서 디자이너가 레이어정리를 이쁘게 잘한 PSD를 넘겨주면 전용툴이 쫙 정리해서 view.ini 랑 관련된 에셋을 묶어서 하나의 swf로 만들어주는걸 생각하고 있습니다 ^^;