웹을 통한 비 동기 호출의 세 번째 시간이 되겠습니다. 이번 강좌에서는 Microsoft에서 IE 전용으로 제공 및 지원했던(과거형입니다) WebService HTC 파일을 이용하는 방법을 설명하려 합니다. 이 기법은 서버 측 모듈로는 웹 서비스를 이용하고, 클라이언트 측 모듈로는 htc 기술을 이용하는 비 동기 호출 방식인데요. 정식 명칭은 WebService Behavior라고 발표되었던 것입니다. Microsoft에서 말입니다.

저번 강좌도 그랬지만, 이번 강좌도 초급자들이 접근하기에는 그리 쉽지 않은 내용일 수 있습니다. 그래서, 약간 죄송하기도 합니다만 ㅜㅜ. 그렇다 하더라도, 이 내용 또한 중급자로 가기 위해서는 알아두면 매우 좋은 내용이기에 과감하게 진행해 보려 합니다. ^^

WebService behavior는 원격 메서드를 호출하기 위한 기능들을 캡슐화하고 있는 경량의 컴포넌트입니다. 웹 서비스라는 이름이 말해 주듯이, 내부적인 프로토콜로는 역시나 SOAP(SOAP을 몰라도 사용하는 데에는 전혀 문제가 없습니다)을 이용하구요. MS의 기술이다보니 이 기법을 이용하려면 클라이언트 브라우저는 IE 5 이상을 사용하셔야 합니다. 서버는 WSDL 1.1을 지원하기만 한다면 어떠한 플랫폼이어도 무관하지만 말입니다. 물론, 일반적인 경우는 .NET 웹 서비스를 이용하겠지요?

사실, 기존 강좌에서는 비 동기 호출에 대한 응답(Response) 결과로 단순 문자열의 나열을 이용했는데요. 출력 데이터로 단순한 문자열의 나열을 이용하는 방식은 고전적인 방식(뭐, 고전적인 방식이 나쁜 것은 아니지만)인지라 크게 권장하지는 않는 편입니다. 다들 아시겠지만, 현재는 그러한 데이터 통신을 위해서 업계 표준인 XML을 권장하는 편이죠. XML을 사용하면 다양한 클라이언트들이 서버의 데이터를 이용함에 있어 그 가용성이 상당히 높다는 장점이 있으니까요. 게다가, 중요한 것은 XML을 이용하는 것이 미래를 내다 봤을 때, 피해갈 수 없는 선택이기도 합니다. 하지만, XML을 이용할 경우의 단점은 클라이언트가 그 XML 데이터를 파싱(해석 및 추출)해서 사용해야 하기에, 파싱을 위한 로직을 별도로 작성해주어야 한다는 것입니다. 클라이언트 측에 별도의 파서가 존재하지 않는다면 말이죠.

기쁘게도, 이번 강좌에서 사용하려는 WebService Behavior 컴포넌트는 자체적으로 작은 규모의 파서 또한 제공하고 있기에, 별도로 파싱 작업을 할 필요가 없습니다. 다행 중 불행이라면, 사용할 수 있는 데이터 형식에 제한이 있다는 것이긴 하지만 말이죠.

WebService Behavior 컴포넌트가 지원하는 데이터 형식은 다음과 같습니다.

ASP.NET 데이터 형식 XML 데이터 형식
String String
Boolean Boolean
float(single) Float
double Double
decimal decimal
long long
int Int
short Short
byte unsignedByte
ulong unsignedLong
uint unsignedInt
ushort unsignedShort
sbyte Byte
DateTime Date
DateTime Time
String[] ArrayOfString
boolean[] ArrayOfBoolean
float[] ArrayOfFloat
double[] ArrayOfDouble
decimal[] ArrayOfDecimal
long[] ArrayOfLong
int[] ArrayOfInt
short[] ArrayOfShort

WebService behavior가 지원하는 데이터 형식이 자세하게 나와있는 링크는 다음과 같으니 참고해 보시는 것도 좋을 듯 합니다.

http://msdn.microsoft.com/library/default.asp?url=/workshop/author/webservice/webservice.asp

또한, 기존 강좌는 서버 모듈을 asp나 aspx 페이지로 제작했었는데요. 그것이 그리 나쁜 선택은 아니겠습니다만, 가급적이면 이 부분도 웹 페이지가 아닌 웹 서비스 모듈(asmx)로 작성해 주는 것이 좀 더 미래지향적인 방식이라 할 수 있겠습니다. 그렇게 하는 것이 보다 확장성 있는 방안이지 않겠습니까? asp나 aspx 를 이용하면 처리 로직이 필요할 때마다 별도의 페이지를 작성해야 하지만, asmx는 그러한 로직을 웹 메서드로 만들 수 있으니까요. 게다가, 그렇게 만들면 자동으로 XML serialization도 지원되니깐 서버 측 데이터 반환 시, XML 구성을 고민할 필요가 없다는 이점도 있죠.

그래서, 권장되는 방식은 서버 측의 모듈은 웹 서비스로 작성하고, 데이터 통신은 XML로 하는 것입니다. 이것이 바탕이 되어 AJAX(Asynchronous JavaScript and XML)로까지 발전이 된 것이기도 하죠(요건 담 번에 다룰 주제이기도 합니다)

서두가 무지하게 길었는데요. 사실 예제는 상당히 단순합니다. 왜냐하면, 이전 강좌에서 다루었던 대부분의 작업을 컴포넌트화 시켜둔 것이 WebService Behavior이다 보니, 이를 이용할 경우 코딩량이 상당히 줄어들 뿐 더러 사용하기도 매우 편하기 때문이죠.

근데, 중요한 것은 비 동기 호출 첫 번째 강좌에서도 말씀드렸다시피, WebService Behavior는 현재는 MS에서 더 이상 지원을 하지 않는 컴포넌트라는 점입니다. 이것이 문제라면 문제죠. WebService Behavior 사이트에 가 보면 다음과 같은 빨간색 경고문이 떠~억 하니 버티고 있는 것을 볼 수 있어요.

Microsoft® Internet Explorer WebService Behavior may be downloaded; however, it is not currently supported.

그렇습니다. 다운로드해서 사용할 수는 있지만, 지원은 현재 안 한다는 거죠. 사용하다가 문제 생기면 너희들이 알아서 고쳐서 사용하세요~ 뭐 그런 의미입니다.

링크는...

http://msdn.microsoft.com/archive/?url=/archive/en-us/samples/internet/behaviors/library/webservice/default.asp

자. 일단 다운로드를 받아봅시다. 그러면, 꼴랑 webservice.htc 파일 하나를 얻을 수 있습니다. Htc가 무엇이고, 어떻게 제작 및 사용할 수 있는지는 송군님의 강좌를 통해서 배울 수 있습니다. 태오 사이트의 강좌 중 매우 훌륭한 강좌로 평가 받고 있는 강좌이기도 하죠. Htc에 대해서 알고 싶다면 다음 링크를 클릭하세요 ^^ (알고 싶어요~ 클릭)

webservice.htc 파일을 열어서 소스를 보시면, 일단 좀.. 부담스럽습니다. 어려운 코드들이 마구 나열되어 있으니까요(게다가 들여쓰기도 안 되어 있어서 보기에 약간 짜증나기도 한다는..). 시간이 나시는 분은 소스를 분석해 보는 것도 나쁘지 않겠습니다. 만일, 소스를 살펴본다면 개념적으로는 이전 강좌인 XMLHTTP 컴포넌트를 이용했던 방식과 크게 다르지 않다는 것을 알 수 있을 것입니다. 즉, XMLHTTP를 이용하는 것을 조금 더 쉽게 사용할 수 있도록 컴포넌트와 시켜놓은 것이 바로 webservice.htc라는 것을 알 수 있죠.

이미 XMLHTTP의 사용법을 알고 있는 여러분들은 그 내부의 처리방식도 이해하고 계실 것이기에 webservice.htc가 뭔가 새롭고, 혁신적인 것은 아니라는 것도 눈치채고 계실 것입니다. 단지, XMLHTTP를 사용해서 비 동기 호출하기 위한 로직들을 쉬운 인터페이스로 제공해주는 일종의 랩퍼(wrapper)라는 것을 이내 눈치채실 수 있을 것이라는 이야기죠. 또한, 수신된 XML 데이터를 사용하기 쉽게 재 구성해 주는 기능도 내장하고 있고 말이죠.

그렇다면, 이제 한번 해 보죠. webservice.htc를 이용해서 쉽게 비동기 호출을 하는 방법에 대해서 말입니다. ^^

webservice.htc는 그 이름이 말해주듯이 서버 측 모듈로 웹 서비스를 이용해야 합니다. 그렇기에, 무엇보다 먼저 서버 쪽의 웹 서비스가 준비되어야 하겠죠?

여러분이 가지고 있는 웹 프로젝트를 아무 것이나 열어도 좋습니다. 웹 프로젝트가 준비되었다면 그 루트에 다음과 같이 Employee.asmx라는 웹 서비스 파일을 하나 만들어 보도록 해요. 예제용 웹 서비스이기에 매우 간단한 수준으로 만들어 볼까 합니다. 즉, 이전 강좌에서 했던 것처럼 사번을 입력하면 사원의 정보를 가져오는 그 로직을 웹 메서드로 재 구성해보려 합니다. ^^. Employee.asmx 파일을 만들었다면 그 안에 다음과 같이 코드를 만들어 보도록 하겠습니다.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Web;
using System.Web.Services;

namespace CallbackEx
{
    /// <summary>
    /// Employee에 대한 요약 설명입니다.
    /// </summary>
    public class Employee : System.Web.Services.WebService
    {
        [WebMethod]
        public string[] GetEmpByEmpID(int empID)
        {
            string[] info = {"", "", "", "", ""};
            string strCon = "server=(local);database=Northwind;uid=sa;pwd=***";
            string sql = " SELECT LastName, FirstName, Title, BirthDate, City FROM Employees "
                        + " WHERE EmployeeID = @empID";

            SqlConnection con = new SqlConnection(strCon);
            SqlCommand cmd = new SqlCommand(sql, con);
            cmd.Parameters.Add("@empID", SqlDbType.Int);
            cmd.Parameters["@empID"].Value = empID;

            con.Open();
            SqlDataReader reader = cmd.ExecuteReader();

            if(reader.Read())
            {
                info[0] = reader[0].ToString();
                info[1] = reader[1].ToString();
                info[2] = reader[2].ToString();
                info[3] = reader[3].ToString();
                info[4] = reader[4].ToString();
            }

            reader.Close();
            con.Close();

            return info;
        }
    }
}

코드는 그리 어렵지 않을 것입니다. 이전 강좌에서 다룬 코드와 유사하기 때문이죠. GetEmpByEmpID라는 웹 메서드는 empID라는 사번을 받아서 쿼리를 수행하고, 해당 직원의 몇몇 정보를 string 배열로 반환하는 역할을 합니다.

웹 메서드는 문자열 배열을 반환하지만, 이 데이터는 ASP.NET에 의해 자동으로 XML serialization이 되어서 다음과 같은 형식의 XML 구조로 데이터를 반환하게 될 것입니다.

<?xml version="1.0" encoding="utf-8" ?>
<ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/">
    <string>Fuller</string>
    <string>Andrew</string>
    <string>Vice President, Sales</string>
    <string>1952-02-19 오전 12:00:00</string>
    <string>Tacoma</string>
</ArrayOfString>

만일, 서버 모듈을 asp나 aspx로 만들었다면 이러한 XML 구조를 직접 수동으로 구성해주어야 하겠지만, 웹 서비스를 이용하면 알아서 이러한 포맷이 만들어지니 참으로 편하죠 ^^. 그게 ASP.NET 웹 서비스의 장점이 아니겠습니까?

자. 준비가 다 되었다면, 제작한 웹 서비스 파일인 employee.asmx를 실행해 보도록 하죠. 그러면, 하나의 메서드(GetEmpByEmpID)만을 갖는 테스트 페이지가 나올 것이고, 그 메서드를 클릭하면, 다음과 같은 메서드 테스트 페이지가 나타납니다.

여기에 사번으로 1을 입력하고 실행하면 다음과 같은 결과가 나타날 것입니다.

예상대로 제대로 된 결과가 멋들어지게 나타나고 있는 것을 보실 수 있죠? 자. 여기까지의 결과가 무난하게 나왔다면 서버 모듈은 완성되었다고 가정할 수 있을 것입니다. 그렇다면, 이제 이 웹 서비스를 이용하는 클라이언트용 페이지를 만들어야 하겠죠? (사실 이 부분이 이번 강좌의 핵심이라 할 수 있겠죠~)

그렇다면, 이제 한번 만들어 보겠습니다. webservice.htc를 이용해서 비동기 호출을 하는 페이지를 말입니다. 이 페이지는 어차피 구동하는 쪽이 클라이언트이기에 aspx와 같은 서버 페이지로 제작할 필요는 없습니다. 그냥 html 페이지로 제작해도 동작하는 데에는 아무런 무리가 없다는 것이죠. 해서, 만들어 보았습니다.

<html>
    <head>
    <Script language="JavaScript">
        function RaiseCallback()
        {
            var wsdl = "http://localhost/CallbackTest/MyWebServices/Employee.asmx?WSDL";
            var params = document.all.EmpID.value;
            MyWs.useService(wsdl,"EmpService");
            MyWs.EmpService.callService(ProcessResult, "GetEmpByEmpID", params);
        }

        function ProcessResult(result)
        {
            var vals = result.value;
            document.all.LastName.value = vals[0];
            document.all.FirstName.value = vals[1];
            document.all.Title.value = vals[2];
            document.all.BirthDate.value = vals[3];
            document.all.City.value = vals[4];
        }
    </Script>
    <LINK href="../Styles.css" type="text/css" rel="stylesheet">
    </head>

    <body>
        <div id="MyWs" style="BEHAVIOR:url('WebService.htc')" />
    <p>
        <table cellpadding="3">
            <tr>
                <td align="center">사번</td>
                <td><input id="EmpID" NAME="EmpID" onchange="RaiseCallback();"></td>
            </tr>
            <tr height="1" bgcolor="silver">
                <td colspan="2"></td>
            </tr>
            <tr>
                <td align="center" width="80">이름</td>
                <td width="200"><input id="LastName" name="LastName"></td>
            </tr>
            <tr>
                <td align="center">성</td>
                <td><input id="FirstName" name="FirstName"></td>
            </tr>
            <tr>
                <td align="center">직급</td>
                <td><input id="Title" name="Title"></td>
            </tr>
            <tr>
                <td align="center">생일</td>
                <td><input id="BirthDate" name="BirthDate"></td>
            </tr>
            <tr>
                <td align="center">도시</td>
                <td><input id="City" name="City"></td>
            </tr>
        </table>
    </p>
    <p>WebService Behavior <a href="http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/samples/internet/behaviors/library/webservice/default.asp"
            target="_blank">WebService Behavior Link</a>
        </p>
    </body>
</html>

클라이언트 소스에서 중요한 부분은 WebService.htc를 페이지에 삽입하는 부분인 다음 코드입니다.

<div id="MyWs" style="BEHAVIOR:url('WebService.htc')" />

이 코드를 삽입함으로써 이제 WebService.htc 컴포넌트를 MyWs라는 이름으로 사용할 수가 있게 됩니다. 코드대로라면, WebService.htc 파일의 위치는 Client.htm 파일이 놓여져 있는 동일 폴더 안에 있어야 합니다. 만일, 그 경로가 다르다면, 예를 들어 sc라는 하위 폴더에 htc 파일이 위치한다면 BEHAVIOR:url('WebService.htc') 대신, BEHAVIOR:url('sc/WebService.htc')와 같이 지정되어야 할 것입니다.

예제 자체는 이전 강좌(XMLHTTP)의 것과 동일합니다. 즉, 사번을 텍스트박스에 입력하고 포커스를 옮기면 그 때 비동기 호출이 이루어져서 사용자 정보를 서버로부터 가져오게 되는 것이죠.

사번 용 텍스트박스의 HTML 태그를 보시면 다음과 같이 onchange 이벤트 시에 RaiseCallback 메서드를 호출하는 코드를 보실 수 있습니다. 함수명이야 여러분이 원하는 대로 줄 수 있겠지만, 저는 그럴 듯 해 보이는 이름인 RaiseCallback을 사용해 보았습니다. 그리고, 그 함수는 다음과 같은 코드로 구성되어 있어요.

function RaiseCallback()
{
    var wsdl = "http://localhost/CallbackTest/MyWebServices/Employee.asmx?WSDL";
    var params = document.all.EmpID.value;
    MyWs.useService(wsdl,"EmpService");
    MyWs.EmpService.callService(ProcessResult, "GetEmpByEmpID", params);
}

WebService.htc를 통해서 비동기적으로 웹 서비스를 호출하기 위해서는 총 3가지 정도의 정보가 필요합니다. 그들은 대략 다음과 같아요

1. 비동기 호출의 대상이 될 웹 서비스의 wsdl 경로
2. 호출할 웹 메서드의 명칭
3. 호출할 웹 메서드가 필요로 하는 매개변수 인자값

해서, 코드에서는 wsdl이라는 변수에 비 동기 통신의 대상이 될 웹 서비스의 wsdl 경로를 지정하고 있구요. params라는 변수에는 인자로 넘길 매개변수 값을 지정해 두고 있습니다. 이는 당연히 사번이 되겠죠?

WebService.htc 컴포넌트의 핵심 메서드는 크게 2가지 입니다. 웹 서비스와의 연결정보를 구성하는 useService라는 메서드와 실질적인 웹 메서드 호출을 수행하는 callService가 바로 그것입니다. 이 메서드들의 구체적인 사용방법과 그 외의 다른 메서드들의 정보는 다음 레퍼런스를 참고하시기 바랍니다.

WebService behavior 참조 레퍼런스

http://msdn.microsoft.com/library/?url=/workshop/author/webservice/webservice.asp

가장 중요한 메서드인 useService는 2개의 인자를 받는데요. 첫 번째 인자는 비 동기 통신의 대상이 될 웹 서비스의 wsdl 경로입니다. 그리고, 두 번째 인자는 그 웹 서비스를 어떠한 이름으로 사용할 것인지 자신이 사용하기 편한 별명을 주는 것입니다. 그리고, 이 별명은 실제 웹 서비스를 호출할 때 사용하게 되지요. 이해를 돕기 위해서 소스를 보겠습니다.

MyWs.useService(wsdl,"EmpService");
MyWs.EmpService.callService(ProcessResult, "GetEmpByEmpID", params);

소스의 첫 라인에서는 useService 메서드를 이용해서, 첫 번째 인자로는 wsdl 경로를 주고 있구요. 두 번째 인자로는 “EmpService”라는 문자열을 주고 있습니다. 이는 해당 웹 서비스를 EmpService라는 이름으로 차후 이용하겠다는 것을 의미합니다.

해서, 두 번째 라인의 소스를 보시면 MyWs.EmpService.. 와 같이 그 이름이 사용되고 있는 것을 보실 수 있습니다. ^^ 만일, useService 메서드의 두 번째 인자로 taeyoService라는 값을 주셨다면, 두 번째 라인의 소스도 MyWs.TaeyoService.. 와 같이 바뀌어야 하겠죠?

그리고, 두 번째 라인에서는 callService라는 메서드를 호출하고 있습니다. 이것이 실질적으로 특정 웹 메서드를 호출하는 것인데요. 첫 번째 인자로는 콜백을 받을 함수명(잠시 뒤에 설명합니다)을 지정해 주시면 되고, 두 번째 인자로는 사용할 웹 메서드의 이름, 세 번째 인자로는 웹 메서드 수행에 필요한 매개변수 값을 지정해 주면 됩니다.

첫 번째 인자로 지정하는 콜백 함수명은 무엇이냐? 바로 비 동기 호출의 결과를 받을 함수명을 말합니다. 즉, 비 동기 호출이 끝난 뒤, 그 호출의 결과값을 받아서 처리할 함수를 말한다는 것이죠. 소스에서는 그 값으로 ProcessResult를 주었구요. 전체 소스를 다시 보시면, 바로 밑에 그 함수가 정의되어 있는 것을 보실 수 있을 것입니다. 다음과 같이 말이죠.

function ProcessResult(result)
{
    var vals = result.value;
    document.all.LastName.value = vals[0];
    document.all.FirstName.value = vals[1];
    document.all.Title.value = vals[2];
    document.all.BirthDate.value = vals[3];
    document.all.City.value = vals[4];
}

ProcessResult 함수는 result라는 인자를 하나 갖는데요. 바로 저 인자로 호출의 결과값이 넘어옵니다. 누가 그 값을 넘겨주냐고요? 그 역할 모두를 WebService.htc 가 해주는 것입니다. ^^

실제 호출 결과 값은 result라는 개체 변수의 value라는 속성을 통해서 얻어낼 수 있습니다. 해서 만일, 서버에서 결과값으로 문자열 배열을 넘겨준다면 result.value는 문자열 배열일 것이고요. 서버에서 호출 결과값으로 Boolean 값을 넘겨준다면 result.value는 불린 값일 것입니다. ^^

우리의 경우는 그 값이 문자열 배열이니, 소스에서는 그 값을 배열형식으로 읽어서 각각에 해당하는 컨트롤에 넣어주는 것을 볼 수 있습니다. 매우 쉽죠?

그럼, 한번 실행해 볼까요?

+ Recent posts