
Dino Esposito (영문)
목차
일반적인 비즈니스 시나리오
윈도우 워크플로우 파운데이션(Windows Workflow Foundation) 적용하기
헬프 데스크 솔루션
휴먼팩터
헬프데스크 프론트엔드
상태의 지속
워크플로우(Workflow) 인스턴스의 재개
결론
2006년 1월 호에, Don Box와 Dharma Shukla가 윈도우 워크플로우 파운데이션을 소개하였고, 프레임웍의 전체적인 구조와 구성하고 있는 컴포넌트들에 대해서 설명 드렸었습니다 (다음의 링크를 참고하시기 바랍니다: WinFX Workflow: Simplify Development With The Declarative Model Of Windows Workflow Foundation (영문)). 위에서 링크된 글이 저로 하여금 한 걸음 더 나아가 이 윈도우 워크플로우 파운데이션을 사용하여 일반적인 비즈니스 시나리오에서 자동으로 반응하는 프로세스가 인간의 반응이 교차하는 곳까지 도달할 수 있기 위해서 여러분이 어떻게 해야 하는지에 대한 설명을 하도록 영감을 주었습니다. 이 윈도우 워크플로우는 복잡한 프로세스들에 기반해서 넒은 범위의 응용 프로그램을 개발하거나 실행할 수 있는 프레임웍을 제공합니다. 가장 일반 적인 예는 문서 관리, 회사와 회사 간에 사용하는 응용 프로그램, 마지막으로 소비자를 위한 응용 프로그램입니다. 관련된 어셈블리와 제일 상위의 응용 프로그램에 추가해서 내부적인 워크플로우를 개발하기 위해서 여러분은 Visual Studio® 2005을 사용하셔야 합니다
일반적인 비즈니스 시나리오
기업들은 일반적인 주문 처리, 구매 요청, 여행 경비 등등과 같은 통상적으로 사용하는 몇 가지의 내부적인 프로세스들을 가지고 있습니다. 워크플로우는 이러한 독립적인 프로세스들에 대해서 투명하고, 동적이고, 견고한 일종의 질서를 가지고 오게 합니다. 일상적인 헬프데스크 워크플로우 프로세스에 대해서 고려해 보도록 하죠. 이 과정은 처음에 헬프데스크에서 근무하는 직원이 고객으로부터 전화를 받아서, 고객의 이름과 전화한 시각, 그리고 문제에 대한 간략한 설명들을 기록해서 접수하는 것에서부터 시작합니다. 이러한 고객의 요청 사항이 공식적으로 접수가 완료되면, 그 헬프데스크에서 일하는 직업은 그 일에 대해서는 완전히 잊어 버리고, 다른 고객의 전화를 기다리게 됩니다. 일과 시간이 끝나면, 그 직원은 컴퓨터를 꺼버리고 집에 갑니다. 동일한 시각에, 다른 부서에서, 혹은 다른 도시에서, 기술 지원 인력들은 이러한 고객의 요청을 접수 받고, 내용을 전달 받게 됩니다. 그 요청을 할당 받은 기술 지원 인력들은 이 요청을 해결하거나 혹은 더 상위에 있는 부서에 이 문제를 전달합니다. 먼저, 이러한 프로세스를 구현하기 위해서 여러분이라면 어떤 방식으로 코드를 작성하시겠는지 여쭤 보고 싶습니다.
아마도 여러분이 이미 그러한 전화에 대한 입력 데이터를 모아서 데이터 베이스에 접수 번호, 시간, 고객이 가지고 있는 문제, 현재 상황 등을 저장하는 응용 프로그램을 가지고 계실지도 모르겠습니다. 두 번째 응용 프로그램의 사용자는 실시간으로 미해결된 고객의 요청에 대한 목록을 볼 수 있고, 그 중에 하나를 선택할 수도 있을 것 입니다. 그리고 오퍼레이터는 그가 할 수 있는 범위 안에서 무엇이든지 문제를 해결하기 위해서 필요한 일들을(고객에게 다시 전화하기, 고객이 요청한 정보를 조회해서 가져오기, 이메일 보내기, 원격으로 필요한 일들을 하기 등등……) 할 것 입니다. 그리고 그 문제가 해결되었는지 혹은 더 깊은 조사가 필요한지를 표시할 것 입니다. 이러한 조치에 대한 결정은 데이터 베이스 안에서 그 특정 문제에 대한 레코드의 정보를 갱신하기 위해서 버튼을 클릭하는 것 같은 사용자의 명백한 행동에 의해 대표될 수 있습니다. 마지막으로, 만약 사용자가 관련된 다른 종류의 일들이 있다면, 특별히 만들어진 프론트엔드에 사용자가 특정한 문제가 성공적으로 해결되어 종료되었는지 혹은 비정상적으로 종료했는지를 표시할 수 있게 해주는 것 입니다.
비록 이러한 프로세스들이 인간의 결정이 꼭 필요한 워크플로우를 대표하지만, 이러한 경우는 고전적인 프로그래밍 언어와 데이터 베이스를 이용한 전통적인 절차적인 언어로 쉽게 구현될 수 있습니다.
윈도우 워크플로우 파운데이션 적용하기여러분이 윈도우 워크플로우 파운데이션 같은, 일련의 행동단위 (activities)로 구성된 워크플로우를 기반으로 한 시스템이 있을 때는, 여러분은 이를 기반으로 해서 사용자의 반응을 요구하는 필수적인 코드, 행동단위들을 선언하는 맵, 그리고 이 두 개를 서로 결합하는 규칙들을 잘 섞음으로써 응용 프로그램을 구현할 수 있습니다. 이렇게 하는 가장 큰 장점은 여러분이 솔루션을 모델링 할 수 있고, 윈도우 워크폴로가 가지고 있는 런타임 서버로 하여금 실시간으로 여러분의 그래프와 여러분이 정의한 링크를 따라서 프로그램이 실행되게 할 수 있습니다. 프로세스가 복잡하면 할수록, 이 프로세스에 대한 흐름을 고안하고 구현하기가 쉬워집니다. 프로세스가 더 동적으로 바뀌면 바뀔수록, 여러분이 작성하고 유지해야 할 코드가 작아집니다. 헬프데스크 시나리오를 통해서 어떻게 윈도우 워크플로우 파운데이션 솔루션을 구현하는지 알아 보기로 합시다.
헬프데스크 솔루션제가 만든 헬프데스크의 워크플로우는 접수를 받으면서 시작해서, 연결된 사용자 혹은 기술자의 응답이 올 때 까지는 정지해 있는 시스템입니다. 이렇게 접수된 사안이 종료되거나 혹은 더 높은 수준으로 올라가든지, 워크플로우는 외부 이벤트를 받아서, 그 이벤트를 추적해서 응용프로그램의 내부적인 상태를 업데이트합니다. 이러한 구성에 비추어 보아, 이 워크플로우는 바깥 세상과의 상호작용이 반드시 요구됩니다. 이러한 종류의 비동기적인 일들은 윈도우 워크플로우 파운데이션이 묘사하는 실제 세상의 워크플로우 프로세스 안에 내제된 문제점 중의 하나입니다. 왜냐하면 시스템의 바깥에 있는 요소와의 상호작용이 필요할 때, 호스트 응용 프로그램과 워크플로우는 필요하다고 증면된 어떤 데이터 교환에 관한 규약을 정의할 수 있기 때문입니다. 아래에서 볼 수 있는 IHelpDeskService 인터페이스는 워크플로우와 호스트간을 연결하기 위해서 사용된 통신 인터페이스를 설명하고 있습니다:
[DataExchangeService]
public interface IHelpDeskService
{
event EventHandler<HelpDeskTicketEventArgs> TicketClosed;
event EventHandler<HelpDeskTicketEventArgs> TicketEscalated;
void CreateTicket(
string description, string userRef, string createdBy);
void CloseTicket(string ticketID);
void EscalateTicket(string ticketID);
}
자 이제, 이 그림 1에서 볼 수 있는 서비스 클래스 이름을 HelpDeskService라고 명명하면서 시작하도록 하겠습니다. HelpDeskService 클래스는 워크플로우 런타임에 추가되어 호스트 응용 프로그램과 워크플로우간의 연결 지점을 의미합니다. 호스트 응용 프로그램은 워크플로우에 외부 이벤트를 발생시키기 위해서 클래스의 공용 메써드를 호출합니다. 이렇게 발생된 이벤트는 특정 영역에 특화되어 동작의 흐름을 제어하는 이벤트에 신호를 보내줍니다. IHelpDeskService의 메써드들은 InvokeMethod 엑티비티를 통하여 워크플로우가 외부 컴포넌트에서 호출할 수 있는 몇 줄의 코드를 의미합니다. 이런 방식으로 구성이 되어, 기본적인 인터페이스의 동작에 대한 실제적인 구현이 기본적으로 워크플로우가 호스트를 호출하도록 되어 있어서, HelpDeskSerive의 로직은 워크플로우의 동작에 많은 유연성을 추가할 수 있게 해 줍니다. IHelpDeskService를 구현하는 클래스를 추가하는 것으로서 서로 다른 호스트 프로그램들이 워크플로우 로직은 변하지 않으면서도 서로 다른 알고리즘과 저장 매체를 이용해서 접수를 생성하고, 해결하고, 혹은 더 높은 수준으로 접수를 이관시킬 수 있습니다. 개발자는 인터페이스와 클래스를 다른 어셈블리에 컴파일 하거나 혹은 워크플로우가 저장되어 있는 동일한 어셈블리에 이 파일들을 보관할 수도 있습니다.
HelpDeskWorkflow는 윈도우 폼 응용 프로그램이 이용하는 순차적인 워크플로우입니다. 그림 2는 이것의 그래픽 버전의 모델을 보여줍니다. 이 버전은 응용 프로그램으로 하여금 사용자에 의해서 제공된 정보를 통하여 접수를 생성하도록 하는 InvokeMethod 활동단위(Activities)에 의해서 시작합니다. 그 이후에는 이 접수는 기술 지원 인력이 이 접수사항을 해결하거나 혹은 좀 더 심화된 기술 지원 부서로 이관시켜주기를 기다리면서 데이터베이스 안에서 대기합니다.

그림 2 비쥬얼 스튜디오 2005에서의 헬프데스크 워크플로우
고객의 요청 혹은 불만을 정식으로 접수한 뒤에, 워크플로우는 잠재적으로는 아주 긴 시간-몇 시간 혹은 몇 일-동안 대기하게 됩니다. 이 시간 동안에 무슨 일이 발생할 수 있을까요? 워크플로우 인스턴스가 메모리 안에 계속 남아 있을까요? 만약 워크플로우가 이렇게 하는 일없이 계속 대기 상태에 있다면, 여러분은 이 인스턴스를 메모리에서 제거할 수 있습니다-이러한 일련의 과정을 수동화(passivation)이라고도 합니다. 헬프데스크 서비스 같은 로컬 서비스는 호스트 프로그램과 대기하고 있는 워크플로우와의 접촉 지점으로서 계속 남아 있게 됩니다.
휴먼 팩터(The Human Factor)
호스트 응용 프로그램에 사람의 동작이 상호 작용하게 될 때, 호스트 응용 프로그램은 워크플로우는 깨우기 위해서 무언가를 하게 되고, 로컬서비스는 수동화된(passivated) 워크플로우를 활성화 하기 위해서 런타임에 요청을 하게 됩니다. 윈도우 워크플로우 파운데이션 툴박스는 listen이라고 불리는 활동단위를 가지고 있고, 이 것의 일은 대기하고 있다가 수동화된 워크플로우를 깨우는 요청을 받아서 처리하는 일을 합니다. 그림 2의 WaitForSoltution이라고 명명된 부분이 바로 listen 활동단위의 인스턴스입니다.
listen 활동단위는 연결될 수 있는 가능한 이벤트를 의미하는 자식 노드들이 없다면 쓸모가 없습니다. 헬프데스크 예제에서, listen 활동 단위는 두 개의 노드를 가지고 있습니다-접수 종료 혹은 접수 이관이 그것들 입니다. 각각의 노드는 이벤트 중심의 활동입니다. lisen과 이벤트 중심의 활동들은 특별한 설정을 요구하지는 않고, 단지 자식 엑티비티의 단순한 컨테이너에 불과합니다. 외부 이벤트를 잡기 위해서는, 워크플로우는 이벤트싱크(EventSink) 컴포넌트가 필요합니다.
그림 2에서, 접수 종료 부분은 로컬 헬프데스크 서비스 안의 접수종료 이벤트에 결합되어 있는 이벤트 싱크입니다. 그림 3은 여기에서 설정된 이벤트싱크의 속성들을 나열하고 있습니다. 이벤트싱크 컴포넌트를 설정하기 위해서, 여러분은 이벤트를 노출하는 서비스의 인터페이스를 선택해야 합니다. 인터페이스타입의 속성들을 설정하는 것은 요구사항입니다; 덧붙여서, 여러분은 그림 1과 같이 [DataExchangeService] 속성으로 데코레이션된 인터페이스만 선택할 수 있습니다.

그림 3 이벤트싱크 속성들
이렇게 해서 데이터 교환 인터페이스가 설정이 되면, EventName 속성은 인터페이스에서 발견되는 모든 이벤트들로 채워져 있을 것 입니다. 여러분은 이벤트를 골라서 파라미터를 설정해 주면 됩니다. 만약 개발자가 언제 그 이벤트가 처리되었는지에 대한 좀 더 복잡한 통지(Notification)를 원한다면, 개발자는 호출된 속성까지도 설정해 주어야 합니다.
이벤트싱크는 사람의 입력이 들어올 때 까지 기다린 후에 워크플로우로 제어권을 반환해 줍니다. 이러한 사람의 입력 이벤트에 뒤따라서, 개발자는 그 워크플로우에 어울리는 어떤 추가의 활동단위를 추가할 수도 있습니다. 예를 들어, 헬프데스크 응용 프로그램에서는 제가 접수 데이터베이스에서 접수된 사항의 상태를 변경하는 로컬 서비스의 함수를 호출하는 것처럼 말입니다.
실제 세계에서는, 워크플로우가 직간접적으로 상호작용하기 위해서는 적어도 하나 이상의 데이터베이스가 존재합니다. 예를 들어서, 저의 경우는 SQL Server 2000 테이블, Tickers를 사용해서 각각의 처리된 접수사항을 저장하고 있습니다. 테이블의 디자인에 의해서, 접수사항에 대한 ID는 이 사항을 관리하기 위해서 사용된 워크플로우의 ID와 일치합니다.
헬프데스크 프론트엔드 (The Helpdesk Front End)
일단 컴파일 되면, 워크플로우는 재사용 가능한 어셈블리 입니다. 그래서 어떤 종류의 .NET 기반의 응용 프로그램에서 참조할 수 있게 됩니다. 자 이제는 헬프데스크 오퍼레이터가 사용할 응용 프로그램에 대해서 상상해 보도록 하겠습니다. 이 응용 프로그램은 그림 4에서 보여지는 것과 같이 사용자로 하여금 폼을 채울 수 있게 하고, 접수사항을 만들어서 워크플로우를 시작하게 합니다.

그림 4 헬프데스크 프론트엔드 응용 프로그램
워크플로우는 로컬 서비스의 CreateTicket 메써드를 호출하여 그 서비스로 하여금 티켓 데이터베이스에 새로운 정보를 추가할 수 있게 합니다. 오퍼레이터가 새로운 접수사항을 개설하고 나서는, 오퍼레이터는 걸려오는 전화에 대한 응답과 이메일에 답장을 하는 것 같은 자신의 일을 계속 한다면, 워크플로우는 시작되어 사람의 응답을 기다리는 상태가 됩니다. 각각의 접수사항은 서로 다른 워크플로우의 인스턴스로 대표될 수 있습니다. 간단히, 워크플로우의 ID는 접수사항의 ID 이기도 합니다.
상태의 지속 (State Persistence)
하루가 끝날 때에, 워크플로우는 활동단위들의 집합체가 됩니다. 그렇다면 이러한 것들의 수명은 어떻게 관리가 될까요? 윈도우 워크플로우 파운데이션 런타임 엔진은 모든 워크플로우의 실행을 관리하고 워크플로우로 하여금 시스템의 재부팅에도 살아남을 수 있을 정도로 오랜 동안 활성 상태로 있게끔 합니다. 이 런타임 엔진은 트랜젝션, 지속성, 추적, 타이머, 그리고 쓰레드를 지원하는 한마디로 말해서 많은 고급 기능을 제공하는 실행 환경을 제공하는 플러거블(pluggable) 서비스에 의해서 구성됩니다.
일반적으로 워크플로우가 특정한 이벤트 혹은 사람의 활동에 의해서 필요로 할 때를 제외하고 호스트 프로세스의 메모리 안에서 계속 남아 있는 일은 잘 일어나지 않을 것 같습니다. 잠깐 언급했듯이, 많은 실제 세계의 사람이 중심이 된 워크플로우는 이러한 일이 발생하기 위해서는 몇 시간 혹은 그 이상이 더 걸릴 수 있습니다. 앞에서의 예제와 같이, 헬프데스크 오퍼레이터는 워크플로우를 시작하고 프론트엔드 응용프로그램의 프로세스 안에 워크플로우 클래스를 불러 들입니다. 그리고 나서 오퍼레이터는 컴퓨터를 끄고 집에 갈 수도 있습니다. 그러나, 접수사항에 대한 정보는 반드시 살아 있어야 하고, 다른 오퍼레이터 혹은 다른 응용 프로그램에서도 이 접수사항에 대해서 볼 수 있어야 합니다. 이러한 일이 일어나기 위해서는, 워크플로우는 반드시 신뢰할 수 있는 저장 매체로의 직렬화(Serialization)을 지원해야 합니다.
워크플로우는 일반적으로 오랜 시간이 필요로 하는 동작이 될 수 있기 때문에, 특정한 객체로 하여금 메모리 안에서 많은 시간 동안 남아 있도록 하는 것은 비실용적입니다. 첫 번째로, 호스트 프로세스는 계속되는 활동들에 의해 누적된 객체들 전부를 저장하는 것은 일반적으로 가능하지 않습니다. 두 번째로, 호스트 프로세스는 한 번 이상 셧다운(shut down) 혹은 재부팅될 수 있습니다.
이러한 것에 대한 해결 방법은 워크플로우가 한가해 질 때, 런타임으로 하여금 메모리에서 이것을 내려주도록 설정하는 것 입니다. 이 경우에는, 호스트 프로그램에서 아래와 같이 코드를 사용해서 런타임을 초기화 할 것 입니다:
WorkflowRuntime wr = new WorkflowRuntime(); wr.StartRuntime();
개발자는 워크플로우가 한가해지거나, 지속되거나 혹은 메모리에서 제거되었을 때 WorkflowRuntime 클래스가 특정한 코드를 실행하도록 이벤트 핸들러 몇 개를 설정할 수도 있습니다. 이러한 방식으로 설정되어, 런타임은 워크플로우가 릿슨 엑티비티 혹은 대기 상태로 들어갈 대 자동으로 저장 매체에 워크플로우를 직렬화합니다. 다른 대안은 호스트 프로그램으로 하여금 특정한 실행 지점에서 프로그래밍 적으로 워크플로우의 상태를 제어하는 것 입니다. 이 경우는, 개발자는 WorkflowInstance 클래스에 있는 EnqueueItemOnIdle를 호출해야 합니다. 개발자는 런타임 클래스의 CreateWorkflow 메써드를 호출했을 때 반환 값으로 WorkflowInstance 클래스의 인스턴스를 얻을 수 있습니다:
WorkflowInstance inst = theRuntime.CreateWorkflow(type); ... inst.EnqueueItemOnIdle(); inst.Unload();
EnqueueItemOnIdle의 호출이 반드시 workflow를 즉시 영속화(Persistence)하지 않는 다는 사실을 기억하시기 바랍니다. 워크플로우가 휴지(Idle) 상태가 되거나 중지(suspend)상태가 될 때 영속화(Persistence)는 즉시 이루어 집니다. 그렇지 않는 경우, 런타임은 정보를 저장해 두고, 나중에 workflow가 아이들 상태가 되거나 중지될 때 워크플로우를 저장 매체에 직렬화시킵니다. EnqueueItemOnIdle이 워크플로우의 인스턴스의 상태를 저장하고 이것을 메모리에서 제거하는 것에 대해서 한계가 있다는 사실을 꼭 기억하시기 바랍니다. 워크플로우의 인스턴스를 메모리에서 제거 하기 위해서는, 개발자는 Unload 메써드를 직접 호출할 필요가 있습니다. 워크플로우 런타임에 의해서 지원되는 일반적인 런타임 서비스 중의 하나는 워크플로우 영속화 서비스입니다. 그림 5와 같이 코드를 통하여 개발자는 이 서비스를 실행시킬 수 있습니다.
WorkflowPersistence 서비스는 주어진 런타임 엔진 안에서 실행되는 모든 워크플로우의 인스턴스를 직렬화할 수 있는 기능을 추가해 줍니다. 워크플로우 서비스의 핵심 기능은 이러한 WorkflowPersistenceSerice 클래스에 의해 대표됩니다. 윈도우 워크플로우 파운데이션은 SqlWorkflowPersistenceService를 통하여 이러한 기능의 구현을 제공합니다. 이 클래스는 워크플로우 데이터를 SQL Server 2000 혹은 SQL Server 2005의 데이터베이스 안에 잘 알려진 스키마(Schema)와 같이 저장합니다. 여러분이 쉽게 대상 데이터베이스를 생성하도록 도와주는 몇 개의 스크립트와 다이얼로그에 기반을 둔 유틸리티들이 있습니다. 이렇게 여러분이 필요한 모든 것들은 윈도우즈 워크플로우 파운데이션 웹사이트 (영문)통해서 다운로드 받으실 수 있습니다. SQLWorkflowPersistenService는 반드시 런타임이 실제적으로 시작되기 이전에 생성되어 런타임에 등록되어야 합니다. 이러한 영속화 서비스의 생성자는 연결 문자열(connection string)을 유일한 인자로 받습니다.
위에서 언급했듯이, 런타임 서비스들은 윈도우 워크플로우 파운데이션 구조에서 교체 가능한 부분입니다. 그래서 여러분은 자신 만의 고유한 서비스를 생성해서 여러분 자신의 서비스로 표준 서비스를 교체할 수도 있습니다.
워크플로우 인스턴스의 재개 (Resuming the Workflow Instance)
위에서 논의되었던 시나리오에서, 다른 오퍼레이터(기술 지원 인력)는 접수된 사항을 열어서, 직접 문제를 해결하거나 혹은 다른 기술 지원 부서로 이 사항을 이관시키도록 되어 있습니다. 이 기술 지원 인력은 다른 응용 프로그램 혹은 최소한 에서 그림 4보여지는 프론트엔드 응용 프로그램의 다른 인스턴스를 사용할 것 입니다. 둘 다의 경우에, 워크플로우 런타임의 서로 다른 인스턴스는 영속성을 지원하면서 생성되어야 하고, 올바른 워크플로우의 상태가 다시 메모리에 올라와서 재개되어야 합니다. 여러분이 볼 수 있듯이, 이러한 일들은 이전에 관련된 워크플로우의 ID가 저장되어야 가능한 일입니다. 헬프데스크 예제 응용프로그램에서, 설계 상으로, 티켓 ID는 워크플로우 ID 그리고 각각의 접수 사항들은 이것을 처리하기 위해서 생성된 워크플로우의 인스턴스에 대응해야 합니다.

Figure 6 문제 해결사
문제 해결 응용 프로그램 ( 그림 6) 을 참고하세요)은 워크플로우 런타임을 그림 4에서 보여지는 헬프데스크 프론트엔드 응용프로그램과 거의 같은 코드를 사용하여 초기화 합니다. 한 가지 유일한 사소한 차이점은 런타임의 WorkflowLoaded 와 WorkflowCompleted 이벤트들에 대해서 이벤트 핸들러를 등록했다는 점 입니다:
WorkflowRuntime wr = new WorkflowRuntime(); wr.WorkflowLoaded += OnWorkflowLoaded; wr.WorkflowCompleted += OnWorkflowCompleted;
오퍼레이터가 목록에서 접수사항을 선택해서 처음에 문제를 제기한 사용자와 같이 작업을 합니다. 작업이 완료되었을 때, “Issue Solved” 혹은 “Escalate” 버튼을 클릭해서, 워크플로우를 중지하게 합니다. 이벤트 핸들러는 접수사항에 대한 ID로부터 저장된 워크플로우를 알아와서 휴지(idle) 상태에 있는 워크플로우를 깨우게 됩니다. 이러한 일을 하기 위해서 아래와 같은 코드가 필요합니다:
string workflowID = (string)ticketList.SelectedValue;
Guid id = new Guid(workflowID);
Type type = typeof(MySamples.HelpDeskWorkflow);
try {
WorkflowInstance inst = theWorkflowRuntime.GetLoadedWorkflow(id);
theHelpDeskService.RaiseCloseTicketEvent(inst.InstanceId);
} catch { ... }
위의 코드에서 가장 중요한 부분은 GetLoadedWorkflow 메써드의 호출입니다. GetLoadedWorkflow는 워크플로우의 인스턴스를 대표하는 GUID를 받아서, 현재 메모리상에 인스턴스가 존재하지 않으면, 구성된 저장 매체로부터 인스턴스를 회수해오게 합니다. 이 경우, 워크플로우는 메모리에 적재되어 실행되도록 설정됩니다. 이러한 일은 이전에 워크플로우 인스턴스가 비정상적으로 종료 되어도 발생합니다. WorkflowLoaded 이벤트는 워크플로가 다시 메모리로 적재되면 WorkflowLoaded 이벤트가 발생되게 굅니다. GetLoadedWorkflow는 WorkflowInstance의 객체 자료형을 반환하고, 이 값을 가지고 개발자는 워크플로우의 현재 상태 뿐만 아니라 현재의 동작까지도 조사할 수 있습니다. 이쯤에서, 개발자는 workflow가 기다리고 있던 이벤트들 중 하나를 발생시킵니다. 대응되는 이벤트 싱크가 이벤트를 잡아서 다음 대기 지점을 만날 때 까지 워크플로우의 코드를 실행시키게 됩니다.
워크플로우가 끝이 나면, 자동으로 윈도우 워크플로우 파운데이션 영속성 데이터 베이스(Windows Workflow Foundation Persistence Database)에서 제거 됩니다. 워크플로우가 끝까지 도달했을 때, WorkflowCompleted 이벤트가 발생됩니다. 헬프데스크 시나리오에서, 워크플로우는 오퍼레이터가 “Issue Solved” 혹은 “Escalate” 버튼을 클릭했을 때 (그림 6을 참고하세요) 워크플로우는 끝이 나게 됩니다.
개발자 입장에서는 두 가지 일을 기억하는 것이 반드시 필요합니다. 첫 번째는 워크플로우에 이벤트를 발생하기 위해서는 개발자는 쓰레드 풀에 이러한 요청을 보내야 한다:
public void RaiseCloseTicketEvent(Guid instanceId) {
ThreadPool.QueueUserWorkItem(JustCloseTheTicket,
new HelpDeskTicketEventArgs(instanceId, "Jim"));
}
public void JustCloseTheTicket(object o) {
HelpDeskTicketEventArgs args = o as HelpDeskTicketEventArgs;
if (TicketClosed != null) TicketClosed(null, args);
}
두 번째는, WorkflowCompleted 이벤트는 메인 윈도우 폼 쓰레드에서 발생하지 않을 수 있습니다. 그래서 개발자는 어떠한 UI 컨트롤이라도 직접적으로 업데이트 시킬 수 없습니다. (이것은 윈도우 폼 컨트롤들이 쓰레드 세이프하지 않다는 사실에 기인합니다.) 윈도우 폼 컨트롤을 생성했던 쓰레드가 아닌 다른 쓰레드에서 컨트롤들의 값을 갱신하는 것은 당연히 가능하지만, 그렇게 하기 위해서는 간접적인 호출이 요구됩니다:
private delegate void UpdateListDelegate();
private void UpdateList() {
this.Invoke(new UpdateListDelegate(FillList));
}
개발자는 WorkflowCompleted 이벤트 핸들러에서 UpdateList를 호출할 수 있습니다. 폼 클래스의 Invoke 메써드는 어떠한 컨트롤이라도 안전하게 갱신될 수 있는 쓰레드에서 FillList가 호출되는 것을 보장해 줍니다.
결론
윈도우 워크플로우 파운데이션은 시각적으로 복잡한 알고리즘을 디자인하게 해줘서 비즈니스 문제와 모델 프로세스를 해결할 수 있게 해 줍니다. 워크플로우는 데이터의 흐름과 동작을 묘사하기 위해서 사용되는 일종의 도구입니다. 그럼으로, IF 혹은 WHILE 문(Statement) 등이 요구되는 어떠한 시나리오들도 워크플로우라고 할 수 있습니다. 그러나, 아무도 단순히 IF 문을 위해서 워크플로우를 사용하지는 않을 것 입니다; 워크플로우 런타임은 단순 계산할 수 없는 플로우의 복잡도가 증가함에 따라 특정한 임계 값을 넘어서 증가하는 비용이 있습니다.
워크플로우의 비용 대비 효율의 한계를 결정짓는 경계는 어디 일까요? 이 글에서 구현되었던, 헬프데스크 시나리오는 아마도 워크플로우를 적용하기에는 너무 간단하고 워크플로우가 이 서비스를 통하여 다루는 동일한 티켓 데이터베이스를 이용하여 다른 방식으로 구현이 될 수도 있었을 것 입니다.
워크플로우에 기반한 솔루션의 진짜 장점은 복잡한 프로세스를 모델링하고 구현하기에 쉽게 하고, 더 나아가서는 이를 기반으로 서비스를 진화시키고 확장시킬 수 있다는 점에 있습니다. 윈도우 워크플로우 파운데이션은 윈도우 워크플로우 프로그램을 위해서 관리되는 실행 환경(Managed Execution Environment)를 제공합니다. 이 환경은 내구성, 견고성, 정지(Suspension)/재개(Resumption) 그리고 프로그램을 보정해주는 특성들을 제공해 줍니다. 어떤 맥락에서는, 활동단위(Activity)들은 IL(Intermediate Language)의 opcode 혹은 프로그램 문과 동일하지만, 어떤 영역에 특화된 지식을 가지고 있습니다. 짧게 말하자면, 윈도우 워크플로우 파운데이션은 여러분의 프로그램 구문을 선언적이고 명료하게 해주고, 여러분이 여러분의 프로그램을 설계 할 때 실제 세계의 일하는 방식과 유사하게 해 줍니다. 특정 일에 딱 맞는 도구인 셈이죠. 여러분은 프론트엔드(front-end) 응용프로그램을 IL을 통하여 개발하지는 않을 것 입니다. 대부분은 RAD 개발 도구 혹은 좀 더 사람이 읽기 쉬운 프로그래밍 언어를 이용해서 작성하실 것 입니다. 윈도우 워크플로우 파운데이션 SDK는 복잡한 비즈니스 프로세스, 특히 이 프로세스가 시간에 따라서 계속 변할 때, 을 모델링 하도록 고안된 확장 프로그램 언어를 제공해 줍니다. 이 경우, 가장 핵심적인 장점은 특별한 활동을 추가해서 일의 흐름에 접근할 수 있고, 흐름을 제어하기 위해서 기본으로 제공되는 활동단위를 사용할 수 있다는 점 입니다. 여러분은 일 자체에만 집중하시기 바랍니다, 런타임이 나머지는 알아서 할 것 입니다. 만역 여러분이 윈도우 워크플로우 파운데이션을 좀 더 알고 싶으시거나 혹은 다른 아이디어가 있으시다면, 것들은 윈도우 워크플로우 파운데이션 사이트 (영문)를 방문해 보시기 바랍니다.
질문이나 코멘트는 여기로 보내주시기 바랍니다 cutting@microsoft.com.
