Silverlight를 설치하려면 여기를 클릭합니다.*
Korea 대한민국변경|Microsoft 전체 사이트
MSDN
|개발자 센터
MSDN Home   MSDN Home

Cutting Edge
윈도우 워크플로우 파운데이션, 파트 2


이 문서에 사용된 코드 다운로드: CuttingEdge0604.exe (153KB)




Dino Esposito (영문)

목차
ASP.NET 과 워크플로우
ASP.NET에서 워크플로우 사용하기
워크플로우의 실행 제어
워크플로우를 위한 ASP.NET 응용 프로그램의 설정
ASP.NET에서의 워크플로우 컴포넌트의 호출
워크플로우를 웹서비스처럼 노출하기
결론


저의 지난 달 칼럼에서, 저는 윈도우 폼 클라이언트 응용프로그램에 중점을 두고 만든 헬프데스크 워크플로우 샘플을 제시하였습니다. 이번 달에는 ASP.NET의 워크플로우 응용 프로그램과 웹서비스로의 워크플로우의 노출할 수 있는 기능 그리고 웹서비스에서의 워크플로우의 호출에 대해서 논의해 보기로 하겠습니다.


페이지 맨 위로페이지 맨 위로


ASP.NET 과 워크플로우

윈도우 워크플로우 파운데이션에 기초한 워크플로우는 동작하기 위해서는 특별한 런타임 환경을 필수적으로 요구하는 컴포넌트입니다. 워크플로우 런타임 환경은 WorkflowRuntime 클래스로 대표될 수 있습니다. 워크플로우 라이브러리를 사용하기 위해서는, 개발자는 특별한 워크플로우 형식에서 동작하기 위해서 WorkflowRuntime 클래스의 인스턴스를 생성하고 설정합니다. 효율 상의 이유로, 개발자는 일반적으로 런타임 환경을 응용프로그램의 실행 시에 단 한 번만 생성해서, 이것으로 하여금 모든 들어오는 요청을 처리하게 할 것 입니다. 윈도우 폼 응용프로그램에서는, 여러분은 폼의 생성자에서 워크플로우의 런타임을 초기화하고, 응용프로그램이 종료 시에 이 것을 제거할 것 입니다. 만약에 여러분이 ASP.NET을 이용하고 있다면, 이러한 일을 어떻게 처리해야 할까요?

일단 여러분이 워크플로우 컴포넌트가 이미 올라와서 동작하고 있다면, 이 컴포넌트를 웹 혹은 일반 윈도우 폼 응용프로그램에서 호출하는 일은 아무런 문제가 되지 않을 것 입니다. 윈도우 워크플로우 파운데이션에 기초한 워크플로우에 대해서, ASP.NET 개발자는 웹 응용프로그램의 속성에 관련된 몇 가지 작은 문제만 가지게 될 것 입니다.

윈도우 폼에서와 같이, ASP.NET에서도 여러분은 각각의 응용 프로그램이 시작될 때 단 하나 만의 워크플로우 런타임의 인스턴스가 생성되는 것을 원하실 것 입니다. 윈도우 폼 응용프로그램과 다르게, 웹 프로그램이 요청을 받아서 처리하는 것으로 동작하지만, 각각의 요청들은 각각 다르게 처리가 되고 이러한 각각의 요청들 간에서는 아무런 관련이 없습니다. 이런 사실이 워크플로우의 영속성(persistence)과 쓰레드에 관련된 문제를 발생시키게 됩니다.

여기에 영속성에 관련된 문제가 있습니다: 개발자가 작업하고 있는 워크플로우 형식의 인스턴스는 연속적인 요청에 대해서 반드시 영속되어야 합니다. 개발자가 요청을 처리하고, 워크플로우의 인스턴스를 생성하고, 그리고 요청에 대한 결과를 받아서 사용자를 위한 응답을 생성합니다. 여러 개의 요청을 받았을 때, 개발자는 과거의 상태에서 워크플로우를 재개(resume)헤서 계속 진행시킬 필요가 있게 될 것 입니다. 일반 윈도우 응용프로그램에서는, 연속적인 사용자의 응답이 동일한 동작 문맥(operational context)에서 일어납니다; ASP.NET에서는, 두 번의 사용자의 응답은 두 번에 걸친 분산된 그리고 어떠한 상태도 자동적으로 유지되지 않는 별개의 요청에 의해서 발생합니다. ASP.NET 응용프로그램의 문맥(context)에서 워크플로우가 올바르게 동작하기 위해서는, 개발자는 요청들 간에 상태를 영속할 수 있는 방법이 필요합니다.

여기에 쓰레드 문제도 있습니다: 기본적으로, 워크플로우 런타임 함수들은 비동기적으로 동작합니다. 런타임이 주어진 워크플로우 형식의 인스턴스를 새로 생성하기 위해서 특정한 명령을 받을 때, Start 메써드는 워크플로우를 초기화 하기 위해서 새로운 쓰레드를 할당합니다. WorkflowCompleted 이벤트는 호출자로 하여금 워크플로우가 완료되어 사용자의 유저 인터페이스를 갱신할 수 있는지의 여부를 파악할 수 있는 기회를 둡니다. 여러분도 금방 파악할 수 있는 것과 같이, 이러한 방식은 ASP.NET에서 구조적인 문제를 발생시킵니다. postback 이벤트에서 Start 메써드를 호출하는 특정 웹 페이지를 상상해 보시기 바랍니다. 이 페이지는 WorkflowRuntime 클래스의 Start 메써드의 호출이 완료되자마자 계속 진행이 됩니다. 그러나 메써드의 동작이 비동기적이기 때문에, Start 메써드는 워크플로우를 처리하기 위한 새로운 쓰레드를 시작시키고, ASP.NET으로 즉시 돌아옵니다. 그렇게 되면 이 페이지는 가장 간단한 워크플로우가 완료되기도 전에 웹 페이지의 실행이 완료되는 지점까지 도달해 버리게 됩니다. 그 결과로, 워크플로우 프로세스의 결과는 최종 사용자에게 보여지는 페이지에 결과를 반영할 수 없게 됩니다. 워크플로우와의 상호작용이 완료되었을 때만 페이지를 표시하기 위해서는, 개발자는 페이지의 렌더링과 워크플로우의 실행을 같이 동기화 해야 할 필요가 있습니다.

이러한 영속성과 쓰레드의 동기화 문제 모두 워크플로우 서비스를 통하여 해결될 수 있습니다. ASP.NET 응용프로그램에서 워크플로우를 가지고 있을 때, 개발자는 응답에 대한 동기적인 디스패치(dispatch) 모델과 워크플로우가 휴지(idle)상태일 때 자동 데이터 직렬화(Serialization)를 지원하는 좀 더 무거운(richer) 런타임 환경을 사용해야 합니다.


페이지 맨 위로페이지 맨 위로

ASP.NET에서 워크플로우의 사용

워크플로우를 사용하는 ASP.NET 페이지를 설정하기 위해서 필요한 모든 과정을 한 번 살펴보기로 하겠습니다. 저는 여기에서 개발자로 하여금 .NET에 기반을 둔 코드를 워크플로우에 추가할 수 있도록 해주는 Code Activity라는 워크플로우 아이템을 포함하는 간단한 워크플로우를 만들어 보는 것에서부터 시작하도록 하겠습니다. 이 예제는 인증 서비스를 구현해서 사용자가 제공한 인증 정보를 확인해 주는 워크플로우입니다.

이러한 일에 워크플로우를 사용하는 거의 대부분의 경우는 닭 잡는데 소 잡는 칼을 쓰는 격이지만, 저는 인증 과정이 상당히 복잡해서, 워크플로우를 이런 일에 쓰는 것을 정당화 할 수 있는 몇몇 프로젝트를 경험해 보기도 하였습니다. 불행하게도 그 당시는 윈도우 워크플로우 파운데이션 자체가 존재하지 않았던 시기였습니다. 아무튼, 중요한 것은 여러분이 그림 1에서 보이는 것과 같은 워크플로우를 보고 있다는 점 입니다. 워크플로우는 이것을 호출했던 대상으로부터 몇 개의 파라미터를 받는 것에서부터 동작해서-대부분의 경우는 이름과 패스워드입니다-사용자가 인증되었는지의 여부를 알려주는 불린(Boolean) 값을 반환합니다. 그림 2 는 워크플로우에 정의된 파라미터의 목록을 보여줍니다.

그림 1 샘플 인증 워크플로우
그림 1 샘플 인증 워크플로우

개발자는 워크플로우 클래스로부터 공개 속성을 노출해서 워크플로우에 파라미터를 추가합니다. 그림 2에서 인증을 하기 위해서 사용자 이름과 패스워드를 가져오기 위해서 저는 두 개의 입력 파라미터를 정의하였습니다. 그리고 저는 인증 과정의 결과를 돌려 주기 위한 불린(Boolean)형의 아웃풋 파라미터를 역시 정의하였습니다. 코드 상으로는, 개발자는 각각의 엔트리(entry)가 워크플로우 클래스의 공개 속성의 이름과 일치하는 name/value 사전을 통하여 입력 파라미터를 워크플로우에 전달해 줍니다. 개발자는 워크플로우의 인스턴스를 생성하고 실행하는 CreateWorkflow 메써드에 이 사전을 넘겨 줍니다.

Dictionary<string, object> parameters = new
   Dictionary<string, object>();
parameters.Add("UserName", UserName.Text); 
parameters.Add("Password", Password.Text);

개발자는 워크플로우의 결과 값을 WorkflowCompleted 이벤트의 핸들러에 제공되는 WorkflowCompletedEventArgs의 인스턴스에 있는 OutputParameters 콜렉션을 통하여 제공 받습니다. 좀 더 정확하게는, OutputParameters 콜렉션은 워크플로우 클래스의 모든 공개 속성의 값을 가지고 있고, 읽기/쓰기 혹은 읽기 전용 속성 간을 구별하지 않습니다. 그림 3은 LoginWorkflow의 코드비하인드(codebehind) 파일을 보여줍니다. CheckCredentials 할동과 연관된 이벤트 핸들러는 제공된 정보를 인증하는 일을 수행하는 내부 함수를 호출합니다. 이 결과는 isAuthenticated 멤버 변수에 저장이 됩니다. 여러분이 그림 3에서 볼 수 있는 것과 같이, 이 값은 IsAuthenticated 결과 파라미터에 대한 데이터 저장 장소 역할을 합니다.

Login 워크 플로우에 대해서는 너무 많은 것을 본 것 같습니다. 이제 ASP.NET 응용프로그램과 이 프로그램의 페이지들, 그리고 구성 파일들을 한 번 살펴 보기로 하겠습니다. 워크플로우를 사용하는 ASP.NET 페이지의 구성을 설명하자면 사용자 이름과 패스워드를 저장하는 에디트 박스, 로그인 버튼, 그리고 사용자를 환영하는 메시지로 구성된 비교적 간단한 페이지로 말씀드릴 수 있습니다. 사용자가 로그인 버튼을 클릭 시에, 개발자는 워크플로우 런타임을 가져오거나 혹은 생성해야 할 필요가 있고 여러분이 사용하는 워크플로우 자료형의 새로운 인스턴스를 생성해야 합니다.


페이지 맨 위로페이지 맨 위로


워크플로우의 실행 관리 (Managing the Execution of the Workflow)

지난 달에, 저는 워크플로우 런타임 객체를 아래와 같이 new 오퍼레이터와 WorkflowRuntime 클래스의 생성자를 사용해서 생성했었습니다:

WorkflowRuntime wr = new WorkflowRuntime();
위의 코드는 개발자가 Application_Start에 넣어 두기만 한다면 ASP.NET에서도 위의 코드를 그대로 사용할 수 있습니다. 그리고 나서 여러분의 페이지 코드 안에서 생성된 워크플로우 런타임에 접근할 수 있는 방법을 찾아야 합니다. 그러나, 각각의 요청에 대해서 워크플로우 런타임을 인스턴스화 하는 것은 예외를 발생시키게 합니다. 왜냐하면, 워크플로우는 동일한 AppDomain 안에서 하나 이상이 올라올 수 있지만, 첫 번째 요청만이 제대로 동작하기 때문입니다. 두 개의 ASP.NET 요청은 구별된 상태와 문맥(context)를 가지고 있지만, 동일한 AppDomain 안에서 동작합니다. 그 결과로서, 첫 번째 요청은 동작하는 반면에 두 번째 요청은 예외를 불러 일으키게 됩니다.

윈도우 워크플로우 파운데이션은 AppDomain 당 정확히 하나 만의 워크플로우 객체가 있게 하는 내부 구조를 제공합니다. 그래서 개발자는 이 객체의 생성과 인스턴스화에 대해서 걱정할 필요가 없습니다. ASP.NET에서는, 사실상 올바른 워크플로우의 인스턴스에 접근하기 위해서 개발자는 WorkflowWebRequestContext를 사용할 수 있습니다. 이 코드는 워크플로우 런타임에 대한 참조를 어떤 방식으로 얻을 수 있는 지를 보여 줍니다:

   WorkflowRuntime wr = 
      WorkflowWebRequestContext.Current
         .WorkflowRuntime;

내부적으로, 현재 속성에 대한 get 접근자(Accessor)는 워크플로우 런타임의 인스턴스를 저장하는 정적 클래스의 공용 멤버를 호출해서, 만약 인스턴스가 존재하지 않으면 새로운 인스턴스를 생성하도록 합니다. 일단, 런타임 객체에 대한 참조를 얻었다면, 개발자는 WorkflowCompleted 이벤트에 대한 핸들러를 등록하고 실제의 워크플로우를 등록할 수 있습니다. 아래의 코드가 어떤 방식으로 하는 지를 보여주고 있습니다:

wr.WorkflowCompleted += 
   new EventHandler<WorkflowCompletedEventArgs>(LoginCompleted);
Type t = typeof(Samples.LoginWorkflow);
WorkflowInstance instance = wr.CreateWorkflow(t, parameters);
instance.Start();

워크플로우가 반환 될 때, LoginCompleted 코드가 실행되어 ( 그림 4를 참조해 보시기 바랍니다.) 그리고 OutputPatameters 콜렉션에서 IsAuthenticated 항목이 있는 지를 검사합니다. OutputPatameters는 WorkflowCompletedEventArgs 클래스를 통해서 얻을 수 있는 이벤트 데이터 인자 중의 하나 입니다. OutputParameters의 내용은 워크플로우가 끝날 때 런타임에 의해서 결정됩니다.

그림 4에 있는 코드는 워크플로우가 정상적으로 동작하고 ASP.NET 페이지가 워크플로우 결과를 받아서 이것을 사용자 페이지에 반영하도록 하는 데에는 충분하지 않습니다. 제가 이미 한 번 말씀 드린 대로, 개발자는 런타임에 몇 가지의 서비스-상태의 영속성, 그리고 동기 요청 디스패칭(synchronous request dispatching)-들을 추가할 필요가 있습니다. 첫 번째로 제 마음에 떠오르는 생각은 일단 개발자가 워크플로우 런타임 인스턴스를 얻어 왔을 때 이러한 서비스들과 인스턴스를 결합 시키는 것입니다. 아래에 예제 코드가 있습니다 (wr이 의미하는 것이 워크플로우 런타임 인스턴스를 의미합니다):

String connString = "...";
SqlWorkflowPersistenceService servState =
    new SqlWorkflowPersistenceService(connString);
wr.AddService(servState);
여러분이 이 코드를 실행했을 때, 여러분은 일단 런타임이 시작했으면 더 이상 추가적인 구성은 불가능하다라고 이야기 하는 예외를 보시게 될 것 입니다. 그러나 페이지에 런타임을 시작시키는 코드도 없었는데, 실제로 누가 그 런타임을 시작시켰을까요?

비밀은 제가 위에서 언급한 WorkflowWebRequstContext 클래스입니다. 이 클래스는 워크플로우의 런타임의 인스턴스를 ASP.NET 응용 프로그램 안에서 생성합니다. 그리고 web.config 파일에서 WorkflowRuntime 섹션을 찾아서 런타임이 결합해야 하는 서비스들의 목록을 읽어 오려고 시도합니다. 이제 무슨 일이 일어났는지 짐작하시겠습니까? WorkflowWebRequestContext.Current를 통한 워크플로우 런타임에 대한 참조는 이미 다른 서비스들과 결합되어 시작된 인스턴스에 대응됩니다. ASP.NET 응용프로그램 내에서 런타임에 결합되는 서비스들을 제어하기 위해서는 여러분은 그림5와 같이 web.config 파일을 수정할 필요가 있습니다.


페이지 맨 위로페이지 맨 위로


워크플로우를 위한 ASP.NET 응용프로그램의 설정하기

윈도우 워크플로우 파운데이션은 설정 파일에 내부적인 용도로 정보를 저장하기 위해서 <workflowRuntime> 섹션을 사용합니다. 이 섹션은 설정 스키마(schema)의 기본 부분이 아니기 때문에, 여러분은 명시적으로 이 부분을 등록해 줘야 합니다.

<configuration>
  <configSections>
     <section name="WorkflowRuntime" 
              type="WorkflowRuntimeSection, System.Workflow.Runtime" />
  </configSections>
  ...
</configuration>

IASP.NET 2.0에서, 여러분이 새로운 설정 섹션을 등록 시에, 여러분은 이름과 ConfigurationSection을 상속 받는 클래스의 어셈블리 또한 명시해 주어야 합니다. <WorkflowRuntime> 섹션에서는, 이 클래스는 WorkflowRuntimeSection입니다. 이 클래스는 System.Workflow.Configuration 네임스페이스의 부분으로 System.Workflow.Runtime 어셈블리 안에 정의되어 있습니다. ASP.NET 2.0에서 주의해야 할 점은, 사용자 설정 섹션들은 ASP.NET 1.x에서 했던 클래스를 생성하고 IConfigurationSectionHandler 인터페이스를 구현했던 방법과는 다른 방식으로 생성이 된다는 점 입니다. 과거에, 이 인터페이스를 이용하기 위해서는 개발자가 직접 XML를 파싱 하는 것이 필요했었습니다. ASP.NET 2.0에서는, 기본 클래스는 기본 파서를 가지고 있고, 적어도 거의 모든 경우에서, 속성을 통해서 개발자가 예상되는 섹션의 인터페이스를 정의하도록 하고 있습니다.

<WorkflowRuntime> 섹션은 <Services> 라는 이름을 가진 중첩된 섹션을 가지고 있고, 이 곳이 여러분이 런타임과 결합되기를 원하는 서비스들의 이름을 나열하는 곳 입니다. 윈도우 워크플로우 파운데이션은 SqlWorkflowPersistenService와 ManualWorkflowSchedulerService를 포함해서 미리 정의된 많은 서비스들을 제공하고 있습니다. SqlWorkflowPersistenService의 경우는 워크플로우가 휴지(idle) (예를 들어서, 사람의 응답을 기다리는 것과 같은) 상태가 되었을 때 자동으로 워크플로우 인스턴스를 지정된 SQL Server에 기록합니다. 스케쥴러 서비스의 경우는 ASP.NET 응용프로그램과 좀 더 관련성이 있다고 할 수 있습니다. 개발자는 이 서비스를 이용해서 워크플로우가 끝이 나기를 기다리고 있는 특정 요청을 ASP.NET의 쓰레드 풀에서 실행하도록 하는 것을 보장하기 위해서 필요합니다. 좀 더 정확하게는, 서비스가 워크플로우의 실행이 동기적이고, Start 메써드가 워크플로우가 끝났거나 혹은 휴지상태일 때만 반환되도록 하도록 하는 것을 보장해 줍니다.


페이지 맨 위로페이지 맨 위로


ASP.NET에서 워크플로우 컴포넌트를 호출하기

ASP.NET에서 워크플로우 컴포넌트를 호출하는 것은 개발자가 영속성과 디스패칭(dispatching)위 두 가지의 문제만 해결할 수 있다면 당연히 가능합니다. 일반적으로 개발자는 워크플로우 런타임의 인스턴스를 설정 섹션에서부터 생성할 수 있습니다. 이러한 일을 아래와 같이 WorkflowRuntime 생성자의 다른 오버로드를 이용해서 할 수 있습니다:

WorkflowRuntime wr = new WorkflowRuntime(sectionName);
이 섹션은 이전에 논의되었던 <WorkflowRuntime> 섹션의 이름을 가지고 있다고 가정되었지만, 실제로는 어떤 이름이라도 가질 수 있습니다. WorkflowWebRequestContext는 이 기능을 이용하여 web.config 파일에 그 섹션에 <WorkflowRuntime> 이름을 부여합니다. 그러나 하부의 구조는 상당히 일반적으로 사용될 수 있습니다.

그림 6 헬프데스크 워크플로우를 위한 웹 프론트엔드
그림 6 헬프데스크 워크플로우를 위한 웹 프론트엔드

개발자는 또한 사용자 서비스를 등록하기 위하여 <WorkflowRuntime> 역시 사용할 수 있습니다. 위의 그림 6에서 볼 수 있듯이, 제가 지난 달 칼럼에서 만들었던 헬프데스크 워크플로우는 ASP.NET 클라이언트를 통해서도 동작합니다. 헬프데스크 워크플로우는 워크플로우에 외부 이벤트를 제공해 주는 외부 서비스에 기반을 하고 있습니다. 윈도우 폼 클라이언트에서는 여러분은 아래와 같은 코드를 사용하셨을 것 입니다:

theHelpDeskService = new HelpDeskService();
wr.AddService(theHelpDeskService);
ASP.NET 클라이언트에서는, <WorkflowRuntime> 요소의 <Services> 섹션 아래에서 부가적인 몇 가지 값들을 추가해 주어야 합니다:
<Services>
   <add type="Samples.HelpDeskService, HelpDeskWorkflow" />
</Services>

워크플로우는 크게 순차적인(sequential) 그리고 상태 머쉰 (State Machine)의 두 가지의 종류로 나눌 수 있습니다. 순차적인 워크플로우는 순차적인 활동을 나타내고 모든 활동들이 종료되었을 때 중지하게 됩니다. 상태 머쉰 워크플로우는 이벤트에 기반한 비동기적인 활동단위(Activity: Workflow에서의 활동을 표현하는 유닛)들의 그래프입니다. 여기서의 각각의 활동단위들은 상태를 대표하고 다른 상태들의 하부집합으로 전이될 수 있습니다. 이런 두 가지 종류의 워크플로우들이 ASP.NET 응용프로그램 안에서 사용될 수 있습니다. 흥미롭게도, 개발자는 사용자 인터페이스를 만들어서, 이 것을 웹에서 사용하도록 감싼 뒤에, SharePoint® (서비스 팩 2) 응용프로그램이나 포탈과 비슷한 ASP.NET 2.0 응용 프로그램에서 워크플로우를 사용할 수 있게 할 수도 있습니다.

순차적인 워크플로우는 잠재적으로 아주 긴 오퍼레이션을 표현할 수 있기 때문에, ASP.NET 응용프로그램에서는 위험할 수도 있습니다. 외부에서 발생하는 이벤트를 받아들이기 위해 대기하지 않는 순차적인 워크플로우는 아주 긴 시간을 요구하는 트랜젝션을 발생시키는 작업을 하고 있을 수도 있고, 이러한 것은 전반적인 응용프로그램의 확장성에 심각한 위협을 가져옵니다.

Jeff Prosise가 2005년 10월 위키드 코드(Wicked Code)에서 설명했던, 비동기적인 페이지는 이러한 긴 시간의 작업을 요구하는 페이지들에 대한 최적의 대응책이 될 수 있습니다. 비동기적인 ASP.NET 페이지는 Begin/End 메써드의 한 쌍을 기반으로 하고 있습니다. 윈도우 워크플로우 파운데이션의 베타 2에서는, 워크플로우를 실행할 수 있는 그러한 종류의 메써드들의 한 쌍이 존재하지 않았습니다. 그러나, 윈도우 워크플로우 파운데이션 워크플로우는 웹서비스로 서비스될 수 있고, 그래서 웹서비스들의 프락시(proxy) 클래스의 특별한 메써드를 통하여 비동기적으로 호출될 수 있습니다.


페이지 맨 위로페이지 맨 위로


워크플로우를 웹서비스로 노출하기
그림 7 워크플로우
그림 7 워크플로우

윈도우 워크플로우 파운데이션 프레임웍은 다른 워크플로우뿐만 아니라 .NET에 기반을 둔 클라이언트들에게 워크플로우를 웹서비스로 노출할 수 있게 합니다. 워크플로우를 웹서비스로 노출하기 위해서는, 개발자는 특별한 방식을 이용해서 워크플로우를 작성해야 합니다. 오직 WebServiceReceive 활동을 사용하는 워크플로우만이 웹서비스로 서비스할 수 있습니다. 기본 아이디어는 개발자가 워크플로우 프로젝트를 생성해서, 최소한 가장 간단한 경우에, WebServiceReceive와 WebServiceResponse 의 한 쌍의 활동단위들을 워크플로우에 추가합니다. 좀 더 복잡한 경우에는 개발자가 아마도 하나 이상의 활동단위들의 입력을 받기 위해서 Listen 활동단위를 사용하기를 원할 수도 있습니다. 가장 흔한 간단한 경우는 워크플로우가 웹서비스의 특정 메써드를 호출해서 활성화되어, 끝까지 처리한 다음에, 결과값을 반환하는 경우일 것 입니다.

웹서비스를 통하여 노출되는 인터페이스는 워크플로우 클래스의 내부에 정의되어야 합니다. 말하자면 새로운 순차적인 워크플로우를 생성한 다음, 개발자는 인터페이스의 정의를 코드비하인드 클래스에 아래와 같이 추가해야 합니다:

public interface ILoginWorkflow
{
    bool Authenticate(string user, string pswd);
}
오로지 인터페이스에 나열된 메써드만 웹서비스를 통하여 외부 클라이언트에서 호출 가능합니다. 워크플로우의 구조는 그림 7에서 보여지는 것과 같이 WebServiceReceive와 WebServiceResponse 활동단위의 한 쌍처럼 간단할 수도 있습니다.

WebSerivceReceive 활동단위는 웹서비스 인터페이스의 특정 메써드와 결합되어 메써드가 호출될 때 데이터를 받게 됩니다. 예를 들어서, 여러분은 WebServiceReceive 활동단위를 ILoginWorkflow 인터페이스의 Authenticate 메써드에 바인드해야 합니다. 또한, 메써드의 입력 인자들은 워크플로우 클래스의 공용 멤버들에 아래와 같이 바인드 되어야 합니다:

   public string receivedUser = default(System.String);
   public string receivedPswd = default(System.String);

WebServiceResponse 활동단위는 웹서비스 인터페이스를 통한 요청에 응답을 합니다. 여러분이 WebServiceReceive 활동단위를 가지고 있는 워크플로우에서는 오로지 WebServiceResponse 활동단위를 사용해서 응답 해야 한다는 것을 알아두시기 바랍니다. 이러한 활동단위의 중요한 속성은 관련된 WebServiceReceive 활동단위의 ID를 나타내는 ReceiveActivityID입니다. 개발자는 동일한 워크플로우에 위치해 있는 WebServiceReceive 활동단위만을 쓸 수 있습니다. 메써드의 반환 값은 워크플로우 클래스의 공용멤버에 반드시 아래와 같은 방식으로 할당 되어야 합니다:

public bool returnedAuthenticate = default(System.Boolean);

기본적으로, 클라이언트와 웹서비스간의 상호작용과 워크플로우는 아래와 같이 요약될 수 있습니다. 클라이언트는 웹서비스를 참조해서, 멤버 메써드들 중 하나를 호출합니다. 웹서비스는 워크플로우를 시작하고 WebServiceReceive 활동단위를 호출해서, 입력 파라미터를 전달합니다. 이 활동단위는 입력 데이터를 저장해 둡니다. 가능한 중간 활동단위 (코드 활동단위 혹은 병렬 활동단위 같은)는 일반적인 경우와 동일하게 동작합니다. 끝에 가서는, WebServiceResponse 활동단위는 결과 값을 생성하기 위해서 호출됩니다. WebServiceResponse 활동단위는 결합된 공용멤버에 위에서 볼 수 있는 불린(Boolena) 형의 returnedAuthenticate 에 결과값을 저장해 놓습니다.

웹서비스를 통하여 노출된 웹서비스는 WebServiceReceie와 WebServiceResponse의 한 쌍의 활동단위로 감싸진 일반적인 워크플로우로 볼 수 있습니다.

입력 값과 결과 값을 관리하기 위한 이벤트 핸들러를 생성하기 위해서는, 여러분은 수신/응답 활동단위에서 마우스 오른쪽 클릭을 한 다음, “Generate Handlers” 메뉴를 클릭하시기 바랍니다. 최소한, WebServiceResponse만이라도 호출 자에게 결과값을 보낼 수 있게 하기 위해서 이런 일을 하셔야 합니다. 코드가 다 되었다면, 프로젝트에서 마우스 오른쪽 클릭을 하신 다음, 그림 8에서 볼 수 있듯이 “Publish as Web Service”라는 메뉴를 클릭하시기 바랍니다. 워크플로우 디자이너는 Web.config, asmx, 그리고 워크플로우를 가지고 있는 어셈블리 파일로 구성된 작은 웹 서비스 프로젝트를 생성할 것 입니다. 그러면 여러분은 이 파일들은 IIS 6.0에 설치하고, 워크플로우를 호출할 수 있습니다.

그림 8 워크플로우를 출판하기
그림 8 워크플로우를 출판하기

이 때 생성된 Web.Config 파일을 한 번 살펴보도록 하겠습니다. 이 글의 처음 부분에서 언급했던 <WorkflowRuntime>에 덧붙여서, 이 설정파일은 HTTP 모듈까지 요구하고 있습니다:

<httpModules>
   <add type="System.Workflow.Runtime.Hosting.WorkflowWebHostingModule, 
              System.Workflow.Runtime" 
        name="WorkflowHost"/>
</httpModules>
HTTP 모듈은 워크플로우 런타임을 인스턴스화하고 관리를 담당하고 있고, 웹서비스 요청을 적절한 워크플로우 인스턴스에 전달해 주는 역할을 합니다. HTTP 모듈은 ASP.NET의 세션 정보를 이용해서 들어오는 요청을 올바른 워크플로우 인스턴스에 전달해 줍니다.

실제 서비스하는 코드에는, 개발자가 세션 상태의 타임아웃(기본은 20분입니다)를 아주 크게 증가시켜야 하고, 워크플로우의 인스턴스 ID들이 AppDomain의 셧다운과 프로세스 재활용의 과정 중에서 잃어버리지 않도록 데이터 베이스 저장 장소를 선택해야 합니다. HTTP 모듈은 웹 팜(Web Farm) 시나리오에서 잘 동작하도록 설계되어 있다는 것을 명심하시기 바랍니다.

HTTP 모듈은 ASP.NET 세션 상태 잠금 메커니즘(Session State Locking Mechanism)에게 영향을 주어 동일한 워크플로우 인스턴스에 전달된 요청들이 직렬화되도록 하는 것을 보장하게 해줍니다. ASP.NET 세션 상태 잠금 메커니즘은 동일한 세션의 요청들을 큐(queue)에 보관해 둡니다. 이러한 잠금 메커니즘 때문에, 사용자는 워크플로우에서의 수신/응답의 한 쌍을 방해할 수 없습니다. 거의 대부분의 경우, 워크플로우를 웹 서버로 노출하는 것은, 단순히 기존의 워크플로우를 수신/응답 활동단위 한 쌍으로 감싸는 것과 같습니다. 워크플로우의 입력은 WebServiceReceive 활동단위를 통하여 모아지고 WebServiceRespose 컴포넌트는 결과 값을 모으게 됩니다.


페이지 맨 위로페이지 맨 위로


결론

ASP.NET 응용프로그램은 윈도우 워크플로우 파운데이션의 워크플로우를 위한 또 다른 플랫폼입니다. 그러나, ASP.NET 응용프로그램의 특별한 속성 때문에, 워크플로우를 사용하는 일이 개발자의 특별한 주의를 요구합니다. 특별히, 개발자는 기본 런타임 환경에, 쓰레드와 SQL 서버 영속화 서비스 같은 몇 가지의 런타임 서비스를 추가한 좀 더 무거운 환경을 쓸 필요가 있습니다.

특별히 쓰레드 서비스의 경우는 페이지가 워크플로우가 완료되었거나 휴지상태에 들어갔을 때 보여주는 것을 보장해 줍니다. 만약 워크플로우를 완료시키는데 시간이 너무 오래 걸린다면 어떻게 해야 할까요? 그런 경우에는, 여러분은 이 워크플로우를 웹 서비스로 노출해서, 워크플로우를 ASP.NET 비동기 페이지로 호출하는 것을 고려해 볼 수 있습니다. 웹 서비스로 워크플로우를 노출하는 것은 워크플로우의 한계를 늘려주고, 다른 플랫폼에서도 이 것을 사용할 수 있게 해 줍니다.


페이지 맨 위로페이지 맨 위로



  • 이 문서는 한국 개발자를 위하여 Microsoft MVP 가 번역하여 제공한 문서입니다.
  • Microsoft 는 이 문서를 번역자의 의도대로 제공해 드리며 더 정확한 표현 또는 여러분의 의견을 환영합니다.
  • Microsoft 는 커뮤니티를 위해 번역자의 의도대로 이 문서를 제공합니다.


  • 질문과 이 글에 대한 느낌을 보내 주세요.  cutting@microsoft.com.

    Dino Esposito는 Solid Quality Learning의 강사이며, Programming Microsoft ASP.NET 2.0 (Microsoft Press, 2005)의 저자이기도 합니다. 이태리에 근거를 두고, 전 세계의 기술 이벤트에 자주 초대 받는 연사이기도 합니다. Dino와 연락을 하시고 싶은 분들은 cutting@microsoft.com로 이메일을 보내주시거나 혹은 weblogs.asp.net/despos (영문)에서 만나보실 수 있습니다.


    페이지 맨 위로페이지 맨 위로QJ: 060410

    Microsoft