이번 강좌는 웹을 통한 비 동기 호출의 두 번째 시간이 되겠습니다. 전 강좌에서는 전반적인 비 동기 호출의 의미와 고전적인 방법에 대해 소개를 드렸었는데요. 이번에는 그에 이어 조금 더 개선된(게다가, 현재도 상당히 쓸만한) 방법을 소개시켜 드리려 하는 것이죠.
이 방법은 현재 다양한 형태로 변형되어 이용하고 있는 방법이기도 합니다. 즉, 외적인 형태만 조금씩 다를 뿐, 대부분의 비 동기 호출이 내부적으로는 이번 강좌의 방법을 사용하고 있다는 의미죠. 그게 도대체 뭔데 이렇게 뜸을 들이냐구요?
비 동기 호출의 두 번째 방법은 XMLHTTP라는 COM 컴포넌트를 이용하는 것입니다. 이는 클라이언트 스크립트인 자바스크립트 안에서도 이용할 수 있기에 매우 유용합니다. XMLHTTP 개체를 사용하게 되면, 일반적인 페이지의 요청/응답 외에도 별도의 요청 및 응답을 처리할 수 있습니다. 즉, 비 동기 호출이 요구하는 별도의 요청/응답 채널을 확보할 수 있다는 의미가 되겠죠. 초심자들에게는 어려운 말일 수도 있지만 말입니다. 일단 그림으로 확인해 보시죠~
보시다시피, XMLHTTP 개체는 자바스크립트 안에서 개체의 인스턴스를 만들어서 이용이 가능하기에, 서버 측의 페이지가 무슨 페이지이든지는 중요하지 않습니다. 즉, 이 방식도 서버 페이지가 ASP이던, PHP이던, JSP이던, .NET 페이지이던 전혀 개의치 않는다는 것이죠. 어떤 동적 페이지이던 간에 데이터 처리 결과로 출력되는 문자열이나 XML 데이터만 받아오면 되기 때문입니다. XMLHTTP는 그러한 결과를 비 동기적으로 수신 받아오는 역할을 하고, 클라이언트 스크립트는 그 반환받은 데이터를 문자열 파싱(parsing)하여 필요한 데이터를 동적으로 웹 페이지에 반영하면 되는 것입니다. 그렇다면, 샘플을 한번 같이 살펴보실까요?
샘플은 각각 클라이언트 페이지와 서버 페이지로 구분해서 코드를 보여드리겠습니다. 클라이언트 페이지는 굳이 동적 페이지일 필요가 없으니 htm 페이지로 만들고요. 서버 페이지는 단순히 서버에서 데이터를 얻어오기 위해 필요한 페이지이니 ASP, JSP 뭐로 만들어도 무관하지만, 사이트가 사이트이니만큼 ASP.NET 페이지로 만들어 보도록 하겠습니다. ASP.NET 페이지이긴 하지만, UI는 전혀 없는 페이지로 말입니다(그런 이유로, aspx 페이지의 소스는 보여드릴 필요가 없고요. Aspx의 코드 비하인드만 보여드리면 될 것입니다).
Client.htm
<html>
<head>
<Script language="JavaScript">
function GetInfo()
{
var empID = document.all.EmpID.value;
var pageUrl = "Process.aspx?param=" + empID;
var xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
xmlRequest.Open("POST", pageUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.onreadystatechange = function() {CallBack(xmlRequest)};
xmlRequest.Send(null);
return xmlRequest;
}
function CallBack(xmlRequest)
{
if (xmlRequest == null || xmlRequest.readyState != 4) return;
if(xmlRequest.responseText.length == 0) return;
var vals = xmlRequest.responseText.split("\t");
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>
<p>
<table cellpadding="3">
<tr>
<td align="center">사번</td>
<td><input id="EmpID" NAME="EmpID" onchange="GetInfo();" ></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>Microsoft.XMLHTTP 컴포넌트를 사용한 예입니다 (IE 5.0 이상)</a></p>
</body>
</html>
|
Process.aspx.cs
public class Process : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
string param = "";
if(Request.Params["Param"] != null)
param = Request.Params["Param"];
else
return;
string info = string.Empty;
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 = param;
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if(reader.Read())
{
info = reader[0].ToString();
info += "\t" + reader[1].ToString();
info += "\t" + reader[2].ToString();
info += "\t" + reader[3].ToString();
info += "\t" + reader[4].ToString();
}
else
{
info = "null\tnull\tnull\tnull\tnull";
}
reader.Close();
con.Close();
Response.Write(info);
Response.End();
}
}
|
일단, 서버 페이지인 Process.aspx 페이지부터 살펴보도록 하겠습니다. Process.aspx의 역할은 보시다시피 대단한 것이 없습니다. 단지 Param이란 인자로 넘어오는 사번을 가지고 데이터베이스를 조회해서, 그 사번에 해당하는 직원의 이름 및 정보들을 탭키(\t)를 구분자로 하는 문자열을 만들어서 화면에 출력하는 것이 전부일 뿐입니다. 단지, 데이터베이스를 조회하여, 그 결과 데이터를 화면에 출력하는 역할을 할 뿐이라는 것이죠. 만일, 사용자가 존재하지 않는다면, 각각의 값을 null이라는 문자열로 만들어서 출력하고 있습니다. 반드시 null이라는 문자열을 써야하는 것은 아니겠지만 예제에서는 그냥 그렇게 해 보았습니다. 맘에 안 드시면 " "와 같은 공백문자를 이용해도 무관하겠죠.
서버 페이지의 실행 결과는 확연할 것입니다. 존재하는 사번을 Param이라는 인자로 넘겨주면 다음과 같이 올바로 사용자 데이터가 화면에 출력될 것이고, 존재하지 않는 사번을 인자로 넘겨주면 두 번째 그림처럼 null이라는 값들이 직원 정보를 대신하겠죠.
서버 페이지는 준비가 되었으니 이제 Client.htm 페이지를 살펴보도록 하겠습니다.
우선, 이 페이지에서 가장 먼저 보아야 할 부분은 사번을 입력받는 컨트롤인 empID 컨트롤입니다.
<input id="EmpID" NAME="EmpID" onchange="GetInfo();">
|
이는 onChange 이벤트 시에 GetInfo라는 자바스크립트 함수를 호출하게 되어 있습니다. 그럼, GetInfo 함수를 살펴볼까요? 그 함수는 어떠한 일을 하나요? 내용은 대략 다음과 같습니다.
function GetInfo()
{
var empID = document.all.EmpID.value;
var pageUrl = "Process.aspx?param=" + empID;
var xmlRequest = new ActiveXObject("Microsoft.XMLHTTP");
xmlRequest.Open("POST", pageUrl, true);
xmlRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlRequest.onreadystatechange = function() {CallBack(xmlRequest)};
xmlRequest.Send(null);
return xmlRequest;
}
|
이 함수는 현재 폼에서 empID란 아이디를 갖는 컨트롤의 입력 값을 얻어와서 그 값을 가지고
Process.aspx 페이지를 요청하는 문자열을 구성합니다.
그 다음, Microsoft.XMLHTTP의 개체 인스턴스, 즉 코드 상에서는 xmlRequest를 만들어서 별도의 페이지 요청을 수행하고 있습니다.
즉, XMLHTTP 컨트롤을 이용해서 스크립트 내부에서 별도의 웹 요청을 서버로 보내고 응답을 받는다는 것입니다.
XMLHTTP 컴포넌트는 특정 URL로 요청을 보내서 응답 데이터를 받아오는 역할을 수행할 수 있습니다.
요청을 할 서버 경로는 Open 메서드에 인자로 실어서 보내면 됩니다.
물론, 그 url 경로 뒤에 GET 방식의 매개변수들을 첨부할 수 있는 것은 너무나도 당연하겠죠?
해서, 소스 첫 번째 라인에서 URL을 구성할 때, URL 뒤에 ?param= 이라는 것을 붙이고, 인자 값을 붙여준 것입니다.
그리고, XMLHTTP 개체의 Send 메서드를 통해서 요청을 보내고 나면, 이제 개체는 해당 URL 페이지를 요청하게 되고,
그 결과 값을 받아오게 됩니다.
한 가지 주의할 점은 이 호출이 비동기적이 되기 위해서는 Open 메서드의 세번째 인자로 반드시
true를 지정해야 한다는 것입니다. 바로 이 세 번째 인자가 호출을 비동기적으로 처리하라는 의미를 가지기 때문입니다.
이 말은 비동기 호출을 위한 방안도 XMLHTTP가 이미 내장하고 있다는 것이라 보시면 됩니다. ^^
그리고, 소스에서는 onreadystatechange 를 특정 함수명으로 지정하고 있습니다.
onReadyStateChange 속성에 콜백을 위한 메서드를 지정해 주게되면, 상태변경(요청시작, 에러발생, 요청완료 등)이 일어날 때마다
지정된 CallBack 메서드는 반복적으로 호출되게 됩니다. 소스에서는 편의를 위해서 인자로 현재의 xmlRequest 개체를 넘기고 있지만 말입니다.
반드시 그래야하는 것은 아닙니다. XMLHTTP 컴포넌트를 전역으로 선언했다면 굳이 인자로 넘길 필요는 없겠죠?
CallBack 메서드는 호출의 상태가 변경될 때마다 호출이 되어지는데요.
아래의 소스에서 보이다시피, 상태를 확인해서 그 상태가 4(성공적으로 처리되었음을 의미)가 아닌 경우에는 처리를 건너뛰게 하고 있구요.
반환된 데이터가 전혀 없는 경우에도 return을 하게 하고 있습니다. 반환된 데이터가 올바를 경우에만 그 데이터를 잘라서 화면에
반영하겠다는 것이죠 ^^
function CallBack(xmlRequest)
{
if (xmlRequest == null || xmlRequest.readyState != 4) return;
if(xmlRequest.responseText.length == 0) return;
var vals = xmlRequest.responseText.split("\t");
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];
}
|
결과 값은 responseText라는 속성을 통해서 얻을 수가 있는데요.
responseText 값은 서버의 페이지가 출력한 문자열 데이터(html 혹은 xml 혹은 예제의 경우, 일반 문자열)을 그대로 가져옵니다.
현재의 경우는 Process.aspx 페이지의 결과, 탭(tab) 키로 구분된 문자열이 넘어올 것입니다.
해서, 스크립트에서는 그 responseText 데이터를 탭 키로 Split해서, 일단 문자열 배열을 만든 뒤, 각각 필요한 문자열을 추출하여 사용하고 있는 것을 보실 수 있습니다.
조금 더 고급스러울 라면, 서버에서는 데이터를 xml 형태로 구성해서 출력하고, 클라이언트는 그 xml 데이터를 Parsing해서 사용하면 더욱 낫겠지만, 그를 위해 추가적인 코드가 필요하기에 예제에서는 간단하게만 해 보았습니다.
자. 그럼 이제 페이지(Client.htm)를 실행하고, 결과를 보도록 하겠습니다. 역시나 예상대로 잘 동작하는 것을 보실 수 있을 것입니다. 사번(1부터 9 사이의 임의의 숫자)을 입력하고 포커스를 옮기기만 하면 자동으로 다른 텍스트박스에는 해당 사번의 직원 정보가 출력되는 것을 보실 수 있을 것입니다. 그쵸?
재미있지 않나요? 하하
강좌 초반에도 강조드렸지만, 이 방식은 현재까지도 여러 형태로 이용되고 있는 방식입니다. 예를 들자면, 형태만 다르다 뿐이지 AJAX도 내부적으로는 이 방식을 이용하고 있습니다. 그 외에 웹 기반의 비 동기 호출방식도 대부분 내부적으로는 XMLHTTP를 이용한다고 봐도 과언이 아닙니다. 그렇기에 이번 강좌는 매우 중요합니다. 알아두면 반드시 차후에 다른 비 동기 호출을 이해할 경우 도움이 될테니까요.
자, 그럼 이제 다음 강좌에서 또 다른 비 동기 호출방식을 알아보도록 하겠습니다. 갈수록 잼나지는 태오의 강좌!! 이번에도 만빵 기대해 주세요 ^^
이번 강좌에 쓰인 소스를 원하시면 클릭하세요 ^^