지난 글에서 우리는 플래시의 통신 방법에 대해 살펴봤다. 지난 글에 살펴본 통신 방법 외에 중요한 통신 방법이 있는데 바로 XML이다. XML에 대한 중요성은 더 얘기할 필요가 없을 것이다. 더구나 다음과 같은 플래시의 이점과 함께 XML의 유연성을 함께 사용할 수 있다면, 금상첨화라고 해야 할 것이다.
◆ 리치 인터넷 애플리케이션을 구현
◆ 동적 업데이트
◆ 백그라운 다운로드, 트래픽 감소
XML 자체가 많은 이슈를 가지고 있지만, 가장 중요한 부분은 물론 XML 웹 서비스(SOAP)이다. 많은 통신 규약들도 XML로 만들어지고 있다. 예를 들어, 오픈 소스 메신저인 Jabber 같은 경우가 그렇고, B2B와 EAI 등의 이슈와 함께 항상 빠지지 않고 등장하는 게 바로 XML이다. 이제 플래시가 지원하는 XML과 ASP.NET의 웹 서비스 등이 어떻게 서로 연동될 수 있는지 그 넓은 가능성을 하나하나 짚어 보자(이 글에서는 플래시의 화면 출력 방법이나 애니메이션 등에 대해서는 설명하지 않는다).
플래시 5 이상의 버전에서 두드러진 변화 중 하나가 바로 XML에 대한 지원 기능이다. 플래시는 기본적으로 XML 문서를 파싱하고, DOM을 다루기 위한 다양한 기능들을 제공한다. XML 문서 구조를 만들거나 XML을 읽어와서 DOM으로 구성하거나 내부에서 DOM을 생성할 수 있다. 다만, XPath와 같은 기능이 없어 XML 내용을 참조하기 위해 hasChildNodes(), firstChild, nextSibling 등을 반복적으로 사용해야 한다는 점이 아쉽지만, 플래시 애플리케이션에서 필요한 XML과 XML 웹 서비스 관련 기능을 대부분 제공하고 있다. XML을 사용하기 위해서는 다음과 같이 XML 클래스를 이용해 인스턴스를 만든다.
xmldoc = new XML(); // XML 객체 생성
혹은, 다음과 같이 XML를 생성자에 직접 지정할 수도 있다.
xmldoc = new XML("<book><title>어떻게 문제를 풀 것인가</title></book>");
하지만, 문자 배열을 사용하는 것이 훨씬 간편하기 때문에 이렇게 내부에서 DOM을 생성하는 경우보다는 외부에서 XML 문서를 읽어서 처리하는 경우가 훨씬 많다. 플래시에서 XML 조작과 관련해 자주 사용하는 메쏘드를 정리하면 <표 1>과 같다.
|
|
getByteLoaded() |
로딩된 XML 문서의 크기를 얻는다. |
hasChildNodes() |
자식 노드가 있는지 확인한다. |
load() |
XML 문서를 로드한다. |
parseXML() |
문자열로 된 XML 소스를 파싱한다. |
send() |
XML 문서를 외부로 보낸다. |
sendAndLoad() |
XML 문서를 외부로 보내고 리턴되는 XML 문서를 로드한다. |
toString() |
XML 객체를 문자열로 리턴한다. |
firstChild |
첫 번째 자식 노드에 대한 참조 |
ignoreWhite |
XML 소스에서 여백을 무시할 것인지 여부 |
loaded |
load(), sendAndLoad() 작업 완료 여부 |
nextSibling |
현재 노드와 같은 레벨에 있는 다음 형제 노드 |
previousSibling |
현재 노드와 같은 레벨에 있는 이전 형제 노드 |
nodeName |
현재 노드의 이름 |
nodeValue |
현재 노드의 값 |
onData |
데이터를 수신했을 때 발생하는 이벤트 |
onLoad |
데이터를 수신한 후 XML 문서를 파싱했을 때 발생하는 이벤트 | | |
| |
|
XML 파일 읽어오기
loadVars 객체를 사용하는 것보다 약간 번거롭지만, XML을 읽고 조작하는 것은 매우 중요하다. XML 자체가 가지는 장점 외에도 XML 웹 서비스를 이해하고 활용하기 위한 필수적인 단계이기 때문이다. 우선 XML 문서를 로드하고, 내용을 어떻게 읽어내는지 살펴보자. 노트패드와 같은 텍스트 편집기로 <화면 1>과 같은 내용을 가진 “sample.xml”이란 XML 파일을 다음과 같이 만든다.
|
<화면 1> sample.xml |
이 파일을 플래시에서 어떻게 읽어내는지 살펴보자. 새로운 플래시 프로젝트를 만든다. 첫 번째 프레임에 <리스트 1>과 같은 액션스크립트를 작성한다(이달의 디스켓 : loadxml_sample1.fla 파일에 포함된 액션스크립트이다).
|
<리스트 1> XML 문서를 읽는 간단한 예제 | |
| |
System.useCodepage = true;
xmldoc = new XML();
xmldoc.ignoreWhite = true;
xmldoc.load("sample.xml");
| |
|
지난 글에 소개한 대로 useCodepage는 코드 페이지를 사용하도록 한다. 플래시 내부적으로는 기본적으로 모두 유니코드 문자를 사용하는데, 외부 파일이나 통신할 때 유니코드(UTF-8)로 되어 있는 경우는 그렇게 많지 않다. 따라서 거의 대부분 첫 프레임에 다음과 같이 기술해 유니코드를 사용하지 않고, 현 시스템의 코드 페이지를 사용한다(이렇게 하면 한글 윈도우의 경우 기본적으로 ‘euc-kr’을 사용하게 된다).
System.useCodepage = true;
XML.ignoreWhite 프로퍼티는 원문 XML에서 공백문자를 무시하도록 한다. 만일, 이것을 지정하지 않으면 공백 문자 부분도 하나의 노드가 되어 다루기가 매우 힘들어진다.
xmldoc.ignoreWhite = true;
타임라인의 5번 키 프레임을 추가하고, <리스트 1.1>의 액션스크립트를 입력한다. <Ctrl+Enter>를 눌러 수행시키면 다음과 같은 수행 결과를 볼 수 있다.
isbn = 8981723427
title = 어떻게 문제를 풀것인가
author = G.폴리아
review = 이책은 수학적 사고 방법에 대해서 설명하고 있다.
<리스트 1.1> 로드 상태를 확인 : <리스트 1>에서 이어짐(loadxml_sample1.fla)
if(xmldoc.loaded = true )
{
var ref = xmldoc.firstChild.firstChild.firstChild;
for( var ch = ref; ch != null; ch = ch.nextSibling ) {
trace ( ch.nodeName + " = "
+ ch.firstChild.nodeValue );
}
this.stop();
} else {
gotoAndPlay(2);
}
예제에서 사용한 gotoAndPlay()는 플래시에서 흔히 사용하는 기법으로, 데이터 로딩이나 복잡한 계산처럼 시간이 많이 걸리는 일이 끝났는지를 확인한다. 즉, 다음과 같이 끝나지 않았으면 이전의 다른 프레임으로 이동해 처리중임을 표시하는 용도로 종종 사용한다.
if( 작업이 끝났는지 확인 )
{
} else {
gotoAndPlay(2);
}
이런 방법보다 더 명시적이고 효과적인 것은 XML.onLoad()를 사용하는 것이다(<리스트 2>). onLoad()는 로드가 끝났을 때 즉, load() 혹은 sendAndLoad() 메쏘드 수행이 끝났을 때 불려지는 이벤트이다. <리스트 2>는 onLoad()를 사용해 앞의 예제와 비슷한 기능을 수행하도록 한 코드이다. 수행 결과는 똑같다.
|
<리스트 2> onLoad()을 이용한 XML 처리 | |
| |
System.useCodepage = true;
xmldoc = new XML();
xmldoc.onLoad = loaded;
xmldoc.ignoreWhite = true;
xmldoc.load("sample.xm");
function loaded(success) {
if (success == true) {
var ref = xmldoc.firstChild.firstChild.firstChild;
for (var ch = ref; ch != null; ch=ch.nextSibling)
{
trace(ch.nodeName+"
= "+ch.firstChild.nodeValue);
}
trace(xmldoc.getBytesTotal());
this.stop();
} else {
trace("xml load error");
}
}
| |
|
XMLSQL의 역할
오라클 역시 XML에 대한 지원이 많지만 SQL 서버 2000은 XML에 대한 다양하고 많은 기능을 제공한다. SQL 서버가 제공하는 XML 기능 가운데 XMLSQL이 있는데, 이것의 큰 특징은 애플리케이션을 거치지 않고 웹 서버(IIS)를 통해 DBMS에 바로 쿼리할 수 있다는 점이다. ASP.NET 코드 없이도 바로 DBMS만으로 웹 애플리케이션을 작성할 수 있게 해준다.
따라서 개발자는 XML과 관계형 데이터의 차이를 극복할 수 있다. 쿼리 결과를 XML로 생성한다든가 혹은 XML 문서를 바로 관계형 데이터베이스에 저장하는 것은 관계형 데이터를 일종의 XML 파일처럼 작업할 수 있게 해준다. 간단한 예를 통해 구현해 보자. SQL 서버에 다음과 같은 스키마의 테이블을 작성하고 데이터를 입력한다.
CREATE TABLE article (
id int IDENTITY (1, 1) NOT NULL ,
c_time datetime NULL ,
u_time datetime NULL ,
title varchar (128) NULL ,
content varchar (2000) NULL
)
SQL 서버 프로그램 그룹에서 “Configure SQL XML Support in IIS”을 선택해 수행하고 다음과 같은 절차로 가상 디렉토리를 만든다(<화면 2>, <화면 3>).
1 General Virtual Directory Name을 입력(여기서는 "board"), 물리적인 경로를 설정한다("C:\Inetpub\wwwroot\board").
2 Security 탭에서 Windows 통합 인증을 선택
3 DataSource 탭 SQL Server("(local)")와 Database 이름("board")을 각각 선택
4 Virtual Names 탭 New를 선택하고, type에서 dbobject를 선택한 후 적절한 가상 이름을 입력("dbo_board")
|
<화면 2> SQL 서버 IIS 가상 디렉토리 관리자 |
|
<화면 3> SQLXML 등록정보 |
XMLSQL은 별도 설치가 가능하며, SQL 서버 2000 SP3에 추가된 내용이다. SQL 서버의 도움말에서 “SQL 서버용 IIS 가상 디렉터리 관리 유틸리티 사용”, “nwind 가상 디렉토리 만들기” 항목을 참조하라. 다음과 같은 URL을 브라우저에 입력해 보자.
http://plusjune/board?sql=SELECT * FROM article FOR XML AUTO, ELEMENTS&root=Articles
앞에 입력한 URL은 자동으로 인코딩돼 다음과 같은 형태가 된다.
http://plusjune/board?sql=SELECT%20*%20FROM%20article%20FOR%20XML%20AUTO,%20ELEMENTS&root=Articles
이러한 내용을 입력했을 때 쿼리의 결과가 표시된다. <화면 4>는 수행 결과를 보여준다.
|
<화면 4> SQL 서버의 HTPP 쿼리 |
SQL 서버의 HTTP-XML 쿼리는 티어를 분리하지 않기 때문에, 다소 유연성은 떨어지지만 간편하게 쿼리하고 그 결과를 XML로 가져오거나 XML 문서를 관계형 데이터베이스에 저장할 수 있다. 때문에 관리자 페이지나 데이터베이스의 내용을 모니터링하는 - 중요한 비즈니스 로직과 크게 관련이 없는 - 내용이라면 충분히 도입을 생각해 볼만하다. <화면 4>와 같이 XML로 결과가 나오면 XSL을 적용해 바로 게시판을 만들 수도 있다. 앞서 살펴본 플래시의 XML 로딩을 여기에 직접 사용할 수도 있다. XML.load() 메쏘드는 기본적으로 URL을 로드하기 때문에, XMLSQL을 이용하면 데이터베이스에 직접 접근하는 애플리케이션을 제작할 수 있다.
XMLSocket를 이용한 다중 사용자 채팅 프로그램 개발
플래시의 getURL(), LoadVars, XML 등의 메쏘드는 모두 URL에서 지정된 리소스를 읽어오거나 HTTP 연결을 사용하는 비 연결형(non-connection oriented) 통신이다.
XMLSocket은 플래시 5에 추가된 클래스로 SWF와 서버 사이에 TCP 연결을 맺을 수 있기 때문에. 채팅이나 게임과 같이 연결이 필요한 서비스를 위한 애플리케이션 개발에 사용할 수 있다. 연결형(connection oriented) 서비스는 서버에서 클라이언트로 알려줄 수 있기 때문에 훨씬 다양한 응용이 가능하다. 즉, 클라이언트가 주기적으로 변동 사항을 조회하는 것이 아니라 변화가 생겼을 때 서버가 클라이언트에 정보를 줄 수 있기 때문에 훨씬 다양하고 대화적인 애플리케이션이 가능하다. XMLSocket 객체는 다음과 같이 생성한다.
socket= new XMLSocket();
서버에 연결은 다음과 같이 한다. 서버를 지정하고 포트를 지정하는데, 포트 번호는 1024번 이상이어야 한다.
sock.connect( "192.168.0.55", 65000);
<표 2>는 XMLSocket 클래스에서 자주 사용되는 메쏘드와 이벤트를 정리한 것이다. 대략 살펴봐도 사용 방법은 다른 통신 객체들과 크게 다르지 않지만 close(), onClose처럼 연결에 관련된 메쏘드와 이벤트가 있다.
|
|
메쏘드 |
connect 서버에 TCP 연결한다. |
send |
서버에 문자열 전송한다. close 연결을 닫는다. |
이벤트 |
서버와 연결이 연결이 끊겼을 때 발생 |
onConnect |
서버로 연결됐을 때(혹은 연결 시도가 실패했을 때) |
onData |
서버에서 데이터가 도착했을 때 |
onXML |
서버에서 도착한 XML 문자열 파싱이 끝났을 때 | | |
| |
|
onConnect(success)는 XMLSocket.connect 메쏘드를 통해 초기화된 연결 요청이 성공하거나 실패했을 때 불려지는 이벤트이다. onData()와 onXML()는 둘 다 서버로부터 데이터가 도착했을 때 불리는데 용도가 다르다. 순서상으로는 onData()가 먼저 불린다. onData()는 데이터가 도착했을 때, onXML()는 도착한 데이터가 XML로 파싱됐을 때 불린다.
<리스트 3>은 XMLSocket을 생성해 연결하고 onXML()을 사용해 수신된 데이터를 처리하는 코드이다. OnConnect()를 사용해 연결의 성공여부를 확인하는 내용도 잘 살펴보자. <리스트 3>을 수행하면 다음과 같은 결과가 출력된다.
>>서버에 연결되었읍니다
PeerAddress = 192.168.0.55:2231
DateTime = 오후 1:53:34
Context = 안녕하세요?
|
<리스트 3> XMLSocket 객체를 이용한 연결 | |
| |
System.useCodePage = true; // 코드페이지 사용
sock = new XMLSocket();
sock.onConnect = OnConnect;
sock.onClose = OnClose;
sock.onXML = OnXml;
sock.connect( "192.168.0.55", 65000);
function OnXML( xmldoc )
{
trace(xmldoc.toString());
}
function OnConnect(success)
{
if (success == true) {
trace(">>서버에 연결되었읍니다")
sock.send("안녕하세요?");
} else {
trace(">>연결실패")
}
}
function OnClose()
{
trace(">>연결이 끊겼습니다")
}
| |
|
서버는 C#으로 작성됐으며, 서버로 전달된 내용을 XML로 만들어 연결된 모든 클라이언트에 보내주는 간단한 채팅 서버이다. 이제 C# 채팅 서버를 제작해 보자. 채팅 서버에서 여러 클라이언트의 연결을 처리하기 위해서는 소켓 연결 객체의 목록을 관리할 필요가 있다. <리스트 4>는 XmlChatServer.cs의 주요 부분이다. 전체적인 흐름을 살펴보자. 이 코드에는 대략 다음과 같은 주요한 이슈들이 구현되어 있다.
◆ 다중 사용자 연결 위해 ClientSocekt 목록 관리
◆ 비동기 I/O
◆ 소켓 입출력에 ‘euc-kr’ 인코딩
이 중 비동기 프로그래밍과 다중 사용자 채팅을 위한 목록 관리 부분은 이 글의 범위를 벗어나므로 논외로 한다.
|
<리스트 4> XmlChatServer.cs의 주요 부분 | |
| |
// XmlChatServer
// 1. 다중 사용자 연결 위해 ClientSocekt 목록 관리
// 2. 비동기 I/O
// 3. 소켓 입출력에 "euc-kr" 인코딩
// (c) 2003 eLasticWare, plusjune
using System
using System.IO;
using System.Net.Sockets;
using System.Collections;
using System.Text;
using System.Xml;
namespace XmlServer
{
class XmlServer
{
private ArrayList clientList;
public XmlServer()
{
clientList = newArrayList(); // 클라이언트 소켓 목록 관리
}
// 중략
private void OnReceive(IAsyncResult ar) // 비동기 데이터 수신
{
ClientSocket client = (ClientSocket)ar.AsyncState;
ClientSocket socketReceiveData = client;
try
{
byte[] recvBytes= client.GetReceivedData(ar);
if ( recvBytes.Length < 1) throw new Exception();
// 수신 데이터를 인코딩한다.
Encoding e = Encoding.GetEncoding("euc-kr");
stringrecvXml = e.GetString( recvBytes );
Console.WriteLine( "recv ({0}) : '" + recvXml + "'",
client.PeerAddress );
// TOD: 수신된 XML을 여기서 처리한다.
// 보낼 문자열을 만든다.
string strSend = @"<?xml version=""1.0"" encoding=""euc-kr"" ?>"
+ "\n" + "<Message>\n" + "<PeerAddress>" +
client.PeerAddress + "</PeerAddress>\n" + " <DateTime>" +
DateTime.Now.ToLongTimeString() + "</DateTime>\n" +
"<Context>" + recvXml + "</Context>\n" + "</Message>\n";
// 연결된 모든 클라이언트에 메시지를 보낸다.
for ( int index = 0; index < clientList.Count; index++ )
{
client = ((ClientSocket)clientList[index]);
client.SendMessage(strSend );
Console.WriteLine("send ({0}) : '" + strSend + "'",
client.PeerAddress );
}
// 비동기 수신 다시 시작
socketReceiveData.Start( new AsyncCallback(OnReceive) );
}
catch
{
clientList.Remove(client);
client.Release();
}
}
static void Main(string[] args)
{
XmlServer tcpServer = new XmlServer();
tcpServer.Run();
}
}
class ClientSocket
{
private Socket socket;
private byte[] receiveBuffer;
// 중략
public void Start(AsyncCallback receiveCallback)
{
socket.BeginReceive(receiveBuffer, 0, receiveBuffer.Length,
SocketFlags.None, receiveCallback, this );
}
public byte[] GetReceivedData(IAsyncResult ar)
{
int nReceived = socket.EndReceive(ar);
byte[] buff = new byte[nReceived];
Array.Copy(receiveBuffer, buff, nReceived);
return buff;
}
public void SendMessage(string str)
{
Encoding e = Encoding.GetEncoding("euc-kr");
byte[] byteBuffer = e.GetBytes(str);
socket.Send(byteBuffer);
}
// 중략
}
}
| |
|
인코딩 문제를 잠깐 살펴보자. XML 문서를 처리할 때 몇 가지 이슈가 있는데, 가장 먼저 극복해야 할 것이 바로 인코딩 문제이다. 이전에도 이야기했듯 애플리케이션 외부의 데이터는 대부분의 KSC5601(“euc-kr”) 인코딩이다. 물론 유니코드로 통신할 수도 있겠으나 환경을 생각해 보면 반드시 인코딩을 고려해야 한다. 닷넷에서 인코딩은 System.Text.Encoding 네임스페이스를 사용한다. 우리에게는 유니코드나 아스키 인코딩보다 한글에 대한 지원이 우선이므로 주로 다음을 사용하게 된다.
System.Text.Encoding.GetEncoding(int codepage)
여기서 코드페이지란 코드 체계에 부여되는 번호이다. 한국어(KSC5601) 코드 페이지는 949번이며, 문자열로는 “euc-kr”로 표시한다. 소켓을 통해 송수신되는 데이터는 바이트 배열인데, 이것을 유니코드 String으로 바꾸려면 인코딩을 해야 한다. <리스트 5>는 KSC5601과 유니코드 사이에 인코딩하는 간단한 예를 보여준다.
|
<리스트 5> KSC5601과 유니코드 인코딩 예제 | |
| |
// Unicode 인코딩 : 유니코드 ==> KSC5601
string str = "abc가나다";
Encoding e = Encoding.GetEncoding(949);
byte[] byteBuffer = e.GetBytes(str);
for(int i = 0; i < byteBuffer.Length; i++)
Console.Write("0x{0:X}
", byteBuffer[i]);
Console.WriteLine();
// 0x61 0x62 0x63 0xB0 0xA1 0xB3 0xAA 0xB4 0xD9
| |
|
onData 활용
XMLSocket 객체의 onData는 데이터가 도착했을 때 불린다. 특이할 만한 점은 onData를 사용하면 XML이 아니어도 처리가 가능하다는 것이다. 따라서 XMLSocket으로 반드시 XML만을 다뤄야 하는 것은 아니다. 즉, XMLSocket은 XML뿐만 아니라 일반적인 TCP 통신을 위한 소켓으로도 사용이 가능하다. 내부적으로 원래 onData는 다음과 같이 동작한다.
XMLSocket.onData = function (src) {
this.onXML(new XML(src));
}
프로그래머가 onData을 오버라이딩하면, 즉 코드에서 onData를 재정의하면 onXML이 불리지 않는다(만일, onData와 onXML을 둘 다 사용하고 싶다면 onData의 핸들러 함수 끝에서 onXML 핸들러를 불러주면 된다). <리스트 6>은 완전한 예제이다. onData를 사용해 XML을 파싱하도록 했다. 이렇게 하는 이유는 ignoreWhite를 사용하기 위해서이다. 이렇게 하면 다양한 처리가 가능하다.
|
<리스트 6> onData(), onXML()을 이용한 파싱 | |
| |
System.useCodePage = true; // 코드페이지 사용
sock = new XMLSocket();
sock.onConnect = OnConnect;
sock.onClose = OnClose;
sock.onData = onData;
sock.onXML = onXml;
sock.connect( "192.168.0.55", 65000);
function OnData ( src )
{
var xmldoc = new XML();
xmldoc.ignoreWhite = true;
xmldoc.parseXML( src );
OnXml( xmldoc )
}
function OnXml( xmldoc )
{
xmldoc.ignoreWhite = true;
var ref = xmldoc.firstChild.firstChild,firstChild;
for (var ch = ref; ch != null; ch=ch.nextSibling) {
trace(ch.nodeName+"
= "+ch.firstChild.nodeValue);
}
}
function OnConnect(success)
{
if (success == true) {
trace(">>서버에 연결되었읍니다")
sock.send("안녕하세요?");
} else {
trace(">>연결실패")
}
}
function OnClose()
{
trace(">>연결이 끊겼읍니다")
}
| |
|
<화면 5>는 <리스트 6>에서 만들어진 SWF를 여러 개 수행하고 XmlChatServer를 수행한 결과이다. 여러 개의 SWF 연결이 관리되고, 메시지가 각 SWF에 전달되는 것을 확인할 수 있다. UI는 없지만 여러 클라이언트가 연결된 플래시 채팅이 가능하다는 것을 보여주고 있다.
|
<화면 5> XmlChatServer 수행 결과 |
XML 웹 서비스 주가 조회 프로그램
다음 URL을 브라우저 창에 입력해 보자.
야후에서 제공하는 주가 조회 서비스이다. 각각 썬과 MS의 주가 현황(20분 단위 갱신)을 보여준다. 약간 암호 같아 보이지만, 다음과 같은 결과가 브라우저 창에 표시된다.
"MSFT",26.1801,"6/20/2003","3:45pm",+0.1101,26.344,26.38,26.01,71121984,
281.1B,26.07,"+0.42%","20.705 - 29.48",0.88,29.62,"MICROSOFT CP"
<리스트 7>은 이 내용을 웹 서비스로 구현한 것이다. <리스트 7>의 내용은 quote.yahoo.com에서 리턴된 문자열을 ‘,’를 분리하여 객체로 만들고, 이 객체를 리턴하는 웹 서비스로 되어 있다(Stock 클래스의 속성이 [Serializable]로 되어 있다는 점을 주목하자). 객체를 리턴하면 XML 형태로 만들어진다. 즉, 객체를 XML로 시리얼라이즈한다).
|
<화면 6> 주가 조회 웹 서비스 수행 화면 |
|
// 야후 주가 조회 XML 웹 서비스
// 1. HttpWebRequest
// 2. 객체 시리얼라이제이션
// 3. Split()을 이용한 문자열 tokenize
// (c) 2003 eLasticWare, plusjune
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Net;
using System.IO;
using System.Text;
namespace StockQuote
{
[Serializable]
public class Stock
{
public string Symbol;
public string Last;
public string Date;
public string Time;
public string Change;
public string Open;
public string High;
public string Low;
public string Volume;
public string MktCap;
public string PrevClose;
public string PctChange;
public string AnnRange;
public string Earns;
public string PE;
public string Name;
}
[WebService(Namespace="http://elasticware.com/stockwebservices/")]
public class StockService : System.Web.Services.WebService
{
public StockService()
{
InitializeComponent();
}
#region Component Designer generated code
// 중략
#endregion
[WebMethod]
public Stock GetQuote(string symbol)
{
string buffer;
string[] tokens = null
stringurl=@"http://quote.yahoo.com/d/quotes.csv?s="+symbol+"&f=sl1d1t1c1ohgvj1pp2wern"
WebRequest req;
WebResponse res;
Stock stock = new Stock();
try
{
req = (HttpWebRequest)WebRequest.Create(url);
res = (HttpWebResponse)req.GetResponse();
StreamReader strm =
newStreamReader( res.GetResponseStream(), System.Text.Encoding.ASCII);
buffer = strm.ReadToEnd();
strm.Close();
buffer = buffer.Replace( "\"", "" );
tokens = buffer.Split( new char[] {','} );
stock.Symbol = tokens[0];
stock.Last = tokens[1];
stock.Date = tokens[2];
stock.Time = tokens[3];
stock.Change = tokens[4];
stock.Open = tokens[5];
stock.High = tokens[6];
stock.Low = tokens[7];
stock.Volume = tokens[8];
stock.MktCap = tokens[9];
stock.PrevClose = tokens[10];
stock.PctChange = tokens[11];
stock.AnnRange = tokens[12];
stock.Earns = tokens[13];
stock.PE = tokens[14];
stock.Name = tokens[15];
}
catch(Exception)
{
stock = null
}
return stock ;
}
}
}
| |
|
<화면 6>은 주가 조회 웹 서비스를 브라우저에서 수행한 결과이다. ASP.NET은 SOAP을 이용하지 않고도 HTTP GET이나 POST로 웹 서비스를 사용할 수 있게 해준다. 따라서 이 웹 서비스를 이용하고 싶다면, 단지 다음과 같이 HTTP 요청을 보내면 된다.
GET /StockQuote/StockQuote.asmx/GetQuote?symbol=string HTTP/1.1
Host: localhost
예를 들어, MS의 주식을 조회하고 싶다면(MSFT는 MS의 주식 시장 코드이다),
http://localhost/StockQuote/StockQuote.asmx/GetQuote?symbol=MSFT
과 같이 HTTP 요청을 하면 된다. 플래시의 XML 객체를 이용하면, ASP.NET 웹 서비스를 간단한 HTTP로 요청해 수신할 수 있다. 앞 URL에서 XML 결과를 리턴받아 처리하는 액션스크립트를 작성해 보자.
|
System.useCodepage = true;
xmldoc = new XML();
xmldoc.onLoad = loaded;
xmldoc.ignoreWhite = true;
xmldoc.load( "http://localhost/StockQuote/StockQuote.asmx/GetQuote?symbol=MSFT"
)
function loaded(success) {
if (success == true) {
var ref = xmldoc.firstChild.firstChild;
for (var ch = ref; ch != null; ch=ch.nextSibling)
{
trace(ch.nodeName+"
= "+ ch.firstChild.nodeValue);
}
trace(xmldoc.getBytesTotal());
this.stop();
} else {
trace("xml load error");
}
}
| |
|
<리스트 8>은 <리스트 7>, <화면 6>의 주가 조회 웹 서비스를 조회하는 액션스크립트이다. 간단하게 나열하고 있다(이 조회 내용을 ‘어떻게 예쁘게 보여줄 것인가’하는 것은 이 글에서 다루지 않는다). 다음은 <리스트 8>의 액션 스크립트를 수행한 결과이다.
Symbol = MSFT
Last = 26.62
Date = 8/19/2003
Time = 4:01pm
Change = +0.92
Open = 25.85
High = 26.65
Low = 25.77
Volume = 73001488
MktCap = 286.7B
PrevClose = 25.70
PctChange = +3.58%
AnnRange = 21.5585 - 29.48
Earns = 0.92
PE = 27.93
Name = MICROSOFT CP
624 byte(s) read
플래시와 SOAP
HTTP GET을 이용해도 ASP.NET의 웹 서비스를 사용할 수 있지만, 웹 서비스를 제대로 쓰려면 UDDI와 SOAP을 사용해야 한다. 다음은 주가 조회 웹 서비스를 이용하는 SOAP 요청이다.
POST /StockQuote/StockQuote.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://elasticware.com/stockwebservices/GetQuote"
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetQuote xmlns="http://elasticware.com/stockwebservices/">
<symbol>string</symbol>
</GetQuote>
</soap:Body>
</soap:Envelope>
앞의 SOAP 요청은 XML 객체로 만들어 XML.sendAndLoad()를 사용해 호출할 수 있다. sendAndLoad()는 기본적으로 POST로 요청을 보낸다. 이런 SOAP 요청을 만들기 위해 HTTP 헤더에 “SOAPAction”을 넣어야 하는데, 플래시 5.0 이하의 버전에는 HTTP 헤더를 프로그래머가 추가할 방법이 없다(5.0 이하 버전에는 HTTP 헤더를 추가하거나 조작하는 기능이 없다). 플래시 5를 써야 한다면, GET으로 요청을 받아 SOAP 요청을 만들고 처리된 결과를 리턴하는 페이지를 만들어야 한다. 예를 들어, soapcall.aspx 페이지를 만들고 다음과 같이 필요한 정보를 GET 요청으로 페이지에 넘긴다.
http://server/soapcall.aspx?url=http://ws_server/StockQuote.asmx&SOAPAction=SoapAction
soapcall.aspx 페이지에서 SOAP 요청을 만들어 URL 인자와 SOAPAction 인자에 지정된 내용으로 SOAP 메시지를 만들어 요청한 다음 결과 XML을 클라이언트에 리턴하는 방법이다. 플래시 플레이어 6.0r65 이상의 버전에서는 HTTP 헤더를 정의하기 위해 XML 및 LoadVars 객체가 사용하는 HTTP 헤더에 사용자 정의 값을 지정할 수 있는 다음 두 가지 새로운 메쏘드가 있다.
◆ addRequestHeader(<header name>, <header value>)
◆ addRequestHeader(<headers>)
첫 번째 addRequestHeader는 여러 번 호출이 가능하다. 같은 헤더 이름을 지정하면, 나중에 설정한 값이 지정된다. 두 번째 addRequestHeader는 문자 배열을 사용해 한꺼번에 지정할 수 있는 메쏘드이다. 예를 들어, 다음과 같이 HTTP 헤더에 SOAPAction을 추가할 수 있다.
xmldoc.addRequestHeader("SOAPAction", "\"GetQuote\"");
addRequestHeader()은 6.0r65 버전 이상에만 적용되므로, 버전 호환성을 생각한다면 별로 좋은 방법이 아니다. 이보다는 간단하게 이용하는 경우에는 loadVars, XML 클래스를 이용하고, 본격적으로 SOAP을 사용하고자 한다면 플래시 리모팅을 이용하는 것이 좋다. 플래시 리모팅은 훨씬 간편한 방법으로 SOAP을 이용할 수 있게 할 뿐만 아니라 DB에 대한 접근 등 다양한 기능을 제공한다. 플래시 리모팅에 대해서는 다음 호에서 자세하게 살펴볼 것이다.
플래시 프로그래머
요즘은 ‘플래시 디자이너’란 말은 익숙하지만 ‘플래시 프로그래머’라는 용어는 그리 널리 알려져 있지 않다. 플래시 클라이언트가 가진 다양한 기능을 최대한 활용하려면, ‘디자이너’로서가 아닌 ‘프로그래머’로의 접근도 중요한 요소가 될 것이다. 특히, 닷넷처럼 웹 서비스를 손쉽게 구축할 수 있는 환경과 맞물린다면 ‘플래시 프로그래머’는 더 큰 힘을 얻을 수 있다.
|
<화면 8> 닷넷 PetShop 플래시 프론트엔드 수행 화면 |
매크로미디어 홈 페이지에 보면 PetMarket이란 블루프린트 프로그램이 있다. J2EE의 청사진 프로그램이었던 ‘자바 Pet Store’를 MS가 ‘'닷넷 Pet Shop’이란 이름으로 닷넷 버전을 개발해 이를 놓고 한 동안 자바와 닷넷 진영이 신경전을 벌였었다. 매크로미디어의 Pet Market은 ‘자바 Pet Store’와 ‘닷넷 Pet Shop’과 비슷한 맥락으로 만들어진 프로그램이다.
‘플래시 클라이언트’ + ‘닷넷 Pet Shop’ 버전(<화면 8>)은 닷넷 개발자든 플래시 개발자든 상당히 연구해 볼만한 가치가 있다. 플래시 클라이언트가 ASP.NET과 어떻게 잘 연동될 수 있는지에 대한 좋은 예제이다. 다음 글에서 플래시와 관련한 보안 문제를 다뤄보자. @