목차
진행률 표시줄이란?
긴 작업 시작
동기 실행을 위한 사용자 인터페이스
상황에 맞는 피드백
ProgressPanel 컨트롤
제한 사항
Atlas 정보
컴퓨터가 시간이 오래 걸리는 작업을 수행할 경우 여러 가지 문제가 발생할 수 있습니다. 사용자의 입장에서는 컴퓨터가 무슨 일을 하고 있으며 얼마나 오래 기다려야 하는지에 대한 아무런 정보가 없을 경우 당황스러울 수 있습니다. 이 경우 사용자는 작업이 완료될 때까지 그대로 기다릴까요? 아니면 결과를 볼 수 있기를 기대하며 해당 작업을 시작할 때 눌렀던 단추를 계속 누를까요?
사용자에게 피드백을 제공하는 것은 중요합니다. 진행률 표시줄은 사용자에게 진행 상황과 기다려야 하는 시간을 알려줄 수 있는 일반적인 방법입니다. 아쉽게도 ASP.NET에는 진행률 표시줄 요소가 포함되어 있지 않지만 HTTP 처리기에서 DHTML에 이르는 다양한 기술을 사용하여 만든 여러 가지 무료 및 상용 구성 요소를 사용할 수 있습니다.
이 칼럼에서는 순수하게 ASP.NET 2.0의 관점에서 진행 중인 작업의 모니터링에 대해 알아봅니다. 브라우저 간에 호환 가능하며 일반적인 상황을 처리할 수 있는 서로 다른 솔루션에 대해 설명하고 추가 프레임워크나 라이브러리는 사용하지 않고 ASP.NET 2.0에서 제공되는 리소스만 사용할 것입니다. 그리고 다음 칼럼에서는 진행률 표시줄을 작성할 때 Microsoft ASP.NET "Atlas"를 사용하는 방법과 몇 가지 기본 제공 기능을 활용하는 방법을 설명합니다.
진행률 표시줄이란?
진행률 표시줄은 선택한 작업의 상태에 대한 정보를 제공하는 그래픽 구성 요소입니다. 대개 완료된 작업을 백분율로 표시하거나 작업을 완료하기까지 남은 시간을 대략적으로 알려 주는 메시지를 표시합니다. 일반적으로 진행률 표시줄은 작업을 나타내는 클래스와 연결되어 사용됩니다. 작업 클래스가 호출자로 이벤트를 보내면 호출자가 진행률 표시줄을 업데이트합니다. 진행률 표시줄 자체는 모니터링하는 작업에 대해 정확히 알지 못합니다.
진행 상황을 백분율로 알려 주는 방법은 작업의 길이를 합리적으로 계산할 수 있는 경우에 적절합니다. 길이를 알 수 없거나 판단할 수 없는 경우 부정형 진행률 표시줄을 사용할 수 있습니다. 진행률 표시줄은 애니메이션이나 연속적인 상황 메시지로 구현되는 경우가 많으며 사용자는 진행률 표시줄의 이러한 변화를 통해 작업이 진행되고 있음을 알 수 있습니다. 이러한 메시지는 사용자에게 보는 즐거움을 제공하거나 주의를 전환하기도 하지만 일반적으로 진행 중인 작업에 대한 실제 정보를 제공하지는 않습니다. 담당자가 모두 통화 중이라고 반복해서 말하는 음성 메시지를 들으면서 무작정 기다려 본 경험이 있으십니까? 이를 유쾌한 경험이라 말할 수는 없을 것입니다. 사용자가 부정형 진행률 표시줄을 대할 때의 느낌도 이와 비슷할 것입니다.
필자는 웹 응용 프로그램에서 백분율 표시줄보다 부정형 진행률 표시줄이 더 일반적이라는 사실을 발견했습니다. ASP.NET에서도 쉽지는 않지만 상황에 맞는 진행률 표시줄을 구현할 수 있으며 절대 불가능한 일은 아니라는 것을 보여드리겠습니다. 우선 일반적인 시나리오와 가능한 솔루션에 대해 살펴보겠습니다.
긴 작업은 일반적으로 다시 게시를 발생시키는 클라이언트 쪽 이벤트와 함께 시작됩니다. 여기부터 어떻게 진행할지를 결정해야 합니다. 사용자가 수동적으로 작업이 완료되기를 기다리도록 하거나 응용 프로그램에서 다른 작업을 계속하도록 할 수 있습니다.
사용자에게 있어 이것은 근본적으로 작업을 동기 실행할 것인지 비동기 실행할 것인지에 대한 문제입니다. 응용 프로그램의 사용자 관점에서 보면 동기 실행은 작업이 완료될 때까지 페이지를 변경하거나 다른 다시 게시 작업을 수행할 수 없음을 의미합니다. 작업의 결과는 사용자 워크플로의 다음 단계가 됩니다. 반면에 비동기 실행의 경우에는 사용자가 요청을 실행한 후 결과가 나올 때까지 잊고 있어도 됩니다. 사용자는 작업이 백그라운드에서 진행되는 동안 사이트에서 다른 페이지를 탐색하거나 다른 작업을 수행할 수 있습니다. 이 시나리오에서도 사용자에게 백그라운드 작업의 진행을 알려 주어야 합니다.
구체적인 솔루션에 대해 알아보기 전에 비슷한 상황에서 비동기 ASP.NET 페이지 사용과 관련된 모든 오해를 바로잡도록 하겠습니다. 비동기 ASP.NET 페이지는 HTTP 런타임에 의해 두 부분으로 실행이 분리되는 페이지입니다. 비동기 페이지에 대해서는 Jeff Prosise가 훌륭한 칼럼을 썼으며 이 칼럼은 http://www.microsoft.com/korea/msdn/msdnmag/issues/05/10/WickedCode/에 있습니다. 요청의 각 부분(사전 렌더링 단계까지를 포함하는 모든 부분과 그 후의 모든 부분)은 서로 다른 시간에 서로 다른 풀링된 스레드에 의해 서비스됩니다. 이것은 확장성을 위한 조치이며 최대한 많은 ASP.NET 풀링 스레드를 유지하기 위한 것입니다. 사용자 관점에서 보면 비동기 페이지는 애니메이션이나 진행률 표시줄이 없다는 점을 제외하면 동기 실행과 다를 바가 없습니다. 사용자는 페이지 요청과 연결된 작업이 진행되는 동안 일반적인 다시 게시처럼 전체 페이지가 새로 고쳐질 때까지 기다려야 하기 때문입니다.
긴 작업을 시작하는 다시 게시 단추가 포함된 간단한 ASP.NET 페이지가 있다고 가정해 보겠습니다. 사용자가 이 단추를 클릭하고 몇 초간 기다리면 작업의 결과가 포함된 업데이트된 페이지가 표시됩니다. 작업 중 사용자에게는 진행 중인 다시 게시 작업의 기존 UI가 표시됩니다. 그동안 단추를 계속 클릭할 수 있는데 추가로 클릭할 때마다 긴 요청이 새로 시작되며 이러한 각 요청은 풀링된 다른 스레드를 사용합니다. 사용자에게 작업 진행에 대한 명확한 피드백이 제공되지 않으므로 점점 답답하게 느껴질 수 있습니다.
여기서 해결해야 할 두 가지 중요한 문제가 있습니다. 우선 반복 클릭을 피하기 위해 단추를 비활성화해야 합니다. 두 번째로 사용자가 최소한 작업이 실제로 수행되고 있음을 알 수 있도록 종류에 관계없이 진행률 표시기를 표시해야 합니다.
약간의 JavaScript 코드를 추가하면 작업을 수행하는 동안과 다시 게시 작업을 하는 동안 모든 중요한 단추와 입력 필드를 비활성화할 수 있습니다. 그림 1에서는 스크립트를 사용하여 활성화한 UI와 비활성화한 UI를 보여 줍니다. 코드에서는 다음과 같이 폼에 onsubmit 이벤트 처리기를 연결해야 합니다.
<form id="form1" runat="server" onsubmit="DisableButtons()">


그림 1 불필요한 다시 클릭 방지
JavaScript 코드는 폼이 서버로 제출될 때 실행됩니다. DisableButtons 메서드에서 false를 반환하는 것이 중요한데 이것은 제출 작업이 아직 완료되지 않았음을 나타냅니다.
<script type="text/javascript">
function DisableButtons()
{
var button1 = document.getElementById("Button1");
button1.disabled = true;
return false;
}
</script>
스크립트 코드에서 간단하게 중요 입력 필드와 컨트롤을 검색하고 비활성화할 수 있습니다. 브라우저 간 지원을 위해 특정 HTML 요소에 대한 DOM(Document Object Model) 참조를 검색하는 데 document.getElementById를 사용해야 합니다. Internet Explorer®에서는 요소의 ID를 개체 이름으로 직접 사용하는 구문을 지원하지만 다른 브라우저는 그렇지 않을 수 있습니다.
기억해야 할 몇 가지 주의 사항이 있습니다. 우선 ASP.NET 2.0에서 HtmlForm 컨트롤은 기본적으로 비활성화된 필드를 게시하지 않습니다. 따라서 HTML 입력 필드를 비활성화하는 경우 해당 서버 쪽 TextBox 컨트롤은 다시 게시를 통해 업데이트되지 않습니다. 상태와 관계없이 모든 입력 필드를 게시하려면 <form> 태그의 SubmitDisabledControls 특성을 true로 설정합니다.
기억해야 할 다른 사항은 제출 단추를 통해 전달하는 모든 요청을 브라우저에서 처리한다는 것입니다. 그러나 브라우저는 일반적으로 요청이 시작될 때 비활성화된 제출 단추를 무시합니다. 따라서 다시 게시가 발생하지만 게시 단추의 이름이 HTTP 요청에 추가되지 않게 됩니다. 결국 ASP.NET은 요청 발신자를 확인할 수 없으며 다시 게시 이벤트가 발생하지 않습니다. 이 문제를 해결하려면 제출 단추의 UseSubmitBehavior 특성을 false로 설정합니다.
<asp:Button runat="server" ID="Button1" UseSubmitBehavior="false"
Text="Start task..." />
물론 제출 단추는 ASP.NET Button 컨트롤에서 내보낸 태그입니다. 그러나 LinkButton 및 ImageButton 컨트롤에서는 같은 문제가 발생하지 않는데 그 이유는 게시 컨트롤의 이름이 JavaScript를 통해 관리되는 숨겨진 필드를 사용하여 전송되기 때문입니다. UseSubmitBehavior 특성이 false로 설정되면 제출 단추는 LinkButton 컨트롤에서 사용하는 것과 같은 기술을 사용하여 게시합니다.
이제는 사용자에게 피드백을 제공하는 문제를 해결할 차례입니다. 일반적으로 사용되는 기술은 사용자를 중간 피드백 페이지로 리디렉션하는 것입니다. 이 리디렉션은 보통 스크립트에서 window.location DOM 개체의 href 속성을 설정하여 수행할 수 있습니다. 피드백 페이지는 일반적으로 간단한 GIF 이미지나 "잠시 기다려 주십시오..."라는 메시지를 표시합니다. 그런 다음 이 피드백 페이지는 사용자를 작업 페이지로 리디렉션합니다. HTML onload 이벤트의 간단한 스크립트가 이 두 번째 리디렉션을 제어합니다. 결과적으로 피드백 페이지는 작업 페이지가 완전히 로드될 때까지 표시되며 작업 페이지는 긴 작업이 완료된 후에 완전히 로드됩니다.
이 솔루션은 유용하기는 하지만 두 번의 페이지 리디렉션이 필요하며 작업을 세 개의 페이지 즉, 호출자 페이지, 피드백 페이지 및 작업 페이지로 분산해야 합니다. 올바르게 설계하면 같은 응용 프로그램 내에서 그리고 다른 응용 프로그램 간에 피드백 페이지를 다시 사용할 수 있지만 작업 페이지는 그렇지 않습니다. 끝으로 이 솔루션은 원래 기존 ASP를 위해 고안되었으며 ASP.NET에서 사용할 수 있는 최상의 옵션은 아닙니다.
오늘날 대부분의 브라우저는 표시된 페이지를 동적으로 변경할 수 있는 W3C DOM을 지원하며 약간의 스크립트 코드를 사용하여 이를 향상시킬 수 있습니다. ASP.NET 페이지에서 <div> 태그를 정의하고 필요한 서식을 지정합니다.
<div id="ProgressBar"
style="display:none; font-weight: bold;
font-size: 12pt; color: navy; font-family: Verdana;
background-color: #ffff99;">
<img alt="" src="images/indicator.gif" />
<span id="Msg">Please, wait ... </span>
</div>
<div> 태그는 초기에는 숨겨지며 onsubmit 이벤트 처리기에서 스크립트 몇 줄을 사용하여 표시되도록 설정할 수 있습니다. 여기에 앞서 설명한 DisableButtons 함수를 확장하여 정적 및 부정형 진행률 표시줄을 표시하는 방법이 있습니다.
function DisableButtons()
{
var button1 = document.getElementById("Button1");
button1.disabled = true;
var progress = document.getElementById("ProgressBar");
progress.style.display = "";
return false;
}
<div> 부분은 작업이 완료될 때까지 표시됩니다(그림 2 참조). 페이지가 다시 게시되면 <div>가 자동으로 다시 렌더링되어 숨겨진 초기 상태로 돌아갑니다.

그림 2 사용자를 위한 피드백 표시
애니메이션 GIF를 사용하는 경우 GIF에 애니메이션이 표시되지 않아 당황할 수 있습니다. 이것은 브라우저가 이미 해당 페이지를 떠나서 애니메이션 GIF의 프레임을 반복하는 데 시간을 소비하지 않기 때문입니다. GIF 애니메이션을 원하는 경우 대역 외 호출(ASP.NET 스크립트 콜백, XMLHttpRequest 직접 사용 또는 AJAX 지원 라이브러리)을 통해 원격 작업을 시작해야 합니다. 대역 외 호출을 사용하는 경우에는 애니메이션 GIF를 표시하는 것 이외에도 많은 작업을 할 수 있습니다. 실제로 대역 외 호출을 사용하면 실제 상황에 맞는 피드백은 선택 가능한 여러 옵션 중 하나가 됩니다.
상황에 맞는 피드백작업이 진행되는 동안 상황에 맞는 실제 피드백 메시지 또는 정확한 백분율 값 정보를 표시하는 것이 가장 이상적입니다. 문제는 작업이 서버에서 실행된다는 것이며 서버 쪽 환경에서는 브라우저에 표시된 페이지에 정보를 전달할 방법이 없다는 것입니다. 서버가 클라이언트에 대한 연결을 열어 두고 있으면 완료되지 않은 페이지로 렌더링할 수 있지만 이 방법은 사용하지 않는 것이 좋습니다. 기본적으로 기존 다시 게시가 수행되는 동안 클라이언트 페이지는 실질적으로 중단되며 다른 클라이언트 작업을 수행할 수 없습니다. 따라서 기존 다시 게시(ASP.NET Button 컨트롤을 사용하여 트리거)를 통해 긴 작업을 시작하면 서버 작업이 실행되는 동안에는 페이지에 새로운 데이터를 검색 및 표시할 수 없습니다.
다행히도 ASP.NET은 현재 페이지를 떠나지 않고도 간단한 왕복을 수행하는 AJAX 스타일의 대역 외 호출을 기본적으로 지원합니다. 이를 위해 우선 Button 컨트롤을 클라이언트 쪽 HTML 입력 단추로 바꾸어야 합니다. 클라이언트 쪽 단추를 클릭하면 같은 ASP.NET 페이지에 대한 비동기 대역 외 호출이 시작됩니다. 그런 다음 JavaScript 코드 조각이 해당 호출의 반환 값을 사용하여 DHTML을 통해 해당 페이지를 업데이트합니다. 이 방법은 긴 작업을 실행하기 위한 대체 방법일 뿐입니다. 대역 외 호출(브라우저가 아닌 페이지에서 요구 및 제어하는 HTTP 요청)을 사용하는 옵션을 선택하면 여러 가지 새로운 방법으로 작업을 수행할 수 있습니다.
백엔드 작업으로 자체 상태의 설명이나 완료한 작업의 백분율을 저장하도록 할 수 있습니다. 이 정보는 공개적으로 액세스할 수 있는 서버 쪽 저장소에 저장되며 다른 대역 외 호출을 사용하는 클라이언트에서도 액세스할 수 있습니다. 다른 대역 외 호출에서 데이터 조각을 고유하게 식별하는 방법이 있다면 이 정보는 실질적으로 어디에서나 사용할 수 있습니다. 저장할 수 있는 데이터에는 데이터베이스 테이블, 디스크 파일, 세션 상태(몇 가지 추가 처리가 필요할 수 있음) 및 ASP.NET Cache 개체가 포함됩니다. 여기서는 성능이 뛰어나고 구현이 단순한 Cache 개체를 사용하겠습니다. ASP.NET 공급자 모델을 적용하도록 메커니즘을 리팩터링할 수 있습니다.
모니터링 가능한 작업은 기본 클래스인 Task에서 상속된 클래스로 나타냅니다. 그림 3은 기본 클래스인 Task와 LengthyTask 클래스 샘플 구현을 보여 줍니다. Task 클래스에는 두 가지 오버로드가 있는 추상 메서드 Run과 작업의 결과를 전달하는 Results라는 속성이 있습니다. 각 작업은 고유 ID를 사용하여 구분합니다. 고유 ID는 저장소 매체에서 작업 상태를 읽고 쓰기 위한 키입니다.
샘플 LengthyTask 클래스를 자세히 살펴보겠습니다. 이 클래스는 완료하는 데 몇 초 정도 걸리는 작업을 나타냅니다. 이 클래스에 실제 작업은 없지만 Run 메서드는 지정된 시간(초) 동안 대기하고 반환합니다. 대기 시간은 단계별로 구분되어 세그먼트로 나누어지며 클래스의 Run 메서드는 초 단위로 작업 상태를 업데이트합니다. Run 메서드는 이를 위해 TaskHelpers 클래스의 도우미 메서드를 호출합니다. UpdateStatus 메서드는 작업 ID를 항목으로 사용하여 ASP.NET Cache에 새 항목을 만듭니다.
Shared Sub UpdateStatus(ByVal taskID As String, ByVal info As String) Dim context As HttpContext = HttpContext.Current context.Cache(taskID) = info End Sub
작업의 현재 상태를 나타내는 메시지가 값으로 기록됩니다.
ASP.NET Cache 개체는 모든 세션에 걸쳐 모든 요청이 액세스할 수 있는 전역 개체입니다. 모든 후속 요청은 작업이 진행되는 동안 Cache에서 작업 상태를 읽을 수 있습니다.
이제 ASP.NET 페이지에서 작업을 트리거하는 방법을 살펴보겠습니다. 클라이언트 단추를 클릭하여 백그라운드 작업을 시작하면 이 단추는 ASP.NET 스크립트 콜백 API를 사용하여 대역 외 호출을 트리거합니다. 클라이언트 단추는 호스트 페이지의 Page_Load 이벤트에 있는 약간의 스크립트 코드에 연결됩니다.
Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) _
Handles Me.Load
If Not IsPostBack Then
Dim script As String = ScriptHelpers.GetStarterScript( _
Me, "StartTask", "Button1")
ClientScript.RegisterClientScriptBlock( _
Me.GetType(), "StartTask", script, True)
Button1.Attributes("onclick") = "javascript:StartTask()"
End If
End Sub
여기서 스크립트 함수의 이름은 StartTask이며 또 다른 도우미 함수가 해당 페이지에서 코드를 내보냅니다. 그림 4에서 ScriptHelpers.GetStarterScript 함수를 볼 수 있습니다.
이 함수는 세 개의 매개 변수 즉, 페이지 개체, 만들 JavaScript 함수의 이름, 비활성화할 단추 이름을 받습니다. 코드에서 첫 번째 단계는 트리거 단추에 대한 참조를 검색하고 이를 비활성화하는 것입니다. 그림 4에서는 단추를 한 개만 처리했지만 여러 단추와 입력 필드를 쉽게 추가할 수 있습니다. 이 경우 ctlName 매개 변수를 문자열 배열로 변환하고 루프에서 컨트롤 이름을 처리하면 됩니다.
각 작업은 고유 ID를 사용하여 식별되며 앞에서 설명했듯이 작업 상태를 저장하기 위한 고유한 키를 제공하는 데 필요합니다. 각 작업 항목은 페이지, 사용자, 세션마다 고유하게 식별되어야 합니다. 페이지 URL 하나만으로는 고유성을 충분히 보장할 수 없습니다. 서로 다른 사용자가 같은 페이지를 실행하면서 같은 단추를 클릭하는 경우를 생각해 보십시오.
끝으로 작업을 인스턴스화할 때마다 GUID를 생성해야 합니다. 서버에 요청을 게시하지 않거나 웹 서비스를 호출하지 않는 경우 클라이언트에서 GUID를 만들기는 쉽지 않습니다. ASP.NET 스크립트 콜백 메커니즘에서는 강제로 페이지에서 단일 끝점을 정의하도록 합니다. 한 페이지에 여러 원격 작업을 정의하려면 단일 끝점에서 하위 분기를 만들어야 합니다. 결론적으로 첫 번째 호출에서 GUID를 가져오고 두 번째 호출로 작업을 시작하는 방법은 불가능한 것은 아니지만 ASP.NET 스크립트 콜백에서 문제가 될 수 있습니다.
그러나 JavaScript에는 random이라는 메서드를 가진 Math 개체가 있습니다. 이 메서드는 0과 1 사이에 있는 임의의 부동 소수점 수를 생성합니다. 다음 래퍼 코드를 사용하면 소수를 여기서 필요로 하는 "고유성"을 충분히 제공할 수 있는 범위의 정수로 변환할 수 있습니다.
function GetGuid() {
var ranNum = Math.floor(Math.random()*100000000);
return ranNum;
}
작업 ID는 전역 JavaScript 변수에 저장되며 진행률 표시줄에서 사용할 수 있습니다. StartTask JavaScript 메서드는 진행률 표시줄을 시작한 후 작업을 실행하는 대역 외 호출을 트리거합니다. 최종적인 StartTask 메서드는 그림 5의 코드와 비슷합니다.
그림 4에서 보여 주는 것처럼 WebForm_DoCallback은 GetCallbackEventReference 메서드에서 내보내며 내부적으로 XmlHttpRequest 개체에 대한 호출을 실행하여 현재 페이지의 동일한 URL에 대한 수정된 HTTP 요청을 준비합니다. 수정된 HTTP 요청은 ASP.NET 페이지 처리기에게 기존 다시 게시 메커니즘을 사용하는 대신 ICallbackEventHandler 인터페이스의 멤버를 호출하도록 지시합니다. 대역 외 호출을 트리거하는 ASP.NET 페이지는 요구에 따라 실행할 코드를 정의하기 위해 ICallbackEventHandler 인터페이스를 구현해야 합니다. 이 경우 인터페이스 멤버는 단지 백그라운드 작업을 시작합니다.
WebForm_DoCallback에 대한 호출은 그림 6에서 보여 주는 것처럼 호스트 ASP.NET 페이지에서 결국 ICallbackEventHandler의 두 메서드를 실행하는 HTTP 요청을 시작합니다. 작업 ID는 인수로 콜백에 전달됩니다. 여기에서는 원격 메서드에 대해 인수를 사용하지 않았습니다. ASP.NET 스크립트 콜백은 문자열 수신 및 반환만 허용합니다. 여러 인수가 필요하면 사용자 지정 논리를 사용하여 문자열로 serialize해야 합니다. 작업에 인수를 전달하려면 작업 ID와 입력 인수를 연결한 문자열을 준비하는 JavaScript 코드를 추가해야 합니다. 문자열을 deserialize하여 정보를 추출하려면 RaiseCallbackEvent 관리되는 메서드에서도 같은 논리를 구현해야 합니다. Atlas 및 AJAX.NET과 달리, ASP.NET 스크립트 콜백은 자동 JavaScript/.NET 데이터 serialization을 지원하지 않습니다.
요약하면 클라이언트 단추를 클릭하면 현재 페이지를 떠나지 않고 서버에서 작업이 시작됩니다. 입력 단추가 비활성화되고 진행률 표시줄이 켜집니다. 작업이 완료되면 ASP.NET 페이지에서 인라인으로 내보낸 JavaScript 코드의 다른 조각이 결과를 수신하고 페이지를 업데이트합니다.
결과 HTML 요소는 작업의 반환 값이 표시되는 <span> 태그입니다. UpdatePage 메서드는 ASP.NET 스크립트 콜백 프로그래밍 모델에 따라 정의된 클라이언트 콜백 함수입니다.
function UpdatePage(response, context)
{
// 진행률 표시줄 중지
StopProgress();
// 작업 결과에 따라 페이지 업데이트
var label = document.getElementById("Results");
label.innerHTML = response;
// 사용하지 않도록 설정된 컨트롤을 다시 사용하도록 설정
var button1 = document.getElementById("Button1");
}
ASP.NET 인프라에서는 UpdatePage의 응답 매개 변수(WebForm_DoCallback에 대한 매개 변수 중 하나)가 코드 숨김 클래스에서 GetCallbackResult 메서드의 반환 값으로 설정됩니다. ProgressPanel 컨트롤
대역 외 작업 실행은 상황에 맞는 진행률 표시줄을 만드는 데 매우 중요합니다. 페이지가 유지되면 내부의 다른 컨트롤이 주기적으로 다른 대역 외 호출을 수행하여 Cache 또는 다른 저장소에서 작업 상태를 읽고 사용자에게 보고할 수 있습니다. ProgressPanel 사용자 지정 ASP.NET 컨트롤은 복합 컨트롤로서 가장 간단한 형태는 Label 컨트롤을 출력하는 것입니다. 이를 통해 손쉽게 사용자 인터페이스를 향상시킬 수 있습니다.
또한 ProgressPanel 컨트롤은 지정한 속도로 대역 외 호출을 보내기 위한 약간의 스크립트 코드를 내보냅니다. 이 방법으로 같은 세션에서 같은 페이지를 대상으로 하는 두 개의 동시 호출 집합을 사용할 수 있습니다. 한 호출이 작업을 시작하고 ASP.NET Cache의 항목을 현재 상태로 업데이트합니다. 두 번째 호출 집합은 작업이 Cache에 기록한 내용을 읽고 페이지를 업데이트합니다. 호스트 페이지에 다음 항목을 넣으면 됩니다.
<x:ProgressPanel runat="server" ID="ProgressPanel1" / >
ProgressPanel은 앞서 설명한 ShowProgress 및 StopProgress 함수를 포함하여 필요한 모든 스크립트를 자율적으로 내보냅니다.
그림 7은 솔루션의 전체 아키텍처를 그림으로 보여 줍니다. ProgressPanel 컨트롤은 ICallbackEventHandler를 구현하고 ASP.NET 스크립트 콜백 API를 사용하여 요청을 자신에게 보냅니다. 각 요청은 작업 ID를 수신하고 Cache(또는 기타 다른 저장소)에서 해당 항목을 읽습니다. 읽은 값은 클라이언트로 반환되어 레이블을 상황에 맞는 메시지로 업데이트하는 데 사용됩니다. ProgressPanel 컨트롤은 작업에 필요한 모든 스크립트 코드를 자동으로 내보냅니다. 그림 8은 해당 컨트롤의 전체 소스 코드를 보여 줍니다.

그림 7 ProgressPanel 사용
대역 외 호출은 클라이언트 스크립트 타이머에 의해 트리거됩니다. 해당 컨트롤의 RefreshRate 속성을 통해 지정된 속도로 원격 호출이 실행됩니다. 내부적으로 ProgressPanel 컨트롤은 TaskHelpers 클래스를 사용하여 지정된 작업의 상태를 읽습니다. 이때 문자열이 클라이언트로 반환되어 콜백으로 전달되며, JavaScript 콜백이 자동으로 내보내집니다. 여기에서 간단하게 ProgressPanel의 클라이언트 태그를 나타내는 <span> 태그의 innerHTML 속성이 설정됩니다. 제한 사항
이 ProgressPanel 솔루션에도 문제는 있습니다. 여러 세션에서도 훌륭하게 작동하고 브라우저 간에 호환되지만 암시적으로 페이지당 한 개의 ProgressPanel 컨트롤만 사용하는 것으로 가정하고 있습니다. 페이지에 여러 개의 ProgressPanel 컨트롤을 포함할 수 있지만 첫 번째 컨트롤만 올바르게 처리됩니다. 이것은 ShowProgress 스크립트에서는 사용할 진행률 컨트롤을 지정할 수 없기 때문입니다. 다시 말해 이 솔루션은 트리거 단추와 진행률 표시줄 간의 직접 연결을 지원하지 않습니다.
ASP.NET Cache를 사용할 때 기억해야 할 다른 문제는 메모리가 부족하고 Cache에도 공간이 부족한 경우 ASP.NET이 Cache에서 데이터를 배출할 수 있다는 것입니다. 따라서 연결된 작업 항목이 손실될 경우 사용자가 작업 상태를 확인하는 데 영향을 받을 수 있습니다. 반대로 작업 상태를 유지 관리하는 데 좀 더 영구적인 방식을 사용하는 경우에는 작업 후에 특히, 어떤 이유에서든 작업이 취소된 후 이를 정리하는 방법에 대해 생각해 보아야 합니다.
ASP.NET 스크립트 콜백 API가 최상의 도구는 아닙니다. Microsoft ASP.NET "Atlas" 환경에서는 이보다 훨씬 다양한 기능을 제공합니다. 다음 달에는 진행률 표시줄과 관련된 Atlas의 기능에 대해 알아볼 예정입니다.
Dino에게 질문이나 의견이 있으면 메일을 보내시기 바랍니다. cutting@microsoft.com.
