WAN용 사용자 지정 웹 파트 최적화

업데이트 날짜: 2009년 4월

적용 대상: Office SharePoint Server 2007

 

마지막으로 수정된 항목: 2009-04-17

요약: SharePoint 목록에서 데이터를 검색 및 렌더링하는 다른 방법에 대한 일반적인 스타일 제안과 특정 정보 및 코드 예제 등 사용자 지정 웹 파트의 대역폭에 미치는 영향을 최소화할 수 있는 기법에 대해 알아봅니다(인쇄본 24페이지).

Steve Peschka Microsoft Corporation

2008년 1월

적용 대상: Microsoft Office SharePoint Server 2007, Windows SharePoint Services 3.0, ASP.NET AJAX 1.0

내용

  • WAN용 사용자 지정 웹 파트 최적화 소개

  • 기본 제공 스타일 재사용 또는 사용자 지정 스타일 만들기

  • 상태 저장

  • 데이터를 표시하는 웹 파트의 성능 최대화

  • 결론

  • 추가 리소스

WAN용 사용자 지정 웹 파트 최적화 소개

대기 시간이 길고 대역폭이 낮은 사이트에서 사용할 사용자 지정 웹 파트를 개발하려면 해당 유형의 환경을 위한 페이지를 만들 때 사용하는 것과 동일한 일반적인 디자인 원칙에 초점을 맞춰야 합니다. 서버에 대한 왕복과 네트워크를 통해 전송되는 데이터의 양을 모두 최소화하는 파트를 디자인하기 위해 노력해야 합니다. 이 문서에서는 이러한 디자인 목표를 충족하기 위해 사용할 수 있는 몇 가지 기법에 대해 알아봅니다.

기본 제공 스타일 재사용 또는 사용자 지정 스타일 만들기

웹 파트에서 HTML을 내보내면 Microsoft Office SharePoint Server 2007 및 Windows SharePoint Services 3.0 스타일시트에서 기본 제공되는 스타일 클래스인 core.css를 사용하십시오. 이 문서에서는 Office SharePoint Server와 Windows SharePoint Services를 Microsoft SharePoint 제품 및 기술이라고 총칭합니다. 이러한 스타일을 재사용하면 페이지에서 파트를 지원하기 위해 추가 스타일시트를 다운로드할 필요가 없으므로 페이지에 미치는 영향을 최소화할 수 있습니다. 또한 사용자가 처음 사이트를 방문하면 core.css 파일이 해당 사용자의 캐시에 다운로드됩니다. core.css의 일부인 스타일을 사용하면 스타일 지원을 위해 추가로 다운로드할 필요가 없습니다.

파트에 사용자 지정 스타일이 필요한 경우에는 BLOB 캐시와 함께 사용할 수 있는 사용자 지정 스타일시트를 사용해 보십시오. 문서 라이브러리에 저장하면 스타일시트에 캐시 가능성 지시문이 연결되므로 처음 페이지를 방문한 이후 다운로드할 필요가 없습니다. 이렇게 하면 예를 들어 파트가 렌더링될 때마다 네트워크를 통해 전송되는 인라인 스타일을 사용하는 경우 등에 비해 사이트에 미치는 영향이 줄어듭니다.

상태 저장

웹 파트에서 사용자, 요청 및 데이터 원본과 같은 정보를 추적해야 할 수 있습니다. 상태를 저장할 수 있는 옵션은 여러 가지가 있지만 이 문서에서는 각 옵션에 대해 설명하지 않습니다. 그러나 일반적으로 웹 파트에 사용할 수 있는 두 가지 옵션으로 ViewState와 서버 Cache가 있습니다. 대역폭이 낮거나 대기 시간이 긴 환경에서는 다운로드할 때와 포스트백으로 페이지에 콘텐츠가 추가되므로 가능한 한 ViewState를 사용하지 마십시오. 이 사항은 쿼리 문자열, 숨겨진 필드 및 쿠키와 같이 네트워크를 통해 데이터가 전송되는 다른 형태의 상태에 적용될 수 있습니다.

서버 Cache 클래스를 사용하면 상태 정보를 서버 수준에서 저장할 수 있습니다. 서버 Cache를 사용할 때의 단점은 상황에 따라 사용자별 상태 메커니즘으로 작동하도록 할 수도 있지만 본래 사용자별 상태 메커니즘으로 사용하기 위한 것이 아니라는 것입니다. 또한 팜의 모든 프런트 엔드 웹 서버에 캐시 정보가 복제되지 않습니다. 사용자 요청이 방문을 종료하는 프런트 엔드 웹 서버에 상관없이 해당 상태 정보가 있는지에 따라 파트가 달라지는 경우 서버 Cache를 선택하지 않는 것이 좋습니다.

이 시나리오에서는 세션 상태를 사용할 수 있습니다. 세션 상태는 기본적으로 해제되어 있지만 팜에서 Microsoft Office IPFS(InfoPath Forms Services)를 활성화할 경우 사용하도록 설정됩니다. 세션 상태를 사용하도록 설정된 경우 Microsoft SQL Server를 사용하여 상태를 추적합니다. 즉, HTTP 요청을 수신하는 프런트 엔드 웹 서버에 상관없이 세션 상태 값을 사용할 수 있습니다. 세션 상태의 단점은 데이터가 제거되거나 만료될 때까지 메모리에 데이터가 유지된다는 것입니다. 따라서 세션 상태에 대용량 데이터 집합이 저장되어 있으면 제대로 관리하지 못할 경우 서버 성능이 저하될 수 있습니다. 이러한 제한 때문에 절대적으로 필요한 경우가 아니면 세션 상태를 사용하지 않는 것이 좋습니다.

웹 응용 프로그램에 대한 web.config 파일을 편집하여 사이트의 모든 페이지에서 ViewState 설정을 해제해 볼 수도 있습니다. 여기에는 enableViewState 특성이 있는 pages 요소가 포함됩니다. 페이지에서 ViewState의 크기가 걱정되는 경우 이 특성을 false로 설정할 수 있습니다(기본값은 true). 이 경우 일부 컨트롤 및 웹 파트에서는 ViewState가 설정되어 있어야 하므로 사이트와 모든 기능이 제대로 작동하는지 철저히 테스트해야 합니다.

웹 파트를 개발하는 과정에서 ViewState와 비슷한 것을 사용해야 하는데 페이지에서 사용할 수 있는지 확실하지 않은 경우에는 대신 ASP.NET 2.0의 새로운 기능인 컨트롤 상태를 사용할 수 있습니다. 즉, 다음 작업을 수행해야 합니다.

  1. 초기화하는 동안 Page.RegisterRequiresControlState를 호출하여 컨트롤 상태 등록

  2. LoadControlState 메서드 및 SaveControlState 메서드 재정의

  3. 컨트롤 상태에 매핑되는 개체 모음의 부분을 수동으로 관리

데이터를 표시하는 웹 파트의 성능 최대화

데이터를 표시하는 데 웹 파트를 사용하는 경우 여러 가지 방법으로 최종 사용자의 성능을 최대화할 수 있습니다. 일반적으로 필요한 서버 왕복 횟수와 요청을 통해 검색되는 데이터의 양 사이의 균형을 조정해야 합니다.

렌더링 제한 제공

데이터 행을 내보내는 컨트롤의 경우 관리자가 표시되는 행의 수를 제어하도록 허용하는 속성을 포함합니다. 컨트롤이 사용될 환경의 대기 시간과 대역폭에 따라 각 페이지 요청에서 렌더링되는 데이터의 양을 늘리거나 줄일 수 있는 유연성이 허용됩니다. 모든 데이터를 보는 데 필요한 요청의 수에도 영향이 미칠 수 있습니다.

반환되는 행의 수가 최종 사용자가 설정할 수 있는 속성인 경우 한 사용자의 선택으로 네트워크에 문제가 발생하지 않도록 제한을 추가해 보십시오.

인라인 XML 데이터 아일랜드 사용

데이터를 표시하는 또 다른 방법은 인라인 XML 데이터 아일랜드를 사용하고 클라이언트 쪽에서 렌더링을 수행하는 것입니다. 이 방법을 사용하면 표시되는 모든 데이터가 페이지에 XML 데이터 아일랜드로 내보내집니다. 클라이언트 쪽 스크립트를 사용하여 페이지의 데이터를 실제로 렌더링하며, 클라이언트 쪽 스크립트에 따라 XML DOM을 사용하여 아일랜드의 데이터를 검색하고 표시합니다.

이렇게 하면 하나의 요청으로 모든 데이터를 검색할 수 있으므로 서버에 대한 왕복의 수가 최소화됩니다. 그러나 다운로드 크기가 증가하므로 초기 페이지 로드에 더 많은 시간이 걸립니다. 또한 포스트백을 발생시키는 다른 컨트롤이 사용되는 페이지에서 사용될 경우 이와 같은 대용량 데이터 다운로드가 매번 발생하게 됩니다. 이 기법은 이러한 상황이 발생하지 않는 것으로 확인된 시나리오에 가장 적합합니다. 그렇지 않은 경우에는 선택적인 데이터 검색 방법으로 사용하십시오. 예를 들어 사이트 관리자가 동작을 제어할 수 있도록 모든 데이터를 다운로드해야 하는지 여부를 추적하는 공용 속성을 만듭니다.

다음은 비교적 간단한 웹 파트의 예제입니다. 이 예제에서는 목록에서 모든 콘텐츠를 읽고 페이지에 XML로 내보낸 다음 클라이언트 쪽 ECMAScript(JScript, JavaScript)를 사용하여 데이터 전체를 렌더링 및 페이징합니다.

참고

이 문서의 코드는 Microsoft에서 권장하는 최상의 코딩 방법을 나타내는 것은 아닙니다. 이 코드는 간편하게 연습해 볼 수 있도록 단순화되었습니다. 프로덕션 환경에서는 코드의 요소를 변경해야 할 수 있습니다.

웹 파트는 Render 메서드를 무시하고 Contacts라는 웹 사이트의 목록에서 콘텐츠를 가져오는 것으로 시작합니다.

// Get the current Web site.
SPWeb curWeb = SPContext.Current.Web;

// Get the list; LISTNAME is a constant defined as "Contacts".
SPList theList = curWeb.Lists[LISTNAME];

// Ensure the list was found.
if (theList != null)
{
...
}

목록에 대한 참조를 가져온 후 StringBuilder 개체를 사용하여 페이지로 내보낼 XML 데이터 아일랜드를 만듭니다.

// stringbuilder to create the XML island
System.Text.StringBuilder sb = new System.Text.StringBuilder(4096);

// Create the island header.
sb.Append("<XML ID='spList'><Items>");

// Enumerate through each item.
SPListItemCollection theItems = theList.Items;

foreach (SPListItem oneItem in theItems)
{
   // Add the tag for an item.
   sb.Append("<Item ");

   // Put the attributes in a separate try block.
   try
   {
      sb.Append("Name='" + oneItem["First Name"] + "' ");
      sb.Append("Company='" + oneItem["Company"] + "' ");
      sb.Append("City='" + oneItem["City"] + "' ");
   }
   catch
   {
      // Ignore.
   }

   // Close out the item.
   sb.Append("/>");
}

// Close off the XML island.
sb.Append("</Items></XML>");

// Write out the XML island; writer is the 
// HtmlTextWriter parameter to the method.
writer.Write(sb.ToString());

페이지에 추가한 XML과 함께 데이터를 표시할 요소도 필요합니다. 이 요구 사항을 충족하기 위해 간단한 <div> 태그가 페이지에 추가됩니다.

// Add a <div> tag - this is where we'll output the data.
writer.Write("<div id='wpClientData'></div>");

필요한 다음 인터페이스 부분이 데이터 전체를 페이징하는 데 사용됩니다. 이 경우 두 개의 링크 단추가 페이지에 추가됩니다. 주목해야 할 두 가지 요소는 OnClientClick 속성과 Attributes 컬렉션입니다. OnClientClick 속성은 클라이언트에서 데이터를 표시하기 위해 작성된 사용자 지정 ECMAScript(JScript, JavaScript) 함수를 사용하도록 설정됩니다.

Attributes 컬렉션은 LinkButton의 탐색 URL을 설정하는 데 사용됩니다. 이 경우 LinkButton을 하이퍼링크로 렌더링하여 사용자가 항목을 클릭할 수 있다는 피드백을 받고 클릭했을 때 조치를 취할 수 있도록 하려고 합니다. 이 경우 실제로는 탐색하려는 것이 아니므로 # 링크를 탐색 URL로 사용합니다. 여기서는 링크를 렌더링하고 링크를 클릭했을 때 캡처하기만 할 것입니다.

// Add the paging links.
LinkButton btn = new LinkButton();
btn.Text = "<< Prev";
btn.Attributes.Add("href", "#");
btn.OnClientClick = "renderPartData('PREV');";
btn.RenderControl(writer);

writer.Write("&nbsp;");

btn = new LinkButton();
btn.Text = "Next >>";
btn.Attributes.Add("href", "#");
btn.OnClientClick = "renderPartData('NEXT');";
btn.RenderControl(writer);

다음으로, 컨트롤 페이징을 추적하기 위해 숨겨진 필드 몇 개를 페이지에 추가합니다. 이 경우 페이지 크기(한 번에 표시할 레코드 수), 현재 레코드 번호 및 레코드 수를 추적하려고 합니다.

// Add fields to track the number of items to see in a page and 
//current item number.  PageSize is a web part property that can be set 
//to control the number of rows returned 
Page.ClientScript.RegisterHiddenField("pageSize", PageSize);
Page.ClientScript.RegisterHiddenField("curPage", "-4");
Page.ClientScript.RegisterHiddenField("totalItems",
   theItems.Count.ToString());

모든 요소가 갖춰졌으면 Render 메서드의 마지막 단계로, javascript를 실행하여 데이터를 렌더링하는 시작 스크립트를 등록합니다.

// Create a startup script to call our dataload method when the page 
//loads
this.Page.ClientScript.RegisterStartupScript(this.GetType(), JSCR_START,
   "renderPartData();", true);

javascript 자체는 Scripts.js라는 프로젝트에서 별도의 파일에 포함되어 있습니다. 포함된 리소스로 구성되며 webresource.axd와 같은 웹 리소스로 페이지에 전송됩니다. 다운로드를 위해 이를 구성하는 코드는 웹 파트의 OnPreRender 이벤트에서 실행됩니다. 우선 IsClientScriptIncludeRegistered 메서드를 호출하여 스크립트 참조가 페이지에 아직 추가되지 않았는지 확인하고, 추가되지 않은 경우 해당 페이지에 대해 include로 등록합니다.

// Register our JScript resource.
if (!Page.ClientScript.IsClientScriptIncludeRegistered(JSCR_NAME))
   Page.ClientScript.RegisterClientScriptInclude(this.GetType(),
JSCR_NAME, Page.ClientScript.GetWebResourceUrl(this.GetType(),
"Microsoft.IW.Scripts.js"));

이 메서드에서는 프로젝트의 AssemblyInfo.cs 클래스에 웹 리소스도 등록해야 합니다.

[assembly: WebResource("Microsoft.IW.Scripts.js", "text/javascript")]

페이지가 렌더링되면 다음과 비슷한 링크가 포함됩니다.

<script src="/WebResource.axd?d=DVBLfJiBYH_yZDWAphRaGQ2&amp;t=633198061688768475" 
type="text/javascript"></script>

ECMAScript(JScript, JavaScript)에서는 XML을 읽고 데이터를 렌더링합니다. 먼저 페이징 범위, 즉 표시할 첫 번째 레코드 번호와 마지막 레코드 번호를 결정합니다. 표시할 레코드를 인식한 후 매우 간단한 메서드를 사용하여 클라이언트에 MSXML의 인스턴스를 만듭니다.

function createXdoc()
{
   ets the most current version of MSXML on the client.
   var theVersions = ["Msxml2.DOMDocument.5.0", 
      "Msxml2.DOMDocument.4.0", 
      "MSXML2.DOMDocument.3.0", 
      "MSXML2.DOMDocument", 
      "Microsoft.XmlDom"];

   for (var i = 0; i < theVersions.length; i++)
   {
      try
      {
         var oDoc = new ActiveXObject(theVersions[i]);
         return oDoc;
      }
      catch (theError)
      {
         // Ignore.
      }
   }
}

XML 데이터와 결과를 출력할 DIV 요소에 대한 참조를 저장하는 데 모두 변수가 사용됩니다.

// Get the XML.
var xData = document.getElementById("spList").innerHTML;
   
// Get the target.
var target = document.getElementById("wpClientData");

다음으로, MSXML의 DOMDocument에 데이터가 로드되고 Item 요소의 목록이 선택됩니다.

// Get the XML DomDocument.  
var xDoc = createXdoc();
   
// Validate that a document was created.
if (xDoc == null)
{
target.innerHTML = "<font color='red'>A required system component 
(MSXML) is not installed on your computer.</font>";
   return;
}
   
// Load the XML from the island into the document.
xDoc.async = false;
xDoc.loadXML(xData);
   
// Check for parsing errors.
if (xDoc.parseError.errorCode != 0) 
{
var xErr = xDoc.parseError;
target.innerHTML = "<font color='red'>The following error was 
encountered while loading the data for your selection: " + 
xErr.reason + "</font>";
   return;
}
   
// Get the items.  
var xNodes;
xDoc.setProperty("SelectionLanguage", "XPath");
xNodes = xDoc.documentElement.selectNodes("/Items/Item");  

이제 노드가 선택되었으므로 페이지에 필요하고 렌더링되는 특정 페이지 집합에 대한 결과를 열거할 수 있습니다.

// Check for data.
if (xNodes.length == 0)
   target.innerHTML = "<font color='red'>No data was found.</font>";
else
{
   // Create a table to render the data.
   output = "<table style='width:250px;'><tr><td>";
   output += "<table class='ms-toolbar'><tr>" +
      "<td style='width:75px;'>Name</td>";
   output += "<td style='width:75px;'>Company</td>";
   output += "<td style='width:75px;'>City</td></tr></table>
</td></tr>";
   output += "<tr><td><table>";
      
   // Cycle through all the data; startID and endID represent
   // the bounds of the page.
   for (var i = (parseInt(startID) - 1); i < parseInt(endID); i++)
   {
      // Create a new row.
      output += "<tr>";
                  
      // Add each cell.
      output += "<td style='width:75px;'>" + 
xNodes(i).getAttribute("Name") +
         "</td>";
      output += "<td style='width:75px;'>" +
         xNodes(i).getAttribute("Company") + "</td>";
      output += "<td style='width:75px;'>" + xNodes(i).getAttribute("City") +
         "</td>";
         
      // Close the row tag.
      output += "</tr>";
   }
      
   // Close the table tag.
   output += "</table>
</td></tr></table>";
      
   // Show the page parameters.
   output += "Records " + startID + " to " + endID + " of " + ti;
      
   // Plug the output into the document.
   target.innerHTML = output;
}

이 단계가 완료되면 파트가 클라이언트에 레코드를 표시하며 서버에 대한 추가 왕복을 만들지 않고 전체 레코드 페이징을 위한 메커니즘을 제공합니다. 그림 1과 그림 2는 서로 다른 데이터 페이지가 포함된 파트를 보여 줍니다.

그림 1. 데이터가 포함된 웹 파트

샘플 웹 파트의 첫 번째 페이지

그림 2. 데이터가 포함된 웹 파트

웹 파트 데이터의 두 번째 페이지

웹 서비스 연결에 클라이언트 쪽 스크립트 사용

네트워크를 통해 전송되는 데이터의 양을 관리하는 또 다른 방법은 웹 서비스에 연결하는 클라이언트 쪽 스크립트를 사용하여 데이터를 검색하는 것입니다. 이 방법에서는 전체 페이지를 포스트백할 필요 없이 필요한 데이터를 검색하는 컨트롤을 사용할 수 있습니다. 전체 페이지를 서버에 보내고 다시 받아 데이터를 검색하는 것이 아니므로 사용자가 사용하기에도 편리하고 네트워크의 부하도 줄어듭니다. 데이터 검색은 XmlHttp 구성 요소를 사용하여 직접 수행하거나 XmlHttp 관련 기능을 래핑하는 Microsoft AJAX Library 1.0을 사용하여 수행할 수 있습니다.

XmlHttp와 ASP.NET AJAX가 높은 수준의 옵션인 반면 SharePoint 목록 데이터를 노출하는 사용자 지정 웹 서비스와 함께 ASP.NET AJAX를 사용하는 것은 매우 쉽게 느껴질 것입니다. ASP.NET AJAX 라이브러리를 SOAP 기반 웹 서비스(예: SharePoint 제품 및 기술에서 기본 제공되는 웹 서비스)와 함께 사용하는 것은 기술적으로 가능하지만 상당히 더 복잡한 작업이며 보다 작은 페이로드 크기 및 JSON(JavaScript Object Notation) 지원 등의 ASP.NET AJAX 스타일 웹 서비스의 추가적인 이점을 제공하지 않습니다.

SharePoint 제품 및 기술은 데이터를 표시하는 데 사용할 수 있는 몇 가지 웹 서비스를 제공합니다. Lists 웹 서비스를 사용하여 SharePoint 제품 및 기술의 목록 및 라이브러리에서 테이블 형식 데이터를 검색할 수 있습니다. 또한 목록 및 링크에 포함된 데이터를 문서나 이미지 같은 목록 내 항목으로 렌더링할 수도 있습니다. Search 웹 서비스를 사용하면 SharePoint 제품 및 기술과 크롤링되는 다른 모든 외부 원본 내에 포함된 콘텐츠의 본문을 검색할 수 있습니다. 제대로 작성된 메타데이터 기반 쿼리에서는 이를 통해 하나 이상의 SharePoint 목록에서 필터링된 데이터 집합을 검색할 수도 있습니다.

다음 예제에서는 사용자 지정 웹 서비스를 만들어 SharePoint 제품 및 기술에서 목록 데이터를 검색하는 방법을 보여 줍니다. 웹 서비스 클래스가 System.Web.Script.Services.ScriptService 특성으로 주석 처리되어 있으므로 ASP.NET AJAX와 함께 제공되는 클라이언트 쪽 웹 서비스 구성 요소를 사용할 수 있습니다. 그런 다음 웹 서비스가 SharePoint 제품 및 기술에 "등록"되므로 기본 제공되는 모든 웹 서비스에서 _vti_bin 디렉터리를 통해 액세스할 수 있습니다. 사용자 지정 웹 서비스에 대한 ASP.NET AJAX ScriptManager 컨트롤 및 선언적 태그를 포함하도록 마스터 페이지가 업데이트됩니다. 그런 다음 클라이언트 쪽 스크립트를 생성하여 사용자 지정 웹 서비스에서 ASP.NET AJAX 구성 요소를 통해 데이터를 검색하고 페이지에 표시하는 웹 파트가 작성됩니다.

AJAX 설치

우선 SharePoint 서버에 ASP.NET AJAX 1.0 바이너리를 설치해야 합니다. ASP.NET AJAX 사이트 (영문)에서 다운로드할 수 있습니다. 팜의 각 프런트 엔드 웹 서버에 설치해야 합니다.

ASP.NET AJAX를 설치한 후에는 ASP.NET AJAX를 사용할 각 웹 응용 프로그램의 web.config 파일을 업데이트해야 합니다. 이 연습에는 시간이 좀 걸릴 수 있습니다. 전체 단계별 지침은 Mike Ammerlan의 블로그 게시물 SharePoint에 ASP.NET AJAX 통합 (영문)을 참조하십시오.

사용자 지정 웹 서비스 만들기

System.Web.Script.Services.ScriptService를 웹 서비스 클래스에 적용하여 ASP.NET AJAX 웹 서비스 프레임워크에 사용할 수 있도록 하려면 사용자 지정 웹 서비스를 만들어 데이터를 검색해야 합니다. 이 경우 기본 매개 변수를 기반으로 목록 데이터를 검색할 수 있도록 비교적 간단한 웹 서비스 클래스가 개발되었습니다.

웹 서비스 클래스에는 ASP.NET AJAX 지원이 가능한 System.Web.Extensions 클래스에 대한 참조가 포함됩니다. 해당 참조가 추가된 후 using(Microsoft Visual C#) 또는 Imports(Microsoft Visual Basic) 문이 클래스에 추가됩니다.

using System.Web.Script.Services;

그런 다음 ScriptService 특성으로 클래스가 장식되므로 ASP.NET AJAX 웹 서비스 프레임워크에서 직접 사용할 수 있습니다. 여기에는 매개 변수 없는 기본 생성자가 포함되므로 ASP.NET AJAX에서 클래스를 직렬화할 수 있습니다.

namespace Microsoft.IW
{
   [ScriptService]
   public class AjaxDataWebService : WebService
   {

      public AjaxDataWebService()
      {
         // Default constructor
   }

첫 번째 예제에서는 문자열을 반환하는 하나의 메서드만 웹 서비스에 포함되어 있습니다. 문자열은 실제로 클라이언트에서 사용되는 XML입니다. 메서드 시그니처는 다음과 같이 정의됩니다.

[WebMethod]
public string GetListData(string webUrl, string listName, int 
startingID, 
int pageSize, string[] fieldList, string direction)

// webUrl: URL to the Web site that contains the list
// listName: name of the list (such as "Contacts")
// startingID: used for paging data - which items to get
// pageSize: how many items to return
// fieldList: an array of fields to retrieve for each item
// direction: flag to indicate page forward or backward

StringBuilder ret = new StringBuilder(2048);
DataTable res = null;
string camlDir = string.Empty;
string camlSort = string.Empty;

코드의 첫 번째 부분에서는 사이트, 웹 및 데이터가 포함된 목록에 대한 참조를 가져옵니다.

// Try getting the site.
using (SPSite theSite = new SPSite(webUrl))
{
   // Get the Web at the site URL.
   using (SPWeb theWeb = theSite.OpenWeb())
   {
      // Try getting the list.
      SPList theList = theWeb.Lists[listName];

다음으로, 페이징 방향, 시작 ID 및 결과 집합의 크기에 따라 쿼리 의미가 만들어집니다.

// Use the direction to determine if we're going up or down.
// If we're going down, then sort it in descending order
// so that we go 20,19,18... for example, instead of 1,2,3; otherwise
// each time you paged backward you would always get the first
// page of records.
if (direction == "NEXT")
{
   camlDir = "<Gt>";
   camlSort = "TRUE";
}
else
{
   camlDir = "<Lt>";
   camlSort = "FALSE";
}

// Create the query where clause.
string where = "<Where>" + camlDir + "<FieldRef Name='ID'/>" +
   "<Value Type='Number'>" + startingID + "</Value>" +
   camlDir.Replace("<", "</") + "</Where>" +
   "<OrderBy><FieldRef Name='ID' Ascending='" + camlSort +
"'/></OrderBy>";

// Plug in the where clause.
qry.Query = where;

// Set the page size.
qry.RowLimit = (uint)pageSize;

// Create the view fields.
StringBuilder viewFields = new StringBuilder(1024);
foreach (string oneField in fieldList)
{
   // Add everything but the ID field; we're doing the ID field 
   // differently because we need to include it for paging, 
   // but we can't include it more than once because it would
   // result in the XML that is returned being invalid. So it
   // is special-cased here to make sure it is only added once.
   if (string.Compare(oneField, "id", true) != 0)
      viewFields.Append("<FieldRef Name='" + oneField + "'/>");
}

// Now plug in the ID.
viewFields.Append("<FieldRef Name='ID'/>");

// Set the fields to return.
qry.ViewFields = viewFields.ToString();

쿼리가 구성되었으면 다음 단계는 실행하는 것입니다. 결과가 반환되면 페이징 방향을 다시 검사합니다. 역방향으로 페이징하면 페이지에 가장 작은 ID부터 큰 순서로 나타나도록 결과의 순서가 다시 반대로 바뀌어야 합니다. 다시 말하지만, 이것은 전적으로 웹 파트에서의 페이징 지원에 대한 것입니다. 정렬을 단순화하기 위해 데이터를 ADO.NET DataTable 개체로 가져옵니다. 적절히 데이터를 가져와 정렬한 후 각 행을 열거하여 메서드 호출에서 반환되는 XML을 만듭니다.

// Execute the query.
res = theList.GetItems(qry).GetDataTable();

// If we are going backward, we need to reorder the items so that
// the next and previous buttons work as expected; this puts it back 
// in 18,19,20 order so that the next or previous are based on 18
// (in this example) rather than 20.
if (direction == "PREV")
   res.DefaultView.Sort = "ID ASC";

// Create the root of the data.
ret.Append("<Items Count='" + theList.ItemCount + "'>");

// Enumerate results.
foreach (DataRowView dr in res.DefaultView)
{
   // Add the open tag.
   ret.Append("<Item ");

   // Add the ID.
   ret.Append(" ID='" + dr["ID"].ToString() + "' ");

   // Add each attribute.
   foreach (string oneField in fieldList)
   {
      // Add everything but the ID field.
      if (string.Compare(oneField, "id", true) != 0)
         ret.Append(oneField + "='" + dr[oneField].ToString() +
"' ");
   }

   // Add the closing tag for the item.
   ret.Append("/>");
}

// Add the closing tag.
ret.Append("</Items>");

앞에 나온 코드는 모두 try…catch 블록에 포함됩니다. finally 블록에서는 DataTable 개체와 연결된 리소스를 해제한 다음 만들어진 XML을 반환합니다.

finally
{
   // release the datatable resources
   if (res != null)
      res.Dispose();
   res = null;
}

return ret.ToString();

사용자 지정 웹 서비스 등록

ASP.NET AJAX 사용 웹 파트에서 사용할 사용자 지정 웹 서비스를 노출하려면 두 가지 종류의 구성이 필요합니다. 하나는 SharePoint 제품 및 기술에서 웹 서비스에 대해 인식하고 SharePoint 웹 응용 프로그램의 컨텍스트에서 호출할 수 있도록 구성하는 것입니다. 여기에는 높은 수준의 몇 가지 단계가 포함되며 다음을 수행해야 합니다.

  1. 웹 서비스의 코드 숨김 클래스를 별도의 어셈블리에 만들고 전역 어셈블리 캐시에 등록

  2. 정적 검색 파일 및 WSDL(웹 서비스 기술 언어) 파일 생성 및 편집

  3. _vti_bin 디렉터리에 웹 서비스 파일 배포

이전 작업을 모두 완료하려면 몇 가지 단계가 필요합니다. 이 작업을 수행하는 방법을 설명하는 규정 문서가 이미 있습니다. 자세한 내용은 연습: 사용자 지정 웹 서비스 만들기 (영문)를 참조하십시오.

웹 서비스를 SharePoint _vti_bin 디렉터리에 통합한 후 마스터 페이지를 수정하여 ASP.NET AJAX <ScriptManager> 태그를 추가해야 합니다. <ScriptManager> 태그 안에는 사용하고 있는 사용자 지정 웹 서비스의 서비스 진입점을 정의합니다. 이 예제에서는 사용자 지정 웹 서비스의 이름이 ListData.asmx입니다. 마스터 페이지에 추가되는 전체 태그는 다음과 같습니다.

<asp:ScriptManager runat="server" ID="ScriptManager1">
   <Services>
      <asp:ServiceReference Path="_vti_bin/ListData.asmx" />
   </Services>
</asp:ScriptManager>

웹 서비스를 구성한 후에는 SharePoint _vti_bin 디렉터리 및 사용자 지정 웹 서비스에 대한 참조로 마스터 페이지에 추가된 <ScriptManager> 태그에서 호출할 수 있으며 이제 ASP.NET AJAX 웹 서비스 클라이언트 구성 요소와 통신할 수 있게 됩니다.

XML 데이터를 사용하여 사용자 지정 웹 파트 만들기

이제 사용자 지정 웹 파트에서 ASP.NET AJAX 클라이언트 스크립트를 생성하여 사용자 지정 웹 서비스에서 데이터를 검색해야 합니다. 웹 파트 자체에는 서버 쪽 논리가 거의 포함되어 있지 않습니다. 코드의 대부분이 웹 파트가 웹 리소스(WebResource.axd 파일)로 추가하는 ECMAScript(Jscript, JavaScript) 파일에 포함됩니다.

첫 번째 단계는 사용자 인터페이스의 페이지에 필요한 모든 HTML을 생성하는 것입니다. 웹 파트에서 HTML을 생성하는 기본적인 방법에는 두 가지가 있습니다. 간단한 방법은 태그를 직접 문자열로 작성하는 것이고, 좀 더 복잡하지만 안전한 방법은 ASP.NET 클래스 라이브러리를 사용하는 것입니다. 상대적으로 간단한 HTML을 사용하면 일반적으로 더 빠르고 쉽게 문자열을 내보낼 수 있습니다. 이 경우 사용되는 HTML은 약간 더 복잡합니다. 여기에는 데이터, 탐색 컨트롤 및 “잠시 기다려 주십시오.” 인터페이스 요소에 대한 세 가지 기본 <div> 태그가 포함됩니다. “잠시 기다려 주십시오.” <div> 태그에는 파트 내에 이미지와 텍스트를 올바르게 배치하기 위한 두 개의 중첩 <div> 태그가 포함되어 있습니다. 이러한 좀 더 복잡한 HTML 요구 사항을 기반으로 다음 코드에서와 같이 ASP.NET 클래스 라이브러리를 사용하여 HTML을 생성합니다.

// Add all the UI that is used to render the data.
// <div id='dataDiv' style='display:inline;'></div>
writer.AddAttribute(HtmlTextWriterAttribute.Id, "dataDiv");
writer.AddAttribute(HtmlTextWriterAttribute.Style, "display:inline;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.RenderEndTag();

// Add a <div> tag to hold the navigation buttons.
// <div id='navDiv' style='display:inline;'>
writer.AddAttribute(HtmlTextWriterAttribute.Id, "navDiv");
writer.AddAttribute(HtmlTextWriterAttribute.Style, "display:inline;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

// Add the paging links inside the navigation <div> tag.
LinkButton btn = new LinkButton();
btn.Text = "<< Prev";
btn.Attributes.Add("href", "#");
btn.OnClientClick = "GetAjaxData('PREV');";
btn.RenderControl(writer);

writer.Write("&nbsp;");

btn = new LinkButton();
btn.Text = "Next >>";
btn.Attributes.Add("href", "#");
btn.OnClientClick = "GetAjaxData('NEXT');";
btn.RenderControl(writer);

// Close out the navigation <div> tag.
writer.RenderEndTag();

// Write the "please wait" <div> tag.
// <div id='waitDiv' style='display:none;'>
writer.AddAttribute(HtmlTextWriterAttribute.Id, "waitDiv");
writer.AddAttribute(HtmlTextWriterAttribute.Style, "display:inline;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

// Write the <div> tag to hold the "please wait" image. 
// <div style='float:left;'>
writer.AddAttribute(HtmlTextWriterAttribute.Style, "float:left;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

// Write the animated GIF tag.
// <img src='_layouts/images/gears_an.gif' alt='Please wait...'/>
writer.AddAttribute(HtmlTextWriterAttribute.Src, 
   "_layouts/images/gears_an.gif");
writer.AddAttribute(HtmlTextWriterAttribute.Alt, "Please wait...");
writer.RenderBeginTag(HtmlTextWriterTag.Img);
writer.RenderEndTag();

// Close the <div> tag for the image.
writer.RenderEndTag();

// Write the <div> tag for the text that goes next to the image.
// <div style='float:left;margin-top:22px;margin-left:10px;'>
writer.AddAttribute(HtmlTextWriterAttribute.Style,
   "float:left;margin-top:22px;margin-left:10px;");
writer.RenderBeginTag(HtmlTextWriterTag.Div);

// Write a header tag.
writer.RenderBeginTag(HtmlTextWriterTag.H4);
            
// Write the text.
writer.Write("Please wait while your data is being retrieved.");

// Close the header tag.
writer.RenderEndTag();

// Close the <div> tag for the text.
writer.RenderEndTag();

// Close the <div> tag for all of the "please wait" UI.
writer.RenderEndTag();

다음으로, 웹 파트가 데이터를 페이징할 때 상태를 추적하는 데 사용되는 몇 가지 숨겨진 필드를 추가합니다.

// Add fields to keep track of number of items to see in a page and 
//current item number. PageSize is a web part property that can be set 
//to control the number of rows returned
Page.ClientScript.RegisterHiddenField("siteUrl", 
SPContext.Current.Web.Url);
Page.ClientScript.RegisterHiddenField("listName", "Contacts");
Page.ClientScript.RegisterHiddenField("pageSize", PageSize);
Page.ClientScript.RegisterHiddenField("totalItems", "-1");
Page.ClientScript.RegisterHiddenField("startID", "-1");
Page.ClientScript.RegisterHiddenField("endID", "-1");

마지막으로, 페이지가 로드될 때 웹 파트에서 첫 번째 데이터 페이지를 검색할 수 있도록 시작 스크립트를 등록합니다.

페이지가 로드될 때 데이터 로드 메서드를 호출하는 시작 스크립트를 만듭니다.

if (!Page.ClientScript.IsStartupScriptRegistered(JSCR_START)) 
Page.ClientScript.RegisterStartupScript(this.GetType(), 
JSCR_START, "GetAjaxData('NEXT');", true);

데이터를 검색하는 데 사용되는 EMCAScript(Jscript, JavaScript)는 OnPreRender 이벤트에 등록됩니다. 이 경우에도 스크립트를 포함된 리소스로 추가하고 AssemblyInfo.cs 파일에 등록하는 데 XML 아일랜드 웹 파트에 대해 설명한 것과 동일한 프로세스가 사용됩니다. ECMAScript 파일을 등록하는 작업은 다음과 같습니다.

// Register our JScript resource.
if (!Page.ClientScript.IsClientScriptIncludeRegistered(JSCR_NAME))
Page.ClientScript.RegisterClientScriptInclude(this.GetType(),
JSCR_NAME, Page.ClientScript.GetWebResourceUrl(this.GetType(),
"Microsoft.IW.ajaxdata.js"));

HTML이 만들어졌으면 JScript 또는 JavaScript 파일에서 “잠시 기다려 주십시오.” 인터페이스를 생성할 수 있습니다. 이 인터페이스는 페이지가 로드되거나 데이터가 요청될 때와 사용자가 다음 또는 이전 링크를 클릭하여 데이터의 새로운 페이지가 검색될 때마다 생성됩니다. 이 경우 사용되는 애니메이션 GIF는 SharePoint 제품 및 기술에 포함된 것이므로 사용자에게 친숙합니다. “잠시 기다려 주십시오.” 인터페이스의 형태는 다음과 같습니다.

그림 3. "잠시 기다려 주십시오." 인터페이스

웹 파트 데이터 검색 메시지

모든 데이터 검색 및 사용자 인터페이스 관리는 클라이언트 쪽 스크립트에서 처리됩니다. JScript 또는 JavaScript에서 목록 데이터 및 페이징 컨트롤이 포함된 DIV 요소를 숨기도록 인터페이스를 변경하고 “잠시 기다려 주십시오.” 인터페이스를 표시하는 것으로 시작합니다. 그런 다음 숨겨진 필드를 사용하여 웹 서비스 메서드 매개 변수에 대한 정보를 수집하고 ASP.NET AJAX 프레임워크를 사용하여 사용자 지정 웹 서비스 메서드를 호출합니다.

// This is declared as a global var but could have also been output
// by the Web Part into a hidden field; notice that the InternalName 
// for the list fields must be used.
var fields = new Array("FirstName", "Company", "WorkCity");

// Get the vars containing the data we're going to use.
var url = document.getElementById("siteUrl").value;
var list = document.getElementById("listName").value;
var ps = document.getElementById("pageSize").value;
var ti = document.getElementById("totalItems").value;
var startID = document.getElementById("startID").value;
var endID = document.getElementById("endID").value;
// Some code here to determine the startID for the page.

// Make the call to get the data.
ret = Microsoft.IW.AjaxDataWebService.GetListData(url, list, startID, 
ps, 
   fields, dir, OnComplete, OnTimeOut, OnError);
return true; 

ASP.NET AJAX를 통해 웹 서비스를 호출하는 것은 일반 SOAP 기반 웹 서비스와 약간 다릅니다. 첫 번째로 주의할 점은 웹 서비스 메서드를 호출할 때 정규화된 클래스 이름을 사용해야 한다는 것입니다. 또 다른 차이점은 웹 서비스 메서드에 모든 매개 변수를 제공할 뿐 아니라 추가로 세 개의 매개 변수가 끝에 추가된다는 것입니다. 이러한 매개 변수는 데이터가 반환될 때(OnComplete), 호출 시간이 만료될 때(OnTimeOut) 또는 오류가 발생할 때(OnError) 호출되는 JScript 또는 JavaScript의 함수를 나타냅니다. 이 예제 파트에서는 OnTimeOut 함수와 OnError 함수가 바로 다시 반환된 정보를 데이터가 일반적으로 표시되는 DIV 요소에 렌더링합니다.

OnComplete 함수는 세 가지 중 유일한 필수 매개 변수이며 다음과 비슷한 JScript 함수입니다.

function OnComplete(arg)
{
  ...
}

arg 매개 변수에는 웹 서비스 메서드 호출의 반환 값이 포함됩니다. 이 경우에는 XML을 포함하는 문자열입니다. 이 프로젝트에서는 웹 서비스가 XML 아일랜드 웹 파트에 사용된 것과 동일한 형식의 XML을 반환합니다. 따라서 데이터를 열거하는 코드와 페이지에 렌더링하는 코드가 거의 같습니다. 우선 MSXML DOMDocument를 만들고 유효한 XML이 반환되었는지 확인합니다.

// Get the XML DOMDocument. 
var xDoc = createXdoc();
   
// Validate that a document was created.
if (xDoc == null)
{
target.innerHTML = "<font color='red'>A required system component 
(MSXML) is not installed on your computer.</font>";
   return;
}
   
// Load the XML from the Web service method into the document.
xDoc.async = false;
xDoc.loadXML(arg);
   
// Check for parsing errors.
if (xDoc.parseError.errorCode != 0) 
{
   var xErr = xDoc.parseError;
target.innerHTML = "<font color='red'>The following error was 
encountered while loading the data for your selection: " + 
xErr.reason + "</font>";
   return;
}
   
// Get all the items.  
var xNodes;
xDoc.setProperty("SelectionLanguage", "XPath");
xNodes = xDoc.documentElement.selectNodes("/Items/Item");  
   
// Check for errors.
if (xNodes.length == 0)
   target.innerHTML = "<font color='red'>No data was found.</font>";
else
{
   // Code in here is virtually identical to XML island code.
}

여기서 렌더링 코드가 XML 아일랜드 웹 파트와 다른 점은 첫 번째 및 마지막 항목의 ID가 숨겨진 필드 startID 및 endID에 저장되어 컨트롤의 페이징 기능을 지원한다는 점뿐입니다. 웹 파트에서 데이터를 검색한 후 적절한 DIV 요소에서 렌더링하여 사용자가 콘텐츠 전체를 앞뒤로 페이징할 수 있도록 합니다. 그림 4와 그림 5에서는 데이터의 처음 두 페이지를 보여 줍니다.

그림 4. 첫 번째 데이터 페이지

AJAX 웹 파트의 첫 번째 페이지

그림 5. 두 번째 데이터 페이지

샘플 AJAX 웹 파트 - 두 번째 페이지

JSON을 사용하여 사용자 지정 웹 파트 만들기

이전 예제에서는 웹 서비스 메서드에서 XML이 반환된 다음 XPath 및 MSXML DOMDocument를 사용하여 콘텐츠를 읽었습니다. 그러나 ASP.NET AJAX의 가장 강력한 기능 중 하나는 JSON(JavaScript Object Notation)을 사용하여 데이터를 사용할 수 있는 기능입니다. 이 기능으로 클라이언트 쪽 개발자는 보다 복잡한 XML을 조작하는 대신 개체 및 속성 작업을 통해 데이터에 액세스할 수 있습니다.

이를 설명하기 위해 사용자 지정 클래스를 반환하는 두 번째 웹 메서드를 만들었습니다. 해당 메서드 시그니처는 다음 코드와 비슷합니다.

[WebMethod]
public Records GetListDataJSON(string webUrl, string listName, int 
startingID, int pageSize, string[] fieldList, string direction)

Records 클래스는 반환 데이터를 저장하기 위해 개발된 사용자 지정 클래스입니다. 해당 정의는 다음 코드와 비슷합니다.

[Serializable()]
public class Records
{
   public int Count = 0;
   public int ItemCount = 0;
   public List<Record> Items = new List<Record>();

   public Records()
   {
      // Default constructor.
   }
}

Records 클래스에서 Count 속성은 발견된 항목의 총 개수를 나타내고 ItemCount 속성은 이 특정 호출에서 반환되는 항목의 개수를 나타냅니다. 이러한 속성은 클라이언트 쪽 데이터의 페이징에 사용됩니다. 표시되는 실제 데이터는 레코드 항목의 목록인 Items 속성에 포함됩니다. Record 클래스는 다음과 같이 정의됩니다.

[Serializable()]
public class Record
{
   public SerializableDictionary<string, string> Item = 
      new SerializableDictionary<string, string>();

   public Record()
   {
      // Default constructor.
   }
}

Record 클래스에는 직렬화를 지원하는 사용자 지정 사전 형식인 하나의 속성만 포함됩니다. Microsoft .NET Framework 2.0의 기본 Dictionary 클래스는 직렬화를 지원하지 않으며 JSON은 기본적으로 모든 반환 데이터를 직렬화합니다. 이 경우 개별 속성 이름을 클래스에 매핑할 필요가 없도록 하려면 Dictionary 형식 클래스가 필요합니다. 대신 myValuePair.Item.Add(someKey, someValue)와 같은 키/값 쌍으로 추가할 수 있습니다.

참고

사용자 지정 사전 클래스에 대해서는 이 문서에서 설명하지 않습니다. 그러나 사용된 클래스는 Paul Welter의 블로그 게시물 XML 직렬화 가능한 일반 사전 (영문)에 설명된 작업에 기반합니다.

웹 메서드는 XML 버전과 동일한 방식으로 데이터를 검색합니다. 다음 코드를 사용하여 메서드의 반환 값을 만듭니다.

Records ret = new Records();
DataTable res = null;

...
// Method is called to retrieve and sort data, and get total number of 
//items.
...

// Set the count of total and returned items.
ret.Count = myInternalWebServiceVariableThatTracksNumItems;
ret.ItemCount = res.Count;

// Enumerate results.
if (res != null)
{
   foreach (DataRowView dr in res)
   {
      // Create a new record.
      Record rec = new Record();

      // Add the ID.
      rec.Item.Add("ID", dr["ID"].ToString());

      // Add each attribute.
      foreach (string oneField in fieldList)
      {
         // Add everything but the ID field.
         if (string.Compare(oneField, "id", true) != 0)
            rec.Item.Add(oneField, dr[oneField].ToString());
      }

      // Add the record to the collection.
      ret.Items.Add(rec);
   }
}
return ret;

이제 메서드에서 클래스를 반환하므로 클래스의 속성을 사용하여 클라이언트 쪽 스크립트에서 전체 데이터를 열거할 수 있습니다. 결과를 열거하기 위해 MSXML DOMDocument를 만들 필요도 없으므로 클라이언트 쪽 스크립트가 더욱 간단해집니다. 세부 사항을 렌더링하는 실제 코드는 다음과 비슷합니다.

// Will hold our output.
var output;
   
// Check for data. Count is a property on the Records class.
if (arg.Count == 0)
   target.innerHTML = "<font color='red'>No data was found.</font>";
else
{
   // Store the total items.
   ti.value = arg.Count;
      
   // Create a table to render the data. Straight
   // HTML goes here.
   ...   
      
   // Cycle through all the data. ItemCount is a 
   // property of the Records class.
   for (var i = 0; i < arg.ItemCount; i++)
   {
      // Store page data for the first and last row.
      if (i == 0) 
         startID.value = arg.Items[i].Item["ID"];
      if (i == (arg.ItemCount - 1)) 
         endID.value = arg.Items[i].Item["ID"];
            
      // Create a new row.
      output += "<tr>";
                  
   // Add each cell.
      output += "<td style='width:75px;'>" + 
         arg.Items[i].Item["FirstName"] + "</td>";
      output += "<td style='width:75px;'>" + 
         arg.Items[i].Item["Company"] + "</td>";
      output += "<td style='width:75px;'>" + 
         arg.Items[i].Item["WorkCity"] + "</td>";
         
      // Close the row tag.
      output += "</tr>";
   }

   // The final HTML goes here to close up the TABLE tags.
   ...
   
   // Plug the output into the document.
   target.innerHTML = output;
}

사용자 인터페이스는 XML을 사용하는 버전과 똑같습니다. 파트에서는 전체 데이터의 앞/뒤 페이징을 모두 지원합니다. 데이터가 처음 로드될 때와 사용자가 다음 또는 이전 페이징 링크를 클릭할 때 “잠시 기다려 주십시오.” 인터페이스가 표시됩니다.

결과 측정

대역폭이 낮거나 대기 시간이 긴 네트워크에서 이와 같은 솔루션은 사용되는 네트워크 리소스에 매우 긍정적인 영향을 미칠 수 있습니다. 동일한 목록에서 동일한 데이터를 내보내는 두 번째 웹 파트가 작성되었습니다. 그러나 모든 데이터는 서버의 웹 파트에 생성된 후 페이지에 HTML로 작성되었습니다. 따라서 이전 또는 다음 링크를 클릭할 때마다 서버에 대한 포스트백이 강제로 실행되고 전체 페이지가 다시 클라이언트로 전송되었습니다. 중요한 점은 ASP.NET AJAX UpdatePanel 컨트롤을 대신 사용하도록 선택했더라도 동일한 프로세스가 발생한다는 것입니다. 페이지의 모든 양식 변수가 서버에 포스트백되고 전체 페이지가 요청에서 다시 전송됩니다. 그러나 UpdatePanel에 포함된 페이지 파트만 업데이트됩니다. 그림 6에서는 이 두 번째 웹 파트에서 다음 링크를 클릭할 때 Fiddler에서 캡처된 요청의 스냅숏을 보여 줍니다.

그림 6. 두 번째 웹 파트에서 다음 링크를 클릭할 때의 요청

웹 파트에 대한 Fiddler 결과

Fiddler 캡처는 목록 데이터의 포스트백 스타일 렌더링을 수행함으로써 요청과 응답에서 네트워크를 통해 총 79,424바이트를 전송했음을 나타냅니다. 다른 방법으로, 그림 7에서는 XML을 사용하여 사용자 지정 웹 서비스를 통해 동일한 데이터를 검색하는 데 ASP.NET AJAX를 사용하는 웹 파트가 사용되었을 때의 Fiddler 캡처를 보여 줍니다.

그림 7. 웹 파트에서 XML을 사용하여 사용자 지정 웹 서비스를 통해 동일한 데이터를 검색한 경우

사용자 지정 웹 서비스에 대한 Fiddler 결과

동일한 목록 데이터가 검색되었지만 네트워크를 통해 1973바이트만 전송되었습니다. 이 차이는 매우 크므로 이 방법을 현명하게 사용하면 네트워크 트래픽을 크게 줄일 수 있습니다. 그러나 그림 8에서와 같이 JSON을 사용하여 Records 클래스를 반환하는 웹 서비스 메서드를 사용하면 가장 작은 페이로드가 생성되었습니다.

그림 8. JSON을 사용하여 Records 클래스를 반환한 경우

JSON에 대한 Fiddler 결과

결론

JSON을 사용하여 하나의 요청에 대해 유선으로 전송되는 총 페이로드를 1817바이트로 줄일 수 있었습니다. 이것은 전체 페이지 포스트백을 수행하여 데이터를 검색하고 페이징하는 파트의 요청 크기에서 98% 줄어든 수치입니다. 또한 데이터를 열거하는 데 사용되는 ECMAScript(JScript, JavaScript)의 크기를 줄일 수 있었으며 그 과정에서 코드도 단순화할 수 있었습니다.

이와 같은 솔루션을 개발하는 것이 좀 더 복잡하기는 하지만 사이트의 대역폭이나 대기 시간에 제한이 있는 경우 이 방법을 사용하면 성능 및 최종 사용자 환경을 향상시키는 데 도움이 될 수 있습니다.

추가 리소스

자세한 내용은 다음 리소스를 참조하십시오.

이 문서의 다운로드

이 항목은 다운로드 가능한 다음 문서에도 포함되어 있어 더 쉽게 읽고 인쇄할 수 있습니다.

사용 가능한 문서의 전체 목록은 다운로드 가능한 Office SharePoint Server 2007 관련 콘텐츠 (영문)를 참조하십시오.