Stream을 실전에 활용하기 1
지난 두 차례에 걸쳐 Stream을 공부해봤습니다. 하지만 실제 Stream을 공부한 목적은 이번 장에 있습니다. Flash10에 적용된 다양한 Stream관련 객체를 근본부터 이해하고 사용하기 위해서 여태 길게 설명했던 것이죠. 이제 하나하나 as3 class lib에 내장된 Stream객체를 살펴보겠습니다.
1. URLStream
일단 이름부터 Stream아니겠습니까? 당연히 Stream을 다루고 있습니다. 먼저 adobe문서에 기술된 본문을 살짝 참고해보죠.
URLStream 클래스는 URL 다운로드에 대한 낮은 레벨의 액세스를 제공합니다. 데이터는 다운로드되는 즉시 응용 프로그램 코드에서 사용할 수 있으며 URLLoader에서 처럼 전체 파일이 완전히 다운로드 될 때까지 기다릴 필요가 없습니다. URLStream 클래스를 사용하면 다운로드가 완료되기 전에 스트림을 닫을 수도 있습니다. 다운로드 된 파일의 내용은 원시 이진 데이터 형태로 사용할 수 있습니다.
뭔가 앞서 배웠던 Stream의 느낌이 짠하고 오시나요? 이제 좀 깊이 이해해봅시다.
URLStream은 load라는 method를 제공하는데 인자로 URLRequest를 받습니다. URLRequest는 HTTP규격의 request header를 문자열로 생성해주는 역활을 하는데, 이미 URLStream이 URLRequest로 load를 한다는 사실 자체가 HTTP를 기반으로 통신하고 있다는 뜻이 됩니다.
HTTP는 잘 알려진 특징을 갖고 있습니다. 가장 많이 들어보셨을 게 아마도 상태유지를 하지 않는다는 것이겠죠? 하지만 더 파고 들어보면 HTTP는 TCP기반으로 통신하고 text기반으로 protocol을 정의합니다. 이 기저의 두 가지로 말미암아 아래와 같은 특성이 자동으로 나옵니다.
1. 요청 및 응답이 TCP에 의해 순차적으로 수신, 송신된다(이것은 바로 Stream의 개념!)
2. 바이너리 데이터는 text로 encoding 된다(base64가 기본입니다)
따라서 URLStream은 URLRequest로 요청한 자원을 Stream으로 수신하는 클래스라고 생각할 수 있습니다.
(바로 위의 문장이 조금이라도 어렵다고 느끼시면 다시 복습 하시길 권장합니다 ^^)
그럼 사용할 때는 어떻게 사용할까요? Stream의 장점을 활용하려면 역시 로드와 동시에 활용해야 합니다. 만약 onComplete에 처리할 생각이었으면 처음부터 Stream계열의 객체를 사용할 이유가 없습니다.
URLStream의 경우엔 최초 로딩이 시작되는 순간을 알아차리기 위해 ProgressEvent를 활용하게 되어있습니다. 또한 전에 살짝 언급한대로 모든 Stream 계열은 소비자측이 더 개발하기가 복잡합니다. URLStream도 어지간히 무책임한 클래스입니다. adobe문서에는 다음과 같은 내용이 있습니다.
URLStream의 읽기 작업은 비차단 방식입니다. 따라서 데이터를 읽기 전에bytesAvailable속성을 사용하여 충분한 데이터가 있는지 확인해야 합니다. 충분한 데이터가 없는 경우EOFError예외가 발생합니다.
즉 클라이언트가 알아서 구현하시라는 뜻입니다. 구체적으로 해석하면 URLStream에게 ProgressEvent Listener를 걸어서 통보를 받은 직후부터 쭉 URLStream.bytesAvailable을 감시하며 원하는 만큼 꾸준히 데이터를 얻어가라 라는 지시입니다.
이제 일전에 만들었던 CstringStream을 개조하여 CURLStringStream으로 만들어봅시다.
package {
import flash.events.*;
import flash.net.*;
public CURLStringStream extends EventDispatcher{
static public const UPDATE:String='update';
private var _stream:NetStream;
private var _readByte:uint=0;
private var _buffer:uint=5;
public function CURLStringStream(){}
public function load($url:String):void{
//기존에 loading중인 Stream있다면 제거한다.
if(_stream!==null){
_stream.close();
}
//읽어들인 위치를 초기화한다.
_readByte=0;
_stream=new URLStream(new URLRequest($url));
//Loading 상황을 처리할 내부 Listener설정
_stream.addEventListener(ProgressEvent.PROGRESS,hnUpdate);
_stream.load();
}
//data를 출력한다.
public function get data():String{
//read위치를 buffer만큼 전진시키고
_readByte+=_buffer;
//buffer만큼 출력한다.
return _stream.readMultiByte(_buffer);
}
//buffer를 셋팅한다.
public function set buffer($buffer:uint):void{
_buffer=$buffer;
}
private function hnUpdate(e:Event):void{
//Loading된 분량
var loaded:uint=_stream.bytesAvailable;
//수신 data가 read data보다 buffer이상 크다면 update!
if(loaded
설명을 주석으로 자세하게 달았기 때문에 따로 언급할 부분은 별로 없습니다만 큰 흐름을 보면 인스턴스를 생성하고 buffer를 셋팅한 뒤 load를 하면 buffer만큼 읽어들일 때마다 데이터를 갱신 받는 스타일입니다. 일전에 짜둔 CtextSubscribe 에서 유일하게 수정해야 하는 부분은 _stream이 CstringStream에서 CURLStringStream으로 타입이 바뀐 것 뿐입니다. 하지만 URL에서 Loading이 순식간에 일어나기 때문에 매번 갱신하던걸 콜론으로 연결해가는 것으로 대체하겠습니다.아래와 같이 새로운 CURLtextSubscribe를 만들어보죠.
package {
import flash.text.*;
import flash.events.*;
public class CURLtextSubscribe extends TextField{
private var _stream:CURLStringStream;
public CURLtextSubscribe(){
text='';
}
//stream을 등록하고 listener를 설정한다.
public set textStream($stream:CURLStringStream):void{
_stream=$stream;
_stream.addEventListener(CURLStringStream.UPDATE,hnUpdate);
}
//update 발생시마다 문자열추가
private function hnUpdate($e:Event):void{
appendText(':'+_stream.data);
}
}
}
Main에서는 약간의 변화가 있습니다. CURLStringStream이 셋팅하는 순서가 다르기 때문입니다.
package {
import flash.display.*;
import flash.utils.*;
public class Main extends Sprite{
private var _stream:CURLStringStream;
public function Main(){
//stream생성
_stream=new CURLStringStream();
//10개의 문자열이 수신될때마다 갱신
_stream.buffer=10;
//text수신자 생성
var subscribe:CtextSubscribe=new CtextSubscribe();
//수신자에게 stream 설정
subscribe.textStream=_stream;
//화면에 등록
addChild(subscribe);
//stream Loading시작
_stream.load('http://test.test/test.txt');
}
}
}
실제 실행하면 URL의 txt가 워낙 빨리 로딩되기 때문에 순식간에 콜론으로 연결된 문자열을 보실 수 있을 겁니다.
이것 참 원래 계획은 이번 글에서 NetStream, Sound, Video 등을 다 다루려고 했습니다만, 완전히 무리군요. 다음 글로 넘기겠습니다.