달력

032010  이전 다음

  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  •  
  •  
  •  
DataTables 간에 DataRows를 복사하는 방법

ImportRow 메서드를 사용하기 전에 대상 테이블의 구조가 원본 테이블과 동일한지 확인해야 합니다.
이 예제에서는 DataTable 클래스의 Clone 메서드를 사용하여 모든 DataTable 스키마, 관계, 제약 조건뿐 아니라 DataTable의 구조도 복사합니다.

// 원본 객체로 DB 가져온 데이터를 매핑 시켜줍니다. (생략)
DataTable tblProducts = new  DataTable();
// 원본 객체에서 복사할 객체입니다.
DataTable tblProductsCopy = new DataTable();
// 복사용 객체에 스키마, 관계, 제약 조건들을 원본 객체와 같게 적용 합니다.
tblProductsCopy = tblProducts.Clone();
for (i=0; i<=4;++i)
{
    // 원본 객체에서 복사를 실행합니다.
    tblProductsCopy.ImportRow(tblProducts.Rows[i]);
}

추가1. DataTable 개체의 Copy 메서드를 사용하여 전체 DataTable을 복사할 수 있습니다.

DataTable DataTable1 = new DataTable();
DataTable DataTable2 = new DataTable();
// DataTable의 Copy 메서드를 이용하여 복사한다.
DataTable2 = DataTable1.Copy();

추가2. 필터링된 DataView 클래스의 결과나 Select 메서드의 결과에서도 DataRow 개체를 복사할 수 있습니다.

// Select 메서드를 사용하여 결과값을 복사한다.
foreach (DataRow MyDataRow in DataTable1.Select("Region = 'WA'"))
{
    DataTable2.ImportRow(MyDataRow);
}
// DataView의 필터 기능을 사용하여 결과값을 복사한다.
DataView1 = DataTable1.DefaultView;
DataView1.RowFilter = "Region = 'WA'";
for (int i = 0; i <= DataView1.Count - 1; ++i)
{
    DataTable2.ImportRow(DataView1[I].Row);
}

작성자 : 상현넘™ [SHBLITZ.NET]
참고 : http://support.microsoft.com/default.aspx?scid=kb;KO;308909
Posted by 상현넘™

댓글을 달아 주세요

이 팁은 Visual Studio Codename "Orcas"에서 사용되는 C# 입니다.
제 생각에는 C# 3.0이라고 생각되어 개인적으로 C# 3.0 미리보기라고 한것입니다.

작업 환경
- Windows Server 2003 Standard Service Pack 2
- Visual Studio Codename "Orcas"

세번째 시간입니다.
이번엔 Extension Methods에 대해서 알아보도록 하겠습니다.
이번 역시 설명보다는 예제로 설명을 하도록 하겠습니다.

일단 예제로 이메일 주소를 가지고 있는 string이 있습니다.
이 이메일 주소의 유효성 검사를 하는 클래스와 함수를 만들고 사용을 합니다.

string email = "shblitz@shblitz.net";
if ( EmailValidator.IsValid(email) )
{
}

위 예제는 EmailValidator라는 클래스에 IsValid라는 함수를 만들어서 email의 유효성을 검사하는 예제입니다.

C# 3.0에서는 저런 방법이 아니라 string이라는 객체에 함수를 추가하여 사용할 수 있도록 할 수 있습니다.
그러면 아래와 같은 방법으로 사용이 가능할 것입니다.

string email = "shblitz@shblitz.net";
if ( email.IsValidEmailAddress() )
{
}

실제적으로 string 객체에는 위에서 보는 봐야 같이 IsValidEmailAddress() 라는 함수는 없습니다.
이 함수는 사용자가 추가한 함수 입니다.

그러면 이젠 어떻게 추가를 하는지 알아보도록 하겠습니다.

새로운 클래스 파일을 생성 하신 후 아래와 같이 확장 메소드를 추가합니다.

public static class ShblitzExtensions
{
    public static bool IsValidEmailAddress(this string s)
    {
        Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
        return regex.IsMatch(s);
    }
}

위 예제를 보시면 이 함수는 string 객체에서 사용하고 결과 값으로 bool을 넘기는 것을 확인할 수 있습니다.

다음은 Visual Studio Codename "Orcas"에서 실제 사용되는 이미지다.

사용자 삽입 이미지

위 이미지에서 보는 것과 같이 string 객체에 IsValidEmailAddress라는 함수가 추가된것을 볼수 있다.
그리고 일반 함수와는 다르게 아이콘 모양도 다르며 풍선 도움말에는 (extension) 이라는 문구가 보인다.

위의 예제는 string 이라는 객체에 한해서 사용할 수 있는 예제입니다.
다음은 모든 객체에서 사용할 수 있는 확장 함수에 대해서 알아보도록 하겠습니다.

먼저 확장 메소드를 만듭니다.

using System;
using System.Collections;
namespace ShblitzExtensions
{
    public static class ShblitzExtensions
    {
        public static bool In(this object o, IEnumerable c)
        {
            foreach(object i in c)
            {
                if(i.Equals(o))
                {
                    return true;
                }
            }
           
            return false;
        }
    }
}

위 예제는 어떤 객체 리스트에 내가 지정한 객체가 포함이 되어 있는지 확인하는 예제입니다.
그러면 이젠 사용을 해보도록 하겠습니다.

예제는 숫자와 문자열에서 사용하는 방법입니다.

using System;
using ShblitzExtensions;
public class ShblitzTest
{
    // string 형에서 In 함수를 사용하는 예제
    public void TestUsage1()
    {
        string[] strList = { "One", "Two", "Three" };
        bool bTest1 = "One".In(strList);
       
        string strTest = "Four";
        bool bTest2 = strTest.In(strList);
    }

    // int 형에서 In 함수를 사용하는 예제
    public void TestUsage2()
    {
        int[] iList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
       
        bool bTest1 = 3.In(iList);
       
        int iTest = 11;
        bool bTest2 = iTest.In(iList);
    }
}

위에서 보는 것과 같이 여러 객체 형에서 사용이 가능하기도 합니다.
이와 같이 사용하면 전에 사용하던 유틸 클래스 자체를 확장 함수 형식으로 변경을 해서 사용하면 편할거 같다고 생각이 듭니다.

이것으로 세번째 팁도 마치도록 하겠습니다.
다음 팁은 언제 올라올지 모르겠습니다. 아직 제가 해보지 못했거든요..
제가 해보고 이해를 하게 되면 올리도록 하겠습니다..

작성자 : 상현넘™ [SHBLITZ.NET]
Posted by 상현넘™

댓글을 달아 주세요

이 팁은 Visual Studio Codename "Orcas"에서 사용되는 C# 입니다.
제 생각에는 C# 3.0이라고 생각되어 개인적으로 C# 3.0 미리보기라고 한것입니다.

작업 환경
- Windows Server 2003 Standard Service Pack 2
- Visual Studio Codename "Orcas"

두번째 시간입니다.
이번엔 Object Initializers, Collection Initializers에 대해서 알아보도록 하겠습니다.
이번 역시 설명보다는 예제로 설명을 하도록 하겠습니다.

일단 먼저 Object Initializers에 대해서 먼저 설명을 하도록 하겠습니다.

1. C# 2.0에서 Object Initializers 사용법

Person person = new Person();
person.FirstName = "Sanghyun";
person.LastName = "Han";
person.Age = 27;

C# 2.0에선 위 예제처럼 일단 객체를 생성하고 그 객체의 각 프로퍼티에 값을 넣어주는 형식입니다.
그러나 이런 방법을 C# 3.0에선 객체를 생성하면서 한번에 가능합니다..

근데, 제 나이까지 공개가 되어 버리네요.. ㅋㅋ

2. C# 3.0에서 Object Initializers 사용법
 
Person person = new Person { FirstName = "Sanghyun", LastName = "Han", Age = 27 };

보시는 바와 같이 아주 간단합니다.
객체 생성시 {} 안에 프로퍼티 = 값 이렇게만 해주면 되는거죠!!

더 확장으로 객체안에 다른 객체를 초기화 하는 방법입니다.
물론 위의 방법과 같습니다.

Person person = new Person
{
    FirstName = "Sanghyun",
    LastName = "Han",
    Age = 27,
    Phone = new Phone
    {
        Home = "02-2222-3333",
        Cellular = "010-2222-3333"
    }
};

위 예제를 보는봐와 같이 객체 안에서 다른 객체를 생성하고 그 안에 프로퍼티의 값을 넣어주면 됩니다.
역시 엄청 편하죠^^

그럼 이번에 좀더 확장된 개념으로 Collection Initializers에 대해서 알아보도록 하겠습니다.

방법은 컬렉션의 Add 함수를 사용하는 방법과 컬렉션 생성에서 바로 추가하는 방법을 알아보도록 하겠습니다.

1.  Collection의 Add 함수를 사용

List<Person> people = new List<Person>();
people.Add(new Person { FirstName = "Sanghyun", LastName = "Han", Age = 27 });
people.Add(new Person { FirstName = "Bill", LastName = "Gates", Age = 50 });
people.Add(new Person { FirstName = "Susanne", LastName = "Guthrie", Age = 32 });

일반적으로 컬렉션 객체를 생성하고 Add 함수를 사용해서 객체를 넣는 방식입니다.
객체를 초기화 하는 방법은 위에서 설명한 방법대로 하면 되는 것입니다.

2. Collection을 초기화 할때 사용

List<Person> people = new List<Person>
{
    new Person { FirstName = "Sanghyun", LastName = "Han", Age = 27 },
    new Person { FirstName = "Bill", LastName = "Gates", Age = 50 },
    new Person { FirstName = "Susanne", LastName = "Guthrie", Age = 32 }
};

방식은 객체 초기화와 비슷하다는걸 알수 있습니다.
역시 많이 편해졌네요^^..
이것으로 두번째 팁을 마치도록 하겠습니다.
다음엔 확장 함수에 대해서 알아보도록 하죠^^

작성자 : 상현넘™ [SHBLITZ.NET]
Posted by 상현넘™

댓글을 달아 주세요

이 팁은 Visual Studio Codename "Orcas"에서 사용되는 C# 입니다.
제 생각에는 C# 3.0이라고 생각되어 개인적으로 C# 3.0 미리보기라고 한것입니다.

작업 환경
- Windows Server 2003 Standard Service Pack 2
- Visual Studio Codename "Orcas"

첫번째로 Automatic Properties에 대해서 알아보도록 하겠습니다.
설명보다는 예제로 설명을 하도록 하겠습니다.


1. C# 2.0 즉, Visual Studio 2005에서 Properties 사용법

public class Person
{
    private string _firstName;
    private string _lastName;
    private int _age;
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }
    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
}

지금까지 여러분들이 프로퍼티를 만들때 위 예제처럼 만들었을겁니다.
변수를 만들어 놓고 그 변수에다 값을 넣고 빼는 방식으로...
그러나 C# 3.0에서는 좀 더 편하게 변경이 되었습니다.


2. C# 3.0 즉, Visual Studio Codename "Orcas"에서 Properties 사용법

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

놀랍지 않습니까?? 소스 코드가 저렇게나 짧아지고 편해지다니...
그리고 프로퍼티가 일반적인 작업이 아닌 특별한 작업을 하는 경우는 C# 2.0에서 사용하던 방식대로 사용을 하시면 됩니다.
빨리 Orcas가 정식으로 나와서 사용을 했으면 좋겠군요^^

다음 팁은 객체와 컬렉션의 초기화에 대해서 알아보도록 하겠습니다.

작성자 : 상현넘™ [SHBLITZ.NET]
Posted by 상현넘™

댓글을 달아 주세요

WCF 서비스 인스턴스 관리
기존 서비스 인스턴스 관리와 상호 비교


지난 11월 10일 닷넷 프레임워크 3.0이 정식 릴리스 되었다. 2.0이 발표된 지 채 1년도 안 돼 등장한 새 버전이지만 3.0은 기존 2.0에 WPF, WF, WCF, CardSpace 등이 추가되었을 뿐이다. 1.x 에서 2.0으로 변화한 것처럼 진화된 것이 아니라 2.0에 새로운 기능들이 추가된 채로 3.0이 발표되었을 뿐이다. 닷넷 프레임워크 3.0의 정식 버전이 릴리스 될 때까지 이들 기술을 습득하는 것을 미뤄온 독자들이 있다면 이젠 핑계거리가 없게 되었다.

이번 호에서는 WCF 에서 서비스 인스턴스를 관리하는 다양한 방법들을 소개하고자 한다. WCF 서비스를 프로그래밍할 때 항상 서비스의 계약(contract), 즉 인터페이스를 정의하고 이 인터페이스를 구현하는(implement) 서비스 타입을 정의해야 했다.

서비스 타입은 대개 닷넷의 클래스를 사용하기 마련이다. 서비스가 클라이언트의 요청을 처리하기 위해서는 클래스만 가지고는 아무런 일도 할 수 없다. 클래스의 인스턴스가 있어야 서비스 호출을 처리할 수 있을 것이다. 이렇게 WCF에서 서비스 타입의 인스턴스를 어떻게 생성하고 관리할 것인가에 대해 살펴보도록 하자.

때때로 새로운 것을 배우기 전에 과거의 것을 보고 정리하는 것이 도움이 되는 경우가 많다. WCF의 배경이 되는 기존 ASP.NET 웹 서비스(ASMX)와 닷넷 리모팅에서 제공하는 인스턴스 관리에 대해 간략하게 먼저 살펴보도록 하겠다. 지금부터 필자가 언급하는 내용은 대부분 독자들도 익히 아는 내용일런지 모르겠지만, 정리한다는 의미에서 읽어 보는 것도 나쁘지 않을 것이다. 그럼 기존 기술의 서비스 인스턴스 관리를 살펴보자.


ASMX의 인스턴스 관리

ASP.NET이 제공하는 XML 웹 서비스는 매우 간단한 서비스 인스턴스 모델을 가지고 있다. 클라이언트가 웹 서비스의 메소드를 호출하면 웹 서비스 클래스의 새로운 인스턴스가 생성되고 그 인스턴스의 메소드가 호출된다. 그리고 메소드 호출이 완료되면 그 인스턴스는 곧바로 버려지게 된다. 이럴 경우에 어느 때 메모리에서 인스턴스가 제거되는 가는 GC (Garbage Collector) 만이 알고 있을 것이다.

구체적인 예제는 리스트 1과 같다. 리스트 1의 웹 서비스를 호출하면 AsmxInstancing 클래스의 인스턴스가 생성되고 이 인스턴스에 대해 Echo 메소드가 호출된다. Echo 메소드가 리턴하게 되면 ASP.NET 런타임은 생성한 인스턴스에 대해 Dispose를 호출한 후에 그 인스턴스를 버린다.

리스트 1의 웹 서비스를 2회 호출했을 때, 이 코드가 생성하는 디버그 메시지의 내용은 다음과 같다.

Service Instance Created...
Echo invoked... call count = 1
Service Instance Disposing...
Service Instance Created...
Echo invoked... call count = 1
Service Instance Disposing...

매번 웹 메소드 호출마다 새로운 인스턴스가 생성되므로 _CallCount 필드의 값이 항상 0으로 초기화 됨은 매우 당연하다. Echo 메소드가 호출될 때 표시되는 호출 회수 값은 항상 1이 됨에도 유의하자.

이렇게 ASMX는 매우 간단한 서비스 인스턴스 모델만을 가지고 있다. ASMX는 항상 이러한 단일 호출(SingleCall)에 대해 고유의 인스턴스를 생성하는 인스턴스 모델을 가짐으로써 간단하면서도 다수의 호출에도 쉽게 확장 될 수 있는(scalable) 모델을 가지고 있다. 이 인스턴스 모델의 장/단점에 대해서는 추후에 좀 더 다루기로 하자.

<리스트 1> ASMX 인스턴스 예제
[WebService(Namespace = "http://simpleisbest.net/example/wcf/instancing")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AsmxInstancing : System.Web.Services.WebService
{
    private int _CallCount = 0;

    public AsmxInstancing ()
    {
        Debug.WriteLine("Service Instance Created...");
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        Debug.WriteLine("Service Instance Disposing...");
    }

    [WebMethod]
    public string Echo(string msg)
    {
        _CallCount++;
        Debug.WriteLine("Echo invoked... call count = " + _CallCount.ToString());
        return "You send, " + msg;
    }
}


.NET Remoting 인스턴스 관리

닷넷 리모팅은 ASMX에 비해 훨씬(?) 풍부한 인스턴스 모델을 가지고 있다. 닷넷 리모팅을 한 번이라도 접해본 독자라면 누구라도(?) 알고 있으리라 생각되는 SingleCall, Singleton, Client Activated 등이 바로 그것이다.

SingleCall 은 앞에서 살펴본 ASMX의 인스턴스 모델과 동일하다. 원격 호출이 발생할 때마다 원격 객체가 새로이 생성되며 이렇게 생성된 인스턴스가 메소드 호출을 처리한다. 메소드 호출이 완료되면 그 인스턴스는 곧바로 GC의 대상이 됨은 물론이다. ASMX와 마찬가지로 서비스 클래스(리모팅에 사용하는 클래스)가 IDisposable 인터페이스를 구현하고 있으면 Dispose 메소드는 닷넷 리모팅 런타임에 의해 자동으로 호출된다.

Singleton 인스턴스 모델은 말 그대로 Singleton 패턴을 사용한다. 오로지 하나의 서비스 인스턴스만이 생성되며 클라이언트의 개수와 원격 메소드 호출 회수에 상관없이 이 하나의 인스턴스가 사용된다. 단일 서비스 인스턴스만이 사용되므로 항상 재진입(re-entrance) 문제, 동기화 문제, 상태 유지 등에 신경을 많이 써야만 하는 인스턴스 모델이다.

Singleton 인스턴스 모델에서 인스턴스가 생성되는 시점은 최초로 클라이언트가 리모팅 호출을 하는 경우이며, 생성된 인스턴스는 애플리케이션 도메인이 종료될 때까지 계속 유지됨에도 유의하자. 이 때문에 IDisposable 인터페이스의 구현은 Singleton 모델에서 큰 의미를 갖지 않는다. 아니, IDisposable 인터페이스를 구현하는 것 자체가 문제가 될 수 있다. 왜냐하면 여러 클라이언트들에 의해 하나의 인스턴스가 공유되기 때문에 한 클라이언트가 Dispose 메소드를 호출함으로써 다른 클라이언트에게 영향을 줄 수 있기 때문이다.

Client Activated Object 인스턴스 모델, 줄여서 CAO 모델은 인스턴스의 생성과 파괴 심지어 Dispose 메소드의 호출까지도 클라이언트에 의해 제어된다. CAO 인스턴스 모델에서 인스턴스의 생성 시점은 클라이언트가 new 를 호출할 때이며, 이 때 생성자(constructor)에 대한 리모팅 호출이 발생하고 서버 측에서 CAO 인스턴스가 생성되게 된다.

클라이언트가 new 키워드를 통해 생성한 객체는 물론 서버 측의 실제 객체에 대한 프록시가 될 것이다. 여러 클라이언트가 CAO 인스턴스를 생성한다면 각 클라이언트는 고유의 인스턴스를 갖게 되며, 클라이언트가 리모팅 호출을 수행할 때마다 자신의 인스턴스가 이 호출을 서비스하게 됨에 유의하자. 인스턴스의 소멸은 클라이언트가 명시적으로 Dispose를 호출하거나 서버 측의 객체 생명 주기 리스(object life-time lease)가 만료되면 객체는 GC의 대상이 되게 된다. 닷넷 리모팅의 CAO 인스턴스 모델은 리스(lease)에 대한 이해를 필요로 하므로 지면 관계상 여기서 더 이상 설명하지 않겠다.

닷넷 리모팅의 세가지 인스턴스 관리 모델은 각기 장단점을 가지고 있다. 이들의 장단점은 WCF에서 제공하는 인스턴스 모델의 장단점과 매우 비슷하므로 WCF의 인스턴스 모델에 대해 설명할 때 이야기하기로 하겠다. 그리고 닷넷 리모팅에 대한 예제는 이달의 디스켓 내의 예제 코드를 참조하기 바란다.


WCF 서비스 인스턴스 관리

ASMX와 닷넷 리모팅과 마찬가지로 WCF 역시 서비스 타입의 인스턴스들에 대한 다양한 관리 모델을 가지고 있다. 이들은 PerCall, Per-Session, Singleton 모델로서 이제부터 각 인스턴스 관리 모델을 예제와 더불어 살펴보도록 하자.

- PerCall Services
PerCall 서비스는 ASMX의 인스턴스 관리나 닷넷 리모팅의 SingleCall 모델과 동일한 인스턴스 관리 방법으로써 WCF의 디폴트 인스턴스 관리 모델이다. 즉, 클라이언트가 서비스의 메소드를 호출하여 SOAP 메시지가 서비스에 도착하면 새로운 서비스 인스턴스가 생성된다. 그리고 이 인스턴스가 SOAP 메시지를 수신하여 메소드 호출을 수행하고, 메소드가 종료되면 그 인스턴스는 GC의 대상이 되게 된다. 물론, 서비스 타입이 IDisposable 인터페이스를 구현하고 있다면 Dispose 호출이 발생하게 됨은 ASMX와 동일하다.

PerCall 모드는 디폴트이기 때문에 구체적으로 서비스에 명시하지 않아도 되지만, PerCall 임을 명시할 수 있다. 인스턴스 모드를 명시하기 위해서는 InstanceContextMode 열거자(enumberation)를 사용하면 된다. 서비스의 인스턴스 모델은 서비스의 계약(contract 혹은 interface)과는 무관한 서버 측의 구현에 대한 상세 내용이기 때문에 인터페이스에 인스턴스 모델을 밝히지 않는다. 대신 서비스의 계약을 구현하고 있는 서비스 타입에 서비스가 갖는 여러 가지 행동 방식(인스턴스 모드, 트랜잭션 등)을 명시 하기 위해 ServiceBehavior 특성(attribute)를 사용한다. 바로 ServiceBehavior 특성에 InstanceContextMode 열거자를 통해 인스턴스 모드를 명시하면 된다.

리스트 2는 PerCall 모드를 명시적으로 표현하는 서비스 구현을 보여주고 있다. 서비스 타입인 PerCallService 클래스는 ServiceBehavior 특성을 포함하고 있으며 InstanceContextMode 속성에 PerCall 을 명시하고 있다. 앞에서 언급한 바 대로 PerCall 모드가 디폴트이므로 이와 같은 특성 선언은 불필요하다. 하지만 인스턴스 모드를 선언하는 방법을 보여주기 위해 포함된 것으로 알아두자.

<리스트 2> PerCall 모드를 사용하는 서비스 구현
[ServiceContract(Namespace="http://simpleisbest.net/wcf/example/instancing")]
public interface IEchoService
{
    [OperationContract]
    string Echo(string msg);
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class PerCallService : IEchoService, IDisposable
{
    private int _CallCount = 0;

    public PerCallService()
    {
        Console.WriteLine("PerCallService Created...");
    }

    public void Dispose()
    {
        Console.WriteLine("PerCallService Disposing...");
    }

    public string Echo(string msg)
    {
        _CallCount++;
        Console.WriteLine("PerCallService::Echo() invoked... call count = {0}", _CallCount);
        return "You send : " + msg;
    }
}

리스트 2를 호출하는 클라이언트를 Visual Studio 2005를 사용하여 작성한 코드는 다음과 같다. Visual Studio 2005에서 WCF 클라이언트 프록시를 생성하기 위해서는 Extension for WCF, WPF 애드인을 설치 해야만 한다. 이렇게 하면 ASMX에 대한 프록시를 만드는 것과 동일한 과정을 통해 WCF 서비스에 대한 프록시 코드를 얻을 수 있다. 다음 코드의 EchoServiceClient 클래스가 바로 이 과정을 통해 생성된 클래스이다.

PerCallService.EchoServiceClient service1 = new PerCallService.EchoServiceClient();
service1.Echo(“Hello, PerCallService”);
service1.Echo(“Hello again, PerCallService”);
service1.Close();

위와 같은 클라이언트 코드를 수행하면 서비스 측에서 다음과 같은 결과를 얻을 수 있다.

PerCallService Created...
PerCallService::Echo() invoked... call count = 1
PerCallService Disposing...
PerCallService Created...
PerCallService::Echo() invoked... call count = 1
PerCallService Disposing...

예상대로 메소드 호출이 발생할 때마다 서비스 타입인 PerCallService 클래스의 인스턴스가 생성되고 메소드 호출이 발생한 후에 곧바로 Dispose 메소드가 호출되는 것을 알 수 있을 것이다. 매번 인스턴스가 생성되기 때문에 멤버 필드인 _CallCount의 값이 매번 0으로 초기화되어 호출 카운트가 매번 1임에도 주의를 하자.

이는 ASMX, 닷넷 리모팅의 SingleCall, COM+ Just-In-Time Activation의 작동 방식과 동일하다. 인스턴스가 매번 생성된다는 것은 인스턴스가 상태를 지속적으로 유지할 수 없음을 의미하고 이는 곧 서비스를 state-less 하도록 설계해야 함을 의미한다.

PerCall 인스턴스 모드가 WCF의 디폴트 인스턴스 모드인 것은 다 이유가 있다. 첫째로 가장 간단한 메커니즘으로써 WCF 런타임이 갖는 부담이 매우 적다. 매 호출마다 새로운 인스턴스를 생성하고 호출 후에 이 인스턴스를 버림으로써 인스턴스를 유지하고 추적하며 관리해야 하는 부담을 줄일 수 있기 때문이다. 대부분의 경우 매번 인스턴스를 생성하는데 소요되는 비용은 매우 작아서 그것을 유지하는 것보다 훨씬 나은 성능을 유발한다. 그리고 우리는 지금까지 ASMX, COM+ 등에서 이렇게 상태를 유지하지 않고 서비스를 작성하는 것에 이미 익숙해져 있지 않은가?

PerCall 모드의 두 번째 장점은 확장성(scalability)이다. 확장성은 클라이언트가 10에서 100, 100에서 1000, 1000에서 10000으로 늘어나더라도 서비스가 다운되거나 크게 성능 저하가 발생하지 않음을 의미한다. 매번 인스턴스를 생성하지 않고 이것을 유지하고 추적해야만 한다면 클라이언트의 숫자가 늘어남에 따라서 관리해야 할 인스턴스의 개수도 늘어나기 마련이다. 이 때문에 클라이언트의 숫자가 크게 늘어나면 서비스는 느려지거나 심지어는 다운되는 경우도 발생하곤 한다. 항상 그렇지는 않지만 PerCall 과 같은 인스턴스 모델은 이 같은 현상이 발생될 가능성을 줄여주곤 한다.

PerCall의 세 번째 장점은 서버 자원을 효율적으로 사용할 수 있도록 해준다는 것이다. 예를 들어 보자. 서비스의 인스턴스가 생성자에서 데이터베이스를 연결하고 Dispose 시점에서 데이터베이스 연결을 해제한다고 가정해 보자. PerCall 이 아닌 전통적인 클라이언트/서버 모델에서 클라이언트는 프로그램 시작과 더불어 프록시를 생성하곤 한다. 프록시는 서비스의 인스턴스 생성을 유발하며, 생성된 서비스 인스턴스는 클라이언트의 종료와 더불어 메모리에 제거되곤 한다.

이 때 클라이언트가 꾸준히 서비스를 호출한다면 모를까, 많은 시간 동안 서비스 인스턴스는 놀고 있을 것이 자명하다(사용자의 입력 시간, 쉬는 시간 등). 이런 상황이라면 데이터베이스 연결은 불필요하게 오랫동안 유지될 것이며 이는 데이터베이스 서버에 부하를 가중시키는 커다란 요인이 되곤 한다. 만약 PerCall 인스턴스 모드를 사용한다면 클라이언트의 프록시 생성과 서비스 인스턴스 생성은 아무런 관계가 없다.

실질적으로 클라이언트가 서비스 메소드를 호출할 때라야 서비스 인스턴스가 생성되고 그 인스턴스도 메소드 호출 후에는 곧바로 Dispose 되므로 아주 짧은 시간 동안만 데이터베이스 접속이 유지될 것이다. 여기에 ADO.NET의 연결 풀링이 접목되므로 데이터베이스 연결은 다른 클라이언트를 위해 재사용 될 수도 있다. 이 어찌 효율적이라 아니할 수 있는가?

필자의 경험에 비춰볼 때 ASMX 이건 닷넷 리모팅 이건 COM+ 이건 PerCall 류의 인스턴스 관리 방법은 대부분 문제를 일으키지 않았으며 만족할만한 성능을 과시하곤 했다. 달리 마이크로소프트에서 이러한 인스턴스 관리 방법을 권장하는 것이 아니다. 그만한 이유가 있기 때문이다. WCF 역시 지난 기술들(ASMX, 닷넷 리모팅, COM+)과 마찬가지로 동등한 인스턴스 관리 모델로서 PerCall 모드를 지원하며 이것을 디폴트로 삼고 있다.

- PerSession 서비스
PerSession 인스턴스 모드는 다소 생소해 보이지만, 닷넷 리모팅의 CAO와 매우 흡사한 인스턴스 관리 방법이다. PerSession 인스턴스 모드를 이해하기 위해 먼저 정확히 해두어야 할 것은 세션의 개념이다. WCF에서 Session은 ASP.NET 의 세션이나 TCP의 세션과는 전혀 다른 의미를 갖는다. WCF에서 하나의 세션이라 함은 서비스와 연결된 하나의 프록시를 말한다. 하나의 클라이언트에서 2개의 프록시를 생성했다면 그 클라이언트는 2개의 WCF 세션을 갖는 것이다. 세션은 프록시를 생성함으로써 생성되고 프록시가 Close 될 때 세션 역시 닫힌다.

PerSession 인스턴스 모드는 세션이 유지되는 동안 서비스 인스턴스 역시 지속되는 인스턴스 모드이다. 세션이 지속되기 위해서 WCF는 바인딩(binding)이 신뢰도(reliability)가 높을 것을 요구한다. 그 이유는 세션이 유지되기 위해서는 클라이언트와 서비스가 신뢰되는 연결, 즉 바인딩을 사용해야 하기 때문이다. 쉽게 말해서 WCF에서 서비스가 어떤 바인딩을 사용하는가에 따라서 PerSession 이 의미를 가질 수도 있고, 그렇지 않을 수도 있다는 것이다.

예를 들어, BasicHttpBinding과 같이 신뢰성을 지원하지 않는 바인딩에서 PerSession은 PerCall과 동일하게 작동한다. 하지만 WsHttpBinding이나 NetTcpBinding과 같이 신뢰성을 제공하는 바인딩을 사용하면 세션 동안 서비스 인스턴스가 유지되게 된다. 바로 이 점이 CAO와 다른 점이다. 닷넷 리모팅의 CAO는 TCP 채널을 사용하건, HTTP 채널을 사용하건 관계가 없다.

<리스트 3> PerSession 모드를 사용하는 서비스 구현
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class PerSessionService : IEchoService, IDisposable
{
    private int _CallCount = 0;

    public PerSessionService()
    {
        Console.WriteLine("PerSessionService Created...");
    }

    public void Dispose()
    {
        Console.WriteLine("PerSessionService Disposing...");
    }

    public string Echo(string msg)
    {
        _CallCount++;
        Console.WriteLine("PerSessionService::Echo() invoked... call count = {0}", _CallCount);
        return "You send : " + msg;
    }
}

PerSession 인스턴스 모드를 사용하기 위해서는 마찬가지로 ServiceBehavior 특성을 이용하면 된다. 그리고 서비스의 바인딩을 WsHttpBinding 이나 NetTcpBinding 등 신뢰성을 지원하는 바인딩을 사용한다. 리스트 3은 PerSession 모드를 사용하는 서비스 구현을 보여주고 있다. 리스트 3에 대한 클라이언트 코드와 수행 시 서비스 측의 결과는 리스트 4와 같다. 예제 코드에서 2개의 프록시를 생성했음에 유의하자. 즉, 2개의 세션을 사용했다는 말이다. 각 세션 별로 서비스 인스턴스는 하나씩 존재하게 되고 인스턴스의 멤버 필드인 _CallCount 값이 계속 유지되는 것을 알 수 있을 것이다. 또한 프록시에 대해 Close 메소드가 호출되는 시점에서 서비스 인스턴스가 제거되고 Dispose 역시 호출됨에도 눈여겨 볼 필요가 있다.

<리스트 4> PerSession 클라이언트 코드와 그 결과
PerSessionService.EchoServiceClient service21 = new PerSessionService.EchoServiceClient();
service21.Echo("Hello, PerCallService");
service21.Echo("Hello again, PerCallService");
PerSessionService.EchoServiceClient service22 = new PerSessionService.EchoServiceClient();
service22.Echo("Hello, PerCallService");
service22.Echo("Hello again, PerCallService");
service21.Close();
service22.Close();

호출 결과-------------------------------------------
PerSessionService Created...
PerSessionService::Echo() invoked... call count = 1
PerSessionService::Echo() invoked... call count = 2
PerSessionService Created...
PerSessionService::Echo() invoked... call count = 1
PerSessionService::Echo() invoked... call count = 2
PerSessionService Disposing...
PerSessionService Disposing...

때때로 클라이언트가 프록시를 Close 하지 않는 경우가 발생하곤 한다. 프로그래밍 상의 실수 이거나 어떤 이유로 클라이언트 프로그램 혹은 시스템 전체가 다운되는 경우가 대표적인 사례가 되겠다. 어찌되었건 이런 상황이 발생하면 서비스 인스턴스는 서버 상에 계속 남아 있게 된다. 이러한 문제는 닷넷 리모팅의 CAO에서도 동일하게 발생할 수 있다. WCF에서는 이처럼 서비스 인스턴스가 일정 시간 이상 호출을 받지 않으면(inactivity state) 인스턴스를 제거하는 타임 아웃 기능을 제공한다. 디폴트 타임아웃은 10분이며 이 값은 프로그램적으로나 설정 파일을 통해 변경이 가능하다.

WsHttpBinding 이나 NetTcpBinding 과 같이 ReliableSession 을 지원하는 클래스는 다음과 같은 코드를 통해 ReliableSession 기능을 활성화하면서 타임아웃을 설정할 수 있다.

WsHttpBinding binding = new WsHttpBinding();
binding.ReliableSession.Enabled = true;
binding.ReliableSession.InactivityTimeout = TimeSpan.FromMinute(5);

위 코드는 .config 파일에서 다음과 같은 표현으로써 타임아웃 설정이 가능하다. 일단 타임아웃이 발생한 후에는 어떤 서비스 호출도 예외를 유발하며, 심지어 Close 호출까지도 예외를 유발함을 잊지 말자.

<wsHttpBinding>
    <binding name="CustomBinding">
        <reliableSession inactivityTimeout="00:05:00" enabled="true"/>
    </binding>
</wsHttpBinding>

이외에도 PerSession 모드는 인스턴스가 세션 내에서 유지되므로 여러 클라이언트에 의해 세션 인스턴스가 공유되도록 할 수도 있다. 이러한 내용들은 지면 관계 상 여기서 모두 다룰 수 없음을 독자들에게 사과하는 바이다. 다음에 기회가 있다면 상세히 알아볼 것을 약속하는 바이다.

이제 PerSession 모드의 장/단점에 대해 살펴보도록 하자. PerSession 모드의 최대 장점은 인스턴스가 세션 동안 상태를 유지한다는 것이다. 얼핏 머리를 스치는 것이 로그온 정보, 사용자 정보 등등을 인스턴스 내에 기록해 두고 싶다는 생각이 들지 않는가? 클라이언트가 구동되면서 서비스를 최초로 호출 함으로써 인스턴스가 생성되므로 인스턴스 생성자에서 필요한 정보를 모두 멤버 필드에 기록해 두고 이후의 메소드 호출에서 기록해 둔 정보를 재사용하는 것이다. ASP.NET 이나 ASP에서 유용하게 써먹었던 방법일 것이다.

PerSession 모드의 또 다른 유용한 사용처는 서비스 인스턴스를 생성하는 데 오랜 시간이 소요되는 경우이다. 예를 들어 서비스가 또 다른 원격 서버에 소켓(socket) 데이터를 전송해야 하거나 시리얼 포트 등을 통해 하드웨어를 액세스해야 한다면 PerCall은 매 호출마다 소켓 연결 혹은 하드웨어적인 접속 과정을 거쳐야 할 것이므로 매우 효율적이지 못할 것은 자명하다. 이 때 PerSession 은 매우 유용한 인스턴스 모드로서 대두될 것이다.

하지만 PerSession 모드는 세션의 개수가 늘어감에 따라 효율이 떨어지기 마련이다. 세션이 많으면 많을수록 WCF 런타임이 유지/추적/관리해야 할 인스턴스가 늘어나기 때문에 일반적으로 확장성이 떨어지는 경우가 많다. 또한 PerCall 에서 설명한 바 대로 불필요한 서버 자원의 낭비가 발생할 수도 있다. 따라서 PerSession 인스턴스 모드를 선택할 때는 상당한 주의가 필요하다.

이달의 디스켓 :
 


WCF 서비스 인스턴스 관리
Single 서비스

Single 모드는 닷넷 리모팅의 Singleton과 매우 비슷한 인스턴스 모드이다. 즉, 서비스 인스턴스는 오직 하나의 인스턴스만이 생성되며 모든 클라이언트의 서비스 메소드 호출을 하나의 인스턴스가 처리하게 된다. Instance ContextMode 열거자의 값 중 Single 값을 다음과 같이 사용하면 Single 모드가 사용되게 된다.

[ServiceBehavior(InstanceContextMode = Instance ContextMode.Single)]
public class SingleService : IEchoService, IDisposable
{ …… }

Single 모드에서 생성될 유일한 인스턴스는 자동으로 WCF 런타임에 의해 생성될 수도 있고, 직접 미리 생성해 놓은 인스턴스를 사용할 수도 있다. 이러한 옵션은 서비스 호스트를 생성할 때 선택할 수 있다. 다음과 같이 서비스 호스트를 생성할 때 서비스 타입을 명시하는 경우에는 최초의 클라이언트 호출이 발생할 때 인스턴스가 생성되고 그 인스턴스가 모든 클라이언트 호출을 처리하게 된다.

ServiceHost host = new ServiceHost(typeof(SingleService),
    new Uri("http://localhost:8083/wcf/instancing/single"));

서비스 호스트를 구현하는 ServiceHost 클래스의 생성자 중 서비스 타입이 아닌 인스턴스를 매개변수로 취하는 생성자가 존재한다. 이 생성자를 사용하면 미리 만들어 놓은 인스턴스를 Single 모드의 서비스 인스턴스로 사용할 수도 있다. 다음과 같이 말이다.

SingleService instance = new SingleService();
// 생성한 인스턴스에 대해 property 혹은 method 호출을 통해 초기화 한다.
ServiceHost host = new ServiceHost(instance, new Uri("http://localhost:8083/wcf/instancing/single"));

닷넷 리모팅의 Singleton에 비해 WCF의 Single 모드가 갖는 유연성이라 할 수 있겠다. 이외에도 WCF의 Single 모드는 생성된 Single 모드의 인스턴스를 액세스할 수 있는 속성 역시 제공한다. ServiceHost 클래스의 Singleton Instance 속성이 바로 그것으로써 서비스 호스트 클래스에 접근할 수 있다면 곧 Single 모드의 서비스 인스턴스에도 접근할 수 있다.

Single 모드는 PerSession 와 달리 특정 바인딩을 요구하지 않으므로 가장 기초적인 BasicHttpBinding 을 사용할 수 있음을 알아두자. Single 모드에 대한 구체적인 코드는 지면 관계상 생략한다. 실제 코드는 이 달의 디스켓의 내용을 참고하도록 하자.

Throttling
인스턴스 관리 방법과는 좀 거리가 있지만 생성되는 서비스 인스턴스의 개수를 제어하는 방법이 바로 쓰로틀링(throttling) 이다. 일반적으로 쓰로틀링은 CPU 혹은 메모리, 클라이언트 접속 개수가 일정 수준 이상으로 사용되는 것을 막기 위해 사용되는 서버 측 기법이다. 쓰로틀링을 통해 서버에 과부하가 걸리는 것을 막거나 DoS(Denial of Service)와 같은 해킹 공격을 막기 위해 사용된다.

WCF 역시 Max Concurrent Sessions, Max Concurrent Calls, Max Instances 의 세가지 방법을 통해 쓰로틀링을 지원하고 있다. Max Concurrent Sessions은 서비스에 접속하는 클라이언트의 세션 개수를 제한하고 Max Concurrent Calls는 모든 서비스 인스턴스를 통틀어 동시에 호출되는 개수를 제한하고 있다. 마지막으로 Max Instances는 말 그대로 생성되는 서비스 인스턴스의 개수를 제한한다. 디폴트 쓰로틀링 설정은 제한이 없도록 설정되어 있다.

쓰로틀링은 서비스 타입별로 설정하고 적용된다. 일단 쓰로틀링이 제한하는 최대 임계치에 도달하면 클라이언트의 호출은 큐에 삽입되게 된다. 그리고 WCF 런타임은 쓰로틀링의 임계치를 넘기지 않는 수준에서 순차적으로 큐에서 클라이언트 요청을 꺼내어 서비스를 처리하게 된다.

PerCall 인스턴스 모드에서 동시 호출의 개수는 서비스 인스턴스의 개수와 동일하다. 앞에서 필자의 설명을 잘 이해했다면 방금 설명 역시 쉽게 이해할 것이다. 따라서 PerCall 모드를 사용하는 서비스는 Max Concurrent Calls 값과 Max Instances 값 중 작은 값에 대해 동시 호출을 허용하게 된다.

PerSession 인스턴스 모드에서 클라이언트들의 전체 프록시 개수가 세션의 개수와 동일하고 각 세션마다 인스턴스가 생성되므로 Max Concurrent Sessions가 제한하는 것과 Max Instances가 제한하는 것이 같다. 따라서 Max Concurrent Session 값과 Max Instances 값 중 작은 것이 실질적으로 쓰로틀링에 의해 클라이언트 요청이 제한될 것이다.

WCF의 쓰로틀링은 프로그램적으로 혹은 설정 파일에 의해 설정될 수 있다. 프로그램적으로 쓰로틀링을 설정하기 위해서는 ServiceThrottlingBehavior 클래스를 통해 다음과 같이 설정할 수 있다.

ServiceHost host1 = new ServiceHost(typeof(PerCallService), new Uri("http://localhost:8083/wcf/instancing/percall"));
ServiceThrottlingBehavior behavior = new ServiceThrottling
Behavior();
behavior.MaxConcurrentCalls = 1;
behavior.MaxConcurrentInstances = 1;
behavior.MaxConcurrentSessions = 1;
host1.Description.Behaviors.Add(behavior);
host1.Open();

설정 파일을 사용하는 경우 다음과 같이 behavior를 설정하고 서비스에서 이 behavior 설정을 참조 하면 된다.

<system.serviceModel>
    <services>
        <service name="WCFService.PerCallService" behaviorConfiguration="InstancingTest">
            <endpoint contract="WCFService.IEchoService" binding="basicHttpBinding"/>
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="InstancingTest">
                <serviceThrottling maxConcurrentSessions="5"
                    maxConcurrentInstances="5"
                    maxConcurrentCalls="5"/>
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

WCF는 PerCall, PerSession, Single 의 세가지 인스턴스 모드를 지원하며 각기 인스턴스를 생성하고 유지/추적/관리하는 독특한 특징을 가지고 있다. 이들은 모두 제각기 특성을 가지며 장/단점을 가지고 있다. 일반적으로는 PerCall 모드를 사용하는 것이 좋지만 항상 그렇지는 않다. 각 인스턴스 모드의 특성과 장단점을 잘 파악하고 서비스의 특성에 맞추어 인스턴스 모드를 선택해야 할 것이다. 이를 위해 POC(Proof of Concept) 작업이나 파일럿 프로젝트에서 어떤 인스턴스 모드를 선택할지 신중히 검토해야만 한다. 대개 이러한 과정을 거치지 않기 때문에 실제 프로젝트에서 어려움을 겪는 경우가 많다.

이번 칼럼에서 다룬 WCF의 인스턴스 관리 방법 외에도 WCF는 세션을 두 클라이언트가 공유하거나(Shared Session), 클라이언트측 프록시를 복사하는(duplicating proxy) 기법을 제공하여 다양한 기법으로 서비스 인스턴스와 세션을 제어하는 방법도 제공한다. 지면 관계상 이들을 모두 다루지 못한 점이 아쉽지만 필자의 블로그나 마소 칼럼을 통해 다시 다룰 것을 독자들에게 약속한다.

출처 : 마이크로소프트웨어 2006년 12월호
Posted by 상현넘™

댓글을 달아 주세요

WCF - ASMX, WSE와 상호운영성
ASMX, WSE, 닷넷 리모팅 상호 비교

지난 호에서 WCF에 대한 프로그래밍 모델을 살펴보았다. 이번 호에서는 WCF의 기본적인 프로그래밍 모델을 기술하고 WCF 서비스를 구현하는 방법을 알아보고자 한다. 이어서 WCF 서비스를 호출하기 위한 클라이언트 구현 방법을 설명할 생각이다. 지난 호에서 소개된 WCF 서비스와 클라이언트를 구현하는 방법을 이해했다면, 다음은 왜 WCF를 써야 하는가라는 의문을 가진 독자들도 있을 것이다. 그럼 원격 메소드 호출 방법인 ASP.NET 웹 서비스(ASMX), WSE(Web Service Extension), 닷넷 리모팅 등과 WCF가 어떻게 다른지 살펴보자.


Web Service World

WCF는 마이크로소프트가 밀고 있는 서비스 기반 아키텍처(SOA; Service Oriented Architecture)를 위해 태어났다. 따라서 WCF는 웹 서비스의 다양한 기능을 제공하는 것에 그 초점이 맞추어져 있다고 봐도 과언이 아니다.

그럼 웹 서비스의 다양한 기능이라면 무엇을 말하는가? 대부분의 독자들이 알고 있는 웹 서비스는 SOAP 프로토콜을 구현하는 기본적인 XML 웹 서비스 호출일 것이다. 하지만 SOAP 프로토콜만으로는 실 세계에서 요구하는 다양한 요구사항을 만족시켜줄 수 없었다. 간단한 예를 들어, 웹 서비스의 메시지를 암호화 하고자 한다면 어떻게 해야하는가? 무작정 웹 서비스 메시지를 암호화 하고 복호화 할 수도 있겠지만 웹 서비스란 것이 상호 운영성(interoperability)를 위해 태어난 것을 감안하면, 임의의 웹 서비스를 호출하기 위해 클라이언트와 서비스가 동일한 방식의 암호화/복호화 방법을 사용하지 않으면 안 될 것이다.

또한 SOAP 프로토콜 만을 사용하면 파일 전송과 같이 대용량의 바이너리 데이터를 전송하기에는 XML은 턱없이 비효율적이다. 암호화나 바이너리 데이터 전송 외에도 메시지가 목적지에 도착하는 것을 장담(grantee of message delivery) 해주는 신뢰도 높은(Reliable) 메시징, 트랜잭션 처리 등이 필요하게 되었다.

이렇게 웹 서비스의 메시징, 보안, 신뢰도, 트랜잭션 등의 요구 사항을 만족시켜주기 위해 마이크로소프트, IBM, BEA 등은 웹 서비스에 SOAP 프로토콜 기반의 새로운 웹서비스 스펙을 만들기 시작했다. 웹 서비스는 XML 기반이며 이 XML을 이용하여 메시지를 전송하는데 이 때 메시징(Messaging) 스펙들이 필요하고 이 메시징 스펙 상에서 보안(Security), 신뢰도 높은 메시징(Reliable Messaging), 그리고 트랜잭션(Transactions) 스펙이 정의되어 있다.

마지막으로 웹 서비스가 어떤 계약(contract; 계약 보다는 인터페이스란 용어가 더 이해하는데 도움이 될 것같다), 바인딩 등으로 구성되어 있는지를 기술하는 메타데이터(Metadata)와 관련된 스펙들 역시 필요하다. <그림 1>은 웹 서비스에 관련된 다양한 스펙들의 카테고리를 보여주고 있다.


<그림 1> 웹 서비스 스펙들이 다루는 영역

이들 스펙들에 대해 좀더 자세하게 살펴보면, 웹 서비스의 메시징과 관련된 스펙들은 SOAP 프로토콜 1.1 및 1.2버전 스펙과 웹 서비스 메시지를 보내고 받는 종점(endpoint)을 명시하기 위한 WS-Addressing 스펙, 웹 서비스를 통해 효율적으로 XML 메시지를 인코딩 하여 바이너리 데이터 등을 전송하기 위한 스펙인 MTOM(Message Transmission Optimization Mechanism) 등이 존재한다.

보안과 관련된 웹 서비스 스펙으로는 XML 메시지를 암호화/복호화 하거나 서명하는데 사용되는 WS-Security, WS-Security 상에서 인증 토큰을 발급하여 상호를 신뢰하는 방식을 정의하는 WS-Trust 스펙, 그리고 서로 다른 다양한 인증 시스템 상에서 인증이 가능하도록 하는 WSFederation 등이 존재한다.

웹 서비스 메시지의 신뢰성(Reliability)에 관여되는 스펙들로는 웹 서비스 메시지가 목적지 종점까지 도착하는 것을 장담하도록 클라이언트와 서비스가 주고 받는 ACK(acknowledge) 메시지 등을 정의하는 WSReliableMessaging 스펙 등이 정의되어 있다. 트랜잭션 관련 스펙으로는 WS-AtomicTransaction 이 있으며 웹서비스의 메타데이터를 기술하는 스펙으로 익히 잘 알고 있는 WSDL 과 다른 웹 서비스 스펙이 서비스에 대한 보다 상세한 설명을 하기 위해 사용되는 WS-Policy 등의 스펙을 가지고 있다.

이들 웹 서비스들에 대한 다양한 스펙명이 몇몇을 제외하고 모두 WS- 로 시작하기 때문에 와일드 카드를 의미하는 문자, *을 사용하여 WS-* 로 표기하기도 한다.


Legacy Remote Call Technology

WCF가 추구하는 서비스 지향 아키텍처에서 핵심적으로 사용되는 웹 서비스의 다양한 프로토콜, 스펙 등을 알아 보았으니 이들과 관련된 닷넷 기술들을 살펴보도록 하자. 현존하는 닷넷의 웹 서비스 기술들을 이해하고 이들의 한계를 살펴봄으로써 WCF가 왜 등장하게 되었는지를 이해 할 수 있기 때문이다. 웹 서비스 기술 외에도 가장 많이 사용되는 MSMQ, EnterpriseServices 등 원격 호출 기술을 살펴보고 이들이 어떤 문제를 가지고 있는지 알아보자. 두 말할 필요도 없이 이들 문제를 해결하는 것이 WCF의 설계 목적이기 때문이다.


ASP.NET Web Service

흔히 닷넷에서 웹 서비스라 하면 ASP.NET이 제공하는 웹 서비스 인프라를 말한다. ASP.NET에서 웹 서비스를 제공하는 파일의 확장자가 .asmx이며 흔히들 닷넷의 웹 서비스를 ASMX라 부르기 때문에 편의상 여기에서도 ASP.NET 기반의 XML 웹 서비스를 ASMX라 부르기로 하겠다.

ASMX가 제공하는 웹 서비스는 상당히 기본적인 것만을 제공하고 있다. ASMX가 제공하는 웹 서비스는 SOAP 1.1 과 1.2 그리고 WSDL이 전부이다. <그림 1>을 살펴보면 XML과 메시징의 일부 그리고 메타데이터의 일부만을 구현하고 있는 것을 알 수가 있다. 좀 다른 각도로 얘기하면, ASMX는 WS-I라 불리는 상호운영성(interoperability) 프로파일(profile)만을 만족시키고 있다.

WS-I는 SOAP 프로토콜(1.1 및 1.2) 그리고 WSDL 1.1에 대해 웹 서비스의 상호운영성을 보다 높게 하기 위한 일련의 규칙들을 정의하고 있다. WS-I가 등장하게 된 이유는 SOAP 프로토콜과 WSDL 스펙을 구현하는 다양한 제품들이 스펙을 만족하고 있음에도 조금씩 그 구현을 다르게 함으로써 상호운영성이 떨어지는 것을 보완하기 위해 등장한 규칙이다.

ASMX가 WS-I를 만족한다는 얘기는 웹 서비스의 다양한 스펙 중 아주 기본적인 SOAP 프로토콜과 WSDL을 만족하고 있음을 의미한다. Visual Studio 2005에서 웹 서비스를 프로젝트에 추가하면 웹 서비스 클래스 코드에 등장하는 WebServiceBinding 특성(attribute)에도 잘 나타나있다.

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

이를 좀더 요약하자면 우리가 가장 많이 사용하는 웹 서비스 기술로서 ASMX는 달랑 SOAP과 WSDL로 표현되는 WS-I를 만족하는 것일 뿐이다. 또 ASMX 만으로는 효율적인 바이너리 데이터 전송, 메시지 수준의 보안, 신뢰도 높은 메시지 전송, 분산 트랜잭션들을 구현하기 매우 어렵다. 마이크로소프트의 한 자료에 의하면 ASMX만을 사용하여 보안, 신뢰도 높은 메시지 전송, 트랜잭션, 그것도 완벽하지 않은 상태로 구현하는데 5만 라인이 넘는 코드가 필요하다고 한다.

ASMX의 또 다른 아픔은 ASP.NET 기반이라는 점이다. IIS와 ASP.NET 엔진 없이는 서비스를 구현할 수가 없다. 이는 P2P 와 비슷하게 80 포트를 이용하여 데스크탑 애플리케이션끼리 웹 서비스로 서로 호출하는 프로그램을 작성하고자 할 때 ASMX를 사용할 수 없다.

ASMX가 ASP.NET에 의존하고 있고 ASP.NET은 IIS와 같은 웹 서버를 요구하기 때문이다. 이 때문에 웹 서비스가 클라이언트에게 무엇인가를 알려주기 위해 클라이언트를 호출하는 콜백(Callback) 메커니즘은 ASMX에서 상상하기 쉽지 않은 기법이 되어 버리기도 한다.


WSE; Web Service Extension

WSE(Web Service Extension)은 ASMX가 제공하는 웹 서비스의 기본에 추가적으로 메시징과 보안에 대한 웹 서비스 스펙들을 구현해 놓은 일종의 애드인(add-in)이 되겠다. 현재 닷넷 프레임워크 2.0에 대한 WSE 버전은 3.0이다. WSE 3.0은 무료로 다운로드 받을 수 있으며 Visual Studio 2005에 통합되어 손쉽게 사용할 수 있도록 되어 있다.

WSE가 구현하는 웹 서비스 스펙은 메시징에 관련하여 WS-Addressing, MTOM 스펙이며 보안에 관련하여 WS-Security, WS-Trust 등을 구현하고 있다. 신뢰도 높은 메시징과 트랜잭션에 대한 지원은 WSE에 포함되어 있지 않다.

WSE를 사용할 때 가질 수 있는 부담감은 이것의 근본이 애드인이기 때문에 WSE 런타임을 클라이언트와 서버에 모두 설치해 주어야 한다는 점이다. 또한 WSE 2.0에서는 성능이 떨어진다는 한계마저 가지고 있으니 메시지 수준의 보안이 큰 문제가 되지 않는 인트라넷에서는 WSE가 외면당하고 있는 현실이 수긍이 갈 것이다.


Other Remote Call Technology in .NET

닷넷이 상대적으로 진보된 웹 서비스 개발환경을 갖추고 있고 웹 서비스가 기술 흐름의 대세라고 하더라도 닷넷은 다양한 원격 호출 메커니즘을 가지고 있다. 닷넷 리모팅, 메시지 큐, EnterpriseService 등으로 대표되는 DCOM 관련 지원이 그것이다. 이들에 대해 간략하게 살펴보자.

- .NET Remoting
닷넷 리모팅은 초창기 DCOM을 대신할 것으로 각광을 받았던 닷넷 고유의 원격 객체 호출 기술이었다. 바이너리 포매터(binary formatter)와 TCP 채널(channel)이 성능이라는 관점에서는 ASMX의 추종을 불허할 정도의 장점을 가졌기 때문이다. 또 한가지 닷넷 리모팅은 ASMX와 달리 웹 서버(그것이 IIS 이건 아니건)를 요구하지 않는다. 자체적으로 닷넷 리모팅 메시지를 수신할 수 있는 리스너(listener)를 구현하고 있기 때문이다. 이 때문에 데스크톱 애플리케이션이나 Windows 서비스 애플리케이션이 닷넷 리모팅의 서버로서 구현 가능한 강점을 가지고 있다.

닷넷 리모팅의 최대의 단점은 상호운영성이다. 닷넷 리모팅이 사용되면 서비스(서버)와 클라이언트는 모두 닷넷으로 구현되어야만 한다. 비록 SOAP 포매터와 HTTP 채널이 존재하기도 하지만 닷넷이 아닌 상대와 메시지를 주고 받기 위해서는 큰 제약이 따를 뿐 더러 ASMX에 비해 성능상의 장점이 거의 없다고 볼 수 있다. XML과 상호운영성이 크게 강조되고 있는 현실과 SOA를 생각해 보면, 다양한 플랫폼을 보유한 대부분의 기업에서 선뜻 닷넷 리모팅을 인프라로 선택하기가 만만치 않다는 것을 이해할 수 있을 것이다.

성능을 위해 바이너리 포매터과 TCP 채널을 선택했을때 닷넷 리모팅의 또 다른 단점은 클라이언트를 위한 프록시 코드 생성이 자동화 되어 있지 않다는 점이다. 이 때문에 서비스를 구현하는 어셈블리(DLL)를 클라이언트에 배포하거나 서비스를 감싸는(wrapping) 껍데기 클래스를 작성하고 이것을 배포해야만 한다. 이는 곧 생산성 저하와 연결되곤 한다.

- Message Queue (MSMQ)
메시지 큐란 말 보다 MSMQ란 이름이 훨씬 더 친숙한 이 메시징 인프라는 Windows NT 시절부터 제공되었다. 다양한 용도로 여러 애플리케이션에서 활용되어 왔다. MSMQ는 닷넷 이전부터 제공되어 온 인프라이기 때문에 닷넷 프레임워크에서도 System.Messaging 네임스페이스를 통해 MSMQ API를 제공해 왔다.

MSMQ를 사용하는 가장 큰 이유는 비동기(asynchronous)적으로 메시지를 목적지까지 배달(delivery)할 수 있다는 점이다. 즉 MSMQ는 신뢰도 높은 메시지 전달 기능을 제공한다. 네트워크가 오프라인이거나 메시지가 목적지까지 전달되는 중간에 유실되더라도 재전송(retransmit)과 ACK(acknowledge) 기법을 통해 비동기적으로 메시지의 전송을 보장해 준다. 또한 MSMQ는 트랜잭션을 지원하기 때문에 다양한 트랜잭션 애플리케이션들과도 잘 융합되어 작동한다는 장점도 있다. 마지막으로 MSMQ는 자체 수준의 메시지를 암호화하는 보안 기능을 제공하기도 한다.

MSMQ의 단점은 이것이 설계되고 개발된 시점에서 인터넷 표준을 거의 만족시키지 못한다는 사실이다. 사용하는 프로토콜도 MS-RPC(Microsoft Remote Procedure Call) 기반이므로 방화벽 통과가 어렵고, 메시지 포맷 역시 바이너리일 뿐 더러 인터넷 표준이 아니기 때문에 메시지를 보내는 쪽도 받는 쪽도 모두 Windows 운영체제를 요구한다는 점이다.

비록 HTTP를 통해 MSMQ 메시지를 전송할 수는 있지만 여전히 이기종 간 상호운영성은 크게 떨어진다. 굳이 <그림 1>과 비교해 설명하자면 신뢰도 높은 메시징과 트랜잭션 정도이며, XML, 메시징, 메타데이터는 MSMQ에 의해 커버되지 않는 영역이 된다.

- EnterpriseServices (DCOM)
닷넷에서 EnterpriseServices라 함은 COM+에 관련된 다양한 기능을 말한다. COM+ 컴포넌트를 작성할 때 ApplicationActivation 특성(attribute)에 서버 타입으로 ActivationOption.Server로 주면 원격 컴퓨터에 존재하는 COM+ 컴포넌트를 호출할 수 있다. 이 때 사용되는 원격 호출 기술이 바로 DCOM 이다.

DCOM은 MS-RPC 기술의 일종이다. 메시지 수준의 보안과 트랜잭션을 지원하면 MSMQ와 함께 사용될 때는 신뢰도 높은 메시징까지 지원할 수 있을 뿐 더러 타입 라이브러리를 통해 어느 정도 메타 데이터를 제공할 수도 있다. VB 6.0과 C++와 같이 unmanaged 코드를 사용하면 성능 역시 상당히 좋은 편이여서 닷넷이 등장하기 전까지 Windows 동네에서는 원격 객체 호출의 표준이었다. 지금도 여전히 운영체제 관리와 WMI 등에서 널리 사용되고 있다.

DCOM은 다른 원격 호출에 비해 <그림 1>에서 봤듯이 많은 영역을 커버하고 있음은 분명하다. 하지만 DCOM의 상호운영성이 크게 떨어진다는 점이 DCOM의 최대 단점이다. DCOM이 방화벽을 통과하기란 낙타가 바늘구멍 빠져나가는 것만큼이나 어렵고 W3C의 표준이라곤 단 하나도 준수하지 않는 오로지 마이크로소프트 만의 기술이기 때문이다. 사실 마이크로소프트가 주도하고 강력하게 밀어붙인 한 웹 서비스(SOAP 프로토콜)가 DCOM, CORBA, 자바의 RMI의 상호운영성 한계 때문임을 고려해 봤을 때 DCOM의 낮은 상호운영성이 얼마나 마이너스적인 요소인지 이해가 될 것이다.

DCOM의 또 다른 단점은 닷넷과의 부조화이다. DCOM이 unmanaged 기술이며 COM에 기반하고 있기 때문에 닷넷에서 DCOM 프로토콜이 사용되면 CCW (Com Callerable Wrapper)와 RCW(Runtime Callerable Wrapper)가 관여될뿐더러 managed 코드와 unmanaged 코드 사이에서 전이가 자주 발생하여 성능상 손해가 상당하다는 점이다. 이 때문에 닷넷으로 COM+를 구현할 때 서버 타입보다는 라이브러리 타입을 사용하는 이유가 서버 타입을 사용하면 어쩔 수 없이 DCOM이 관여되고 이 때문에 성능 저하와 시스템 불안정이 뒤따르기 때문이다.


WCF의 당위성

지금까지 웹 서비스의 다양한 표준들과 닷넷에서 사용 가능한 다양한 원격 호출 기술들을 살펴보았다. 각 기술들의 장점과 단점을 간략하게 언급하였으므로 왜 마이크로소프트가 차세대 메시징(원격 호출) 인프라로서 WCF를 내놓았는지 조금이나마 감을 잡았으리라 생각된다. 이제부터 좀더 구체적으로 살펴 보자.

- 표준 기반의 상호운영성
상대적으로 오래된 기술인 MSMQ나 DCOM은 다양한 기종의 플랫폼상에서는 작동하지 않는다는 상호운영성 문제를 가지고 있다. 마이크로소프트는 수년 전부터 자사의 제품, 기술들이 갖는 상호운영성 문제를 해결하기 위해 적극적으로 인터넷 표준들을 준수하거나 새로운 인터넷 표준을 제안해 왔다. XML 만을 보더라도 그렇다. 마이크로소프트는 더 이상 바이너리 형태로 데이터를 주고 받는 애플리케이션을 내놓지 않는다. 앞으로 등장할 Office 2007의 문서들은 XML 기반의 문서이며 SQL Server는 SQL Server 2000 버전부터 꾸준히 XML에 대한 지원을 늘려오고 있다.

따라서 WCF는 철저히 XML 기반의 메시징 인프라이다. NetTcpBinding 을 사용하고, TCP 프로토콜을 사용하여 원격 호출을 하더라도 주고 받는 메시지는 XML 이다. 비록 NetTcpBinding이 메시지를 바이너리로 인코딩/디코딩 하는 한이 있더라도 메시지 자체는 XML로 되어있다. 이것이 닷넷 리모팅, MSMQ, DCOM과 WCF가 다른 점이라 할 수 있다. XML로 메시지가 구성되어 있고, 대부분의 플랫폼이 텍스트 기반의 XML을 처리할 수 있기 때문에 상호운영성은 증대되는 것이다.

또한 WCF는 WS-* 로 대표되는 웹 서비스 스펙들을 대다수 준수하고 있다. 메시징을 위해 SOAP, WS-Addressing, MTOM을 구현하며, 메시지 보안을 위해 WSSecurity, WS-Trust, WS-Federation 등의 스펙을 구현하고 있다.

WCF가 이렇게 다양한 웹 서비스에 대한 국제적 표준을 준수함에 따라 이들 표준을 준수하는 어떤 플랫폼과도 상호 연동이 가능한 메시징 인프라가 될 수 있는 것이다.

- 프로그래밍 모델의 통합
WCF가 상호운영성과 더불어 강조하는 것은 ASMX, WSE, 닷넷 리모팅, MSMQ, DCOM 등 분산 애플리케이션에서 사용하는 다양한 원격 호출 기술을 WCF라는 하나의 프로그래밍 모델로 통합하고자 한다는 점이다. 이에 대해서는 구체적인 예를 들어 설명하는 것이 보다 이해가 빠를 것이다.

상당수 기업들은 신규 애플리케이션을 닷넷 플랫폼으로 개발하고자 한다. 이 애플리케이션의 요구 사항 중 하나는 기존에 존재하는 ASP 웹 애플리케이션과 자바 애플리케이션과 상호 연동이다. 상호운영성을 고려한다면 애플리케이션의 클라이언트와 서버는 웹 서비스를 원격 호출 메커니즘으로 선택하는 것이 지극히 당연하다.

따라서 개발 생산성과 운영의 편리함 등등을 고려하여 이 애플리케이션의 서버는 IIS 상에 호스팅 되는 ASMX 기반으로 비즈니스 로직을 구현하고 데이터베이스를 액세스하는 구조를 가지게 될 것이다. 비즈니스 로직을 COM+ 기반의 컴포넌트로 구현하더라도 클라이언트 호출을 위해 .asmx와 관련된 클래스를 구현해야 함은 물론이다.

만약 이 애플리케이션의 또 다른 요구 사항 중 하나가 다수의 사용자에 대해서도 빠른 응답 속도를 요구하는 고성능 애플리케이션이라면 어떻겠는가? 서버의 하드웨어 사양이나 데이터베이스 설계 등 고려할 사항이 많을 수 있겠지만 원격 호출 메커니즘의 성능적인 요소도 무시할 수 없다. 오로지 성능만을 고려한다면 닷넷은 닷넷 리모팅, 특히 바이너리 포매터와 TCP 채널을 사용하는 원격 호출 방식을 취해야 한다.

그런데 정작 문제는 ASMX와 닷넷 리모팅의 프로그래밍 모델에서 공통점이 거의 없다는 점이다. 전형적인 ASMX 프로그래밍 모델은 .asmx 파일과 이를 위한 웹 서비스 구현 클래스(WebService 클래스에서 파생된)이지만 닷넷 리모팅은 MarshalByRefObject 에서 파생된 클래스와 이를 위한 설정(configuration)이다. 클라이언트를 구현하는 코드 역시 유사성이 거의 없다. ASMX의 경우 프록시를 생성하고 이 프록시를 호출하는 형태이지만 닷넷 리모팅은 원격으로 호출하고자 하는 클래스의 타입 정보를 반드시 필요하다.

이 뿐인가? 애플리케이션이 신뢰도 낮은 WAN 상에서도 구동하도록 하려면, 메시지가 네트워크 어디에서인가 유실되더라도 재전송 등을 통해 메시지 전달을 보장하는 MSMQ를 써야 할 것이다. MSMQ의 프로그래밍 모델은 ASMX와 닷넷 리모팅과 더욱 더 차이가 난다. ASMX와 닷넷 리모팅은 메소드 호출이 자동적으로 메시지 형태로 변환되지만 MSMQ는 메시지 자체를 만들어야 한다. 더욱더 가관인 것이, MSMQ는 메시지 큐를 리스닝 하는 리스너 코드도 작성해야 한다.

상황이 이러하기 때문에 지금까지는 서버의 비즈니스로직은 완전히 분리하여 컴포넌트로 구현하고(그것이 COM+ 기반이건 아니건 관계 없이) 이 컴포넌트를 다양한 원격 호출 기술 기반의 퍼사드(fa?ade)로 감싸는 것이 일반적인 방법이 되어 왔다. 보다 구체적으로 예를 들어 보자. 주문 관리 서비스를 위해 주문 관리 비즈니스 로직을 COM+ 기반의 컴포넌트로 구현한다. 그리고 ASP나 자바 클라이언트를 위해 컴포넌트의 인터페이스를 ASMX 혹은 ASMX + WSE로 구현한다. 물론 이 구현은 COM+로 작성해 놓은 주문 관리 비즈니스 로직 컴포넌트를 호출할 것이다.

이젠 인트라넷의 클라이언트를 위해 높은 성능을 내는 닷넷 리모팅 래퍼 클래스를 작성한다. 이 클래스는 MarshalByRefObject 에서 파생된 클래스로서 단순히 클라이언트 호출을 COM+ 컴포넌트로 전달하는 역할만을 한다. 닷넷 리모팅 클라이언트가 직접 COM+ 컴포넌트를 호출할 수는 없다. 클라이언트가 COM+ 컴포넌트를 직접 호출하려면 이 컴포넌트 클래스를‘참조’해야 하는데 이를 위해서는 COM+ 컴포넌트를 구현하는 클래스를 클라이언트에 배포해야 하기 때문이다.


<그림 2>다양한 클라이언트를 위한 다양한 인터페이스 구현

<그림 2>는 다양한 요구사항에 따른 여러 원격 호출 기술을 사용하여 다양한 클라이언트를 서비스하는 모습을 보여주고 있다. <그림 2>는 다양한 클라이언트를 모두 지원하는 잘 구성된 아키텍처처럼 보인다. 그렇다. 마이크로소프트의 Pattern & Practice 역시 이러한 아키텍처를 사용하길 권장할 정도로 이 모습에는 문제가 없어 보인다. 하지만 <그림 2>는 상호운영성을 높이고 다양한 클라이언트를 위해 ASMX 혹은 ASMX + WSE를 구현하는 웹 서비스 인터페이스를“구현”해야 하며, 성능을 위해서는 닷넷 리모팅 인터페이스를“구현”해야 한다.

개발자의 관점에서 <그림 2>는 상당히 고통스럽다. ASMX 및 WSE 웹 서비스에 대한 기술과 프로그래밍 모델, API 등을 알아야 하며 닷넷 리모팅도 잘아야 할 뿐더러 들어보지도 못한 MSMQ에 대한 코딩 능력까지 갖추어야만 한다. 누군들 한숨이 나올 법하지 않은가?


<그림 3> WCF의 서비스와 종점(Endpoint)


<그림 4> WCF와 다양한 바인딩

이처럼 WCF는 다양한 기술들을 통합하는 프로그래밍 모델을 제시한다. 지난 10월호에 게재한 그림을 다시 떠올려보자(그림 3 참조). WCF 서비스는 서비스를 정의하는 인터페이스와 이 인터페이스를 구현하는 서비스 클래스로서 구현한다. 그리고 이 서비스에 접근하기 위한 종점(endpoint)은 다양하게 정의될 수 있다. 종점은 다시 주소와 계약(contract), 바인딩으로 구성된다는 것 역시 이미 알고 있을 것이다. 계약이야 서비스의 인터페이스가 될 것 이므로 논외로 하고, 바인딩과 주소에 주목해 보자. 바인딩은 웹 서비스를 호출할 때 사용할“방법”을 정의하고 있다. 즉, 바인딩은 HTTP를 사용할 것인지 TCP를 사용할 것인지 혹은 MSMQ를 사용할 것인지 트랜스포트(transport)를 결정할 뿐 만 아니라 XML 메시지에 대한 보안을 어느 수준에서 수행할 것인지, 세션이나 신뢰성을 유지할 것인지 등 어떤 바인딩을 사용하는가에 따라서 달라진다.

출처 : 마이크로소프트웨어 2006년 11월호
TAG ASMX, WCF, WSE
Posted by 상현넘™

댓글을 달아 주세요

요약 : 이 기사에서는 Windows WF(Workflow Foundation)를 사용하여 작성한 워크플로가 WCF(Windows Communication Foundation)를 사용하여 만든 서비스 내에서 어떻게 호스팅되는지에 대한 개요를 제공합니다. 또한 WCF에서 제공되는 광범위한 기능 중 일부를 활용하여 이중 채널을 통해 클라이언트-이벤트 콜백을 용이하게 하는 방법에 대해서도 설명합니다.

"Windows Workflow Foundation Sample Integrating WF and WCF"를 다운로드하려면 여기 (영문)를 클릭하십시오.

소개

Microsoft는 Windows WF(Workflow Foundation) 출시에 맞춰 .NET 개발자 플랫폼에 워크플로 기능을 도입하고 있습니다. 개발자는 이 기능을 사용하여 간단한 순차 워크플로부터 정교한 사용자 상호 작용을 포함하는 복잡한 상태 컴퓨터 기반 워크플로에 이르기까지 광범위한 시나리오를 충족하는 워크플로를 구축할 수 있습니다.

이와 동시에 캡슐화된 서비스 끝점을 통해 비즈니스 기능을 제공하여 비즈니스 기능과 프로세스의 재사용 및 결합을 가능하게 함으로써 서비스 지향 아키텍처를 구성하는 경향이 늘어나고 있습니다. WCF(Windows Communication Foundation)는 일관성 있는 개발자 API, 강력한 호스팅 런타임, 그리고 배포 작업을 돕는 유연한 구성 기반 솔루션을 통해 개발자가 손쉽게 연결된 시스템을 개발할 수 있는 기능을 제공합니다.

이 문서의 마지막에는 WF 및 WCF에 대해 더 배우고자 할 때 참조할 수 있는 추가 리소스 목록이 있습니다.


비용 보고 샘플

이 기사의 코드 샘플은 직원의 비용 청구를 제출 및 승인하는 표준 비즈니스 프로세스를 모델링하는 비용 보고 워크플로 샘플을 기반으로 합니다. 이 원본 샘플은 WCF 및 .NET 3.0 Framework를 활용하여 이 시나리오를 더욱 효과적으로 호스팅하는 방법을 보여 주도록 업데이트되었습니다.

비용 보고 샘플의 첫 릴리스에서는 워크플로 런타임 인스턴스가 포함된 호스트 응용 프로그램과 클라이언트 응용 프로그램 간의 통신 기능을 제공하기 위해 .NET Remoting을 사용했습니다.

그러나 이번에는 비용 보고 구현을 리팩터링하여 클라이언트와 서비스 간 통신에 WCF를 사용하도록 만들었습니다. 또한 솔루션 내의 다양한 고려 사항을 분류하기 위해 논리적인 솔루션 구조를 사용했습니다.


그림 1. 리팩터링된 솔루션의 구조

설계에 메시지를 통합하기 위해서는 이러한 메시지가 비즈니스 프로세스 컨텍스트 내에서 어떻게 사용되는지 이해하는 것이 중요합니다. 비용 보고의 수명 주기에는 여러 개의 상호 작용 지점이 있습니다. 이러한 상호 작용 지점을 간단히 검토해 보겠습니다.

  • 프로세스에는 '클라이언트', '관리자'및 비용 보고 '호스트' 시스템이라는 세 가지 관계 요소가 있습니다.
  • '클라이언트'가 새 비용 청구를 제출하면 프로세스가 시작됩니다.
  • 규칙의 '정책'을 사용하여 비용 청구를 자동으로 승인할 수 있는지 판단합니다.
  • 비용 청구가 자동으로 승인되지 않은 경우 요청을 승인할 '관리자'가 필요합니다. 관리자는 승인해야 하는 새로운 보고가 있는지 확인하거나 알림을 받아야 합니다.
  • 관리자가 '대기' 시간(조정 가능) 동안 승인하지 않으면 자동으로 청구가 거부됩니다.
  • 청구를 검토한 후 '클라이언트'와 '관리자'는 검토 결과에 따라 업데이트되어야 합니다.

WF를 사용하면 프레임워크에서 제공하는 표준 작업을 사용하여 이러한 프로세스를 모델링할 수 있습니다. DelayActivity를 사용하면 일정 기간이 지난 뒤의 이벤트 트리거를 관리할 수 있고, 규칙 엔진 및 PolicyActivity를 사용하면 결과에 대한 질문을 받는 융통성 있는 규칙 집합을 관리할 수 있습니다.

이 프로세스는 사용자 지향 프로세스이므로 개발자는 최종 사용자와 상호 작용하고 이러한 상호 작용이 발생하도록 워크플로를 구성해야 합니다. WF는 로컬 서비스인 HandleExternalEventActivityCallExternalMethodActivity를 통해 호스트 및 워크플로 간 통신을 허용하는 포괄적인 프로그래밍 모델을 제공합니다.

이 부분은 대화형 워크플로를 구축하는 데 있어 중요한 개념이므로 WF 설계에 어떻게 반영되었는지 간략하게 살펴보겠습니다.

WF에서 상호 작용을 모델링하기 위해서는 여러 이벤트와 메서드를 제공하는 계약을 설계해야 합니다. 이 계약은 워크플로 및 호스트 프로세스 양쪽에서 모두 인식됩니다. 구축하려는 계약/인터페이스는 워크플로-데이터 교환을 위해 설계되었음을 식별하는 [ExternalDataExchange()] 특성으로 표시해야 합니다. 샘플에서는 워크플로에 IExpenseLocalService 인터페이스를 사용합니다.

그런 다음 워크플로 런타임으로 해당 인터페이스를 구현하는 클래스(로컬 서비스라고 함)를 등록합니다. 워크플로 작업은 이벤트에 등록하거나, 인터페이스 유형에 대해 정의된 메서드를 사용할 수 있으며, 등록한 로컬 서비스로 연결됩니다. 여기에서는 로컬 서비스의 기반 형식과 워크플로 간의 밀결합을 제거하는 제어 반전(Inversion of Control)이라고 하는 패턴을 사용합니다. 샘플에서는 ExpenseLocalService 클래스가 IExpenseLocalService 계약을 구현합니다.

워크플로는 처음 실행될 때 작업할 초기 데이터를 받을 수 있습니다. 워크플로가 외부 상호 작용이 필요한 지점에 이르면 워크플로 내에서 HandleExternalEventActivity에 바인딩할 수 있는 이벤트를 발생시킬 수 있습니다. 이 작업은 인터페이스 유형과 이벤트를 인수로 취하며, 이벤트가 발생하여 실행을 계속하도록 허용하면 워크플로가 가동을 시작합니다.

워크플로에서 로컬 서비스를 콜백해야 하는 경우 CallExternalMethodActivity를 사용하고 인터페이스 및 메서드 이름을 인수로 제공하면 됩니다.

이러한 작업을 사용하여 실행 중인 워크플로가 있는 호스트 프로세스 내에서 양방향 통신을 수행할 수 있으며 WF 내에서 제어 반전 패턴을 사용하여 워크플로와 로컬 서비스 간의 밀결합을 방지할 수 있습니다.

그러나 호스트 프로세스보다 범위를 넓혀 다른 시스템 또는 사용자에 의한 상호 작용을 허용해야 합니다. 다른 서비스나 사용자 기반 응용 프로그램에서 호출할 수 있는 모든 서비스에 대화식 작업을 분산하면 이러한 수준의 상호 작용을 달성할 수 있습니다. WCF는 이러한 메시징 기능을 유연하게 구축할 수 있는 프레임워크입니다.

이번 시나리오에서 WCF와의 통합이 주는 주요 이점은 다음과 같습니다.

  • 서비스 구현을 메시징 내부 작업 코드에서 분리할 수 있습니다.
  • 시스템 연결을 위한 코드 및 복잡성이 훨씬 줄어듭니다.
  • 배포 유연성을 확보할 수 있습니다.
  • 호스트에서 클라이언트로 직접 콜백을 사용할 수 있으므로 더 빠르고 오버헤드가 적은 방법으로 정보를 업데이트할 수 있습니다.

통합을 위한 검사 목록

WF와 WCF의 통합을 완료하려면 사용자가 워크플로를 시작하거나 실행 중인 워크플로와 상호 작용할 수 있는 여러 인터페이스 지점을 제공하는 서비스 인터페이스를 노출해야 합니다. 서비스는 비즈니스 프로세스가 외부 엔터티(예: 프로세스에 관련된 사용자)와 상호 작용하는 지점을 중심으로 모델링해야 합니다.


그림 2. 비용 보고 시나리오의 상호 작용 지점

이를 위해서는 다음을 수행해야 합니다.

  • 서비스 계약을 정의합니다.
  • 이벤트를 통해 새로운 워크플로를 만들거나 기존 워크플로와 상호 작용하는 서비스 작업을 구현합니다.
  • 서비스 호스트 내에서 워크플로 런타임 인스턴스를 호스팅합니다.

단순히 워크플로를 호스팅하는 것 이외에도 WCF 이중 채널을 활용하여 워크플로의 이벤트를 소비 클라이언트에서 발생시킬 수 있습니다. 비용 보고의 경우 솔루션이 정기적인 데이터 업데이트를 위해 서비스를 폴링하는 클라이언트에 의존하므로 이러한 기능이 도움이 됩니다. 클라이언트는 이러한 방법 대신 서비스에서 직접 알림을 받을 수 있습니다.

이를 위해서는 다음을 수행해야 합니다.

  • 콜백 계약을 정의합니다.
  • 이중 채널을 지원하는 바인딩을 사용합니다.

서비스 계약 정의

WCF(Windows Communication Foundation)는 서비스의 기능과 데이터 교환을 추상적으로 정의하는 공식 계약을 선언하도록 요구합니다. 이 계약은 코드에서 인터페이스 선언을 통해 정의합니다.

비즈니스 서비스를 설계할 때는 일반적으로 요청/응답 공동 작업 패턴을 사용하게 됩니다. 이 패턴을 사용하는 경우 제공하려는 계약에서 다음과 같은 세 가지 측면을 준비해야 합니다.

  • 게시되는 작업. 서비스가 해당 소비자에게 게시하는 기능이며 인터페이스상의 메서드입니다.
  • 각 요청 및 응답에 대해 구조화된 데이터를 캡슐화하는 메시지. 각 메서드에 대한 인수 및 반환 형식입니다. WCF 용어로는 대개 메시지 계약이지만 비교적 간단한 시나리오에서는 데이터 계약이 됩니다.
  • 서비스를 통해 교환할 수 있는 핵심 비즈니스 엔터티의 데이터 정의. 메시지의 일부를 구성하는 요소로, WCF 용어로는 데이터 계약이 됩니다.

서비스 계약은 작업을 노출하는 계약을 정의한 후 연결을 통해 게시되는 특정 작업을 정의하는 특성 기반 태그를 사용하여 정의됩니다.

각 서비스 계약은 [ServiceContract] 특성으로 명시적으로 표시됩니다. 이 특성은 다음과 같은 매개 변수와 함께 선언할 수 있습니다.

  • Name. WSDL <portType> 요소에 선언된 계약 이름을 제어합니다.
  • Namespace. WSDL <portType> 요소에 선언된 계약의 네임스페이스를 제어합니다.
  • SessionMode. 계약에 세션을 지원하는 바인딩이 필요한지 여부를 지정합니다.
  • CallbackContract. 클라이언트 콜백에 사용될 계약을 지정합니다.
  • ProtectionLevel. 계약에 ProtectionLevel 속성을 지원하는 바인딩이 필요한지 여부를 지정합니다. 이 속성은 암호화 및 디지털 서명을 위한 요구 사항을 선언하는 데 사용됩니다.
작업 선언

이제 서비스는 여러 개의 게시된 작업으로 구성됩니다. 작업은 [OperationContract] 특성으로 표시되어 명시적으로 계약에 포함됩니다. ServiceContract와 마찬가지로 OperationContract에는 끝점에 바인딩되는 방식을 제어하는 다음과 같은 여러 매개 변수가 있습니다.

  • Action. 이 작업을 고유하게 식별하는 이름을 제어합니다. 메시지가 끝점에 수신되면 발송자는 제어 및 작업을 사용하여 호출할 메서드를 결정합니다.
  • IsOneWay. 작업이 요청 메시지를 받지만 응답은 생성하지 않음을 나타냅니다. 이는 단순히 void 반환 형식을 반환하는 것(이 경우 결과 메시지가 생성됨)과는 다릅니다.
  • ProtectionLevel. 작업에 필요한 암호화 또는 서명 요구 사항을 지정합니다.

다음은 코드에서 서비스 계약 부분을 보여 주는 예제입니다.

[ServiceContract]
public interface IExpenseService
{
    [OperationContract]
    GetExpenseReportsResponse GetExpenseReports();

    [OperationContract]
    GetExpenseReportResponse GetExpenseReport(GetExpenseReportRequest getExpenseReportRequest);
}

메시지 및 데이터 엔터티 선언

메시지는 전송할 각 메시지에 대한 페이로드 또는 본문을 정의하는 클래스로 모델링하는 것이 좋습니다. 이는 ASP.NET을 사용하여 웹 서비스를 구축할 때 WSCF(WS Contract First)와 같은 도구를 사용하여 메시지를 모델링하는 방식과 비슷합니다.

WCF는 기본적으로 DataContractSerializer라는 serialization 엔진을 사용하여 데이터를 serialize(데이터를 XML로 변환) 및 deserialize(XML을 데이터로 변환)합니다. DataContractSerializer를 사용하기 위해 System.Runtime.Serialization 네임스페이스에 대한 참조를 추가한 후 클래스에 [DataContract] 특성을 표시하고 게시할 멤버를 [DataMember]로 표시합니다.

[DataContract]
public class GetExpenseReportsResponse
{
    private List<ExpenseReport> reports;

    [DataMember]
    public List<ExpenseReport> Reports
    {
        get { return reports; }
        set { reports = value; }
    }
}

메시지 내에서 사용되는 데이터 엔터티는 비즈니스 영역 내의 엔터티를 나타냅니다. 메시지 계약과 마찬가지로 DataContractSerializer 및 특성을 사용하여 배포되는 멤버를 명시적으로 포함할 수 있습니다. 또는 데이터 모델링만 수행하려는 경우에는 공용 필드 방식을 사용하고 클래스를 serialize 가능한 것으로 표시할 수 있습니다.

샘플에서는 메시징 태그 처리를 위해 데이터 계약 방식을 사용했습니다. 실제 시나리오에서는 복잡한 스키마, 스키마에서의 속성 사용, 그리고 SOAP 헤더 사용 요구 사항을 처리해야 하는 경우가 많습니다. WCF는 이러한 극한의 상황을 위해 본문만이 아닌 전체 SOAP Envelope를 기술하는 [MessageContract] 특성으로 표시되는 클래스를 정의하는 기능을 제공합니다.

데이터 계약과 메시지 계약에 대한 자세한 내용은 이 기사의 마지막 부분에 있는 추가 정보에서 각 해당 MSDN Library 기사를 참조하십시오.


워크플로 런타임 호스팅

서비스에서는 일반적으로 서비스 형식의 새 인스턴스가 생성되고 세션 수명 주기 동안 유지 관리되는 동시 동작을 허용합니다. 이러한 상황에서 워크플로를 사용하려면 호출별로 실행하는 대신 워크플로 런타임의 인스턴스를 만들어 서비스-호스트 인스턴스의 수명 동안 이를 유지 관리해야 합니다.

이를 위해 권장되는 방식은 호스트 서비스 생성 시에 활성화되는 확장 클래스를 사용하는 것입니다. 이 확장은 워크플로 런타임의 전역 인스턴스를 만들고 유지 관리하며, 각 독립 서비스 인스턴스에서 이를 액세스할 수 있도록 허용합니다.

ServiceHost에 확장을 구현하려면 IExtension<ServiceHostBase>를 구현하는 클래스를 만듭니다. 솔루션에서는 WcfExtensions 코드 프로젝트 아래에 있는 WfWcfExtension 클래스를 통해 이에 대한 예제를 볼 수 있습니다.

여기에서는 두 가지 메서드, 즉 확장이 해당 부모 개체에 연결될 때 호출되는 Attach와 부모 개체가 언로드될 때 호출되는 Detach를 구현해야 합니다.

다음에서 볼 수 있듯이 Attach 메서드는 WorkflowRuntime의 새 인스턴스를 만들어 필요한 서비스로 인스턴스화합니다. 이 인스턴스는 workflowRuntime이라는 로컬 개인 필드에 저장합니다.

void IExtension<ServiceHostBase>.Attach(ServiceHostBase owner)
{
    workflowRuntime = new WorkflowRuntime(workflowServicesConfig);
    ExternalDataExchangeService exSvc = new ExternalDataExchangeService();
    workflowRuntime.AddService(exSvc);
    workflowRuntime.StartRuntime();
}

여기에서 볼 수 있듯이 워크플로 런타임의 초기화에는 시작하기 전에 서비스 인스턴스를 런타임에 추가하는 작업이 포함됩니다. 솔루션을 구축할 때는 일반적으로 런타임을 시작하기 전에 모든 서비스를 추가하는 것이 좋습니다. 그러나 결합이 문제시되는 경우에는 후기 바인딩 방식을 사용하는 것이 더 적합할 수 있습니다.

샘플에서는 WorkflowRuntime이 시작된 후에 ExpenseService 클래스에 있는 SetUpWorkflowEnvironment 메서드의 일부로 ExpenseLocalService 인스턴스를 ExternalDataExchangeService에 추가합니다.

다음에서 볼 수 있는 Detach 메서드는 StopRuntime을 호출하여 런타임을 종료합니다.

void IExtension<ServiceHostBase>.Detach(ServiceHostBase owner)
{
    workflowRuntime.StopRuntime();
}

WorkflowRuntime이 서비스-호스트 시작의 일부로 생성 및 초기화되므로 모든 기존 워크플로는 서비스 호출이 수행되기 전에 진행할 수 있게 됩니다. 서비스 호스트가 종료되면 워크플로 런타임도 깔끔하게 종료됩니다.

참고   워크플로를 호스팅하고 장시간 실행되는 워크플로를 모델링하는 경우에는 일반적인 방법인 워크플로 지속성 서비스(예: SqlWorkflowPersistenceService)를 사용하는 것이 좋습니다. 이는 응용 프로그램이나 프로세스가 다시 시작되더라도 상태 지속성을 유지하는 메커니즘을 제공합니다.
서비스 작업 만들기

서비스 동작을 포함하는 클래스를 만들려면 서비스 계약을 정의하는 인터페이스를 한 개 이상 구현해야 합니다.

public class ExpenseService :
        IExpenseService,
        IExpenseServiceClient,
        IExpenseServiceManager

워크플로와의 통합을 위해 서비스 메서드는 비즈니스 논리를 포함하지 않지만 대신 이벤트를 제어하거나 비즈니스 프로세스를 캡슐화하는 실행 중인 워크플로에 이벤트를 발생시키는 코드를 포함합니다.

워크플로를 처리하는 작업에서는 새 워크플로를 시작하거나 이미 실행 중인 워크플로와 상호 작용하게 됩니다.

새 워크플로 인스턴스를 만들려면 WorkflowRuntime을 사용하여 원하는 워크플로 유형의 새 인스턴스를 인스턴스화해야 합니다. ServiceHost 확장 클래스에서 이미 이러한 인스턴스를 만들었습니다. 이 인스턴스에 대한 참조를 얻으려면 OperationContext를 사용하여 사용자 지정 확장을 찾아야 합니다.

WfWcfExtension extension = OperationContext.Current.Host.Extensions.Find<WfWcfExtension>();
workflowRuntime = extension.WorkflowRuntime;

OperationContext는 서비스 메서드의 실행 컨텍스트에 대한 액세스를 제공하는 클래스입니다. 앞에 있는 코드를 보면 알 수 있듯이 이 클래스는 현재 서비스 메서드에 대한 컨텍스트를 제공하는 Current라는 단일 항목을 제공합니다. Host 속성을 호출하여 실행 중인 ServiceHost로 인스턴스를 가져온 다음 그 유형을 기반으로 확장을 찾습니다.

확장 인스턴스에 대한 참조를 확보한 다음에는 공용 속성을 통해 WorkflowRuntime을 반환하고 이를 사용하여 SequentialWorkflow의 새 인스턴스를 만들 수 있습니다.

Guid workflowInstanceId = submitExpenseReportRequest.Report.ExpenseReportId;

Assembly asm = Assembly.Load("ExpenseWorkflows");
Type workflowType = asm.GetType("ExpenseWorkflows.SequentialWorkflow");

WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(workflowType, null, workflowInstanceId);
workflowInstance.Start();

expenseLocalService.RaiseExpenseReportSubmittedEvent(workflowInstanceId, submitExpenseReportRequest.Report);

위의 코드에서는 미리 정의된 유형을 바탕으로 새 워크플로 인스턴스를 만듭니다. 이는 직접적인 유형 인스턴스화를 통해서도 가능하지만 여기에서는 엄격하게 형식이 지정된 바인딩 대신 런타임에 동적인 규칙을 바탕으로 워크플로를 만드는 유연성을 발휘할 수 있음을 보여 줍니다.

마지막 줄은 워크플로의 첫 번째 HandleExternalEventActivity에서 처리되는 이벤트를 발생시켜 워크플로의 시작을 알립니다. 이 이벤트는 ExpenseLocalService 클래스의 인스턴스를 통해 발생시킵니다. 샘플에서는 ExpenseLocalService를 사용하여 새 워크플로를 시작하거나 기존 워크플로에 이벤트를 발생시켜 워크플로와 상호 작용합니다. 이 클래스는 비즈니스 프로세스를 캡슐화하는 메커니즘으로 사용되며 내부적으로는 WF를 사용하여 구현됩니다.


그림 3. 워크플로는 HandleExternalEventActivity로 시작됩니다.

처리해야 할 다른 유형의 상황은 기존 워크플로로 콜백하고 이벤트를 발생시키는 것입니다. 워크플로 엔진으로 이벤트를 발생시켜 기존 워크플로가 이벤트를 받고 처리를 계속할 수 있도록 해야 합니다.

비용 보고 흐름 내에서 이러한 상황이 발생하는 예로는 관리자 승인이 필요한 경우를 들 수 있습니다. 워크플로는 RequestManagerApproval에 대한 외부 메서드를 호출하고 이 메서드는 관리자에게 새 비용 보고서를 승인하거나 거부해야 한다는 알림을 생성합니다.

워크플로에는 가능한 이벤트 중 하나가 발생할 때까지 차단하는 ListenActivity가 포함되어 있습니다. 이 경우에는 관리자가 보고서를 검토했거나 DelayActivity에 따라 시간이 초과되었음을 알리는 이벤트를 수신하게 됩니다.


그림 4. ManagerApproval 사용자 지정 작업 흐름

Guid workflowInstanceId =
submitReviewedExpenseReportRequest.Report.ExpenseReportId;

ExpenseReportReviewedEventArgs e =
   new ExpenseReportReviewedEventArgs(workflowInstanceId, report, review);

if (ExpenseReportReviewed != null)
{
   ExpenseReportReviewed(null, e);
}

관리자가 ManagerApplication을 사용하여 보고서를 검토하면 호스트로 서비스 호출이 다시 수행되어 ExpenseReportReviewed 이벤트를 발생시키는 SubmitReviewedExpenseReport 메서드가 호출됩니다.

워크플로에서 HandleExternalEventActivity로 이벤트를 발생시키는 경우 이벤트를 라우팅할 수 있도록 현재 처리하고 있는 워크플로 인스턴스의 GUID를 알아야 합니다.

각 이벤트는 EventArgs와 함께 생성되는데, EventArgs를 사용하면 이벤트 모델을 통해 워크플로로 데이터를 다시 전달할 수 있습니다. 이 예에서는 검토 작업의 컨텍스트를 제공하는 데이터와 보고서의 현재 상태에 대한 세부 정보를 모두 전달할 수 있습니다.

워크플로에서 이벤트는 HandleExternalEventActivity의 속성을 통해 자동으로 워크플로에 연결됩니다.


그림 5. HandleExternalEventActivity를 IExpenseLocalService 인터페이스에 연결합니다.

[ExternalDataExchange] 특성으로 표시해야 하는 인터페이스 유형을 지정한 후 HandleExternalEventActivity에서 구독할 해당 인터페이스의 이벤트를 지정합니다.

이벤트 인수는 ExternalDataEventArgs 클래스에서 파생되어야 합니다. 이는 최소한 각 이벤트가 워크플로의 InstanceId와 같은 컨텍스트를 포함하게 된다는 것을 의미합니다. 그러면 워크플로 런타임은 올바른 워크플로 인스턴스로 이벤트를 라우팅하여 계속 진행되도록 합니다. 지속성 서비스를 사용하는 경우에는 런타임은 실행 기간 동안 워크플로에 대한 실행 상태의 하이드레이션 및 리하이드레이션 작업도 관리하게 됩니다.

서비스 호스팅

WCF 서비스를 호스팅하려면 ServiceHost 컨테이너 내에서 실행해야 합니다.

WCF를 사용하여 호스팅하는 방법을 검토하기에 앞서 선택할 수 있는 대안에 대해 살펴보겠습니다.

  • 표준 Windows 프로세스에서는 ServiceHost 인스턴스를 수동으로 만들고 열 수 있습니다.
  • Microsoft 인터넷 정보 서비스(IIS) 6.0을 통해 웹 끝점(웹 서비스)을 호스팅하는 경우 System.ServiceModel 네임스페이스에서 제공되는 사용자 지정 HttpHandler를 사용합니다.
  • IIS 7에서 호스팅하는 경우에는 WAS(Windows Activation Service)를 사용하여 끝점을 호스팅할 수 있습니다.

일반적으로 웹 서비스를 구축하는 경우에는 인터넷 정보 서비스를 사용하여 호스팅하는 방법을 선택하고, 디먼 역할을 하는 단일 인스턴스 끝점을 구축하는 경우에는 Windows 서비스를 사용하여 호스팅하는 방법을 선택합니다.

예제에서는 Windows 콘솔 응용 프로그램 내에서 주 서비스 인스턴스를 호스팅하는데, 이는 Windows 서비스를 호스팅하는 방식과 비슷합니다.

서비스를 배포하려면 ServiceHost 클래스의 인스턴스를 만들고 게시하려는 각 서비스 유형에 대해 이 인스턴스의 끝점을 엽니다. ServiceHost는 생성자의 일부로 여러 인수를 취하지만 가장 중요한 인수는 Type 인수 또는 ServiceContract를 구현하는 클래스의 인스턴스입니다.

  • PerCall 또는 PerSession 인스턴스를 사용하려는 경우에는 Type을 사용합니다.
  • Single 인스턴스를 사용하는 경우에는 단일 인스턴스를 사용하십시오.

WCF 내의 인스턴스 및 동시성에 대한 자세한 내용은 MSDN Library의 Sessions, Instancing, and Concurrency (영문)를 참조하십시오.

호스트가 설정된 후에는 사용할 수 있는 모든 구성을 구문 분석하고(이에 대한 자세한 내용은 배포 구성 섹션 참조) 이를 명시적으로 추가된 구성과 병합하여 사용할 수 있는 끝점을 확인한 다음 게시를 위해 이 끝점을 엽니다. 클라이언트에서 호출을 수신하면 요청은 새로운 백그라운드 작업자 스레드에서 처리되며, 메시지의 SOAP 계약 이름 및 작업에서 지정된 대로 적절한 서비스 작업으로 라우팅됩니다.

using (ServiceHost serviceHost = new ServiceHost(new ExpenseService()))
{
    WfWcfExtension wfWcfExtension = new WfWcfExtension("WorkflowRuntimeConfig");
    serviceHost.Extensions.Add(wfWcfExtension);
    serviceHost.Open();

    // 이 시점에서 프로세스를 차단합니다(예:Console.ReadLine();).

    serviceHost.Close();
}

ServiceHost 구성은 연결에 대한 끝점을 열기 전에 수행해야 합니다. 앞에서 보았듯이 이를 위해서는 .Open()을 호출하기 전에 호스트 개체와 상호 작용합니다. using 범위를 사용하여 ServiceHost를 사용하기 전에 삭제되도록 하고, 이 범위 끝에 명시적으로 Close()를 호출하여 모든 활성 연결 및 끝점을 깔끔하게 종료하는 것이 좋습니다.


배포 구성

WCF는 XML 구성을 통해 끝점을 구성할 수 있도록 함으로써 구현에서 배포 문제를 분리하는 메커니즘을 제공합니다. 이로써 관리자는 코드를 다시 개발하지 않고도 서비스 정책을 수정할 수 있습니다.

각 서비스는 한 개 이상의 끝점에 배포됩니다. 간단히 말해 끝점은 클라이언트가 서비스를 사용할 수 있는, 주소 지정이 가능한 연결 지점입니다. WCF에서 각 끝점은 WCF의 기초로 잘 알려진 세 가지 특성으로 선언됩니다.

이 세 특성은 Address, BindingContract입니다.

Address: 주소 지정이 가능한 이 끝점의 고유한 위치입니다. 일반적으로 서비스가 요청을 수신하는 위치의 절대 주소를 가리키는 URI입니다(예: http://myhost/myservice 또는 net.tcp://myhost:400/myservice).

Binding: 서비스와 소비자 간의 통신을 위한 프로토콜을 결정하는 정책입니다. Binding은 사용되는 전송 유형, 메시지 인코딩 방법 및 데이터를 serialize하는 방법과 같은 측면을 지정합니다. WCF에는 대부분의 일반적인 시나리오를 지원하는 즉시 사용 가능한 바인딩이 다수 포함되어 있습니다.

Contract: 코드에서 인터페이스를 통해 정의한 대로 게시되는 작업 및 데이터입니다.

서비스를 구성하려면 서비스를 선언하는 구성을 선언하고 해당 서비스에 대한 끝점을 모두 구성해야 합니다. 서비스에서 구현할 수 있는 계약의 수에는 제한이 없기 때문에 사용자가 게시해야 하는 끝점의 수도 여기에 맞춰 결정됩니다.

다음은 구성 예제입니다.

<services>
    <service name="ExpenseServices.ExpenseService">
        <endpoint
            address="http://localhost:8081/ExpenseService/Manager"
            binding="wsHttpBinding"
            contract="ExpenseContracts.IExpenseServiceManager" />
        <endpoint
            address="http://localhost:8081/ExpenseService/Client"
            binding="wsDualHttpBinding"
            contract="ExpenseContracts.IExpenseServiceClient" />
    </service>
</services>

이 구성 예제에서는 ExpenseServices.ExpenseService 유형의 서비스에 대해 구성을 선언합니다. 이렇게 하면 이 유형을 기반으로 새 ServiceHost를 인스턴스화할 때 런타임에서 구성을 찾을 수 있습니다. 바인딩에 대한 자세한 내용은 MSDN Library의 WCF Bindings (영문)를 참조하십시오.


서비스 사용

WCF를 통한 서비스 사용은 ChannelFactory 클래스를 사용하여 수행됩니다. ChannelFactory는 구성에 지정된 끝점에 연결하는 서비스 계약의 프록시 인스턴스를 제공하기 위해 팩토리 패턴을 사용합니다. 메시지 암호화를 위한 보안 자격 증명 및 인증서와 같은 런타임 정보를 사용하거나 동적으로 끝점 정보를 확인하도록 팩토리를 구성할 수 있습니다.

private IExpenseServiceManager CreateChannelExpenseServiceManager()
{
    ChannelFactory<IExpenseServiceManager> factory = new ChannelFactory<IExpenseServiceManager>("ExpenseServiceManager");
    IExpenseServiceManager proxy = factory.CreateChannel();

    return proxy;
}

여기에서 볼 수 있듯이 처음에 팩토리의 인스턴스를 만듭니다. 이 인스턴스는 서비스 계약에 대한 제네릭 인수를 사용하므로 원하는 계약의 인스턴스만 반환하는 더 정확한 팩토리를 만들 수 있습니다. 끝점에 사용될 구성을 결정하는 인수도 지정합니다. 이 경우에는 응용 프로그램 구성 파일에 있는 구성을 참조하는 ExpenseServiceManager라는 이름의 끝점 구성을 사용합니다.

<system.serviceModel>
    <client>
        <endpoint name="ExpenseServiceManager"
            address="http://localhost:8081/ExpenseService/Manager"
            binding="wsHttpBinding"
            contract="ExpenseContracts.IExpenseServiceManager" />
    </client>
</system.serviceModel>

끝점 정의가 호스트의 구성에 선언된 정의와 정확하게 일치하는 것을 알 수 있습니다. 일반적으로 구성이 다른 유일한 경우는 네트워크 구성이나 사용자 지정 동작이 구현되고 있어 클라이언트와 서버 간의 주소가 다를 때입니다.

Windows SDK를 설치했다면 프록시 클래스 및 끝점 구성 생성을 자동화하며 사용자의 솔루션으로 통합 가능한 도구(svcutil)를 사용할 수 있습니다. 이 도구를 활용하려면 대상 서비스가 WSDL 또는 WS-MetadataExchange를 통해 메타데이터 설명을 게시해야 합니다.


이중 채널 구성

지금까지는 통신 흐름이 소비자가 메시지를 보내고 서비스가 이에 응답하는 요청 응답 공동 작업 패턴을 사용한다고 가정했습니다. WCF는 이외에도 단방향(전송한 후 더 이상 추적하지 않음) 또는 양방향 이중 통신과 같은 여러 가지 다른 메시지 흐름을 지원합니다. 어느 쪽에서든 대화를 시작할 수 있는 메시지 흐름을 다루는 경우에는 이중 또는 양방향 채널을 사용해야 합니다. 이중 채널은 어느 쪽에서든 데이터를 전송할 수 있는 강력하게 연결된 시스템에서 매우 효과적입니다. 이 채널을 유용하게 활용할 수 있는 예는 이벤트에서 콜백을 제공하는 경우입니다.

클라이언트 콜백 구현

WCF에서 클라이언트 콜백은 CallbackContracts라고 하는 개념을 통해 구현됩니다. 여기서 게시하는 계약의 경우 클라이언트가 게시할 작업을 정의하는 두 번째 계약을 지명할 수 있으며, 이는 서비스에서 실행되는 코드를 사용하여 콜백할 수 있습니다.

CallbackContract를 선언하려면 인터페이스 유형을 콜백하려는 서비스 계약의 일부로 지정합니다.

[ServiceContract(CallbackContract = typeof(IExpenseServiceClientCallback))]

또한 netTcpBinding 또는 wsDualHttpBinding과 같이 이중 채널을 지원하는 바인딩을 사용해야 합니다. TCP를 통한 이중 통신은 메시지 교환이 이루어지는 동안 설정 및 유지되는 양방향 연결을 통해 이루어집니다. HTTP를 통한 통신은 클라이언트 수신기에 대한 콜백에 의해 수행됩니다. 클라이언트가 반환 경로를 인식하지 못하는 경우도 있고, 사용자가 구성을 통해 이를 엄격하게 정의하려는 경우도 있으므로 사용자 지정 바인딩 구성을 사용하여 대체 clientBaseAddress를 선언할 수 있습니다.

<endpoint binding="wsDualHttpBinding" bindingConfiguration="AlternativeClientCallback"/>
<bindings>
    <wsDualHttpBinding>
        <binding name="AlternativeClientCallback"
            clientBaseAddress="http://localhost:8082/ExpenseService/ClientCallback"/>
    </wsDualHttpBinding>
</bindings>

클라이언트에서 콜백 구현

콜백 계약 구현은 서비스 계약 구현과 완전히 동일합니다. 정의한 인터페이스의 구현을 제공해야 합니다.

class CallbackHandler : IExpenseServiceClientCallback
{
    public void ExpenseReportReviewed(ExpenseReportReviewedRequest expenseReportReviewedRequest)
    {
        // 여기에서 콜백에 응답하기 위한 클라이언트 논리를 구현합니다.
    }
}

호스트가 콜백할 CallbackHandler 클래스 인스턴스를 갖도록 하려면 연결의 이중 특성을 인식하도록 클라이언트 채널을 설정해야 합니다.

우선 앞서 설명한 대로 이중 채널을 지원하는 바인딩을 사용합니다. 다음으로, 서비스 끝점에 대한 연결을 초기화할 때 서비스에 대한 이중 연결을 만드는 DuplexChannelFactory라는 ChannelFactory의 하위 클래스 버전을 사용합니다.

private IExpenseServiceClient CreateChannelExpenseServiceClient()
{
    InstanceContext context = new InstanceContext(new CallbackHandler());

    DuplexChannelFactory<IExpenseServiceClient> factory = new DuplexChannelFactory<IExpenseServiceClient>(context, "ExpenseServiceClient");
    IExpenseServiceClient proxy = factory.CreateChannel();

    return proxy;
}

DuplexChannelFactory를 사용할 때 중요한 차이점은 CallbackHandler 클래스의 인스턴스를 초기화하고 이를 팩토리의 생성자에 전달하여 콜백에 사용될 컨텍스트를 초기화한다는 것입니다.

호스트에서 콜백 구현

호스트의 관점에서는 IExpenseServiceClient 계약에 정의된 콜백 채널을 통해 클라이언트로의 콜백에 대한 참조를 얻을 수 있습니다.

[ServiceContract(CallbackContract = typeof(IExpenseServiceClientCallback))]
public interface IExpenseServiceClient : IExpenseService

CallbackContract 특성은 호스트에서 이루어지는 콜백에 대한 계약을 정의하는 인터페이스를 선언합니다.

콜백을 수행하려면 다음과 같이 OperationContext.Current.GetCallbackChannel을 호출하여 콜백 계약에 대한 참조를 가져옵니다.

IExpenseServiceClientCallback callback = OperationContext.Current.GetCallbackChannel<IExpenseServiceClientCallback>();
callback.ExpenseReportReviewed(new ExpenseReportReviewedRequest(e.Report));

콜백 채널에 대한 참조를 가져온 다음에는 정상적으로 이를 호출할 수 있습니다.


결론

Windows Workflow Foundation은 워크플로 정의를 위한 범용 프레임워크와 실행 중인 워크플로를 호스팅하고 이 워크플로와 상호 작용할 수 있는 강력한 런타임 엔진을 제공합니다.

Windows Communication Foundation은 연결된 시스템을 구축하기 위한 범용 프레임워크를 제공하며, 통신 방법을 정의하기 위한 일관성 있는 API와 광범위한 기능 집합을 개발자에게 제공합니다.

이 두 가지 프레임워크를 함께 사용하면 현재 환경 내에서 분산 비즈니스 프로세스를 구축 및 배포하기 위한 유연하고 포괄적인 응용 프로그램 플랫폼을 제공할 수 있습니다. WF는 비즈니스 논리 및 프로세스를 모델링하고 캡슐화할 수 있도록 하며 WCF는 시스템 분산의 매개체가 되는 메시징 인프라를 제공합니다.

다음은 서비스를 설계할 때 기억해야 할 몇 가지 지침입니다.

  • 장시간 실행되는 워크플로는 지속성 서비스를 사용하여 제공해야 합니다.
  • 서비스 작업은 이벤트 발생을 통해 실행 중인 워크플로에서 상호 작용이 가능합니다. 워크플로는 사용자의 주의가 필요할 때 이벤트를 발생시키고, 외부(예: 외부 서비스 또는 사용자)와 상호 작용할 때 이벤트에 응답하도록 설계되어야 합니다.
  • 워크플로는 서비스 호출과 비동기적으로 실행되므로 어느 시점에 서비스 데이터를 반환해야 하고 해당 시점에서의 데이터 상태는 어떠한지 고려하여 이에 맞게 적절히 설계해야 합니다. 동기적 방식을 원하는 경우에는 ManualWorkflowSchedulerService 클래스를 사용하여 워크플로 실행을 수동으로 예약하도록 할 수 있습니다.

추가 정보
  1. Sessions, Instancing, and Concurrency (영문)
  2. WCF Bindings (영문)
  3. Using Data Contracts (영문)
  4. Using Message Contracts (영문)
  5. .NET Framework 3.0 커뮤니티 사이트 (영문)
  6. Windows Workflow Foundation 포럼 (영문)
출처 : 한국 마이크로소프트 MSDN (2007년 1월)
Posted by 상현넘™

댓글을 달아 주세요

요약: WPF(Windows Presentation Foundation)의 기본적인 목표는 개발자가 세련되고 편리한 사용자 인터페이스를 만들 수 있도록 돕는 것입니다. 이 문서에서는 WPT 통합 플랫폼을 사용하여 디자이너가 사용자 인터페이스 개발 작업에 보다 적극적으로 참여하는 방법과 독립 실행형 응용 프로그램 및 브라우저 응용 프로그램을 만들기 위한 공통적인 프로그래밍 모델을 제공하는 방법에 대해 설명합니다.

Windows Presentation Foundation 설명

기본적으로 기술은 주로 전문가들이 중요하게 생각하는 부분으로 대부분의 소프트웨어 전문가들은 응용 프로그램과 사용자 간의 상호 작용 방식보다는 응용 프로그램의 작동 방식에 훨씬 더 많은 관심을 가집니다. 그러나 응용 프로그램을 구입하는 실제 사용자에게는 사용자 인터페이스가 매우 중요합니다. 응용 프로그램의 인터페이스는 해당 소프트웨어에 대한 전체적인 사용자 환경에서 중요한 부분을 차지하며, 사용자에게 이러한 환경은 응용 프로그램 '그 자체'를 의미합니다. 더 나은 인터페이스를 통해 향상된 사용자 환경을 제공하면 생산성을 높이고 우수 고객을 더 많이 확보할 수 있으며 웹 사이트에서의 매출을 늘리는 등 다양한 효과를 얻을 수 있습니다.

이전에는 문자 기반 인터페이스만으로 충분했지만 오늘날의 사용자들은 그래픽 인터페이스에 익숙해졌으며 사용자 인터페이스에 대한 요구 사항은 계속해서 증가하고 있습니다. 그래픽과 미디어가 더욱 광범위하게 사용되고 있으며 웹의 발전은 소프트웨어와의 편리한 상호 작용을 기대하는 사용자 계층을 형성하게 되었습니다. 사용자들이 응용 프로그램을 사용하는 시간이 늘어날수록 해당 응용 프로그램의 인터페이스는 더욱 중요해집니다. 이렇듯 점점 높아지는 인터페이스 요구 사항에 부응하기 위해서는 사용자 인터페이스를 만드는 기술도 함께 발전해야 합니다.

WPF(Windows Presentation Foundation)의 목표는 Windows에 바로 이러한 고급 기능을 제공하는 것입니다. Microsoft .NET Framework 버전 3.0에 포함된 WPF를 사용하면 문서, 미디어, 2차원 및 3차원 그래픽, 애니메이션, 웹 특성 등을 포함하는 인터페이스를 만들 수 있습니다. WPF는 .NET Framework 3.0의 모든 구성 요소와 마찬가지로 Windows Vista, Windows XP 및 Windows Server 2003에서 사용할 수 있으며 Windows Vista와 함께 출시될 예정입니다. 이 백서에서는 WPF를 소개하고 WPF의 다양한 구성 요소에 대해 설명하며 이 기술을 통해 해결할 수 있는 문제점과 WPF가 제공하는 솔루션에 대해 살펴봅니다.

문제점 예시

병원에서 환자를 진료하고 모니터링하는 데 사용할 새 응용 프로그램을 만든다고 가정해 보겠습니다. 새로 만들 응용 프로그램에는 다음과 같은 사용자 인터페이스가 필요할 수 있습니다.

  • 환자에 대한 이미지와 텍스트 표시
  • 심장 박동수, 혈압 등 환자의 활력 징후를 보여 주는 2차원 그래픽 표시 및 업데이트
  • 환자 정보에 대한 3차원 보기 및 오버레이 제공
  • 초음파 또는 기타 진단 비디오를 표시하고, 가능한 경우 의사나 간호사가 메모를 추가할 수 있는 기능 제공
  • 병원 직원이 환자 및 환자 상태에 대한 기록을 읽고 메모를 추가할 수 있는 기능
  • 병원 직원이 모든 기능을 사용할 수 있도록 Windows 응용 프로그램으로 실행되고 다른 위치에 있는 의사가 인터넷을 통해 제한적인 방식으로 액세스할 수 있도록 엄격한 보안이 유지되는 웹 브라우저 응용 프로그램으로도 실행

이러한 요구 사항은 야심적이기는 하지만 충분히 실현 가능합니다. 필요한 정보를 적시에 적절한 방법으로 제공하는 사용자 인터페이스는 업무상 중요한 가치를 지닙니다. 앞에서 설명한 의료 응용 프로그램의 예에서는 적절한 사용자 인터페이스가 생명을 구할 수도 있습니다. 온라인 상점 또는 기타 고객 지향 응용 프로그램과 같은 일반적인 시나리오의 경우 강력한 사용자 환경을 제공하면 회사의 서비스를 경쟁사의 서비스와 차별화하여 매출 증진은 물론 회사의 브랜드 가치를 높일 수 있습니다. 중요한 점은 많은 수의 최신 응용 프로그램에서 그래픽, 미디어, 문서 및 기타 최신 사용자 환경의 요소를 통합하는 인터페이스를 제공함으로써 다양한 이점을 누릴 수 있다는 것입니다.

2006년 현재의 기술로 Windows에 이러한 유형의 인터페이스를 만들 수도 있지만 이러한 작업은 다음과 같은 주요 장애 요인으로 인해 실현하기 매우 어렵습니다.

  • 그래픽, 이미지 및 비디오로 작업하는 데 서로 다른 여러 기술이 사용됩니다. 이러한 다양한 기술을 활용하기 위해 유능한 개발자를 찾는 것은 이러한 개발자가 만드는 응용 프로그램을 유지 관리하는 것만큼 어렵고 비용도 많이 듭니다.
  • 이러한 모든 기능을 사용자에게 효과적으로 제공하는 인터페이스를 디자인하는 일은 도전적인 과제입니다. 소프트웨어 개발자의 기술만으로는 이러한 인터페이스를 디자인할 수 없기 때문에 전문 디자이너가 필요하지만 여기에서 설명한 모든 기능을 갖춘 인터페이스를 개발하기 위해 디자이너와 개발자가 공동으로 작업하는 데는 많은 제약이 따릅니다.
  • 모든 기능을 갖춘 인터페이스를 독립 실행형 Windows 응용 프로그램 버전과 브라우저에 호스팅되는 버전의 두 가지 형식으로 제공하려면 서로 다른 두 가지 분야의 기술을 사용하여 각 버전을 개별적으로 구현해야 합니다. Windows 데스크톱 응용 프로그램은 Windows Forms 및 기타 기본적인 Windows 기술을 사용하여 구현하고 브라우저에 호스팅되는 응용 프로그램은 HTML 및 JavaScript를 사용하여 구현할 수 있습니다. 이 경우 서로 다른 두 분야의 기술을 갖춘 두 개의 개발자 그룹이 필요합니다.

강력한 기능을 제공하는 최신 사용자 인터페이스를 만드는 작업을 복잡하게 생각할 필요는 전혀 없습니다. 공통적인 기반을 사용하면 이러한 모든 문제를 해결하고 개발자에게 단일화된 접근 방식을 제공하는 동시에 디자이너도 작업에서 중요한 역할을 담당하도록 할 수 있습니다. 이것이 바로 WPF의 목표이며, 자세한 내용은 다음 섹션에서 설명합니다.

문제점 해결: Windows Presentation Foundation의 기능

WPF가 제공하는 가장 중요한 세 가지 특징은 최신 사용자 인터페이스를 제공하기 위한 통합 플랫폼, 개발자와 디자이너가 공동으로 작업할 수 있는 환경, 그리고 Windows 사용자 인터페이스와 웹 브라우저 사용자 인터페이스를 개발하기 위한 공통의 기술입니다. 이 섹션에서는 이러한 세 가지 특징에 대해 설명합니다.

최신 사용자 인터페이스를 제공하기 위한 통합 플랫폼

WPF를 사용하지 않는 환경에서 위 설명과 같은 Windows 사용자 인터페이스를 만들기 위해서는 여러 가지 기술을 함께 사용해야 합니다. 다음 표에서는 필요한 기술을 간략하게 보여 줍니다.

  Windows Forms PDF Windows Forms/
GDI+
Windows Media Player Direct3D WPF
그래픽 인터페이스(예: 폼 및 컨트롤) X         X
화면에 표시되는 문서 X         X
고정된 형식의 문서   X       X
이미지     X     X
비디오 및 오디오       X   X
2차원 그래픽     X     X
3차원 그래픽         X X

개발자가 폼, 컨트롤 및 기타 Windows 그래픽 사용자 인터페이스의 일반적인 요소를 만들려면 대개 .NET Framework의 일부인 Windows Forms를 사용해야 합니다. 인터페이스에서 문서를 표시해야 할 경우 Windows Forms에서 부분적으로 지원되는 화면 표시 문서를 사용할 수 있지만 고정된 형식의 문서를 사용해야 할 경우에는 Adobe의 PDF 형식이 적합합니다. 이미지와 2차원 그래픽을 표시하려는 경우 개발자는 Windows Forms를 통해 액세스할 수 있는 고유한 프로그래밍 모델인 GDI+를 사용할 수 있습니다. 또한 비디오와 오디오 기능은 Windows Media Player를 통해 제공하고 3차원 그래픽 기능은 Windows 표준인 Direct3D를 통해 제공할 수 있습니다.

이렇게 복잡한 작업은 단지 이론적인 설명을 위한 것일 뿐이며 이를 실제로 실행하는 것은 적절하지 않다는 데 누구나 동의할 것입니다. 그러나 통합된 단일 솔루션인 WPF에서는 이 모든 것이 가능합니다. WPF가 설치된 컴퓨터에서 응용 프로그램을 만드는 개발자는 WPF 하나만으로 위에서 언급한 모든 문제를 해결할 수 있습니다. 즉, 이제는 여러 가지 독립적인 기술을 사용하는 대신 하나의 일관된 기반을 사용하여 사용자 인터페이스를 만들 수 있습니다.

물론 WPF가 이 표에 나와 있는 모든 기술을 대체하지는 않습니다. Windows Forms 응용 프로그램은 여전히 유용하게 활용될 것이며, WPF 환경에서도 일부 새 응용 프로그램에는 Windows Forms를 사용해야 할 수 있습니다. WPF를 Windows Forms와 상호 운용할 수 있다는 점은 주목할 만한 가치가 있습니다. 이에 대한 자세한 내용은 이 문서의 뒷부분에서 설명합니다. Windows Media Player도 여전히 독자적인 가치를 제공할 것이며 PDF 문서도 계속해서 사용될 것입니다. Direct3D 또한 게임이나 몇 가지 다른 유형의 응용 프로그램에서는 중요한 기술입니다. 실제로 WPF에서도 Direct3D를 통해 모든 렌더링 작업을 수행합니다.

그러나 WPF라는 단일 기술을 통해 광범위한 기능을 제공하면 최신 사용자 인터페이스를 훨씬 쉽게 구현할 수 있다는 장점이 있습니다. 다음 화면에서는 이러한 통합된 접근 방식의 이점을 쉽게 이해할 수 있도록 앞에서 설명한 WPF 기반 버전의 의료 응용 프로그램을 보여 줍니다.

사용자 삽입 이미지

그림 1. WPF 인터페이스에서는 이미지, 텍스트, 2D 그래픽, 3D 그래픽 등을 결합할 수 있습니다.

이 화면에서는 텍스트와 이미지를 비롯하여 2차원 그래픽 및 3차원 그래픽을 함께 볼 수 있습니다. 이 모든 인터페이스는 개발자가 GDI+ 또는 Direct3D와 같은 특수 그래픽 기술을 사용하는 코드를 작성할 필요 없이 WPF를 사용하여 생성되었습니다. WPF를 사용하면 다음 그림에서 볼 수 있는 초음파 진단 결과처럼 비디오를 표시하고 메모를 입력하는 기능을 구현할 수도 있습니다.

사용자 삽입 이미지

그림 2. WPF 인터페이스에는 비디오뿐만 아니라 사용자가 입력할 수 있는 텍스트 메모를 모두 포함할 수 있습니다.

WPF를 사용하면 읽기 편리한 방식으로 문서를 표시할 수 있습니다. 예를 들어 병원 응용 프로그램의 경우 의사가 환자의 처방전에 대한 기록을 조회하거나 관련 항목에 대한 최근의 의학 연구 자료에 액세스할 수 있습니다. 아래의 화면에서 볼 수 있는 것처럼 의사는 메모를 추가할 수 있습니다.

사용자 삽입 이미지

그림 3. WPF 인터페이스에서는 메모를 포함하여 여러 개의 열이 있는 문서를 표시할 수 있습니다.

이 화면에서 문서는 가독성이 뛰어난 열 형식으로 표시되고 사용자는 스크롤 방식이 아니라 문서를 한 페이지씩 이동할 수 있습니다. 화면을 보기 쉽게 표시하는 것도 WPF의 중요한 목표 중 하나입니다. 그러나 화면에 표시되는 문서보다 고정된 형식의 문서를 사용하는 것이 적절한 경우도 있습니다. 고정된 형식의 문서는 화면에서 볼 때나 인쇄했을 때 모양이 동일하기 때문에 항상 일관된 모양을 제공합니다. 이러한 형식의 문서를 정의하기 위해 Microsoft는 XPS(XML Paper Specification)를 만들었습니다. WPF는 개발자가 XPS 문서를 만들고 작업하는 데 사용할 수 있는 API(응용 프로그래밍 인터페이스) 그룹도 제공합니다.

그러나 최신 사용자 인터페이스를 만드는 것은 독립적인 여러 기술을 단순히 통합하는 것이 아니라 최신 그래픽 카드의 장점을 활용할 수 있다는 의미를 가지고 있습니다. 즉, WPF는 시스템의 GPU(Graphics Processing Unit)에 가능한 한 많은 작업 로드를 분산시킴으로써 GPU를 최대한 활용합니다. 또한 최신 인터페이스는 비트맵 그래픽의 한계에 제약을 받지 않아야 하므로 WPF는 화면의 크기와 해상도에 맞게 이미지 크기가 자동으로 조정되도록 벡터 그래픽 기술을 사용합니다. 따라서 개발자는 작은 모니터와 대형 TV 화면에 표시할 그래픽을 개별적으로 만들지 않고 WPF를 통해 이러한 크기 조정 작업을 자동으로 처리할 수 있습니다.

WPF는 사용자 인터페이스를 만드는 데 필요한 모든 기술을 하나의 기반에 집약함으로써 이러한 인터페이스를 만드는 개발자의 업무 부담을 크게 낮추어 줍니다. WPF를 사용하는 경우 개발자는 하나의 작업 환경만 파악하면 되기 때문에 응용 프로그램 개발 및 유지 관리에 소요되는 비용이 훨씬 저렴합니다. 또한 WPF를 사용하면 그래픽, 비디오 등의 다양한 요소가 포함된 인터페이스를 간단하게 구현할 수 있으므로 사용자가 Windows 응용 프로그램과 상호 작용하는 방식을 개선하고 업무 효율성을 높일 수 있습니다.

개발자와 디자이너가 공동으로 작업할 수 있는 환경

모든 기능을 갖춘 사용자 인터페이스를 만들기 위한 통합된 기술 기반을 제공하는 것은 매우 바람직합니다. 그러나 개발자가 외부의 도움 없이 이러한 강력한 기능을 제대로 활용하여 이해하기 쉽고 사용이 편리한 인터페이스를 만들 수 있을 것이라고 기대하는 것은 무리입니다. 특히 앞에서 설명한 병원 사례와 같이 포괄적이고 유용한 사용자 인터페이스를 만들기 위해서는 대부분의 소프트웨어 전문가들이 갖추지 못한 인터페이스 디자인 기술이 필요합니다. 소프트웨어 전문가들이 많은 수의 응용 프로그램을 단독으로 개발하고 있지만 최상의 사용자 인터페이스를 구현하기 위해서는 전문 인터페이스 디자이너의 도움이 필요합니다.

그러나 개발자와 디자이너는 업무 진행 방식이 서로 다르기 때문에 두 분야의 전문가가 공동으로 작업하는 데는 여러 가지 문제가 있습니다. 일반적으로 디자이너는 그래픽 도구를 사용하여 응용 프로그램에 표시할 화면 레이아웃의 정적 이미지를 만듭니다. 그런 다음 이러한 이미지를 개발자에게 전달하고, 개발자는 해당 이미지를 구현하는 코드를 작성합니다. 그러나 디자이너는 만들기 쉽더라도 개발자는 구현하기 어렵거나 전혀 구현할 수 없는 이미지도 있습니다. 예를 들어 개발자는 기술적인 한계, 촉박한 일정, 기술 부족, 사소한 오해 또는 단순한 의견 차이로 인해 디자이너의 의도를 완벽하게 구현하지 못할 수 있습니다. 따라서 최상의 인터페이스를 만들기 위해서는 독립적인 두 분야의 전문가가 인터페이스의 품질을 떨어뜨리지 않으면서 공동으로 작업할 수 있는 환경이 필요합니다.

WPF는 이러한 공동 작업이 가능하도록 XAML(eXtensible Application Markup Language)을 사용합니다. XAML은 사용자 인터페이스의 모양을 정확하게 정의하기 위해 Button, TextBox, Label 등 여러 XML 요소의 집합을 정의합니다. 일반적으로 XAML 요소는 다양한 옵션을 설정할 수 있도록 특성도 포함합니다. 예를 들어 다음의 간단한 XAML 코드는 "아니요"라는 단어가 표시되는 빨간색 단추를 만듭니다.

<Button Background="Red">
아니요
</Button>

각 XAML 요소는 WPF 클래스에 해당하고 요소의 각 특성은 클래스의 해당 속성이나 이벤트를 갖습니다. 예를 들어 다음과 같은 C# 코드를 사용하여 동일한 빨간색 단추를 만들 수 있습니다.

Button btn = new Button();
btn.Background = Brushes.Red;
btn.Content = "아니요";

XAML로 표현할 수 있는 모든 것을 코드를 사용하여 동일하게 작성할 수 있다면 XAML을 사용할 필요가 없다고 생각할 수도 있습니다. 그러나 XML 기반의 설명을 생성하고 사용하는 작성 도구를 사용하면 코드를 사용하여 동일한 작업을 할 때보다 훨씬 쉽게 작업할 수 있습니다. XAML은 사용이 편리한 도구 방식으로 사용자 인터페이스를 설명하기 때문에 개발자와 디자이너가 더 효율적으로 함께 작업할 수 있습니다. 다음 그림은 이러한 작업 과정을 보여 줍니다.

사용자 삽입 이미지

그림 4. 디자이너와 개발자의 공동 작업을 돕는 XAML

디자이너는 Microsoft Expression Interactive Designer와 같은 도구를 사용하여 사용자 인터페이스의 모양과 상호 작용 방식을 지정할 수 있습니다. 이 도구는 WPF 인터페이스의 모양과 느낌을 정의하는 용도로 사용되며 해당 인터페이스에 대한 설명을 XAML 표현으로 생성합니다. 예제로 사용된 간단한 단추만 포함하더라도 이 설명은 실제로 위에 나온 코드 조각보다 훨씬 복잡합니다. 개발자는 이 XAML 설명을 Microsoft Visual Studio와 같은 도구로 가져옵니다. 이렇게 하면 디자이너가 만든 정적 이미지를 토대로 인터페이스를 처음부터 다시 만들 필요 없이 인터페이스 정의 자체를 그대로 가져올 수 있습니다. 그런 다음 개발자는 이벤트 처리기와 같이 인터페이스에 필요한 코드 및 응용 프로그램에 필요한 기타 기능에 해당하는 코드를 작성합니다. 또한 응용 프로그램 인터페이스에 전체적으로 적용할 수 있는 스타일을 만들 수도 있으며, 이러한 스타일은 나중에 필요에 따라 사용자 지정할 수 있습니다.

디자이너와 개발자가 이러한 방식으로 함께 작업하면 디자이너가 만든 이미지를 토대로 개발자가 인터페이스를 구현할 경우 발생할 수 있는 변환 오류를 줄일 수 있을 뿐만 아니라 이러한 두 분야의 전문가가 반복 작업을 신속하게 수행하고 효과적으로 의견을 교환하면서 동시에 작업을 진행할 수 있습니다. 또한 두 환경 모두에서 동일한 빌드 시스템을 사용하므로 두 개발 환경 간에 WPF 응용 프로그램을 주고 받을 수도 있습니다. 3차원 인터페이스 요소를 만들기 위한 Electric Rain의 ZAM 3D와 같이 XAML로 정의된 인터페이스를 디자인하는 데 사용할 수 있는 특수한 도구도 있습니다.

더 나은 인터페이스를 사용하면 생산성을 높일 수 있을 뿐만 아니라 여러 가지 측면에서 업무상 매우 유용합니다. 그러나 효과적인 인터페이스를 만드는 데는 특히 WPF가 제공하는 다양한 환경의 경우 디자이너의 역할이 가장 중요합니다. XAML 및 XAML을 지원하는 도구의 기본적인 목표는 디자이너의 작업을 돕는 것입니다.

Windows 사용자 인터페이스와 웹 브라우저 사용자 인터페이스를 개발하기 위한 공통의 기술

Windows 응용 프로그램에 사용할 효과적인 사용자 인터페이스를 만드는 것도 중요하지만 웹 기반 응용 프로그램에 사용할 효과적인 인터페이스를 만드는 것도 그만큼 중요합니다. 기본적으로 이러한 인터페이스는 웹 브라우저에서 제공합니다. 인터페이스를 만드는 가장 간단한 방법은 브라우저에 전달되는 모든 HTML을 브라우저가 수동적으로 표시하도록 하는 것입니다. 응답 성능이 더 뛰어난 브라우저 인터페이스에서는 대개 AJAX(Asynchronous JavaScript And XML)를 사용하여 JavaScript로 실행되는 논리를 제공합니다. Adobe Flash Player 또는 몇 가지 다른 기술을 사용하여 애니메이션, 비디오 등을 인터페이스에 사용할 수도 있습니다. 이와 같이 기능이 다양한 인터페이스 유형을 제공하는 웹 소프트웨어를 '다기능 인터넷 응용 프로그램'이라고도 합니다. 이 웹 소프트웨어를 사용하면 사용자 환경을 크게 향상시킬 수 있을 뿐만 아니라 더 많은 사용자의 참여를 유도하는 웹 응용 프로그램을 만들어 상당한 비즈니스 가치를 얻을 수 있습니다.

일반적으로 이러한 유형의 인터페이스를 구현하기 위해서는 기본적인 Windows 인터페이스에 사용되는 것과는 완전히 다른 기술을 사용해야 합니다. 결과적으로 개발자는 서로 다른 두 가지 작업, 즉 Windows 인터페이스를 개발하는 작업 또는 웹 인터페이스 개발하는 작업 중 하나에 초점을 두고 작업해야 합니다. 그러나 Windows에서 액세스할 다기능 인터넷 응용 프로그램을 만드는 데 이러한 양분된 방식으로 작업할 필요는 없으며 기본적인 Windows 인터페이스와 웹 브라우저 인터페이스 모두에 동일한 기술을 사용하지 못할 이유도 없습니다.

WPF를 사용하면 동일한 기술을 사용하여 Windows 인터페이스와 웹 브라우저 인터페이스 모두를 만드는 것이 가능합니다. 개발자는 WPF를 사용하여 Internet Explorer에서 실행되는 XBAP(XAML Browser Application)를 만들 수 있습니다. 실제로 동일한 코드를 사용하여 독립 실행형 WPF 응용 프로그램과 XBAP를 만들 수 있습니다. 예를 들어 아래의 화면에서는 독립 실행형 Windows 응용 프로그램으로 실행되는 재무 서비스 응용 프로그램을 보여 줍니다. 앞에서 설명한 병원 응용 프로그램과 마찬가지로 이 응용 프로그램도 텍스트, 이미지 및 다양한 종류의 그래픽을 함께 사용합니다. 이 화면에서는 시계 등의 가젯 및 응용 프로그램 창 주위에 반투명 테두리를 사용하는 Aero 테마가 적용된 Windows Vista 바탕 화면도 볼 수 있습니다.



사용자 삽입 이미지

그림 5. 재무 서비스 응용 프로그램을 독립 실행형 WPF 응용 프로그램으로 실행할 수 있습니다.

다음 그림에서는 이 인터페이스를 Internet Explorer에서 XBAP로 실행할 때의 모양을 보여 줍니다.

사용자 삽입 이미지

그림 6. 동일한 응용 프로그램을 XBAP로도 실행할 수 있습니다.

이 경우 인터페이스가 자체 창에서 실행되는 대신 브라우저에서 실행된다는 점만 다를 뿐 모든 기능은 동일합니다. 두 경우 모두에 동일한 코드를 사용하면 두 가지 유형의 인터페이스를 개별적으로 만드는 데 소요되는 것보다 작업량을 줄일 수 있습니다. 동일한 코드를 사용한다는 것은 동일한 개발자를 투입할 수 있음을 의미합니다. WPF를 사용하면 개발자를 Windows 인터페이스 개발자 또는 웹 인터페이스 개발자라는 분리된 두 개의 그룹으로 구분할 필요 없이 하나의 개발 리소스를 두 작업 모두에 활용할 수 있습니다.

동일한 기술을 사용하여 Windows 인터페이스와 웹 인터페이스를 만들면 응용 프로그램 작성자가 응용 프로그램의 인터페이스 유형을 미리 결정하지 않아도 된다는 또 하나의 장점이 있습니다. 즉, 대상 클라이언트가 XBAP를 실행하는 데 필요한 요구 사항을 충족하기만 하면 응용 프로그램에서는 거의 동일한 코드를 사용하여 Windows 인터페이스나 웹 인터페이스 또는 두 가지 인터페이스 모두를 제공할 수 있습니다.

XBAP는 필요할 때 웹 서버에서 다운로드되기 때문에 독립 실행형 Windows 응용 프로그램에 비해 더욱 엄격한 보안 요구 사항이 적용됩니다. 이러한 이유로 XBAP는 .NET Framework의 코드 액세스 보안에서 제공하는 보안 샌드박스(Security Sandbox)에서 실행됩니다. 또한 XBAP는 WPF가 설치되어 있는 Windows 시스템에서 Internet Explorer 버전 6 및 7에서만 실행됩니다. 이러한 요구 사항이 충족되면 인터넷 응용 프로그램이 독립 실행형 Windows 응용 프로그램과 동일한 기반을 활용하는 것이 가능합니다.


Windows Presentation Foundation 사용

WPF로 해결할 수 있는 문제점만 알고 있어도 도움이 되지만 WPF가 이러한 문제점을 해결하는 방법을 이해한다면 더 큰 도움이 됩니다. 이 섹션에서는 WPF 기술 자체를 자세하게 살펴본 후 Windows 데스크톱 응용 프로그램, XBAP 및 XPS 문서에 WPF 기술이 적용되는 다양한 방식에 대해 알아봅니다.

Windows Presentation Foundation의 기술

WPF는 사용자 인터페이스를 만들기 위한 통합된 기반을 제공하지만 WPF에 포함된 기술을 문서, 이미지, 그래픽, 애니메이션 등과 같은 개별 단위로 나누어 살펴볼 수 있습니다. 이러한 모든 개별 단위는 다음에 설명할 WPF의 기본 응용 프로그램 모델에 의존합니다.

응용 프로그램 모델

.NET Framework의 다른 구성 요소와 마찬가지로 WPF의 기능은 System.Windows 네임스페이스에 포함된 네임스페이스 그룹으로 나뉩니다. 모든 WPF 응용 프로그램의 기본 구조는 어느 기능을 사용하는지에 관계없이 거의 동일합니다. 일반적으로 응용 프로그램은 독립 실행형 Windows 응용 프로그램이나 XBAP에 관계없이 XAML 페이지 집합과 이들 페이지에 연결된 코드로 구성됩니다.

기본적으로 모든 응용 프로그램은 WPF의 표준 Application 클래스에서 상속됩니다. 이 클래스는 모든 응용 프로그램에서 유용하게 사용할 수 있는 공통 서비스를 제공하며, 여기에는 전체 응용 프로그램에 필요한 상태를 유지하는 서비스 및 응용 프로그램을 시작하는 Run, 응용 프로그램을 종료하는 Shutdown 등의 표준 메서드를 제공하는 서비스가 포함됩니다.

Application 개체는 Application 요소를 통해 XAML을 사용하여 만들거나 Application 클래스를 통해 코드를 사용하여 만들 수 있습니다. 이러한 특징은 사실상 WPF의 모든 요소에 적용되지만 이 문서에서는 간단히 설명하기 위해 XAML을 사용하는 방식만을 소개할 것입니다. 다음은 간단한 XAML을 보여 줍니다.

<Application xmlns=  
    "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     StartupUri="Page1.xaml"
     x:Class="Example.SimpleApp">
. . .
</Application>

이 정의에서는 먼저 WPF 네임스페이스를 이 요소의 기본값으로 지정한 다음 XAML 네임스페이스의 접두사를 정의합니다. XAML은 WPF에만 사용되는 것이 아니므로 이 두 네임스페이스는 서로 다릅니다. 다음으로 StartupUri 특성을 사용하여 응용 프로그램을 시작했을 때 로드하고 표시할 XAML 페이지의 이름을 나타냅니다. 마지막 특성인 Class는 이 Application과 관련된 코드가 들어 있는 클래스를 식별하는 데 사용됩니다. 앞에서 살펴본 것처럼 일반적으로 WPF 응용 프로그램에는 C# 또는 Visual Basic으로 작성된 코드와 XAML이 모두 들어 있으므로 이 클래스의 코드는 '코드 숨김' 파일에 저장됩니다. 여는 Application 태그 다음에는 이 응용 프로그램을 정의하는 데 필요한 나머지 XAML이 위치하고(여기에서는 생략됨), 마지막으로 Application 요소의 닫는 태그가 위치합니다.

모든 WPF 응용 프로그램은 동일한 루트 클래스에서 파생되지만 개발자는 다양한 옵션을 선택할 수 있습니다. 그 중 가장 중요한 옵션은 응용 프로그램에 일반적인 대화 상자 형식의 인터페이스를 사용할지 아니면 탐색 형식의 인터페이스를 사용할지 결정하는 것입니다. 대화 상자 형식의 인터페이스는 Windows 사용자라면 누구나 익숙한 단추 및 기타 요소를 제공합니다. 반면 탐색 형식의 인터페이스는 브라우저와 동작 방식이 거의 유사합니다. 예를 들어 탐색 형식의 인터페이스를 사용하면 대화 상자가 새 창에서 열리는 대신 새 페이지가 로드됩니다. 이와 같은 인터페이스는 페이지 그룹으로 구현되며, 각 페이지는 XAML에 정의된 사용자 인터페이스와 프로그래밍 언어로 표현된 논리로 구성됩니다. HTML로 정의된 브라우저 페이지와 마찬가지로 XAML은 페이지를 서로 연결하는 데 사용할 수 있는 Hyperlink 요소를 제공합니다. 사용자는 웹 기반 응용 프로그램의 페이지를 탐색할 때와 같은 방법으로 기록 목록을 통해 앞으로 또는 뒤로 이동하면서 여러 페이지를 탐색할 수 있습니다. 그러나 이것은 Windows 응용 프로그램이므로 혼동하지 말아야 합니다. 일반적으로 XBAP에서 이러한 유형의 인터페이스를 사용하지만, 필요에 따라 독립 실행형 Windows 응용 프로그램에서도 탐색 형식의 인터페이스를 통해 사용자와 상호 작용할 수 있습니다. 어떤 방식을 사용할지는 응용 프로그램을 만드는 개발자의 선택에 달려 있습니다.

응용 프로그램에서 어떤 인터페이스 스타일을 사용하든지 여부에 관계없이 응용 프로그램은 일반적으로 하나 이상의 창을 표시합니다. WPF는 창을 표시하기 위한 몇 가지 옵션을 제공합니다. 기본 Window 클래스에서는 창을 표시하고 숨기고 닫는 등의 기본적인 창 기능을 제공합니다. 일반적으로 탐색 형식의 인터페이스를 사용하지 않는 WPF 응용 프로그램에서 이 클래스를 사용합니다. NavigationWindow 클래스는 탐색 형식의 인터페이스를 사용하는 응용 프로그램에서 사용하며 기본 Window 클래스에 탐색 기능이 추가됩니다. 추가적인 탐색 기능으로는 응용 프로그램에서 새 페이지로 이동하는 데 사용할 수 있는 Navigate 메서드, 사용자의 탐색 기록을 저장하는 추적 정보 및 다양한 탐색 관련 이벤트가 있습니다.

레이아웃 및 컨트롤

WPF 응용 프로그램에서는 인터페이스의 다양한 요소를 구성하기 위해 '패널'을 사용하여 레이아웃을 지정합니다. 각 패널에는 단추, 텍스트 상자 등의 '컨트롤' 및 다른 패널이 자식 요소로 포함될 수 있습니다. 패널 종류마다 서로 다른 레이아웃 옵션을 제공합니다. 예를 들어, 이름에서 알 수 있듯이 DockPanel을 사용하면 자식 요소를 패널 가장자리에 도킹하여 배치할 수 있고, Grid를 사용하면 자식 요소를 눈금 위에 정확하게 배치할 수 있습니다. 이때 개발자는 눈금의 행 및 열 개수를 정의한 다음 자식 요소를 배치할 정확한 위치를 지정해야 합니다. Canvas를 사용하면 패널 경계 내의 아무 곳에나 자식 요소를 자유롭게 배치할 수 있습니다.

WPF는 다른 사용자 인터페이스 기술과 마찬가지로 다양한 컨트롤 집합을 제공합니다. 개발자는 사용자 지정 컨트롤을 만들 수도 있습니다. 표준 컨트롤 집합에는 Button, Label, TextBox, ListBox, Menu, Slider 및 기타 일반적인 사용자 인터페이스 디자인 요소가 포함됩니다. 또한 SpellCheck, PasswordBox, 잉크(예: Tablet PC)로 작업하는 데 필요한 컨트롤 등 보다 복잡한 컨트롤도 사용할 수 있습니다.

WPF 응용 프로그램에서는 일반적인 그래픽 인터페이스와 마찬가지로 마우스 이동 및 키 누르기 동작과 같이 사용자가 생성하는 이벤트를 컨트롤을 통해 포착하고 처리할 수 있습니다. 컨트롤 및 기타 사용자 인터페이스 요소는 XAML을 사용하여 완벽하게 지정할 수 있지만 이벤트는 코드에서 처리해야 합니다. 예를 들어 다음 XAML에서는 Canvas에 간단한 Button을 정의합니다.

<Canvas xmlns=
   "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="Example.CodeForCanvas">
  <Button Click="Button_Click">
   여기를 클릭하십시오.
  </Button>
</Canvas>

여는 Canvas 태그는 일반적인 WPF 및 XAML 네임스페이스 정의로 시작됩니다. 다음으로 이 XAML과 관련된 코드를 .NET Framework 네임스페이스 Example에 들어 있는 CodeForCanvas 클래스에서 찾을 수 있음이 지정되고, 그 다음에 "여기를 클릭하십시오."라는 화면 표시 텍스트를 지정하는 Button이 정의됩니다. 여는 Button 태그의 Click 특성은 이 단추의 click 이벤트가 Button_Click이라는 메서드를 사용하여 처리됨을 나타냅니다. 다음은 이 메서드의 코드입니다.

namespace Example {
  public partial class CodeForCanvas : Canvas  {
    void Button_Click(object sender, RoutedEventArgs e)    {
      Button btn = e.Source as Button;
      btn.Background = Brushes.Purple;
    }
  }
}

네임스페이스 및 클래스의 이름은 위의 Canvas 태그에 지정된 것과 일치합니다. CodeForCanvas 클래스는 WPF에서 제공하는 기본 Canvas 클래스에서 상속되며 partial 클래스로 정의됩니다. Partial 클래스는 .NET Framework 버전 2.0에 새로 추가된 요소로서 개별적으로 정의된 코드를 단일 클래스로 결합하는 데 사용됩니다. 이 경우 XAML로 정의한 Canvas에서 생성되는 partial 클래스와 이 코드의 partial 클래스가 결합됩니다. 그 결과 단추가 포함된 캔버스를 표시하고 해당 단추의 이벤트도 처리할 수 있는 완전한 클래스가 생성됩니다.

이벤트를 처리하는 Button_Click 메서드는 CodeForCanvas 클래스 안에 선언됩니다. 이 메서드는 일반적인 .NET Framework 규칙에 따라 이벤트를 처리하지만 이벤트의 인수는 WPF로 정의된 RoutedEventArgs 클래스를 사용하여 전달됩니다. 이 클래스의 Source 속성은 이벤트를 생성한 Button에 대한 참조를 포함하며 메서드는 이 정보를 사용하여 단추의 색을 보라색으로 변경합니다.

이러한 간단한 예제에서 볼 수 있는 것처럼 WPF 사용자 인터페이스의 요소는 시각적 트리 형식으로 구성됩니다. 이 예제에서는 트리가 Canvas와 유일한 자식 요소인 Button으로 구성되지만 실제 WPF 응용 프로그램에서는 일반적으로 트리가 훨씬 더 복잡해집니다. 화면 표시 인터페이스를 실제로 만들려면 이 시각적 트리를 렌더링해야 합니다. WPF는 가능한 한 하드웨어 렌더링을 사용함으로써 응용 프로그램의 컴퓨터에 설치되어 있는 그래픽 카드를 통해 작업을 처리합니다. 그러나 컴퓨터의 그래픽 하드웨어로 작업을 처리할 수 없는 경우 WPF는 자체 소프트웨어를 사용하여 인터페이스를 렌더링합니다. 이러한 결정은 런타임에 WPF에서 내리기 때문에 개발자는 별도의 작업을 할 필요가 없습니다.

렌더링을 하드웨어 또는 소프트웨어에서 처리하는지 여부에 관계없이 WPF는 항상 유지 모드 그래픽이라는 방식을 사용합니다. 응용 프로그램 작성자가 일반적으로 XAML과 코드를 조합하여 시각적 트리의 모양을 정의하면 WPF 자체에 이 트리의 정보가 유지됩니다. 예를 들어 사용자가 창을 표시할 경우 응용 프로그램에서 창 전체 또는 일부를 다시 그릴 필요 없이 WPF에서 이 작업을 자체적으로 처리합니다. 트리를 구성하는 요소는 화면에 픽셀로 저장되지 않고 개체로 저장되기 때문에 WPF는 이러한 유형의 렌더링을 처리하는 데 충분한 정보를 항상 유지합니다. 창과 창에 포함된 컨트롤의 크기를 조정하는 경우에도 WPF는 자체적으로 모든 렌더링을 다시 수행할 수 있습니다. WPF는 선, 타원 등의 그래픽 형태를 파악하고 비트맵이 아니라 벡터 그래픽을 사용하기 때문에 이와 같은 충분한 정보를 사용하여 새로운 크기로 인터페이스를 다시 만들 수 있습니다.

스타일 및 서식 파일

몇 가지 사용자 인터페이스 요소의 모양을 한 번 정의한 다음 그 모양을 여러 번 다시 적용하면 편리한 경우가 있습니다. 예를 들어 HTML 페이지에서는 CSS(Cascading Style Sheet)를 사용하여 이러한 작업을 수행할 수 있습니다. WPF에서는 '스타일'이 이와 유사한 기능을 합니다. 이미 널리 사용되고 있는 CSS 스타일시트의 경우에서 볼 수 있듯이 스타일을 정의하는 기능을 매우 유용하게 사용할 수 있습니다. 예를 들어 스타일을 사용하면 디자이너와 개발자의 작업 영역을 보다 명확하게 구분할 수 있기 때문에 디자이너가 인터페이스에 일관된 모양을 만들면 개발자는 이러한 세부 사항에 신경쓰지 않아도 됩니다.

WPF 응용 프로그램 작성자는 XAML의 Style 요소를 사용하여 특정 항목의 모양에 대한 특징을 하나 이상 정의한 후 해당 스타일을 반복적으로 적용할 수 있습니다. 예를 들어 ButtonStyle이라는 스타일을 다음과 같이 정의할 수 있습니다.

<Style x:Key="ButtonStyle">
  <Setter Property="Control.Background" Value="Red"/>
  <Setter Property="Control.FontSize" Value="16"/>
</Style>

이 스타일을 사용하여 정의하는 모든 단추에는 빨간색 배경이 사용되고 글꼴 크기가 16으로 지정됩니다. 예를 들면 다음과 같습니다.

<Button Style="{StaticResource ButtonStyle}">
   여기를 클릭하십시오.
  </Button>

이 예제에 나와 있는 "StaticResource"의 형태에서 알 수 있듯이 일반적으로 WPF 스타일은 리소스로 정의됩니다. 리소스는 응용 프로그램 코드와는 별도로 정의되는 데이터입니다.

스타일은 이 예제에서 설명한 것보다 훨씬 다양한 기능을 제공합니다. 예를 들어 다른 스타일에서 스타일을 파생시킬 수 있습니다. 이 경우 원래의 설정을 상속하거나 필요에 따라 재정의할 수 있습니다. 스타일을 사용하면 대화형 동작의 일반적인 측면을 지정하는 '트리거'를 정의할 수도 있습니다. 예를 들어 마우스로 단추를 가리키면 단추 배경이 노란색으로 바뀌도록 스타일을 지정할 수 있습니다.

WPF는 '서식 파일' 기능도 지원합니다. 서식 파일은 스타일과 유사하며, 다음과 같은 두 가지 유형으로 나뉩니다.

  • 데이터 서식 파일: XAML의 DataTemplate 요소를 사용하여 데이터 표시 방법에 대한 특성 그룹을 지정할 수 있습니다. 데이터 서식 파일에 색, 정렬 방식 등의 특성을 정의한 후 응용 프로그램의 사용자 인터페이스에 자유롭게 사용할 수 있습니다.
  • 컨트롤 서식 파일: XAML의 ControlTemplate 요소를 사용하여 컨트롤의 모양을 정의할 수 있습니다.

응용 프로그램의 인터페이스 모양을 간단하게 정의할 수 있는 방법을 제공하면 응용 프로그램 작성자가 작업하는 데 큰 도움이 될 수 있습니다. WPF에서는 기본적으로 스타일과 서식 파일을 통해 인터페이스 모양을 보다 쉽게 정의할 수 있습니다.

텍스트

대부분의 사용자 인터페이스에는 적어도 어느 정도의 텍스트가 표시되고, 다른 특성은 거의 없이 텍스트만 표시되는 사용자 인터페이스도 있습니다. 그러나 대다수의 사용자들은 화면에서 텍스트를 읽는 것보다 인쇄된 페이지를 읽는 것을 더 선호합니다. 사람들은 책이나 잡지에서 일반적으로 볼 수 있는 고품질의 문자 모양과 글자에 익숙해져 있기 때문에 화면에 표시되는 텍스트를 읽을 때는 왠지 모르게 불편함을 느끼는 것이 사실입니다.

WPF는 인쇄된 페이지를 읽는 것처럼 화면 표시 텍스트도 편하게 읽을 수 있도록 이러한 차이를 줄이는 것을 목표로 합니다. 이러한 목표의 일환으로 WPF에서는 기존 글꼴 라이브러리를 사용할 수 있도록 산업 표준의 OpenType 글꼴은 물론 최근에 정의된 ClearType 기술도 지원합니다. ClearType은 최신 모니터에서 각 픽셀을 구성하는 하위 요소를 개별적으로 다듬는 기법인 하위 픽셀 위치 조정을 통해 텍스트를 보다 매끄럽게 표시하는 기술입니다. WPF는 또한 Glyphs 클래스를 통한 텍스트 렌더링 기능을 부분적으로 지원합니다. 이 클래스는 XPS 문서에서 문자를 나타내는 데 사용되며, 자세한 내용은 이 문서의 뒷부분에서 설명합니다.

WPF는 가독성을 높이기 위해 문자 그룹을 연결된 단일 이미지로 대체하는 합자 방식도 지원합니다. 예를 들어 "ffi"라는 문자 그룹은 일반적으로 인쇄 페이지에서는 이 세 문자가 들어 있는 연결된 단일 합자로 대체됩니다. 이 합자를 화면 표시 텍스트에 사용하면 사용자는 사용된 세부적인 기술은 눈치채지 못한 채 인쇄된 페이지를 보는 것처럼 텍스트를 편하게 읽을 수 있습니다.

문서

텍스트는 단추, 목록 및 사용자 인터페이스의 다른 여러 요소에 표시되므로 텍스트의 가독성을 높이는 것은 매우 바람직합니다. 그러나 텍스트는 문서에서와 같이 더 긴 단락 형식으로 표시되는 경우에 그 기능이 더 중요합니다. 따라서 화면 표시 가독성을 높이기 위해서는 문서가 표시되는 방법도 함께 개선해야 합니다. 이를 위해 WPF에서는 두 가지 유형의 문서, 즉 '고정' 문서와 '동적' 문서를 지원합니다.

고정 문서는 화면에서 렌더링할 때나 인쇄할 때의 모양이 동일합니다. 일부 양식, 법률 문서, 기타 출판물 유형 등 문서 모양을 항상 일관되게 유지해야 하는 경우에는 고정된 형식의 문서를 사용하는 것이 좋습니다. WPF에서 지원하는 고정된 형식의 문서는 XPS로 정의됩니다. XPS에 대한 자세한 내용은 이 문서의 뒷부분에서 설명합니다. 고정 문서의 내용은 XAML의 FixedDocument 요소를 사용하여 지정할 수 있습니다. 이 간단한 요소에는 PageContent 요소의 목록만 포함되고, 각 PageContent 요소에는 고정 문서에 포함된 각 페이지의 이름이 들어 있습니다. WPF에서는 DocumentViewer 컨트롤을 사용하여 고정 문서를 표시합니다. 이 컨트롤은 XPS 문서를 읽기 전용 모드로 표시하고, 사용자는 문서에서 뒤로 또는 앞으로 이동하거나 특정 텍스트를 검색하는 등의 작업을 할 수 있습니다.

고정 문서가 화면과 인쇄 용지 모두에서 사용할 수 있는 문서 유형이라면 동적 문서는 화면 표시 전용 문서입니다. 동적 문서는 문서 내용의 가독성을 최대한 높이기 위해 창 크기와 기타 요인에 따라 텍스트와 그래픽을 자동으로 조정하여 표시합니다. 고정 문서에 대한 설명을 통해 예상할 수 있듯이 동적 문서는 FlowDocument라는 XAML 요소를 사용하여 정의됩니다. 다음은 간단한 예제입니다.

<FlowDocument
    ColumnWidth="300"
    IsColumnWidthFlexible="True"
    IsHyphenationEnabled="True">
      <Paragraph FontSize="12">
       <Bold>WPF 설명</Bold>
      </Paragraph>
      <Paragraph FontSize="10">
       WPF는 .NET Framework 3.0을 위한 사용자 인터페이스
       기술입니다. WPF는 문서, 2차원 및 3차원 그래픽, 미디어 등을
       지원할 뿐만 아니라 최신 사용자 인터페이스를 만들기 위한
       통합된 기반을 제공합니다. 또한 동일한 방법과 동일한 코드를
       사용하여 독립 실행형 Windows 응용 프로그램 및 브라우저
       내에서 실행되는 응용 프로그램을 만들 수 있도록
       지원합니다.
      </Paragraph>
  </FlowDocument>

이 문서는 너비가 300이상인 열에 표시되도록 지정됩니다. 이때 너비는 각 픽셀이 1/96인치인 '장치 독립적 픽셀' 단위로 측정됩니다. 그러나 문서 작성자는 바로 다음 줄에 IsColumnWidthFlexible 속성을 true로 설정하여 이 너비를 변경 가능하도록 설정했습니다. 이 설정은 이 문서를 표시하는 데 사용되는 열의 개수와 너비를 WPF에서 변경할 수 있도록 허용합니다. 예를 들어 이 문서가 표시되는 창의 너비를 사용자가 변경하면 WPF에서는 문서의 텍스트를 표시하는 데 사용되는 열의 개수와 너비를 적절하게 늘리거나 줄일 수 있습니다.

다음으로 이 문서에서는 하이픈을 사용하도록 IsHyphenationEnabled 속성이 true로 설정되었습니다. 바로 아래에는 이 문서에 포함된 두 개의 단락이 나옵니다. 각 단락의 텍스트는 글꼴 크기가 서로 다르게 설정된 두 개의 Paragraph 요소 안에 각각 들어 있고 첫 번째 단락의 텍스트는 굵게 표시되도록 설정되었습니다.

WPF에서는 가독성을 높이기 위한 몇 가지 FlowDocument 옵션을 추가적으로 정의합니다. 예를 들어 IsOptimalParagraphEnabled 속성을 true로 설정하면 WPF는 단락 전체에서 공백을 가능한 한 균등하게 사용합니다. 이 속성을 사용하면 가독성을 떨어뜨리는 "연속적인" 공백이 나타나는 것을 방지할 수 있습니다. 이 방법은 인쇄된 문서에 일반적으로 사용됩니다. 동적 문서에는 일반 텍스트나 잉크(Tablet PC에 해당)에 노트를 추가하는 것과 같이 메모를 사용할 수도 있습니다. 각 메모는 문서에서 메모가 연결되어 있는 내용을 식별하는 '앵커(anchor)'와 메모 내용 자체가 들어 있는 '카고(cargo)'로 구성됩니다.

WPF에서는 다음과 같은 몇 가지 컨트롤을 사용하여 FlowDocument를 표시할 수 있습니다.

  • FlowDocumentPageViewer: 문서에서 한 번에 한 페이지씩 이동할 수 있습니다. 이 컨트롤은 앞으로/뒤로 단추 및 문서 크기를 조정하는 데 사용할 수 있는 확대/축소 컨트롤을 제공합니다.
  • FlowDocumentScrollViewer: 페이지 오른쪽에 있는 스크롤 막대를 사용하여 문서를 스크롤하는 일반적인 보기 형식으로 FlowDocument를 표시합니다.
  • FlowDocumentReader: FlowDocumentPageViewerFlowDocumentScrollViewer의 기능을 결합한 형식으로 문서를 표시합니다. 이 컨트롤을 사용하면 사용자가 동적 문서의 페이지 단위 보기(두 페이지씩 보기 포함)와 스크롤 보기 사이를 전환할 수 있습니다.

디지털 방식으로 전달되는 정보가 증가함에 따라 화면 표시 가독성도 점차 그 중요성이 높아지고 있습니다. WPF는 동적 문서를 통해 정보를 보다 효과적으로 표시함으로써 더욱 편리한 Windows 사용자 환경을 제공합니다.

이미지

회사 로고, 노을 사진 등 이미지는 많은 사용자 인터페이스의 기본 요소입니다. WPF에서 이미지는 일반적으로 Image 컨트롤을 사용하여 표시됩니다. 예를 들어 JPEG 파일을 표시하려면 다음과 같은 XAML을 사용할 수 있습니다.

<Image
Width="200"
Source="C:\Documents and Settings\All Users\Documents\
  My Pictures\Ava.jpg" />

이 예제에서는 이미지 너비를 200으로 설정합니다. 이 경우에도 장치 독립적 픽셀 단위가 사용됩니다. Source 특성은 이미지가 들어 있는 파일을 식별합니다.

이미지 파일에는 키워드, 사용자가 지정한 등급 등 이미지에 대한 정보를 나타내는 메타데이터가 들어 있으며 WPF 응용 프로그램에서는 이 정보를 읽고 기록할 수 있습니다. 회전하는 3차원 개체에 그림을 그리는 것처럼 더 재미있는 방법으로 이미지를 사용할 수도 있습니다. 이 문서에 포함된 간단한 예제에서는 가장 일반적인 이미지 사용 방법을 보여 주지만 WPF에서 이미지를 사용하는 방법은 이보다 훨씬 다양합니다.

WPF의 Image 컨트롤은 JPEG, BMP, TIFF, GIF, PNG 등의 다양한 형식뿐만 아니라 Windows Vista에 새롭게 추가된 Microsoft Windows Media Photo(WMPhoto) 형식의 이미지도 표시할 수 있습니다. 이미지 형식에 관계없이 WPF는 WIC(Windows Imaging Component)를 사용하여 이미지를 생성합니다. WIC는 위에서 언급한 모든 이미지 형식에 대한 코더/디코더(일반적으로 '코덱'이라고 함)뿐만 아니라 타사 코덱을 추가할 수 있는 프레임워크도 제공합니다.

비디오 및 오디오

네트워크 및 프로세서 속도가 빨라짐에 따라 비디오는 사용자가 소프트웨어와 상호 작용하는 방식에서 중요한 부분을 차지하게 되었습니다. 또한 사용자들은 컴퓨터에서 음악이나 기타 오디오를 듣는 데 많은 시간을 보내기도 합니다. 이러한 요구에 부응하기 위해 WPF는 비디오 및 오디오 기능을 기본적으로 제공합니다.

비디오 및 오디오 기능은 MediaElement 컨트롤을 통해 제공됩니다. 다음은 이 컨트롤을 사용하는 간단한 XAML 예제입니다.

<MediaElement
Source="C:\Documents and Settings\All Users\Documents\
  My Videos\Ruby.wmv" />

이 컨트롤은 WMV, MPEG 및 AVI 비디오와 다양한 형식의 오디오를 재생할 수 있습니다.

2차원 그래픽

지난 20년 동안 Windows용 2차원 그래픽 작성자들은 GDI(Graphics Device Interface)와 그 후속 버전인 GDI+를 사용해왔습니다. 그러나 2차원 그래픽은 사용자 인터페이스 기술에 통합되어 있지 않기 때문에 Windows Forms 응용 프로그램에서도 별도의 네임스페이스를 통해 이 기능에 액세스해야 합니다. 3차원 그래픽의 경우에는 완전히 다른 기술인 Direct3D를 사용해야 하기 때문에 작업이 더 번거로웠습니다. 그러나 WPF를 사용하면 대부분의 응용 프로그램에서 이러한 복잡한 작업 과정을 단순화할 수 있습니다. 즉, 2차원 그래픽과 3차원 그래픽 모두를 XAML에서 직접 만들거나 WPF 라이브러리를 사용하여 프로시저 코드에서 만들 수 있습니다. 이러한 작업에 사용되는 요소는 WPF의 다른 모든 요소와 마찬가지로 응용 프로그램의 시각적 트리의 일부입니다.

2D 그래픽의 경우 WPF는 응용 프로그램에서 이미지를 만드는 데 사용할 수 있도록 다음과 같은 '모양' 그룹을 정의합니다

  • Line: 두 지점 사이에 직선을 그립니다.
  • Elllipse: 타원을 그립니다.
  • Rectangle: 사각형을 그립니다.
  • Polygon: 서로 연결된 직선의 그룹으로 정의되는 닫힌 모양을 그립니다.
  • Polyline: 서로 연결된 직선의 그룹으로 정의되는 열린 모양을 그립니다.
  • Path: 임의의 경로로 표시되는 모양을 그립니다. 열린 모양이나 닫힌 모양을 사용할 수 있으며 경로에는 직선이나 곡선을 사용할 수 있습니다. Path를 사용하면 실제로 선, 타원, 사각형, 다각형, 다중선 등을 모두 그릴 수 있기 때문에 다른 모든 모양은 편의를 위해서만 제공됩니다.

이 클래스를 사용하면 간단한 그래픽을 쉽게 만들 수 있습니다. 예를 들어 다음 XAML은 빨간색 타원을 그립니다.

<Ellipse Width="30" Height="10" Fill="Red" />

모양을 채우려면 '브러시'를 사용합니다. 위의 예제에서는 기본값인 단색 브러시를 사용하지만 WPF에서는 몇 가지 다른 옵션도 제공합니다. 예를 들어 다음 예제에서는 가로 방향으로 빨간색에서 노란색으로 바뀌는 색 그라데이션으로 채워진 사각형을 정의합니다.

<Rectangle Width="30" Height="10"
Fill="HorizontalGradient Red Yellow" />

이 외에도 이미지, 비트맵 등을 사용하여 그리는 브러시, 수직 그라데이션 및 방사형 그라데이션을 비롯하여 여러 가지 다른 브러시를 사용할 수 있습니다. 이 문서에서 다루지는 않지만 모양에 '펜'을 사용하여 윤곽선의 색, 너비 및 스타일을 지정할 수도 있습니다.

WPF에서는 모든 요소가 공통의 기반에서 구현되므로 서로 다른 요소를 간단하게 결합할 수 있다는 점을 기억해 두십시오. 예를 들어 응용 프로그램에서는 Rectangle 안에 Image를 표시하고, Button 안에 Ellipse를 배치하는 등 다양한 방법으로 요소를 결합할 수 있습니다. 따라서 2D 그래픽을 3D 그래픽 및 다른 인터페이스 요소에 결합하는 것 역시 매우 간단합니다.

WPF는 모양 외에도 2차원 그래픽 작업에 사용할 수 있는 다른 클래스 그룹도 제공합니다. '기하 도형'이라고 하는 이러한 클래스는 모양과 여러모로 비슷합니다. Line, Rectangle, EllipsePath 같은 옵션을 제공하는 모양과 마찬가지로 이 클래스도 LineGeometry, RectangleGeometry, EllipseGeometryPathGeometry 같은 옵션을 제공합니다. 일반적으로 모양은 실제 이미지를 그리는 데 사용되고 기하 도형은 주로 영역을 정의하는 데 사용된다는 것이 이 두 클래스 간의 가장 중요한 차이점입니다. 예를 들어 정사각형 이미지를 원 안에 맞추기 위해 잘라야 할 경우 EllipseGeometry 클래스를 사용하여 원의 경계를 지정할 수 있습니다. 마찬가지로 응용 프로그램에서 마우스 클릭이 감지된 영역과 같이 적중 테스트 영역을 정의해야 하는 경우에는 해당 영역에 기하 도형을 지정하면 됩니다.

이 섹션에서 설명한 모든 내용은 실제로 '시각적 레이어'라는 하위 수준의 인터페이스를 기반으로 구현된다는 점을 알아야 합니다. 이 레이어를 직접 사용하여 그래픽, 이미지 및 텍스트를 만들 수도 있습니다. 간단한 고성능 그래픽을 만들 때와 같이 레이어를 직접 사용하는 방법이 유용할 때도 있지만 대부분의 응용 프로그램에서는 WPF가 제공하는 모양 및 다른 상위 수준의 추상화를 사용합니다.

3차원 그래픽

2차원 그래픽은 Windows 인터페이스의 일반적인 요소이기 때문에 WPF에서는 2차원 그래픽과 관련된 다양한 기술을 제공합니다. 반면, 3차원 그래픽은 뛰어난 데이터 시각화, 3D 차트, 제품 렌더링 등의 다양한 기능을 통해 상당한 가치를 제공할 수 있음에도 불구하고 아직까지는 2차원 그래픽만큼 일반화되지 않았습니다. 이전에는 3D로 작업하기 위해 주로 게임 개발자나 기타 전문 집단에서만 사용하는 고유한 기술을 사용해야 했습니다. 그러나 WPF는 3D 그래픽 기능을 표준 환경에 통합함으로써 이전과는 다른 새로운 작업 환경을 제공합니다.

WPF가 없는 경우 Windows 플랫폼에서는 Direct3D API를 사용하여 3D를 개발해야 합니다. WPF의 다른 요소와 마찬가지로 WPF의 3D 그래픽 기능은 기본적으로 Direct3D에 기반을 두고 있지만 개발자의 실제 작업 환경은 훨씬 단순해졌습니다. 이 문서의 뒷부분에서 설명하는 것처럼 WPF보다는 Direct3D를 사용하는 것이 더 적합한 경우도 있지만, Microsoft의 목표는 Windows 인터페이스에 사용할 대부분의 3D 작업에 WPF를 사용하도록 하는 것입니다.

WPF에서는 응용 프로그램에 Viewport3D 컨트롤을 사용하여 3D 그래픽을 표시합니다. 이 컨트롤은 기본적으로 응용 프로그램에서 나타내는 3차원 공간에 창을 제공합니다. Viewport3D 컨트롤은 WPF 인터페이스의 아무 곳에나 사용할 수 있기 때문에 3D 그래픽을 원하는 위치에 표시하는 것이 가능합니다.

3D 그래픽을 만들 경우 개발자는 하나 이상의 '모델'을 정의한 다음, 이러한 모델에 적용할 조명과 보기 방식을 지정합니다. 이 경우에도 XAML이나 코드 또는 두 방법을 함께 사용하여 모든 옵션을 지정할 수 있습니다. WPF에서는 모델의 모양을 정의할 수 있는 GeometryModel3D 클래스를 사용하여 모델을 정의합니다. 모델을 정의한 후에는 다양한 종류의 '재질'을 사용하여 모델의 모양을 제어할 수 있습니다. 예를 들어 SpecularMaterial 클래스는 표면에 광택 효과를 적용하지만 DiffuseMaterial 클래스는 이 효과를 적용하지 않습니다.

사용한 재질에 관계없이 다양한 방법으로 모델에 조명 효과를 적용할 수 있습니다. DirectionalLight 클래스는 특정 방향의 광원을 제공하고 AmbientLight 클래스는 전체적으로 균일한 조명을 제공합니다. 마지막으로 모델을 보는 방식을 정의하는 '카메라'를 지정합니다. 예를 들어 PerspectiveCamera를 사용하면 모델을 보는 거리와 원근감을 지정할 수 있고, OrthographicCamera를 사용하면 원근감만 제외하고 동일한 옵션을 지정할 수 있습니다. 즉, 이 경우에는 카메라에서 멀리 떨어진 개체도 더 작게 표시되지 않습니다.

XAML 또는 코드에서 직접 복잡한 3D 화면을 만드는 것은 쉽지 않습니다. 3D를 사용하는 대부분의 WPF 응용 프로그램에서 개발자는 그래픽 도구를 사용하여 필요한 정의를 생성하는 경우가 많습니다. 어떤 방법을 사용하든지 표준 사용자 인터페이스에 3D 그래픽을 사용하면 사용자에게 표시되는 화면의 품질을 크게 향상시킬 수 있습니다.

변환 및 효과

WPF에서는 개발자가 모양 및 기타 요소를 정의할 수 있을 뿐만 아니라 회전, 크기 변경 등의 작업을 통해 요소를 변환할 수도 있습니다. XAML에서는 RotateTransformScaleTransform 같은 요소를 사용하여 이러한 작업을 할 수 있습니다. 이러한 변환은 모든 사용자 인터페이스 요소에 적용할 수 있습니다. 다음은 변환 작업을 보여 주는 간단한 예제입니다.

</Button>
  <Button Content="여기를 클릭하십시오.">
   <Button.RenderTransform>
    <RotateTransform Angle="45" />
   </Button.RenderTransform>
  </Button>

RotateTransform 요소는 단추를 45도 회전시킵니다. 이 방법으로 단추를 회전시키는 것은 그다지 유용하지 않지만 이러한 기능이 있다는 것만으로 WPF 디자인의 다양성을 짐작할 수 있습니다. 사용자 인터페이스의 여러 요소는 동일한 기술에 기반을 두고 있기 때문에 여러 가지 방법으로 결합될 수 있습니다.

WPF는 몇 가지 미리 정의된 효과를 제공합니다. 변환과 마찬가지로 이러한 효과도 Buttons, ComboBoxes 등 사용자 인터페이스의 다양한 요소에 적용할 수 있습니다. 미리 정의된 효과로는 인터페이스 요소를 흐릿하게 표시하는 흐리게 효과, 요소를 빛나게 만드는 외부 글로우 효과, 인터페이스 요소 뒤에 그림자를 추가하는 그림자 효과 등이 있습니다.

애니메이션

인터페이스의 요소를 움직이게 만드는 애니메이션을 사용하면 여러 가지로 매우 유용합니다. 예를 들어 단추를 클릭할 때 단추가 실제로 눌린 것처럼 아래로 내려갔다가 다시 올라오면 훨씬 실감나는 인터페이스를 연출할 수 있습니다. 이보다 복잡한 애니메이션을 사용하여 사용자의 주의를 끌거나 이야기를 들려주는 방법으로 사용자의 참여를 유도하는 인터페이스를 만들 수 있습니다. WPF의 애니메이션 기능은 이러한 모든 효과를 지원합니다.

변환과 마찬가지로 애니메이션을 단추, 모양, 이미지 등 사용자 인터페이스의 다양한 요소에 적용할 수 있습니다. 애니메이션은 시간 흐름에 따라 개체의 속성 중 하나 이상의 속성 값을 변경하여 만듭니다. 예를 들어 EllipseHeight 속성 값을 2초 동안 조금씩 줄이면 타원이 천천히 찌그러지는 것처럼 보일 수 있습니다.

서로 관련된 애니메이션 그룹을 정의하면 유용할 수 있습니다. WPF에서 제공하는 Storyboard 클래스를 사용하면 이러한 작업을 할 수 있습니다. 각 Storyboard는 하나 이상의 '시간 표시 막대'를 포함할 수 있고, 각 시간 표시 막대는 하나 이상의 애니메이션을 포함할 수 있습니다. 제공되는 다양한 시간 표시 막대를 사용하면 애니메이션을 순차적으로 실행하거나 병렬로 실행할 수 있습니다. 다음은 Ellipse를 찌그러뜨리는 애니메이션을 보여 주는 간단한 XAML 예제입니다.

<Ellipse Width="100" Height="50" Fill="Blue"
     Name="EllipseForSquashing">
     . . .
     <Storyboard>
      <DoubleAnimation
       Storyboard.TargetName="EllipseForSquashing"
       Storyboard.TargetProperty="Height"
        From="50" To="25" Duration="0:0:2" />
       </Storyboard>
       . . .
    </Ellipse>

앞에서 살펴본 예제에서와 마찬가지로 이 예제 역시 먼저 Ellipse를 정의합니다. 이 예제에서는 Name 속성도 함께 사용하여, 나중에 이 이름으로 Ellipse를 참조할 수 있도록 했습니다. 이 예제에는 몇 가지 세부 사항이 생략되었지만 XAML로 애니메이션을 정의하려면 Storyboard 요소가 반드시 있어야 합니다. 이 경우에는 EllipseHeight 속성이 Double 형식이기 때문에 StoryboardDoubleAnimation 요소가 들어 있습니다. 이 요소는 애니메이션을 만들 Ellipse의 이름, 변경할 속성 및 변경할 내용을 지정합니다. 이 예제에서는 Height의 값을 2초 동안 50에서 25로 변경합니다.

이 예제보다 훨씬 복잡한 애니메이션을 만들 수 있습니다. 예를 들어 애니메이션을 마우스 클릭 같은 이벤트를 통해 트리거하거나, 일시 중지했다가 다시 재생하거나, 지정한 횟수만큼(또는 계속) 반복하는 등 복잡한 애니메이션도 만들 수 있습니다. 애니메이션을 통해 개발자는 더 실감나고 사용하기 쉬우면서 보다 많은 기능을 제공하는 사용자 인터페이스를 만들 수 있습니다.

데이터 바인딩

대부분의 사용자 인터페이스는 특정한 유형의 데이터를 표시합니다. 인터페이스 개발자는 데이터 바인딩을 통해 데이터를 더 쉽게 표시할 수 있습니다. 데이터 바인딩을 사용하면 WPF 컨트롤에 표시되는 요소를 해당 컨트롤 외부에 있는 데이터와 직접 연결할 수 있습니다. 예를 들어 WPF TextBox 컨트롤의 Text 속성 값을 이 응용 프로그램의 비즈니스 논리의 일부인 Employee 개체의 Name 속성에 바인딩할 수 있습니다. 두 속성 중 하나가 변경되면 바인딩된 다른 한 속성에 해당 변경 내용이 반영됩니다. 예를 들어 사용자가 TextBox에서 값을 변경하면 Employee 개체의 Name 속성에도 변경된 값이 적용되고, 그 반대의 경우도 마찬가지로 적용됩니다.

두 개체의 속성 간에 이러한 연결을 만들려면 WPF Binding 클래스를 사용해야 합니다. 다음은 이러한 바인딩을 보여 주는 간단한 XAML 예제입니다.

<TextBox . . . >
<TextBox.Text>
  <Binding Path="Name" />
</TextBox.Text>
</TextBox>

이 예제에서는 Binding 요소의 Path 특성을 사용하여 TextBoxText 속성을 바인딩할 대상 속성을 식별합니다. Path는 이 속성이 속해 있는 개체(런타임에 지정됨)가 C# 또는 Visual Basic 등의 언어로 정의된 CLR(공용 언어 런타임) 개체인 경우에 사용됩니다. WPF의 데이터 바인딩 기능을 사용하면 CLR 개체는 물론 BindingXPath 속성을 사용하여 XML 데이터에 직접 연결할 수도 있습니다. 이 옵션은 지정한 데이터를 참조하는 XML 문서에서 하나 이상의 노드를 선택하는 XPath 쿼리를 만듭니다.

더 복잡한 데이터 바인딩 옵션을 사용할 수도 있습니다. 예를 들어 목록 바인딩을 사용하면 표준 IEnumerable 인터페이스를 구현하는 CLR 개체를 통해 ListBox 컨트롤에 내용을 채울 수 있습니다. 필요한 경우 데이터를 표시하기 전에 필터링 또는 정렬을 적용할 수도 있습니다. WPF에서는 ADO.NET Dataset에 바인딩할 수 있지만 관계형 데이터베이스 관리 시스템의 데이터에 직접 바인딩하는 기능은 지원하지 않습니다. 어떤 데이터 바인딩 옵션을 사용하는지에 관계없이 기본적으로 데이터 바인딩은 최대한 간단하게 사용자 인터페이스에 데이터를 표시하는 데 필요한 기능입니다.

사용자 인터페이스 자동화

WPF 인터페이스의 가장 일반적인 사용자는 당연히 사람입니다. 그러나 사용자 인터페이스를 작동하는 주체가 사람이 아니라 다른 소프트웨어인 경우도 있습니다. WPF의 UI(사용자 인터페이스) 자동화를 사용하면 이러한 방식으로 인터페이스를 작동할 수 있습니다.

예를 들어 개발자가 인터페이스를 실행하는 자동화된 스크립트를 만들어야 한다고 가정해 보겠습니다. 이 경우 개발자는 UI 자동화 기능이 제공하는 프로그래밍 방식의 액세스를 통해 사람이 실행하는 것처럼 인터페이스를 작동시키는 스크립트를 만들 수 있습니다. UI 자동화는 인터페이스의 다양한 요소를 음성으로 읽어 주는 도구와 같은 내게 필요한 옵션 도구를 만드는 데도 유용합니다. UI 자동화를 사용하면 이러한 요소가 들어 있는 트리에서 각 요소를 프로그래밍 방식으로 하나씩 실행할 수 있기 때문에 이러한 종류의 도구를 만들 수 있는 것입니다.

WPF에서는 UI 자동화를 사용하기 위해 UI 자동화 트리를 만듭니다. 이 트리는 인터페이스의 각 요소를 나타내는 일련의 AutomationElement 개체로 구성됩니다. 이 트리의 루트는 Desktop이고, 열려 있는 각 응용 프로그램은 이 루트의 자식이 됩니다. 트리는 각 응용 프로그램으로 세분화되고 각 WPF 컨트롤은 AutomationElement 개체 하나로 표시되거나, 경우에 따라 둘 이상의 개체로 표시됩니다. 이 경우 프로그래밍 방식으로 인터페이스에 액세스할 수 있도록 사용자가 상호 작용할 수 있는 모든 요소가 고유한 AutomationElement로 나타납니다. 예를 들어 단추가 여러 개 포함된 컨트롤에는 컨트롤 자체와 각각 고유한 AutomationElement 개체로 표시되는 단추가 모두 있습니다. 이와 같이 세분화된 UI 자동화 트리를 만들면 테스트 스크립트, 내게 필요한 옵션 도구 등의 클라이언트 응용 프로그램이 인터페이스의 각 구성 요소를 사람이 실제 사용하는 것처럼 액세스할 수 있습니다.

UI 자동화가 WPF의 핵심적인 기능은 아니며 대다수의 일반 사용자들은 이 기능을 거의 사용하지 않을 수도 있습니다. 그러나 소프트웨어 테스트 담당자나 움직임이 불편한 사용자와 같이 이를 필요로 하는 사용자들에게는 '없어서는 안 될' 중요한 기능입니다. 다수의 사용자들이 사용하는 기능만 중요한 것은 아닙니다.

Windows Presentation Foundation 적용

WPF에는 방대한 양의 기술이 포함되어 있습니다. 이러한 모든 기술이 사용자와의 상호 작용과 관련이 있지만 가장 많이 적용되는 세 가지 영역으로는 독립 실행형 WPF 응용 프로그램, XBAP 및 XPS 문서가 있습니다. 이 섹션에서는 이 세 가지 영역을 자세하게 살펴봅니다.

독립 실행형 WPF 응용 프로그램

WPF를 사용하는 가장 일반적인 방법은 독립 실행형 응용 프로그램에 사용하는 것입니다. 독립 실행형 WPF 응용 프로그램은 다른 일반적인 Windows 응용 프로그램처럼 실행되며 웹 브라우저를 사용하지 않습니다. 이러한 이유로 독립 실행형 응용 프로그램에는 완전한 신뢰 수준이 부여되고 WPF의 모든 기능을 사용할 수 있습니다. 완전 신뢰란 독립 실행형 WPF 응용 프로그램이 컴퓨터에 설치되어 있는 WCF(Windows Communication Foundation) 등의 다른 서비스를 자유롭게 사용할 수 있음을 의미합니다.

다른 Windows 응용 프로그램과 마찬가지로 독립 실행형 WPF 응용 프로그램을 로컬 디스크 또는 네트워크 서버에서 설치할 수 있으며 .NET Framework의 기능인 ClickOnce를 사용하여 설치할 수도 있습니다. ClickOnce는 사용자가 Internet Explorer를 통해 Windows 응용 프로그램(WPF 응용 프로그램 포함)을 다운로드하여 설치한 후 해당 응용 프로그램의 업데이트가 릴리스될 때 이를 자동으로 설치할 수 있게 해 주는 간단한 방법을 제공합니다.

XAML 브라우저 응용 프로그램: XBAP

독립 실행형 WPF 응용 프로그램이 대부분의 기능을 제공하지만 모든 경우에 적합한 것은 아닙니다. 클라이언트를 Windows 응용 프로그램으로 실행하는 것보다 웹 브라우저에서 실행하는 것이 더 적절한 경우도 많이 있습니다. WPF는 이러한 클라이언트에서도 최신 사용자 인터페이스를 사용할 수 있도록 XBAP를 제공합니다.

다음 그림에서 볼 수 있는 것처럼 XBAP는 Internet Explorer에서 실행됩니다. XBAP는 ASP.NET, JSP(JavaServer Pages) 및 기타 웹 기술을 사용하여 만든 웹 응용 프로그램의 클라이언트로 동작합니다. XBAP는 HTTP 또는 SOAP를 사용하여 이러한 웹 응용 프로그램과 통신할 수 있습니다. XBAP는 서버 플랫폼에 관계없이 항상 ClickOnce를 통해 로드되며 이 과정에서 사용자에게 대화 상자나 메시지를 전혀 표시하지 않고 일반적인 웹 페이지처럼 로드됩니다. 이러한 이유로 XBAP는 시작 메뉴나 프로그램 추가/제거에 표시되지 않습니다.

사용자 삽입 이미지

그림 7. Internet Explorer에서 실행되는 XBAP

일반적으로 XBAP는 탐색 형식의 인터페이스를 제공하지만 이는 선택 사항입니다. 탐색 형식의 인터페이스를 사용하면 사용자에게 익숙한 기존의 웹 클라이언트처럼 응용 프로그램을 실행할 수 있습니다. Internet Explorer 7에서는 XBAP가 브라우저의 기본 앞으로/뒤로 단추를 사용하고, 사용자가 액세스하는 XAML 페이지는 브라우저의 기록 목록에 나타납니다. 그러나 Internet Explorer 6에서는 XBAP가 자체적인 앞으로/뒤로 단추와 별도의 기록 목록을 사용합니다. XBAP는 현재의 실행 환경을 자동으로 파악하여 적절한 방식으로 동작하기 때문에 개발자는 개별 브라우저별로 버전을 만들지 않아도 됩니다.

XBAP는 웹에서 로드되어 브라우저에서 실행되기 때문에 .NET Framework의 코드 액세스 보안 기능이 부여하는 제한된 신뢰 수준에서만 실행됩니다. 이러한 이유 때문에 독립 실행형 WPF 응용 프로그램에서 사용할 수 있는 기능 중 일부를 XBAP에서는 사용하지 못할 수 있습니다. 예를 들어 인터넷 영역에서 배포된 XBAP에서는 다음과 같은 기능을 사용할 수 없습니다.

  • 독립 실행형 창 만들기
  • 응용 프로그램에서 정의한 대화 상자 표시
  • XBAP 자체에서 시작한 저장 대화 상자 표시
  • 제한적으로 사용할 수 있는 격리된 저장소 영역 이외의 파일 시스템에 액세스
  • UI 자동화 클라이언트로 동작
  • WCF 사용. WCF 응용 프로그램을 사용하려면 완전 신뢰 수준이 필요하기 때문에 인터넷에서 배포된 XBAP는 이 응용 프로그램을 사용할 수 없습니다. 대신에 ASMX라고 하는 ASP.NET 웹 서비스를 사용하여 원래 로드된 위치의 웹 응용 프로그램과 통신할 수 있습니다.
  • Windows Forms, MFC(Microsoft Foundation Class) 또는 Win32 직접 호출을 통해 만든 사용자 인터페이스 코드 사용. 다음 섹션에서 설명하는 것처럼 독립 실행형 WPF 응용 프로그램은 이러한 모든 이전 기술과 상호 운용될 수 있지만 신뢰 수준이 제한된 XBAP 환경에서는 여기에 나열된 기술 중 어떤 것도 사용할 수 없습니다.
  • 비관리 코드 사용

앞에서 살펴본 것처럼 독립 실행형 WPF 응용 프로그램 및 XBAP 모두에 동일한 코드 베이스를 사용할 수 있습니다. 이를 위해 개발자는 XBAP에서 사용할 수 없는 모든 기능을 ifdef에 래핑하여 조건부 컴파일을 사용할 수 있습니다. XBAP 버전에서는 문서 표시, 2차원 및 3차원 그래픽 사용, 비디오와 오디오 재생 등 독립 실행형 응용 프로그램 버전에서 사용할 수 있는 대부분의 기능을 동일하게 수행할 수 있을 뿐만 아니라 XBAP가 실행되고 있는 컴퓨터의 그래픽 하드웨어를 활용할 수도 있습니다.

Internet Explorer에는 XBAP뿐만 아니라 순수 XAML 페이지도 직접 표시할 수 있습니다. 'XAML 사용 완화'라고 하는 이러한 페이지는 브라우저에 정적 페이지를 표시할 때 유용합니다. 그러나 이벤트를 처리하려면 코드를 사용해야 하므로 결과적으로 XBAP를 만들어야 합니다.

개발자는 XBAP를 사용하여 브라우저 응용 프로그램에서 WPF 기능의 대부분을 사용할 수 있습니다. 또한 XBAP를 사용하면 독립 실행형 응용 프로그램과 브라우저 응용 프로그램에 코드가 거의 유사한 공통적인 프로그래밍 모델을 사용할 수 있습니다. 해당 클라이언트가 최신 Windows 플랫폼에서 실행되도록 디자인된 웹 응용 프로그램의 경우에는 XBAP를 사용하는 것이 좋습니다.

XPS 문서

XPS 문서는 WPF 환경에 사용되는 고정된 형식의 문서로, 사용자 인터페이스에서 매우 중요한 역할을 합니다. 앞에서 살펴본 것처럼 WPF는 DocumentViewer 컨트롤을 사용하여 XPS 문서를 표시합니다. 이 컨트롤을 WPF에 포함해야 하는 이유는 쉽게 이해할 수 있지만 XPS 자체를 WPF의 일부로 간주해야 하는 이유는 납득하기 어렵습니다. 실제로 XPS 사양에는 고정된 형식의 문서를 정의하기 위한 상당히 구체적인 방법이 명시되어 있으며, 이러한 문서는 다양한 방법으로 사용될 수 있습니다. 반면, WPF의 다른 모든 기능은 사용자 인터페이스를 만드는 데만 초점이 맞춰져 있습니다. 이와 같은 특징을 고려할 때 사용 범위가 더 넓은 XPS를 WPF라는 틀 안에 포함하는 것에 의문이 생길 수 있습니다.

XPS를 WPF의 일부로 보는 한 가지 중요한 이유는 XPS 문서가 XAML을 사용하여 정의된다는 점입니다. 레이아웃을 지정하는 Canvas 요소, 텍스트를 나타내는 Glyphs 요소, 2차원 그래픽을 만드는 Path 요소 등을 포함하여 XAML의 일부만 제한적으로 사용되지만 사실상 모든 XPS 문서는 XAML 문서입니다. 이 점을 고려하면 XPS를 WPF의 일부로 볼 수도 있습니다.

그러나 화면 표시 사용자 인터페이스는 XPS가 제공하는 핵심적인 기능과는 여전히 거리가 멉니다. Windows Vista부터는 XPS가 Windows의 기본 인쇄 형식으로 사용됩니다. XPS는 페이지 설명 언어로 사용되기 때문에 XPS를 지원하는 프린터에서 XPS 문서를 직접 렌더링할 수 있습니다. 따라서 화면 작업에서 프린터 작업에 이르는 모든 과정에서 XAML이라는 단일 설명 형식을 사용할 수 있습니다. 또한 XPS는 Windows에서 사용하던 기존의 GDI 기반 인쇄 방식을 향상시키므로 투명도 및 그라데이션과 같은 고급 그래픽 효과를 보다 정확하게 인쇄할 수 있습니다.

XPS 문서에는 XAML을 비롯하여 다양한 형식의 이미지(JPEG, PNG, TIFF 및 WMPhoto), 글꼴 데이터, 문서 구조에 대한 정보 등의 이진 데이터가 포함될 수 있습니다. 또한 필요한 경우 W3C XML 서명 정의 및 X.509 인증서를 사용하여 XPS 문서에 디지털 서명을 추가할 수도 있습니다. 모든 XSP 문서는 어떤 정보를 포함하는지에 관계없이 OPC(Open Packaging Convention)에서 정의하는 형식으로 저장됩니다. OPC는 XPS나 XAML 문서뿐만 아니라 XML 문서를 구성하는 여러 요소 간의 관계 및 이들 요소가 표준 ZIP 형식으로 저장되는 방법 등을 지정합니다. Microsoft Office 2007에서는 XML 형식에도 OPC를 사용하기 때문에 이 두 가지 형식의 문서 간에는 어느 정도의 공통점이 존재합니다.

앞에서 설명했듯이 WPF 응용 프로그램 사용자는 WPF DocumentViewer 컨트롤을 통해 XPS 문서를 볼 수 있습니다. Microsoft에서는 다음 그림에서 볼 수 있는 것처럼 DocumentViewer 컨트롤을 기반으로 하는 XPS 뷰어 응용 프로그램도 제공합니다. 이 응용 프로그램을 사용하면 컨트롤과 마찬가지로 문서에서 한 페이지씩 이동하고, 텍스트를 검색하는 등의 작업을 할 수 있습니다. XPS 문서는 사용 범위가 Windows로 제한되지 않기 때문에 Microsoft에서는 Apple Macintosh 같은 다른 플랫폼에서도 사용할 수 있는 XPS 뷰어를 제공할 예정입니다.

사용자 삽입 이미지

그림 8. XPS 뷰어를 사용하면 XPS 문서를 한 번에 한 페이지씩 읽을 수 있습니다.

WPF는 개발자가 XPS 문서를 만들고 로드하고 조작하는 데 사용할 수 있는 API 집합을 제공합니다. WPF 응용 프로그램에서도 OPC 수준의 문서를 사용할 수 있기 때문에 일반화된 방식으로 XPS 문서, Office 2007 문서 등에 액세스할 수 있습니다. Microsoft Windows Workflow Foundation을 사용하여 만든 응용 프로그램에서도 이러한 API를 통해 XPS 문서를 사용하는 워크플로를 만들 수 있습니다.

WPF는 응용 프로그램에서 고정된 형식의 문서를 표시하고 사용할 수 있도록 허용함으로써 최신 사용자 인터페이스의 구성 요소를 WPF가 지향하는 일관된 환경에 통합합니다. Windows Vista에서는 이와 동일한 형식으로 문서를 인쇄할 수 있기 때문에 화면에 표시되는 내용과 인쇄된 용지에 표시되는 내용 간의 일관성이 향상됩니다. XSP 문서 형식이 사용자 인터페이스에서 가장 중요한 기술은 아닐 수도 있지만 광범위하게 사용되는 XPS를 통해 WPF 기술이 얼마나 포괄적으로 사용되는지 그 범위를 단적으로 알 수 있습니다.


Windows Presentation Foundation 도구

WPF는 개발자가 유용하게 사용할 수 있는 다양한 기능을 제공합니다. 아무리 강력한 기술도 유용한 도구가 없다면 효용성이 떨어질 수 있습니다. Microsoft에서는 WPF를 사용하는 개발자와 디자이너에 각각 맞추어 최적화된 도구를 제공합니다. 이 섹션에서는 이 두 가지 도구에 대해 간략하게 설명합니다.

개발자용: Visual Studio

Visual Studio는 소프트웨어 개발자를 위한 Microsoft의 주력 응용 프로그램입니다. WPF의 최초 릴리스에는 개발자가 WPF 응용 프로그램을 만드는 데 사용할 수 있는 Visual Studio 2005 확장 기능이 함께 제공될 예정입니다. Visual Studio의 다음 릴리스(코드 이름: "Orcas")에는 Visual Designer for WPF(코드 이름: "Cider") 등의 다른 기능도 추가될 것입니다. 개발자는 이 비주얼 도구를 사용하여 WPF 인터페이스를 그래픽 방식으로 만든 후 기본적인 XAML을 자동으로 생성시킬 수 있습니다. 공식 릴리스 날짜는 아직 발표되지 않았지만 Orcas는 2007년 중에 출시될 예정입니다.

디자이너용: Expression Interactive Designer

앞에서 살펴본 것처럼 WPF의 기본적인 목표는 사용자 인터페이스를 만드는 작업에 디자이너가 적극적으로 참여할 수 있는 작업 환경을 만드는 것입니다. 이와 같은 목표는 XAML을 통해 실현할 수 있지만 새 작업 환경에서 디자이너가 작업하는 데 사용할 수 있는 도구가 필요합니다. 이를 위해 Microsoft에서는 Expression Interactive Designer를 만들었습니다.

아래의 스크린 샷에서 볼 수 있는 것처럼 Expression Interactive Designer는 사용자가 편리하게 작업할 수 있도록 가장 일반적인 디자인 도구 인터페이스를 제공합니다. 그러나 Designer는 WPF 응용 프로그램용 인터페이스를 만드는 데만 초점을 두어 설계되었습니다. 실제로 이 도구의 인터페이스도 WPF를 사용하여 만든 것입니다. 예를 들어 아래 화면에서 오른쪽 상단에 표시되는 WPF 컨트롤 목록과 맨 아래의 그래픽 시간 표시 막대가 이러한 인터페이스에 해당됩니다. 이러한 모든 특징은 이 문서의 앞부분에서 설명한 WPF 기능을 그대로 구현한 것으로, 인터페이스 디자이너가 자유롭게 사용할 수 있도록 설계되었습니다. 이 도구를 사용하면 애니메이션은 물론 변환, 효과 등의 다양한 기능을 그래픽 방식으로 만들 수 있습니다. 디자이너의 작업 결과는 자동으로 생성되는 XAML 파일 형식으로 저장되어 나중에 Visual Studio로 가져올 수 있습니다.

사용자 삽입 이미지

그림 9. 디자이너는 Expression Interactive Designer에서 WPF 인터페이스(그림 8)를 만들 수 있습니다.

Expression Interactive Designer는 Microsoft Expression 제품군의 세 가지 제품 중 하나입니다. 나머지 두 제품은 표준 기반 웹 인터페이스를 만드는 도구인 Expression Web Designer와 벡터 및/또는 비트맵 이미지를 만드는 도구인 Expression Graphic Designer입니다. 이 세 가지 응용 프로그램 중에서 Expression Interactive Designer만 WPF 응용 프로그램용 사용자 인터페이스를 만드는 데만 초점을 두어 설계되었습니다. Expression Graphic Designer에서 인터페이스의 GIF 이미지를 만드는 것처럼 나머지 두 제품을 사용하여 사용자 인터페이스의 구성 요소를 만들 수도 있지만 이 둘은 WPF 전용 도구가 아닙니다. 날짜는 아직 발표되지 않았지만 모든 Expression 도구는 WPF의 릴리스 이후에 출시될 예정입니다.


Windows Presentation Foundation 및 기타 Microsoft 기술

대부분의 Microsoft 신기술과 마찬가지로 WPF는 Windows 작업 환경의 다른 영역에 영향을 줍니다. 그러나 WPF 기술이 다른 영역에 미치는 영향을 살펴보기 전에 알아야 할 것은 시스템에 WPF를 설치해도 Windows Forms, MFC 등과 같이 기존의 다른 기술을 사용하는 소프트웨어와의 충돌이 발생하지 않는다는 점입니다. 즉 .NET Framework 3.0을 지원하는 시스템용으로 작성하는 새 응용 프로그램의 인터페이스는 대부분 WPF를 사용하여 만들지만 이전 기술을 사용하는 응용 프로그램도 이전과 마찬가지로 계속 실행할 수 있습니다.

Windows Presentation Foundation과 Windows Forms

.NET Framework의 최초 릴리스 이후 Windows Forms는 수많은 응용 프로그램을 만드는 데 사용되어 왔으며 WPF가 릴리스된 이후에도 일부 응용 프로그램에는 Windows Forms가 계속 사용될 것입니다. 예를 들어 이전 버전의 Windows와 같이 WPF를 사용할 수 없는 시스템에서 실행해야 하는 모든 구성 요소의 사용자 인터페이스는 Windows Forms를 사용하여 만들게 됩니다. Windows Forms가 제공하는 광범위한 컨트롤 집합을 사용하려는 경우와 같이 새 응용 프로그램에서도 필요에 따라 WPF 대신 Windows Forms를 사용할 수도 있습니다.

WPF를 사용하여 만든 응용 프로그램에서도 Windows Forms의 기능 중 일부를 활용하는 것이 유용할 수 있습니다. 예를 들어 Windows Forms는 WPF에 비해 더 많은 수의 컨트롤 집합을 제공합니다. .NET Framework 버전 2.0에 포함된 DataGridView 컨트롤의 경우 WPF에는 이 컨트롤을 대체할 수 있는 컨트롤이 없으며, 타사에서 다른 여러 가지 용도로 만든 Windows Forms 컨트롤도 상당히 많습니다. 경우에 따라서는 WPF 응용 프로그램에 이러한 기존 컨트롤을 사용해야 할 수도 있습니다. 이와는 반대로 WPF에도 3D 그래픽, 애니메이션 등 Windows Forms에서는 제공하지 않는 많은 기능이 있습니다. 이 경우에는 기존의 Windows Forms 응용 프로그램에 WPF의 기능을 통합하는 것이 좋습니다.

이러한 상호 보완은 충분히 가능합니다. 즉, WPF 응용 프로그램에서 Windows Forms 컨트롤을 호스팅하고 Windows Forms 응용 프로그램에서 WPF 컨트롤을 호스팅할 수 있습니다. 이렇게 하면 사용자는 한 응용 프로그램에서 WPF 대화 상자 및 Windows Forms 대화 상자를 별다른 불편 없이 사용할 수 있습니다.

WPF 응용 프로그램에서는 WPF의 WindowsFormsHost 컨트롤을 사용하여 Windows Forms 컨트롤을 호스팅합니다. 컨트롤의 이름에서 알 수 있듯이 이 컨트롤은 Windows Forms 컨트롤을 호스팅하여 WPF 응용 프로그램에서 Windows Forms 컨트롤을 사용할 수 있게 해 줍니다. 이 컨트롤은 ActiveX 컨트롤도 호스팅할 수 있기 때문에 이전 기술을 사용하여 만든 기존의 대규모 라이브러리를 WPF 응용 프로그램에서 액세스할 수 있습니다. 마찬가지로 Windows Forms 응용 프로그램에서는 WPF 컨트롤, 패널 및 기타 요소를 호스팅할 수 있는 Windows Forms 컨트롤인 ElementHost 컨트롤을 사용합니다. 이러한 각각의 기술을 구현하는 도구에서도 Windows Forms와 WPF용으로 작성된 소프트웨어를 모두 사용할 수 있습니다. 즉 WPF용 Visual Designer를 사용하여 Windows Forms 컨트롤을 배치하고, 마찬가지로 Windows Forms 디자이너를 사용하여 WPF 컨트롤을 배치할 수 있습니다.

그러나 WPF와 Windows Forms를 함께 사용할 경우 몇 가지 제한 사항도 있습니다. 예를 들어 WPF 컨트롤을 Windows Forms 컨트롤 위에 배치할 수는 없습니다. Windows Forms 컨트롤은 항상 최상위 계층에서 사용됩니다. WPF의 투명 효과나 변환 기능은 Windows Forms 컨트롤에 사용할 수 없습니다. 또한 WindowsFormsHostElementHost 컨트롤을 사용하려면 완전한 신뢰 수준이 필요하기 때문에 이러한 컨트롤을 사용하는 WPF 응용 프로그램을 XBAP로 실행할 수 없습니다. 그러나 대부분의 Windows 응용 프로그램의 사용자 인터페이스는 WPF와 Windows Forms를 함께 사용하여 만들 수 있습니다.

Windows Presentation Foundation과 Win32/MFC

2002년 .NET Framework가 출시되기 전에는 일반적으로 Windows 개발자가 Win32 API를 직접 호출하거나, 이 API를 래핑할 수 있는 C++ 래퍼를 제공하는 MFC를 사용하여 사용자 인터페이스를 만들었습니다. 따라서 이 방식으로 만든 인터페이스의 코드는 지금까지도 많이 사용되고 있습니다. WPF 환경에서는 이 코드가 어떻게 사용될까요?

이 질문에 대한 대답은 Windows Forms의 경우와 유사합니다. 즉, 기존 Win32/MFC 코드에서 WPF 컨트롤을 호스팅하는 것이 가능하고, WPF에서 기존 Win32/MFC 컨트롤을 호스팅하는 것도 가능합니다. 실제로 WPF와 Windows Forms 간의 상호 운용에 사용되는 기능은 Win32/MFC 상호 운용성 서비스를 기반으로 만들어졌습니다. WPF에서는 WPF 환경에서 Win32/MFC 컨트롤을 사용하는 데 필요한 HwndHost 클래스와 Win32/MFC 응용 프로그램에서 WPF 컨트롤을 사용하는 데 필요한 HwndSource 클래스를 제공합니다. 각 클래스는 필요에 따라 두 기술을 매핑합니다. 예를 들어 HwndHost는 Win32/MFC 컨트롤을 참조하는 데 사용되는 hWnd를 WPF 컨트롤인 것처럼 표시하고, 반대로 HwndSource는 WPF 컨트롤을 hWnd인 것처럼 표시합니다.

그러나 Windows Forms의 경우와 마찬가지로 이 두 환경을 함께 사용하는 경우에도 몇 가지 제약이 따릅니다. 실제로 Windows Forms 상호 운용성은 HwndHostHwndSource를 기반으로 하기 때문에 레이어 및 투명 효과에 대한 제약 등과 같이 앞에서 Windows Forms 컨트롤에 대해 설명한 모든 제한 사항이 이 경우에도 동일하게 적용됩니다. 또한 Windows Forms와는 달리 WPF와 Win32/MFC 코드를 함께 사용하는 응용 프로그램에서는 WPF의 관리 코드 환경과 Win32의 비관리 코드 환경 간의 상호 운용으로 인한 문제도 발생합니다. 이와 같은 모든 이유 때문에 Win32/MFC 코드를 사용하는 WPF 응용 프로그램은 XBAP로 실행할 수 없습니다. 그러나 앞에서 설명한 것과 마찬가지로 중요한 것은 Windows 응용 프로그램에서 WPF와 Win32/MFC를 함께 사용할 수 있다는 사실입니다. 즉, WPF를 사용하더라도 응용 프로그램의 기존 사용자 인터페이스 코드를 모두 버릴 필요는 없습니다.

Windows Presentation Foundation과 Direct3D

Direct3D는 Microsoft DirectX API 제품군의 일부로, 3차원 그래픽을 만드는 Windows 개발자들이 주로 사용하는 제품으로서 WPF가 출시된 이후에도 독자적인 가치를 제공할 것입니다. 앞에서 설명한 것처럼 실제로 WPF는 Direct3D를 통해 모든 렌더링 작업을 수행합니다. WPF의 출시 이후에 달라지는 것은 이제는 WPF에서도 3D 그래픽을 만들 수 있기 때문에 3D 개발자들이 둘 중 하나를 선택해야 한다는 점입니다.

그러나 이러한 결정은 비교적 쉽게 내릴 수 있을 것입니다. 고도의 과학적 시각화가 요구되는 3D 중심의 기술적인 응용 프로그램, 게임 등과 같이 3D를 집중적으로 개발해야 하는 경우에는 Direct3D를 사용하는 것이 좋습니다. WPF는 이러한 유형의 소프트웨어를 개발하기 위해 디자인된 플랫폼이 아니므로 적어도 WPF의 초기 릴리스는 이러한 용도로 사용하기에 적합하지 않습니다.

대신 WPF를 사용하면 전문적인 지식이 없는 일반 사용자들도 비교적 쉽게 3D 그래픽을 만들 수 있습니다. 또한 WPF 환경에서는 XBAP를 통해 웹에서 3D 그래픽을 사용할 수 있을 뿐만 아니라 2차원 그래픽과 문서를 비롯하여 응용 프로그램 사용자 인터페이스의 다른 여러 요소에 3D 그래픽을 통합할 수 있습니다. 앞에서 살펴본 HwndHost 클래스를 통해 WPF 응용 프로그램에서 Direct3D 코드를 호스팅할 수도 있습니다. WPF와 Direct3D는 각자 독자적인 가치를 제공하며 앞으로도 Windows 플랫폼에서 중심적인 기능을 할 것입니다.

Windows Presentation Foundation과 AJAX/"Atlas"

개발자는 AJAX를 사용하여 응답 성능이 뛰어난 브라우저 클라이언트를 만들 수 있습니다. AJAX를 사용하면 요청이 있을 때마다 페이지를 새로 고치지 않고도 응용 프로그램과 상호 작용할 수 있기 때문에 웹 브라우저와 웹 서버 사이의 데이터 교환 작업을 줄일 수 있습니다. AJAX는 브라우저에서 지원하는 XMLHttpRequest 개체를 사용하여 이 기능을 제공합니다. 이러한 개념은 1990년대 후반 Internet Explorer 5.0에 처음 소개되었으며 2000년대 중반에 이르러서는 더 많은 브라우저에서 XMLHttpRequest를 지원하게 되었고 AJAX가 보편화되었습니다.

그러나 AJAX 클라이언트를 만드는 작업은 그리 간단하지 않습니다. Microsoft는 이 작업을 위해 "Atlas"라는 코드 이름의 기술 집합을 만들었습니다. Atlas는 AJAX 응용 프로그램을 만드는 데 사용할 수 있는 라이브러리, 컨트롤 및 기타 요소의 집합으로, Internet Explorer뿐 아니라 다른 여러 브라우저에 사용할 수 있는 클라이언트 스크립트 라이브러리 및 서버 쪽에서 사용할 수 있는 ASP.NET 확장 기능으로 구성됩니다. Atlas는 AJAX 클라이언트가 포함된 웹 응용 프로그램을 보다 간단하게 만들 수 있도록 도와 줍니다.

AJAX는 다양한 브라우저에서 사용할 수 있다는 점에서 많은 개발자들이 관심을 갖는 기술입니다. AJAX를 사용하면 보다 뛰어난 대화형 인터페이스를 만들어 웹 사용자에게 제공할 수 있지만 지원되는 콘텐츠 형식이 매우 제한적이라는 단점이 있습니다. 예를 들어 AJAX만으로는 그래픽, 비디오, 애니메이션 및 기타 최신 상호 작용 방식을 지원할 수 없습니다. 따라서 해당 클라이언트에서 WPF를 지원하도록 해야 하는 응용 프로그램을 만들려면 AJAX 대신 XBAP를 선택하는 것이 좋습니다.

Windows Presentation Foundation과 "WPF/E"

XBAP를 사용하면 웹 응용 프로그램에서 WPF 기능의 대부분을 구현할 수 있지만 이를 위해서는 클라이언트 컴퓨터에 WPF가 설치되어 있어야 하므로 웹 응용 프로그램의 사용 범위가 제한될 수 있습니다. 또한 WPF를 지원하지 않는 Macintosh나 기타 시스템에서도 액세스할 수 있고 최신 인터페이스를 제공하는 웹 응용 프로그램을 만들어야 하는 경우가 생길 수도 있습니다.

코드 이름 "WPF/E"는 이와 같은 문제를 해결하기 위해 곧 제공될 기술입니다. WPF/E("E"는 Everywhere를 나타냄)는 Macintosh와 같은 다양한 클라이언트 플랫폼, 소형 장치 등을 비롯하여 Internet Explorer, FireFox, Netscape 등의 다양한 웹 브라우저에 WPF의 기능 중 일부를 제공할 예정으로, 여기에는 2차원 그래픽, 이미지, 비디오, 메모 및 텍스트가 포함됩니다. 그러나 XBAP에서 지원하는 기능 중 3차원 그래픽, 문서, 하드웨어 가속 등의 일부 기능은 WPF/E에서 지원되지 않습니다.

개발자는 JavaScript를 사용하여 WPF/E 응용 프로그램을 만들 수 있습니다. C# 및 Visual Basic 언어를 사용하여 개발할 수 있도록 WPF/E에는 .NET Framework의 플랫폼 간 호환 기능 중 일부도 포함될 것입니다. WPF/E는 .NET Framework 3.0의 일부가 아니며 2007년 중에 릴리스될 것으로 예상됩니다. WPF/E가 릴리스되면 웹 응용 프로그램 작성자는 광범위한 플랫폼에서 다양한 기능을 사용하여 클라이언트 응용 프로그램을 만들 수 있는 또 다른 기술을 경험할 수 있을 것입니다.


결론

사용자 인터페이스는 대부분의 응용 프로그램에서 매우 중요한 부분을 차지합니다. 효과적인 인터페이스를 만들면 사용자 및 사용자가 속한 조직에 상당한 이점을 제공할 수 있습니다. WPF의 기본적인 목표는 개발자가 이러한 효과적인 사용자 인터페이스를 만들 수 있도록 돕는 것이기 때문에 WPF는 Windows 응용 프로그램을 만드는 개발자나 일반 사용자 모두가 기대할 만한 기술입니다.

WPF는 최신 사용자 인터페이스를 만들기 위한 통합 플랫폼을 제공하고, 이러한 인터페이스를 만드는 작업에 디자이너가 보다 적극적으로 참여할 수 있도록 도와 주며, 독립 실행형 응용 프로그램과 브라우저 응용 프로그램을 위한 공통적인 프로그래밍 모델을 제공함으로써 Windows 사용자 환경을 크게 향상시키는 것을 목표로 합니다. WPF는 지난 20년 동안 Windows 사용자 인터페이스의 기반이 되어 온 기술을 대체하는 새로운 기술을 제시하여 향후 20년 동안 사용할 기반 기술로서의 역할을 할 것입니다.

출처 : 한국 마이크로소프트 MSDN (2006년 9월)

Posted by 상현넘™

댓글을 달아 주세요

요약: Windows WF(Workflow Foundation)를 호스팅하는 응용 프로그램이 실행 중인 워크플로를 관리 및 모니터링하는 방법과 런타임 서비스 및 기본 구현 방법에 대한 개요를 제공합니다. 이 기사는 Microsoft .NET Framework, C# 및 WF 프로그래밍 모델에 대한 지식이 있는 사용자를 대상으로 합니다.

소개

이 기사는 Windows WF(Workflow Foundation)를 사용하는 개발자가 실행 중인 워크플로 인스턴스를 관리 및 모니터링하는 데 사용할 수 있는 응용 프로그램의 다양한 옵션을 쉽게 이해하도록 돕기 위한 것입니다. 따라서 이 기사는 독자가 Microsoft .NET Framework, C# 및 WF에 대한 기본 지식이 있다는 가정 하에 작성되었습니다.

WF는 작업 라이브러리 및 프레임워크, 런타임 엔진 및 런타임 서비스 구성 요소로 구성되어 있으며, 이러한 구성 요소는 호스트 응용 프로그램 프로세스 내에서 실행되어야 합니다. 워크플로는 런타임 엔진에서 실행하는 일련의 작업으로 구성되어 있으며, 런타임 엔진은 호스트 응용 프로그램 프로세스 내에서 실행되어야 합니다. 다음 그림에서는 워크플로, 작업 및 워크플로 런타임 엔진을 호스트 응용 프로그램 프로세스에서 호스팅하는 방법을 보여 줍니다.


그림 1. Windows Workflow Foundation 호스트 프로세스

WF는 워크플로 실행 및 상태 관리를 담당하는 런타임 엔진을 제공합니다. WF 런타임은 ASP.NET, Windows 서비스, 콘솔 응용 프로그램 및 Windows Forms 응용 프로그램을 포함한 모든 .NET 프로세스에서 호스팅할 수 있습니다. 워크플로를 사용하는 응용 프로그램을 빌드할 때 개발자가 이러한 호스트 프로세스를 작성해야 합니다. 런타임 서비스는 호스트 프로세스에서 실행되어 런타임 엔진이 워크플로 실행을 관리할 때 런타임 엔진에 추가 기능을 제공합니다.

호스트 응용 프로그램을 WF에 맞게 구현할 경우 여러 가지 사항을 고려해야 합니다. 이 기사에서는 호스트 응용 프로그램이 워크플로를 관리 및 모니터링하는 방법에 대한 개요를 제공하며 기본 런타임 서비스 및 기본 구현 방법에 대해 간략하게 설명합니다.


워크플로 인스턴스 수명 주기 관리

WF는 WorkflowInstance 클래스에서 기본 작업 및 제어 작업 메서드를 제공하여 워크플로 상태 및 수명 주기를 관리합니다. 워크플로 인스턴스 수명 주기 관리 섹션에서는 다양한 워크플로 인스턴스 관련 런타임 이벤트, 이러한 이벤트 간의 전환 및 워크플로 상태 관계에 대해 설명합니다.

보존 지점

워크플로가 장시간 실행되는 빈도가 많아져서 사실상 유휴 시간이 길어지면 사용자나 다른 시스템이 입력 작업에 대해 대기하는 시간이 길어집니다. 메모리에 유휴 워크플로를 두는 것은 실용적이지 못하므로 워크플로가 대기 중인 이벤트를 수신할 때까지 워크플로 인스턴스 상태를 저장 매체에 보존하는 것이 좋습니다. 또한 워크플로 인스턴스 상태를 저장해 두면 이후 프로세스에서 오류가 발생할 경우 저장했던 지점부터 워크플로를 다시 시작할 수 있습니다.

그림 2에서는 보존 지점을 사용하여 실행 중인 워크플로 인스턴스를 다시 시작하는 방법을 보여 줍니다.


그림 2. 보존 지점을 사용하여 실행 중인 워크플로 인스턴스 다시 시작

워크플로 인스턴스 상태가 B 지점에서 보존되고 C 지점에서 오류가 발생한 경우 A 지점과 B 지점 사이에 완료된 작업이 손실되지 않고 워크플로 인스턴스를 B 지점부터 다시 시작할 수 있습니다. 하지만 보존 서비스를 사용할 수 없거나 워크플로 인스턴스 상태가 보존되지 않은 경우에는 A 지점과 B 지점 사이에 완료된 작업을 잃게 됩니다.

WorkflowPersistenceService가 있는 경우 즉, WorkflowRuntime 인스턴스에 추가된 경우 워크플로 런타임 엔진이 이 서비스를 사용하여 워크플로 인스턴스 상태를 저장 매체에 보존합니다. 이러한 작업은 다음 지점에서 수행될 수 있습니다.

  • PersistOnCloseAttribute로 표시된 작업 완료 시(예: 트랜잭션 범위 작업)
  • 워크플로 인스턴스 완료 전
  • 워크플로 인스턴스 종료 전
  • 워크플로가 유휴 상태로 되는 경우
  • WorkflowInstance.Unload 또는 WorkflowInstance.TryUnload가 호출된 경우

WF 런타임 엔진은 WorkflowPersistenceService에서 SaveWorkflowInstanceState() 메서드를 호출하여 워크플로 인스턴스 상태를 저장하며 필요한 경우 LoadWorkflowInstanceState() 메서드를 호출하여 워크플로 인스턴스 보존 상태를 검색합니다. 워크플로 런타임은 보존 수행 시기에 관한 모든 구문을 처리하고, 보존 서비스는 워크플로 인스턴스에 대한 실제 저장 및 로드를 담당하며, 작업 상태 및 워크플로 인스턴스 ID는 직렬화되어 보존 저장소에 저장됩니다. 또한 워크플로 인스턴스 실행을 다시 시작하는 데 필요한 다른 모든 정보(예: 대기열)도 직렬화되어 저장됩니다.

워크플로 인스턴스 이벤트

워크플로 인스턴스의 상태는 5가지, 즉 Created, Running, Suspended, Completed, Terminated 중 하나가 될 수 있습니다. 워크플로 인스턴스가 실행되는 동안 워크플로에는 13개의 이벤트가 발생할 수 있는데, 이러한 이벤트는 다른 상태로의 전환을 나타낼 수 있습니다. 예를 들어 WorkflowCompleted 이벤트는 인스턴스가 Running 상태에서 Completed 상태로 전환되었음을 나타냅니다. 일부 이벤트는 인스턴스가 다른 상태로 전환되었음을 나타내지 않기도 합니다. 예를 들어 WorkflowPersisted 이벤트는 인스턴스가 Persisted 상태이지만 여전히 Running 상태임을 나타냅니다. 13개의 이벤트 중 11개는 런타임 이벤트 및 워크플로 추적 이벤트를 통해 호스트 응용 프로그램과 통신하지만 Changed 이벤트와 Exception 이벤트는 워크플로 추적 이벤트를 통해서만 호스트 응용 프로그램과 통신할 수 있습니다.

호스트 응용 프로그램은 WF가 WorkflowInstance 클래스에 제공하는 제어 작업 메서드를 사용하여 워크플로 수명 주기를 관리할 수 있습니다. 또한 워크플로 수명 주기는 정책 설정을 통해서도 관리할 수 있습니다. 예를 들어 응용 프로그램은 언로드 정책을 사용하여 WF 엔진이 워크플로 인스턴스를 언로드하도록 지시할 수 있습니다. WF는 워크플로 인스턴스 상태에 영향을 줄 수 있는 기본 작업을 제공합니다. 예를 들어 SuspendActivityTerminateActivity 작업을 사용하여 워크플로 인스턴스를 각각 일시 중단하거나 종료할 수 있습니다. 다음 섹션에서는 워크플로 인스턴스의 상태 전환 및 워크플로 인스턴스 이벤트와 통신하기 위해 워크플로 런타임이 발생시키는 다양한 워크플로 인스턴스 관련 이벤트에 대해 설명합니다.

WorkflowAborted

워크플로 런타임 엔진이 메모리 내 인스턴스를 제거하면 워크플로 인스턴스가 중단됩니다. 호스트 응용 프로그램은 WorkflowInstance.Abort()를 호출하여 워크플로 인스턴스를 중단할 수 있습니다. 중단된 워크플로 인스턴스는 WorkflowInstance.Resume()을 호출하여 마지막 보존 지점부터 다시 시작할 수 있습니다. 워크플로 인스턴스 중단은 응용 프로그램이 마지막 보존 지점부터 WorkflowInstance.Abort()가 호출되기 직전까지 완료된 모든 작업을 취소하기로 결정하는 경우처럼 극단적인 상황에 사용됩니다.

WorkflowCompleted

워크플로 인스턴스가 실행을 끝마치면 해당 인스턴스가 완료됩니다. 이때 호스트 응용 프로그램은 워크플로 인스턴스가 소비하지 않은 다른 이벤트 및 메시지 대기열을 검사할 수 있습니다.

WorkflowCreated

워크플로는 인스턴스 생성을 마친 후 작업 실행이 시작되기 전에 생성됩니다. 워크플로 인스턴스는 여러 개의 오버로드된 WorkflowRuntime.CreateWorkflow() 메서드 중 하나를 호출하여 생성할 수 있습니다.

WorkflowIdled

외부 이벤트(예: 타이머, 메시지 또는 기타 사용자 지정 이벤트)가 실행되는 동안 기다릴 때 워크플로 인스턴스는 유휴 상태가 됩니다. 시스템 리소스를 절약하기 위해 응용 프로그램은 언로드 정책을 설정하여 유휴 상태인 워크플로 인스턴스를 메모리에서 언로드할 수 있습니다. 호스트 응용 프로그램이 기본 SqlWorkflowPersistenceService를 사용하는 경우 응용 프로그램 구성 파일에 UnloadOnIdle 플래그를 설정하여 인스턴스가 유휴 상태일 때 WF 런타임 엔진이 워크플로 상태를 보존하도록 지시할 수 있습니다.

WorkflowLoaded

인스턴스 상태를 보존 저장소에서 메모리로 로드하는 경우 WorkflowLoaded 이벤트가 발생합니다.

WorkflowPersisted

WorkflowRuntime 인스턴스에 기본 SqlWorkflowPersistenceService 또는 사용자 지정 보존 서비스가 추가된 경우 워크플로 인스턴스 상태를 보존 저장소에 저장하면 워크플로 인스턴스가 보존됩니다.

WorkflowResumed

중단 또는 일시 중단된 워크플로 인스턴스에서 WorkflowInstance.Resume()을 호출하면 해당 워크플로 인스턴스가 다시 시작됩니다.

WorkflowStarted

WorkflowInstance.Start()를 호출하면 WorkflowStarted 이벤트가 발생합니다. WorkflowStarted 이벤트는 워크플로 런타임 엔진이 워크플로 작업을 실행하기 전에 발생합니다.

WorkflowSuspended

WorkflowInstance.Suspend()를 호출하거나 SuspendActivity 작업을 실행하면 워크플로 인스턴스가 일시 중단됩니다. 그 결과 워크플로 인스턴스는 일시 중단 상태가 됩니다.

WorkflowTerminated

WorkflowInstance.Terminate()를 호출하거나, TerminateActivity 작업을 실행하거나, 실행 중인 워크플로 인스턴스에서 처리되지 않는 예외가 발생하면 워크플로 인스턴스가 종료됩니다. 이 이벤트가 발생하면 워크플로 인스턴스는 종료 상태가 됩니다.

WorkflowUnloaded

워크플로 인스턴스가 메모리에서 보존 저장소로 언로드되는 경우 WorkflowUnloaded 이벤트가 발생하며, 이는 보존 정책에 따라 수행되거나 WorkflowInstance.Unload() 또는 WorkflowInstance.TryUnload() 호출을 통해 수행됩니다.

워크플로 인스턴스 이벤트 전환

워크플로 인스턴스 이벤트는 워크플로 런타임 이벤트와 워크플로 추적 이벤트를 통해 호스트와 통신합니다. 호스트 응용 프로그램은 런타임 이벤트를 구독하거나 추적 서비스를 사용하여 알림을 수신할 수 있습니다. Exception 및 Changed 이벤트는 추적 서비스를 통해서만 호스트 응용 프로그램과 통신합니다. Exception 이벤트는 워크플로 인스턴스 실행 중에 예외가 발생했음을 나타내며 Changed 이벤트는 실행 중에 워크플로 인스턴스가 동적으로 업데이트되었음을 나타냅니다.

그림 3에서는 다양한 워크플로 이벤트 간의 전환과 워크플로 상태를 보여 줍니다.

사용자 삽입 이미지

그림 3. 워크플로 이벤트 간 전환 및 상태(더 큰 이미지를 보려면 이미지를 클릭하십시오.)

보존 서비스를 사용할 수 있다면 그림 3에 나타나 있듯이 보존 지점이 생성됩니다. WorkflowPersisted, WorkflowUnloadedWorkflowLoaded 이벤트를 사용할 수 있는 경우 호스트 응용 프로그램이 이러한 이벤트를 확인할 수 있어야 합니다. 워크플로 인스턴스가 메모리에 없을 때 보존 서비스를 사용하면 워크플로 인스턴스가 먼저 로드된 후 인스턴스에서 유효한 작업(Resume, Abort, Terminate 등)이 수행됩니다. 예를 들어, 워크플로 인스턴스가 일시 중단된 채 언로드된 경우 Resume을 호출하면 다이어그램에 나타나 있듯이 워크플로 인스턴스가 먼저 로드된 후 Resumed 이벤트가 발생합니다.

워크플로 인스턴스 작업

앞에서도 설명했듯이 WorkflowInstance 클래스는 워크플로 인스턴스의 수명 주기를 제어하기 위해 메서드를 사용합니다. 이 섹션에서는 이러한 메서드에 대해 설명합니다.

WorkflowInstance.Start()

생성된 워크플로 인스턴스를 실행합니다. WorkflowInstance.Start()가 실행되면 워크플로 런타임이 WorkflowStarted 이벤트를 발생시키며 워크플로 인스턴스는 Running 상태가 됩니다. 이미 시작된 워크플로 인스턴스에 대해 Start()를 호출하면 InvalidOperationException이 발생합니다.

WorkflowInstance.Abort()

워크플로 인스턴스를 중단합니다. 성공적으로 중단되면 워크플로 런타임이 WorkflowAborted 이벤트를 발생시킵니다.

WorkflowInstance.Load()

언로드된 워크플로 인스턴스를 보존 저장소에서 메모리로 로드합니다. 그 다음 이 인스턴스는 언로드되기 전에 있었던 상태부터 실행됩니다. 성공적으로 로드되면 워크플로 런타임이 WorkflowLoaded 이벤트를 발생시킵니다.

WorkflowInstance.Resume()

중단 또는 일시 중단된 워크플로 인스턴스를 다시 시작하고 실행합니다. 워크플로 런타임은 워크플로 인스턴스 실행이 다시 시작되기 바로 전에 WorkflowResumed 이벤트를 발생시킵니다.

WorkflowInstance.Suspend()

워크플로 인스턴스 실행을 일시 중단합니다. WorkflowInstance.Suspend()를 성공적으로 호출하면 워크플로 런타임이 WorkflowSuspended 이벤트를 발생시킵니다.

WorkflowInstance.Terminate()

워크플로 인스턴스를 종료하고 메모리 내 워크플로 인스턴스를 지웁니다. 워크플로 런타임은 워크플로 인스턴스가 메모리에서 지워졌음을 등록된 보존 서비스에 알립니다. SqlWorkflowPersistenceService의 경우 종료 시에 해당 워크플로 인스턴스에 대한 모든 상태 정보가 데이터베이스에서 삭제되므로 이전에 저장한 보존 지점부터 워크플로 인스턴스를 다시 로드할 수 없습니다. WorkflowInstance.Terminate()에 성공하면 워크플로 런타임이 WorkflowTerminated 이벤트를 발생시킵니다.

WorkflowInstance.Unload()

워크플로 인스턴스를 메모리에서 보존 저장소로 언로드합니다. WorkflowInstance.Unload()는 동기식이므로 현재 예약된 작업을 끝마칠 때까지 또는 트랜잭션 범위의 끝부분에 도달할 때까지 다른 작업을 차단하여 언로드 작업이 성공적으로 수행될 수 있도록 합니다. WorkflowInstance.Unload()에 성공하면 워크플로 런타임이 WorkflowUnloaded 이벤트를 발생시킵니다. 등록된 보존 서비스가 없는 경우 Unload()를 호출하면 InvalidOperationException이 발생합니다.

WorkflowInstance.TryUnload()

WorkflowInstance.TryUnload()WorkflowInstance.Unload()와 달리 워크플로가 언로드될 때까지 다른 작업을 차단하지 않습니다. WorkflowInstance.TryUnload()는 워크플로 인스턴스를 메모리에서 보존 저장소로 언로드하며 인스턴스가 일시 중단 또는 유휴 상태인 경우 true를 반환하고 그렇지 않은 경우에는 false를 반환합니다. 등록된 보존 서비스가 없는 경우 TryUnload()를 호출하면 InvalidOperationException이 발생합니다.

WorkflowInstance의 다양한 제어 메서드에 대한 자세한 내용은 Windows Foundation SDK를 참조하십시오.


관리 효율 및 모니터링

워크플로를 호스팅하는 응용 프로그램은 호스팅되어 실행되는 워크플로를 관리 및 모니터링해야 합니다. WF에서는 다양한 관리 효율 및 모니터링 도구를 지원합니다. 예를 들어 WF에서는 하위 수준 디버깅에 사용할 수 있는 종단 간 추적 기능과 워크플로 데이터 추출 및 모니터링을 위한 추적 인프라를 제공합니다.

이 섹션에서는 관리 효율 및 모니터링 인프라 그리고 호스트 응용 프로그램에서 이를 사용하는 방법에 대해 설명합니다.

추적

WF에서는 워크플로 인스턴스가 실행되는 동안 워크플로, 작업 및 사용자 이벤트와 데이터를 캡처하기 위한 추적 인프라를 제공합니다. 모든 워크플로 런타임 인스턴스가 등록된 추적 서비스를 여러 개 사용하거나 전혀 사용하지 않을 수 있습니다. 추적 정보는 등록된 추적 서비스로 전송되며 호스트 응용 프로그램의 요구에 따라 추적 서비스가 이 정보를 저장 및 처리합니다. 또한 WF에서는 호스트 응용 프로그램이 사용할 수 있는 기본 SQL 기반 추적 서비스(SqlTrackingService)를 제공하며, 호스트 응용 프로그램 개발자가 자체적으로 사용자 지정 추적 서비스를 작성하여 호스트 응용 프로그램에 사용할 수도 있습니다.

추적 기능을 사용하여 워크플로 인스턴스의 실행 기록을 검사할 수 있으며 사용자 시스템에서 실행 중인 워크플로 인스턴스의 현재 상태를 확인할 수 있습니다. 또한 추적 기능을 사용하면 함께 사용 가능한 정보를 워크플로 정의에 제공하여 시스템에서 실행 중인 워크플로 인스턴스의 향후 예상 실행 경로를 예측할 수 있습니다. WF에서 제공하는 응용 프로그램 샘플(Workflow Monitor Sample)은 기본 SqlTrackingService를 사용하며 워크플로 디자이너는 현재 실행 중인 워크플로와 완료된 워크플로에 대한 워크플로 및 작업 상태 정보가 표시되도록 이 샘플을 제어합니다.

추적 기능을 사용한 워크플로 모니터링에 대한 자세한 내용은 Applications Samples/Workflow Monitor Sample의 Workflow Monitor SDK Tool 섹션을 참조하십시오. 사용자 지정 추적 서비스를 빌드하는 방법에 대한 예제를 보려면 Technology Samples/Tracking의 ConsoleTrackingService Sample 및 File Tracking Service and Query Sample 섹션을 참조하십시오. 기본 SqlTrackingService를 사용하는 방법에 대한 예제를 보려면 Technology Samples/Tracking out-of-box의 Simple Tracking Sample 및 Query Using SQLTracking Service Sample 섹션을 참조하십시오.

추적 및 종단 간 추적

추적 기능을 사용하면 응용 프로그램 상태를 모니터링한 후 실행 중인 시스템을 방해하지 않고 문제를 분리하여 수정할 수 있습니다. WF는 System.Diagnostics API를 사용하여 워크플로 런타임 및 워크플로 인스턴스 실행에 대한 정보를 추적합니다(규칙 집합 평가 정보 포함). 기본적으로 추적은 OFF로 설정되어 있지만 원하는 경우 ON으로 전환할 수 있습니다.

WF는 종단 간 추적에도 참여합니다. 종단 간 추적 기능을 사용하면 추적 뷰어에서 다양한 구성 요소 간의 연속 추적 정보 및 이러한 구성 요소 간의 전환 상태를 확인할 수 있습니다. 따라서 종단 간 디버깅이 수월해집니다.

응용 프로그램 구성 파일을 사용하는 경우 다음 구문을 추가하면 여러 WF 네임스페이스에 대한 추적을 로깅할 수 있습니다.

<system.diagnostics>
    <switches>
        <add name="System.Workflow LogToTraceListeners" value="1" />
        <add name="System.Workflow.Runtime" value="All" />
        <add name="System.Workflow.Runtime.Hosting" value="All" />
        <add name="System.Workflow.Runtime.Tracking" value="All" />
        <add name="System.Workflow.Activities" value="All" />
        <add name="System.Workflow.Activities.Rules" value="All" />
    </switches>
</system.diagnostics>

LogToTraceListeners가 사용되는 경우 WF는 호스트 응용 프로그램에서 만든 각 TraceListener를 열거하며 모든 로깅 정보를 여기에 전송합니다. 예제의 나머지 행에서는 네임스페이스를 지정하여 로깅 정보 및 추적된 정보의 양을 캡처할 수 있습니다. 값 특성에는 All, Off, Critical, Error, Warning, Information 및 Verbose를 사용할 수 있습니다. 값 특성 사용에 대한 자세한 내용은 WF SDK를 참조하십시오.

워크플로 런타임 이벤트

런타임 이벤트는 워크플로 런타임에 의해 발생되며, 워크플로 런타임 및 워크플로 인스턴스의 수명 주기를 관리하기 위한 수단을 호스트 응용 프로그램에 제공합니다. 이벤트 처리기는 WorkflowRuntime 클래스에 정의되므로 이를 사용하려면 호스트 응용 프로그램이 이러한 이벤트를 구독해야 합니다.

런타임 이벤트는 호스트 응용 프로그램이 쿼리를 위해 이벤트 및 관련 데이터를 저장하는 대신 특정 이벤트에 대해 영향을 줄 필요가 있을 때 간단한 알림 시스템의 역할을 합니다. 전자의 경우에는 추적 인프라를 사용하는 것이 좋습니다.

WorkflowRuntime 인스턴스는 여러 개의 워크플로 인스턴스를 실행할 수 있으며, 각 워크플로 인스턴스는 자체 수명 주기를 가집니다. 따라서 워크플로 인스턴스 이벤트의 이벤트 인수에는 워크플로 인스턴스 ID 및 다른 정보가 포함됩니다. 이 정보를 사용하면 해당 이벤트와 워크플로 런타임이 이 이벤트를 발생시키게 하는 워크플로 인스턴스를 연결할 수 있습니다.

다음 섹션에서는 사용 가능한 워크플로 런타임 이벤트에 대해 설명합니다.

WorkflowRuntime.ServiceExceptionNotHandled

이 이벤트는 서비스 소유 스레드가 예외를 생성하는 경우에 발생합니다. WorkflowRuntimeService 클래스에서 파생된 서비스는 RaiseServicesExceptionNotHandledEvent() 메서드를 호출하여 구독자에게 ServicesExceptionNotHandled 이벤트에 대해 알려 줍니다. 이 이벤트는 실행 중에 예외를 발생시키는데, 이 예외는 처리할 수 없습니다. 기본 서비스는 이러한 조건에서 이 이벤트를 발생시킵니다. 호스트 응용 프로그램은 이 이벤트를 구독하여 복구 메커니즘을 구현할 수 있습니다. 이 이벤트와 관련된 이벤트 인수는 ServicesExceptionNotHandledEventArgs입니다.

WorkflowRuntime.Started

이 이벤트는 WorkflowRuntime의 지정된 인스턴스가 시작되는 경우에 발생합니다. 이 이벤트와 관련된 이벤트 인수는 WorkflowRuntimeEventArgs입니다.

WorkflowRuntime.Stopped

이 이벤트는 WorkflowRuntime의 지정된 인스턴스가 중지되는 경우에 발생합니다. 이 이벤트와 관련된 이벤트 인수는 WorkflowRuntimeEventArgs입니다.

표 1. WorkflowInstanceEvents
이벤트 설명 이벤트 인수
WorkflowAborted 워크플로 인스턴스가 중단되면 발생합니다. WorkflowEventArgs
WorkflowCompleted 워크플로 인스턴스가 완료되면 발생합니다. WorkflowCompletedEventArgs
WorkflowCreated 워크플로 인스턴스가 생성된 후 작업이 처리되기 전(즉, 워크플로가 실행되기 전)에 발생합니다. WorkflowEventArgs
WorkflowIdled 워크플로 인스턴스가 유휴 상태로 들어설 때 다시 말해 외부 이벤트(예: 타이머, 메시지 등)가 실행되는 동안 기다릴 때 발생합니다. WorkflowEventArgs
WorkflowLoaded 워크플로 인스턴스가 일반적으로 보존 저장소에서 메모리로 로드될 때 발생합니다. WorkflowEventArgs
WorkflowPersisted 워크플로 인스턴스가 보존되면 발생합니다. WorkflowEventArgs
WorkflowResumed 워크플로 인스턴스가 일시 중단 상태나 중단 상태에서 다시 시작되는 경우 발생합니다. WorkflowEventArgs
WorkflowStarted 워크플로 인스턴스가 실행되면 발생합니다. WorkflowEventArgs
WorkflowSuspended 워크플로 인스턴스가 일시 중단되면 발생합니다. WorkflowSuspendedEventArgs
WorkflowTerminated 워크플로 인스턴스가 종료되면 발생합니다. WorkflowTerminatedEventArgs
WorkflowUnloaded 워크플로 인스턴스가 메모리에서 보존 저장소로 언로드될 때 발생합니다. WorkflowEventArgs

다양한 이벤트 및 워크플로의 상태 전환 방법에 대한 자세한 내용은 이 기사의 앞부분에 있는 "워크플로 인스턴스 수명 주기 관리" 섹션을 참조하십시오.

다양한 워크플로 런타임 이벤트의 사용 방법에 대한 자세한 내용은 Windows Workflow Foundation SDK Samples를 참조하십시오.

성능 카운터

Windows 성능 도구를 사용하여 워크플로 성능을 모니터링할 수 있습니다. 성능 도구는 시스템 모니터와 성능 로그 및 경고의 두 부분으로 구성되어 있습니다. 성능 로그 및 경고를 통해 성능 카운터를 구성하여 성능 데이터를 기록할 수 있으며, 특정 카운터 값이 정의된 임계값을 넘거나 아래로 내려갈 때 사용자에게 알려 주도록 시스템 경고를 설정할 수 있습니다.

WF에서는 워크플로 성능을 추적하는 데 사용할 수 있는 WF 성능 개체를 성능 카운터 집합에 제공합니다. 성능 카운터의 전체 목록을 보려면 WF SDK의 Workflow Performance Counters 섹션을 참조하십시오.

성능 도구에 성능 카운터를 추가하는 방법에 대한 자세한 내용은 Microsoft TechNet 웹 사이트를 참조하십시오.

언로드 정책

시스템에서는 지정된 시간에 수천 개의 워크플로가 동시에 실행되기도 합니다. 따라서 모든 워크플로를 메모리에 두는 것은 비효율적입니다. 시스템 리소스를 보다 잘 관리하려면 워크플로 상태를 보존한 후 메모리에서 언로드하도록 언로드 정책을 설정하는 것이 좋습니다.

기본 보존 서비스를 사용하는 경우 WF는 유휴 시 언로드 정책을 제공합니다. 이 정책은 SqlWorkflowPersistenceService 클래스에서 UnloadOnIdle 속성을 설정한 경우 활성화되어 유휴 상태일 때 런타임 엔진이 워크플로를 언로드하도록 합니다. 호스트 응용 프로그램이 구성 파일을 통해 SqlWorkflowPersistenceService를 사용할 수 있는 경우 UnloadOnIdle 플래그를 true로 설정하여 이 작업을 수행하면 됩니다. SqlWorkflowPersistenceService가 생성되어 코드를 통해 사용할 수 있는 경우에는 호스트 응용 프로그램이 SqlWorkflowPersistenceService(String, Boolean, TimeSpan, TimeSpan) 생성자를 사용하여 생성하면 됩니다. 호스트 응용 프로그램은 다른 복잡한 언로드 정책도 구현할 수 있습니다.

또한 WorkflowInstance.Unload() 메서드를 호출하여 이러한 특정 워크플로 인스턴스를 메모리에서 언로드한 후 상태를 보존하도록 요청할 수 있습니다. 호스트 응용 프로그램은 나중에 이 인스턴스에 대해 Load() 메서드를 호출하여 마지막 보존 지점부터 인스턴스를 계속 실행할 수 있습니다. 워크플로 인스턴스가 언로드되면 런타임 이벤트인 WorkflowUnloaded 이벤트가 발생합니다.


안정성 및 고가용성

WF에서는 다음 사항을 지원하여 호스트의 안정성 및 고가용성을 지원합니다.

SQL 클러스터링

기본 SQL 기반 서비스는 클러스터링 설치를 지원합니다. WF의 기본 SQL 기반 서비스는 일괄 처리를 SQL Server로 커밋할 때 재시도를 허용합니다. 따라서 장애 조치 시나리오 또는 일시적으로 액세스할 수 없는 SQL 서버를 지원할 수 있습니다. 재시도 논리는 다음 서비스 조합에서 설정할 수 있습니다.

  • DefaultWorkflowCommitWorkBatchService
  • SharedConnectionWorkflowCommitWorkBatchService
  • SqlTrackingService
  • SqlWorkflowPersistenceService

기본적으로 재시도 논리는 OFF로 설정되어 있습니다. 호스트 응용 프로그램이 이 기능을 사용하려면 재시도 논리를 명시적으로 ON으로 설정해야 합니다. 응용 프로그램은 재시도를 서비스 단위로 수행할지 아니면 앞에서 언급된 모든 서비스에 대해 수행할지를 선택할 수 있습니다. 이 작업은 서비스 클래스에서 EnableRetries 속성을 설정하거나 구성 파일을 통해 수행할 수 있습니다. 구성 파일을 사용하는 경우 호스트 응용 프로그램은 공유 플래그인 EnableRetries를 사용하여 영향을 받는 모든 서비스에 대한 재시도 사용 여부를 ON 또는 OFF로 설정할 수 있습니다. 서비스에 EnableRetries 속성을 설정한 경우에는 이 값이 EnableRetries 공유 플래그 값을 덮어쓰게 됩니다. WF의 기본 SQL 기반 서비스는 일정 횟수만큼 재시도를 수행하며, 이 재시도 횟수는 구성할 수 없습니다. 그러나 서비스의 연결 문자열에서 연결 시간 제한을 조정하여 재시도 간격을 부분적으로 조정할 수는 있습니다.

재시도 설정

다음 예제에서는 공용 매개 변수인 EnableRetries를 추가한 후 해당 값을 True로 설정하여 EnableRetries를 모든 기본 서비스에 대해 설정하는 방법을 보여 줍니다. 또한 SqlWorkflowPersistenceService에 대한 EnableRetries 플래그를 추가한 후 이 값을 False로 설정하여 이 서비스에 대해 EnableRetries를 사용하지 않는 방법도 보여 줍니다.

<WorkflowRuntime Name="SampleApplication" UnloadOnIdle="false">
    <CommonParameters>
        <add name="ConnectionString" value="Initial Catalog=WorkflowStore;Data Source=localhost;Integrated Security=SSPI;" />
        <add name="EnableRetries" value="True" />
    </CommonParameters>
    <Services>
        <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService,
System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" EnableRetries="False"  />
    </Services>
</WorkflowRuntime>

일반적으로 모든 서비스에 대해 재시도를 ON 또는 OFF로 설정하는 것이 좋습니다.

WorkflowCommitWorkBatchService 클래스는 TransactionScopeActivity가 아닌 다른 모든 작업 관련 일괄 처리 커밋(보존 지점)을 모두 재시도해야 합니다. WorkflowCommitBatchService 클래스는 TransactionScopeActivity 작업에 대한 작업 일괄 처리 커밋을 재시도할 수 없습니다. 이는 트랜잭션를 시작하지 못하여 소유하지 못했기 때문으로, 이런 경우에는 TransactionScopeActivity 작업에 대한 작업 일괄 처리 커밋 재시도를 워크플로로 모델링해야 합니다. 이 작업은 일반적으로 TransactionScopeActivity 외부의 예외 처리기 및 while 루프 형식으로 이루어집니다.

기본 SqlTrackingService 재시도(비트랜잭션 모드에서 실행되는 경우) 및 기본 SqlWorkflowPersistenceService는 작업 일괄 처리 커밋과 관련 없는 SQL 관련 작업을 제어합니다. 여기에는 만료된 타이머에 대한 확인 작업 및 워크플로 인스턴스 로드 작업 등이 포함됩니다.

부하 분산 및 프런트 엔드 크기 조정

WF를 호스팅하는 응용 프로그램은 부하 분산 시나리오 및 프런트 엔드 크기 조정 시나리오를 관리해야 합니다. WF에서는 서로 다른 호스트 응용 프로그램 인스턴스가 동일한 보존 또는 추적 SQL 데이터베이스를 가리킬 수 있도록 돕는 기본 SQL 기반 서비스 및 엔진을 지원합니다.

여러 개의 호스트 응용 프로그램이 동일한 보존 저장소에 연결되어 있는 경우 이 중 어느 응용 프로그램이든지 데이터베이스로부터 모든 형식의 워크플로 인스턴스를 로드할 수 있습니다. WF의 기본 SqlWorkflowPersistenceService에서는 호스트가 동일한 보존 저장소를 공유하더라도 특정 호스트에 로드될 워크플로 형식이나 인스턴스를 할당할 수 없습니다. 이 동작이 사용자의 호스트 응용 프로그램 요구에 맞지 않는 경우에는 사용자 지정 보존 서비스를 구현해야 합니다.

여러 개의 호스트 응용 프로그램이 동일한 보존 저장소를 사용하는 경우 WF 런타임 엔진이 잠금 구문을 제공하여 프런트 엔드 크기 조정 시나리오를 지원합니다. 잠금 구문을 사용하면 한 응용 프로그램에서 이미 로드한 워크플로 인스턴스를 다른 응용 프로그램이 로드할 수 없습니다. WorkflowPersistenceService 클래스를 사용하면 SaveWorkflowInstanceState() 메서드에 매개 변수를 제공하여 데이터 저장소에 있는 워크플로 인스턴스의 상태 정보 잠금 해제 여부를 지정하고, UnlockWorkflowInstanceState() 메서드를 통해 이전에 잠겨 있던 워크플로 상태 정보의 잠금을 해제하여 워크플로 런타임 엔진 기능을 지원할 수 있습니다. 잠금을 구현하는 보존 서비스에서 LoadWorkflowInstanceState() 메서드를 호출하면 워크플로 인스턴스의 상태 정보가 잠깁니다.

워크플로 인스턴스 잠금 구문에 대한 자세한 내용은 WF Programming Guide의 Windows Workflow Persistence Services 및 Creating Custom Persistence Services 섹션을 참조하십시오.


기본 런타임 서비스

WF 런타임 엔진은 런타임 서비스를 사용하여 워크플로를 실행합니다. 런타임 서비스 모델을 통해 호스트 응용 프로그램은 융통성 있게 WF 런타임 엔진에 다양한 서비스를 제공할 수 있습니다. 이 섹션에서는 WF가 제공하는 런타임 서비스 및 이러한 서비스의 기본 구현 방법에 대해 설명합니다.

런타임 서비스 구현에 대한 자세한 내용은 WF Programming Guide의 Windows Workflow Foundation Services 및 Developing Windows Workflow Foundation Services 섹션을 참조하십시오.

WF 런타임은 4개의 서비스를 제공하며, 이러한 서비스는 기본 형태로 구현할 수도 있고 호스트 응용 프로그램이 자체 서비스를 구현하여 워크플로 런타임에 제공할 수도 있습니다.

워크플로 트랜잭션 서비스

Windows 워크플로 트랜잭션 서비스(WorkflowCommitWorkBatchService)는 '보존 지점'이라고 하는 작업 일괄 처리 커밋에 관한 사용자 지정 논리를 사용하기 위한 것입니다. 작업 일괄 처리가 커밋되면 런타임이 현재 트랜잭션 서비스를 호출하고 실제 작업 일괄 처리 커밋을 담당할 대리자를 호출에 전달합니다. 커밋 수행은 여전히 런타임에서 담당하지만 서비스를 프로세스에 삽입하여 커밋 프로세스를 사용자 지정할 수 있습니다.

WF 프레임워크에서는 사용자 소유의 트랜잭션을 외부에서 워크플로 인스턴스로 가져오는 기능을 지원하지 않습니다. WorkflowCommitWorkBatchService가 지원하는 앰비언트 트랜잭션 형식은 워크플로 인스턴스가 생성한 트랜잭션뿐입니다. 워크플로 런타임을 실행하는 호스트 응용 프로그램이 생성한 앰비언트 트랜잭션은 현재 스레드에서 일시적으로 제거되므로 부작용이 줄어듭니다. 워크플로가 유휴 상태를 벗어나면 원본 앰비언트 트랜잭션을 포함하는 호스트 응용 프로그램이 다시 스레드로 돌아옵니다.

WF에서는 트랜잭션 서비스에 대한 두 가지 기본 구현 방법(DefaultWorkflowCommitWorkBatchServiceSharedConnectionWorkflowCommitWorkBatchService)을 제공합니다.

WF 런타임 엔진에는 워크플로 트랜잭션 서비스가 필요합니다. 기본적으로 DefaultWorkflowCommitWorkBatchService가 사용됩니다. 호스트 응용 프로그램은 DefaultWorkflowSchedulerServiceSharedConnectionDefaultWorkflowCommitWorkBatchService 또는 사용자 지정 서비스로 바꿀 수 있습니다.

DefaultWorkflowCommitWorkBatchService

워크플로 런타임 엔진이 시작되면 DefaultWorkflowCommitWorkBatchService 인스턴스가 생성되어 WorkflowRuntime에 추가됩니다(다른 트랜잭션 서비스가 추가되지 않은 경우). 이 서비스는 각 데이터베이스 연결에 대해 .NET Framework 트랜잭션을 생성합니다. 예를 들어 SQL 추적 서비스와 SQL 보존 서비스 간의 연결은 공유되지 않습니다. 사용자의 워크플로에서 이 서비스를 사용하면 데이터 무결성에 필요한 트랜잭션을 지원할 수 있습니다.

SharedConnectionWorkflowCommitWorkBatchService

이 서비스는 여러 개체 간에 공유되는 연결을 사용하는 데이터베이스 트랜잭션에 사용됩니다. 호스트 응용 프로그램이 이 서비스를 사용하려면 AddService 메서드를 사용하거나 구성 파일을 통해 WorkflowRuntime에 서비스를 추가해야 합니다.

워크플로 스케줄러 서비스

워크플로 인스턴스가 비동기 모드에서 처리되는지 아니면 수동 동기 모드에서 처리되는지에 관계 없이 워크플로 스케줄러 서비스는 워크플로 런타임 엔진에서 워크플로 인스턴스를 예약하는 방법을 관리합니다. WF에서는 WorkflowSchedulerService에 대해 두 가지 기본 구현 방법(DefaultWorkflowSchedulerServiceManualWorkflowSchedulerService)을 제공합니다.

워크플로를 실행하기 위해 WF 런타임 엔진에는 워크플로 스케줄러 서비스가 필요합니다. 기본적으로 DefaultWorkflowSchedulerService가 사용됩니다. 호스트 응용 프로그램은 DefaultWorkflowSchedulerServiceManualWorkflowSchedulerService 또는 사용자 지정 서비스로 바꿀 수 있습니다.

DefaultWorkflowSchedulerService

DefaultWorkflowSchedulerService는 워크플로 런타임 엔진에서 워크플로 인스턴스를 비동기 방식으로 실행하는 스레드를 생성 및 관리하며 기본적으로 런타임 스레드 풀에 여러 개의 워크플로 인스턴스가 대기할 수 있도록 지원합니다. WorkflowRuntime에 다른 워크플로 스케줄러 서비스 인스턴스가 추가되지 않은 경우 기본적으로 DefaultWorkflowSchedulerService가 사용됩니다.

ManualWorkflowSchedulerService

ManualWorkflowSchedulerService는 워크플로 인스턴스의 동기식 실행에 사용됩니다. 이 서비스를 사용하는 경우 워크플로 인스턴스는 호스트 응용 프로그램의 호출 스레드에서 실행되므로 워크플로 인스턴스가 유휴 상태로 될 때까지 호스트 응용 프로그램의 실행이 차단됩니다.

워크플로 보존 서비스

보존 서비스는 워크플로 인스턴스 상태의 저장 및 검색(로드 및 언로드)을 담당합니다. WF에서는 보존 서비스에 대한 기본 SQL 기반 구현 방법(SqlWorkflowPersistenceService)을 제공합니다.

SqlWorkflowPersistenceService

이러한 기본 구현은 상태 및 타이머 정보를 SQL Server/MSDE에 저장합니다. SqlWorkflowPersistenceService는 워크플로 트랜잭션에 참여하여 액세스 잠금을 구현합니다. 또한 SqlWorkflowPersistenceService는 SQL 서버를 사용할 수 없는 경우 재시도할 수 있는 기능을 제공합니다. 이 기능은 서비스에서 EnableRetries 속성을 설정하여 제어할 수 있습니다. SqlWorkflowPersistenceService에 대한 자세한 내용은 WF Programming Guide를 참조하십시오.

워크플로 추적 서비스

추적 서비스는 추적 프로필과 추적 정보 저장소를 관리합니다. WF에서는 SqlTrackingService 클래스에서 구현되는 추적 서비스에 대한 기본 SQL 기반 구현 방법을 제공합니다.

SqlTrackingService

이 구현에서는 추적 프로필 및 데이터를 SQL Server/MSDE에 저장합니다. 이 서비스는 다음을 지원합니다.

  • 기본적으로 워크플로 트랜잭션에 참여합니다. 이 동작은 SqlTrackingService.IsTransactional 속성이 제어합니다.
  • 모든 형식에 기본 추적 프로필을 사용하거나 워크플로 형식 또는 워크플로 인스턴스에 따라 추적 프로필을 다르게 연결하는 메커니즘을 제공합니다.
  • 라이브 및 주문형 분할 기능을 제공하여 데이터 유지 관리를 지원합니다.

    데이터 유지 관리에 대한 자세한 내용은 WF Programming Guide의 Data Maintenance with SqlTrackingService 섹션을 참조하십시오.

또한 WF에서는 SqlTrackingService에 저장된 추적 데이터를 쿼리하는 데 사용할 수 있는 SqlTrackingQuery API를 제공합니다. SqlTrackingService에 대한 자세한 내용은 WF Programming Guide를 참조하십시오.


결론

WF에서는 워크플로 실행 및 워크플로 상태 관리를 위한 런타임 엔진과 서비스를 제공합니다. 호스트 응용 프로그램 또는 프로세스는 WF 워크플로를 호스팅할 수 있도록 작성되어야 하며, 이 호스트 응용 프로그램은 워크플로 런타임 엔진에 다양한 런타임 서비스를 제공해야 합니다. WF의 기본 런타임 서비스는 일반적인 시나리오를 따르도록 되어 있지만, 기본 서비스가 호스트 응용 프로그램의 요구에 맞지 않는 경우 호스트 응용 프로그램은 사용자 지정 서비스를 구현하여 워크플로 런타임에 제공해야 합니다.

또한 WF에서는 실행 중인 워크플로 인스턴스를 관리 및 모니터링하고 호스트 응용 프로그램의 안정성 및 고가용성을 지원하기 위한 인프라를 제공합니다. 호스트 응용 프로그램은 호스트 관련 시나리오를 기반으로 하는 다양한 옵션의 사용 방법을 지정해야 합니다.


추가 정보

워크플로 기술을 처음 접하는 개발자들의 경우 다음 리소스와 함께 이 기사를 참조하면 이 기술을 배운 후 활용하여 생산성을 빠르게 향상시킬 수 있을 것입니다.

출처 : 한국 마이크로소프트 MSDN (2006년 8월)

Posted by 상현넘™

댓글을 달아 주세요

요약:이 기사에서는 ASP.NET 웹 서비스와 Windows Communication Foundation을 비교하며, Windows Communication Foundation의 발표를 앞둔 시점에서 이미 사용 중이거나 계획 단계에 있는 ASP.NET 웹 서비스를 어떻게 처리해야 하는지에 대해 설명합니다.

ASP.NET은 웹 서비스를 빌드하기 위한 .NET Framework 클래스 라이브러리와 도구뿐만 아니라 인터넷 정보 서비스(IIS) 내에서 이러한 웹 서비스를 호스트하기 위한 기능도 제공합니다. Indigo라는 코드명의 Windows Communication Foundation은 소프트웨어 엔터티가 웹 서비스에서 사용되는 프로토콜을 포함한 모든 프로토콜을 사용하여 통신할 수 있도록 하는 .NET 클래스 라이브러리, 도구 및 호스팅 기능을 제공합니다. 이처럼 웹 서비스 빌드를 위한 더욱 새롭고 포괄적인 기술이 등장하고 있는 시점에서 ASP.NET 웹 서비스에 대한 전망을 알아보도록 하겠습니다.

이 문서에서는 두 가지 기술을 비교하고, ASP.NET 웹 서비스 응용 프로그램을 Windows Communication Foundation 응용 프로그램과 함께 사용할 수 있는 방법에 대해 설명합니다. 또한 ASP.NET 웹 서비스를 Windows Communication Foundation으로 마이그레이션하기 위한 준비 방법과 실제로 마이그레이션을 수행하는 방법을 설명합니다.

이 문서에서는 ASP.NET에서 웹 서비스를 빌드하기 위해 제공하는 기능과 Microsoft .NET용 Web Services Enhancements(WSE)를 별개로 취급합니다. WSE를 사용하여 개발된 응용 프로그램에 대한 전망은 다른 곳에서 살펴볼 것입니다.

의사 결정권자를 위한 조언

Windows Communication Foundation은 2006년 하반기에 발표될 예정입니다. Windows Communication Foundation에는 이전 기술인 ASP.NET 웹 서비스와 비교하여 중요한 몇 가지 이점이 있습니다. ASP .NET 웹 서비스를 사용하는 조직에서는 이러한 이점을 고려해야 합니다.

ASP.NET 웹 서비스 도구는 단지 웹 서비스를 빌드하기 위한 것이지만, Windows Communication Foundation은 소프트웨어 엔터티들이 서로 통신하도록 설정되어야 하는 모든 환경에서 사용할 수 있는 도구를 제공합니다. 이는 개발자가 다양한 소프트웨어 통신 시나리오를 적용하기 위해 알아야 하는 기술의 가짓수를 줄여 주며, 따라서 소프트웨어 개발 리소스 비용과 소프트웨어 개발 프로젝트를 완성하는 데 소요되는 시간도 줄여 줍니다.

웹 서비스 개발 프로젝트의 경우에도 Windows Communication Foundation은 ASP.NET 웹 서비스에서 지원하는 것보다 훨씬 많은 웹 서비스 프로토콜을 지원합니다. 이러한 프로토콜은 신뢰할 수 있는 세션과 트랜잭션을 비롯한 여러 가지 장점을 수반하는, 더욱 정교한 솔루션을 제공합니다.

Windows Communication Foundation은 ASP.NET 웹 서비스에서 지원하는 것보다 훨씬 많은 메시지 전송 프로토콜을 지원합니다. ASP.NET 웹 서비스는 HTTP(Hypertext Transfer Protocol)를 통한 메시지 전송만 지원합니다. 그러나 Windows Communication Foundation은 HTTP뿐만 아니라 TCP(Transmission Control Protocol), 명명된 파이프 및 MSMQ(Microsoft Message Queuing)를 통한 메시지 전송도 지원합니다. 더욱 중요한 사실은 Windows Communication Foundation은 추가되는 전송 프로토콜을 지원하도록 즉시 확장 가능하다는 점입니다. 따라서 Windows Communication Foundation을 사용하여 개발된 소프트웨어는 다양한 다른 소프트웨어와 함께 작동하도록 손쉽게 확장할 수 있어 잠재적 투자 수익을 높여 줍니다.

Windows Communication Foundation은 ASP.NET 웹 서비스에서 제공하는 것보다 훨씬 더 풍부한 응용 프로그램 배포 및 관리 기능을 제공합니다. ASP.NET에도 있는 구성 시스템 외에도 Windows Communication Foundation은 구성 편집기, 원하는 수의 중간 단계를 통한 송수신자 간 활동 추적, 추적 뷰어, 메시지 로깅, 많은 수의 성능 카운터 및 WMI(Windows Management Instrumentation) 지원을 제공합니다. 이처럼 풍부한 관리 도구를 사용하면 운영 비용, 실패 위험 및 실제 가동 중단 시간을 줄일 수 있습니다.

ASP.NET 웹 서비스에 대한 Windows Communication Foundation의 이러한 잠재적 이점을 고려할 때, ASP.NET 웹 서비스를 사용하고 있거나 사용할 예정인 조직에서는 다음과 같은 선택을 할 수 있습니다.

  • ASP.NET 웹 서비스를 계속 사용하고 Windows Communication Foundation에서 제공하는 이점은 무시합니다. Microsoft의 현재 지원 주기 정책 하에서 ASP.NET 웹 서비스에 대한 기본 지원은 2011년까지, 확장 지원은 2016년까지 제공됩니다. 따라서 ASP.NET 웹 서비스를 계속 사용하더라도 큰 위험은 없습니다.
  • 미래의 어느 시점에 Windows Communication Foundation을 채택할 의향을 갖고 ASP.NET 웹 서비스를 계속 사용합니다. 이 문서에서는 새로운 ASP.NET 웹 서비스 응용 프로그램을 미래의 Windows Communication Foundation 응용 프로그램과 함께 사용할 수 있는 가능성을 최대화하는 방법에 대해 설명합니다. 또한 Windows Communication Foundation으로 더 쉽게 마이그레이션할 수 있도록 새 ASP.NET 웹 서비스를 빌드하는 방법도 설명합니다. 그러나 서비스 보안이 중요하거나 신뢰성 또는 트랜잭션 보증이 필요하거나 사용자 지정 관리 기능을 구성해야 한다면 Windows Communication Foundation 채택을 연기하는 것은 잘못된 결정입니다. Windows Communication Foundation은 정확히 이러한 시나리오를 위해 설계되었으며, 기술에 대한 생산 라이선스는 이미 제공되고 있습니다.
  • 기존 ASP.NET 웹 서비스 응용 프로그램을 계속 유지하면서 새로운 개발에는 Windows Communication Foundation을 채택합니다. 대부분의 경우 이것이 최적의 선택입니다. Windows Communication Foundation의 이점을 누리면서 이를 사용하기 위해 기존 응용 프로그램을 수정해야 하는 비용은 절약할 수 있기 때문입니다. 이 시나리오에서는 새 Windows Communication Foundation 응용 프로그램과 기존 ASP.NET 응용 프로그램이 공존할 수 있습니다. 또한 새 Windows Communication Foundation 응용 프로그램에서 기존 ASP.NET 웹 서비스를 사용할 수 있으며, Windows Communication Foundation의 ASP.NET 호환 모드를 통해 Windows Communication Foundation을 사용하여 기존 ASP.NET 응용 프로그램에 새 작업 기능을 프로그래밍할 수도 있습니다.
  • Windows Communication Foundation을 채택하고 기존 ASP.NET 웹 서비스 응용 프로그램을 Windows Communication Foundation으로 마이그레이션합니다. 조직에서는 개발자들이 ASP.NET 웹 서비스에 대해 알아야 할 필요가 없도록 하기 위해 이 옵션을 선택할 수 있습니다. 그러나 최신 기술 외의 다른 기술에 대한 지원을 배제한다는 것은 비현실적이며, 따라서 이 옵션을 선택하는 이유로는 적절하지 않습니다. Windows Communication Foundation에서 제공되는 기능으로 기존 응용 프로그램이 향상되거나, 기존 ASP.NET 웹 서비스의 기능을 더욱 강력한 새 Windows Communication Foundation 응용 프로그램으로 다시 만드는 경우에만 마이그레이션을 하는 것이 좋습니다. 이 문서에서는 마이그레이션 방법에 대해 설명합니다.
기술 비교: 목적

ASP.NET 웹 서비스 기술은 SOAP(Simple Object Access Protocol) over HTTP를 통해 메시지를 송수신하는 응용 프로그램을 빌드하기 위해 개발되었습니다. 메시지의 구조는 XML 스키마를 사용하여 정의할 수 있으며 .NET 개체와 주고 받는 메시지를 용이하게 serialize할 수 있는 도구가 제공됩니다. 이 기술을 사용하면 웹 서비스를 WSDL(Web Services Description Language)로 설명하는 메타데이터를 자동으로 생성할 수 있으며, WSDL에서 웹 서비스용 클라이언트를 생성하는 보조 도구가 제공됩니다.

Windows Communication Foundation은 .NET 응용 프로그램에서 다른 소프트웨어 엔터티와 메시지를 교환할 수 있도록 하기 위한 것입니다. 기본적으로 SOAP가 사용되지만 메시지는 모든 형식을 가질 수 있으며 모든 전송 프로토콜을 통해 전달될 수 있습니다. 메시지의 구조는 XML 스키마를 사용하여 정의할 수 있으며 .NET 개체와 주고 받는 메시지를 serialize할 수 있는 다양한 옵션이 제공됩니다. Windows Communication Foundation은 이 기술을 사용하여 빌드되는 웹 서비스를 WSDL로 설명하는 메타데이터를 자동으로 생성할 수 있으며, WSDL에서 그러한 응용 프로그램을 위한 클라이언트를 생성하는 도구도 제공합니다.

기술 비교: 표준

ASP.NET 웹 서비스에서 지원되는 표준 목록은 XML Web Services Created Using ASP.NET (영문)을 참조하십시오.

Windows Communication Foundation에서 지원되는 표준에 대한 자세한 목록은 Web Services Protocols Supported in WCF (영문)를 참조하십시오.

기술 비교: 개발

ASP.NET 호환 모드

Windows Communication Foundation에는 특정 Windows Communication Foundation 응용 프로그램을 ASP.NET 웹 서비스처럼 프로그래밍하고 구성하며 동작을 모방할 수 있는 ASP.NET 호환 모드 옵션이 있습니다. 아래에서는 ASP.NET 호환 모드를 채택하는 방법에 대해 설명하며, 이러한 옵션이 정확히 어떤 효과를 내는지에 대한 자세한 정보를 제공합니다.

데이터 표현

ASP.NET으로 웹 서비스를 개발하는 경우 일반적으로 서비스에서 사용할 복합 데이터 형식을 정의하는 것부터 시작합니다. ASP.NET에서는 System.Xml.Serialization.XmlSerializer를 사용하여 .NET 개체로 표현된 데이터를 서비스와 주고 받을 XML로 변환하고 XML로 수신된 데이터를 .NET 개체로 변환합니다. 따라서 ASP.NET 서비스에서 사용할 복합 데이터 형식을 정의할 경우 System.Xml.Serialization.XmlSerializer를 통해 XML로 또는 XML에서 serialize할 수 있는 .NET 클래스에 대한 정의가 필요합니다. 물론 이러한 클래스를 수동으로 작성하거나 명령줄 XML 스키마/데이터 형식 지원 유틸리티인 xsd.exe를 사용하여 XML 스키마의 형식 정의에서 생성할 수 있습니다.

System.Xml.Serialization.XmlSerializer를 통해 XML로, 또는 XML에서 serialize할 수 있는 .NET 클래스의 정의에 대해 알아야 하는 주요 사항은 다음과 같습니다.

  • .NET 개체의 공용 필드와 속성만 XML로 변환됩니다.
  • 클래스에서 IEnumerable 또는 ICollection 인터페이스를 구현하는 경우에만 컬렉션 클래스의 인스턴스를 serialize할 수 있습니다.
  • 그 결과 System.Collections.Hashtable과 같은 System.Collections.IDictionary 인터페이스를 구현하는 클래스는 XML로 serialize할 수 없습니다.
  • 클래스의 인스턴스가 XML로 표현되는 방식을 정확히 제어하기 위해 System.Xml.Serialization 네임스페이스의 많은 특성 형식을 .NET 클래스 및 그 구성원에 추가할 수 있습니다.

Windows Communication Foundation 응용 프로그램 개발 역시 대개 복합 형식을 정의하는 것부터 시작합니다. Windows Communication Foundation은 ASP.NET 웹 서비스와 같은 .NET 형식을 사용하도록 만들 수 있지만 더 나은 다른 방법이 제공됩니다.

Windows Communication Foundation System.Runtime.Serialization 어셈블리의 System.Runtime.Serialization.DataContractSystem.Runtime.Serialization.DataMember 특성을 .NET 형식에 추가하여 해당 형식의 인스턴스가 XML로 serialize된다는 점을 표시하고 이 형식에서 serialize되는 특정 필드 또는 속성을 표시할 수 있습니다. 다음의 세 가지 예제는 모두 유효합니다.

//예제 1:
[DataContract]
public class LineItem
{
    [DataMember]
    public string ItemNumber;
    [DataMember]
    public decimal Quantity;
    [DataMember]
    public decimal UnitPrice;
}

//예제 2:
public class LineItem
{
    [DataMember]
    private string itemNumber;
    [DataMember]
    private decimal quantity;
    [DataMember]
    private decimal unitPrice;

    public string ItemNumber
    {
        get
        {
            return this.itemNumber;
        }

        set
        {
            this.itemNumber = value;
        }
    }

    public decimal Quantity
    {
        get
        {
            return this.quantity;
        }

        set
        {
            this.quantity = value;
        }
    }

    public decimal UnitPrice
    {
        get
        {
            return this.unitPrice;
        }

        set
        {
            this.unitPrice = value;
        }
    }
}

//예제 3:
public class LineItem
{
    private string itemNumber;
    private decimal quantity;
    private decimal unitPrice;

    [DataMember]
    public string ItemNumber
    {
        get
        {
            return this.itemNumber;
        }

        set
        {
            this.itemNumber = value;
        }
    }

    [DataMember]
    public decimal Quantity
    {
        get
        {
            return this.quantity;
        }

        set
        {
            this.quantity = value;
        }
    }

    [DataMember]
    public decimal UnitPrice
    {
        get
        {
            return this.unitPrice;
        }

        set
        {
            this.unitPrice = value;
        }
    }
}

System.Runtime.Serialization.DataContract 특성은 형식의 필드 또는 속성을 0개 이상 serialize한다고 표시하지만 System.Runtime.Serialization.DataMember 특성은 특정 필드 또는 속성을 serialize한다고 표시합니다. System.Runtime.Serialization.DataContract 특성은 클래스 또는 구조체에 적용될 수 있습니다. System.Runtime.Serialization.DataMember 특성은 필드 또는 속성에 적용될 수 있으며, 이 특성이 적용되는 필드 및 속성은 공개 필드 및 속성이거나 개인 필드 및 속성일 수 있습니다. System.Runtime.Serialization.DataContract 특성을 가진 형식의 인스턴스는 Windows Communication Foundation 용어로 데이터 계약이라고 합니다. 이러한 인스턴스는 Windows Communication Foundation의 System.Runtime.Serialization.DataContractFormatter를 사용하여 XML로 serialize됩니다.

System.Runtime.Serialization.DataContractFormatter, System.Runtime.Serialization.DataContract, System.Runtime.Serialization.DataMember 특성은 System.Xml.Serialization.XmlSerializer와, 그리고 System.Xml.Serialization 네임스페이스의 다양한 특성과 어떻게 다를까요? 중요한 차이점이 많이 있습니다.

  1. System.Xml.Serialization.XmlSerializer와 System.Xml.Serialization 네임스페이스의 특성은 XML 스키마에 정의된 유효 형식에 .NET 형식을 매핑할 수 있도록 디자인되었으므로 .NET 형식의 XML 표현 방식을 매우 정확하게 제어할 수 있습니다. System.Runtime.Serialization.DataContractFormatter, System.Runtime.Serialization.DataContract, System.Runtime.Serialization.DataMember 특성은 .NET 형식의 XML 표현 방식에 대한 제어를 극히 제한적으로 제공합니다. 형식 및 해당 필드 또는 속성을 XML로 표현하는 데 사용되는 네임스페이스와 이름, 그리고 필드와 속성이 XML로 나타나는 순서만 지정할 수 있습니다.

    [DataContract(
    Namespace="urn:Woodgrove:2006:January:29",
    Name="LineItem")]
    public class LineItem
    {
        [DataMember(Name="ItemNumber",IsRequired=true,Order=0)]
        public string itemNumber;
        [DataMember(Name="Quantity",IsRequired=false,Order = 1)]
        public decimal quantity;
        [DataMember(Name="Price",IsRequired=false,Order = 2)]
        public decimal unitPrice;
    }

    .NET 형식을 표현하는 데 사용되는 XML 구조에 관한 다른 모든 요소는 System.Runtime.Serialization.DataContractFormatter로 결정됩니다.

  2. .NET 형식을 XML로 표현하는 방식에 대해 많은 제어를 허용하지 않기 때문에 System.Runtime.Serialization.DataContractFormatter에 대해 serialize 프로세스의 예측이 매우 쉽고, 따라서 최적화가 더 용이합니다. 결과적으로 System.Runtime.Serialization.DataContractFormatter 디자인의 실질적 이점은 약 10% 정도의 성능 향상에 있습니다.
  3. 다음의 두 형식을 비교해 보십시오. 아래의 첫 번째 형식에는 System.Xml.Serialization.XmlSerializer에 의한 serialize 특성이 있습니다.

    [Serializable]
    [XmlRoot(Namespace="urn:Woodgrove:2006:January:29")]
    public class LineItem
    {
        public string ItemNumber;
        public decimal Quantity;
        public decimal UnitPrice;
    }

    아래의 두 번째 형식에는 System.Runtime.Serialization.DataContractFormatter에서 사용하는 특성이 있습니다.

    [DataContract(Namespace="urn:Woodgrove:2006:January:29")]
    public class LineItem
    {
        [DataMember]
        public string ItemNumber;
        [DataMember]
        public decimal Quantity;
        [DataMember]
        public decimal UnitPrice;
    }

    System.Xml.Serialization.XmlSerializer에서 사용하는 특성은 XML로 serialize될 형식의 필드 또는 속성을 표시하지 않지만, System.Runtime.Serialization.DataContractFormatter에서 사용하는 System.Runtime.Serialization.DataMember 특성은 serialize될 필드 또는 속성을 명시적으로 보여 줍니다. 따라서 데이터 계약은 응용 프로그램에서 송수신할 데이터의 구조에 관한 명시적 계약이라고도 할 수 있습니다.
  4. System.Xml.Serialization.XmlSerializer는 .NET 개체의 공개 구성원만 XML로 변환할 수 있지만 System.Runtime.Serialization.DataContractFormatter는 .NET 개체의 구성원에 대한 액세스 한정자와 관계없이 이러한 구성원을 XML로 변환할 수 있습니다.
  5. 형식의 비공개 구성원을 XML로 serialize할 수 있기 때문에 System.Runtime.Serialization.DataContractFormatter에는 XML로 serialize할 수 있는 .NET 형식의 다양성에 대한 제한이 더 적습니다. 특히 Systems.Collections.IDictionary 인터페이스를 구현하는 System.Collections.Hashtable과 같은 XML 형식으로 변환할 수 있습니다. 일반적으로 System.Runtime.Serialization.DataContractFormatter는 형식의 정의를 수정하거나 형식을 위한 래퍼를 개발하지 않고도 기존 .NET 형식의 인스턴스를 serialize할 수 있는 가능성이 훨씬 더 높습니다.
  6. 그러나 System.Runtime.Serialization.DataContractFormatter가 형식의 비공개 구성원에 액세스할 수 있다는 점에 기인한 또 다른 결과는 System.Xml.Serialization.XmlSerializer와 달리 완전 신뢰가 필요하다는 점입니다.
  7. System.Runtime.Serialization.DataContractFormatter는 버전 관리를 위한 일부 지원을 통합하고 있습니다.
    • System.Runtime.Serialization.DataMember 특성에는 IsRequired 속성이 있습니다. 이 속성에는 새 버전의 데이터 계약에 추가된, 이전 버전에는 없었던 구성원에 대해 false 값을 지정할 수 있기 때문에 새 버전의 데이터 계약이 있는 응용 프로그램에서 이전 버전을 처리할 수 있습니다.
    • 데이터 계약에서 간단한 System.Runtime.Serialization.IExtensibleDataObject 인터페이스를 구현하도록 함으로써 System.Runtime.Serialization.DataContractFormatter가 새 버전의 데이터 계약에 정의된 구성원을 이전 버전의 데이터 계약을 사용하는 응용 프로그램을 통해 전달하도록 허용할 수 있습니다.

이러한 모든 차이점에도 불구하고 System.Xml.Serialization.XmlSerializer에서 기본적으로 형식을 serialize하는 XML은 해당 XML에 대한 네임스페이스가 명시적으로 정의되어 있는 경우 System.RuntimeSerialization.DataContractFormatter에서 형식을 serialize하는 XML과 의미상 동일합니다. 따라서 다음과 같이 두 serializer에서 사용하는 특성을 가지고 있는 클래스는 System.Xml.Serialization.XmlSerializer와 System.RuntimeSerialization.DataContractFormatter에서 의미상 동일한 XML로 변환됩니다.

[Serializable]
[XmlRoot(Namespace="urn:Woodgrove:2006:January:29")]
[DataContract(Namespace="urn:Woodgrove:2006:January:29")]
public class LineItem
{
    [DataMember]
    public string ItemNumber;
    [DataMember]
    public decimal Quantity;
    [DataMember]
    public decimal UnitPrice;
}

Windows Communication Foundation을 포함하고 있는 소프트웨어 개발 키트에는 서비스 모델 메타데이터 도구라고 하는 svcutil.exe 명령줄 도구가 있습니다. svcutil.exe는 ASP.NET 웹 서비스에서 사용되는 xsd.exe 도구와 마찬가지로 XML 스키마에서 .NET 데이터 형식의 정의를 생성할 수 있습니다. System.Runtime.Serialization.DataContractFormatter에서 XML 스키마에 의해 정의된 형식의 XML을 내보낼 수 있으면 이러한 .NET 데이터 형식은 데이터 계약이 됩니다. 그렇지 않으면 이러한 형식은 System.Xml.Serialization.XmlSerializer를 통해 seriallize됩니다. 또한 svcutil.exe 도구와 /dataContractOnly 스위치를 사용하여 데이터 계약에서 XML 스키마를 생성할 수도 있습니다.

ASP.NET 웹 서비스에서 System.Xml.Serialization.XmlSerializer를 사용하고, Windows Communication Foundation의 ASP.NET 호환 모드를 통해 Windows Communication Foundation 서비스에서 ASP.NET 웹 서비스의 동작을 모방하는 경우에도 ASP.NET 호환 옵션에서 System.Xml.Serialization.XmlSerializer를 사용하도록 제한되지는 않습니다. ASP.NET 호환 모드에서 실행하는 서비스에서 System.Runtime.Serialization.DataContractFormatter도 계속 사용할 수 있습니다.

서비스 개발

다음 예에서 볼 수 있는 것처럼, ASP.NET을 사용하여 서비스를 개발하는 통상적인 방법은 단순히 클래스에 System.Web.Services.WebService 특성을, 서비스의 작업이 되는 해당 클래스의 메서드에 System.Web.Services.WebMethod 특성을 추가하는 것입니다.

[WebService]
public class Service : System.Web.Services.WebService
{
    [WebMethod]
    public string Echo(string input)
    {
        return input;
    }
}

ASP.NET 2.0에서는 다음과 같이 클래스가 아니라 인터페이스에 System.Web.Services.WebServiceSystem.Web.Services.WebMethod 특성을 추가하고 해당 인터페이스를 구현하는 클래스를 작성하는 옵션이 새로 제공됩니다.

[WebService]
public interface IEcho
{
    [WebMethod]
    string Echo(string input);
}

public class Service : IEcho
{

    public string Echo(string input)
    {
        return input;
    }
}

System.Web.Services.WebService 특성을 가진 인터페이스는 서비스에 의해 수행되는 작업에 대한 계약을 설정하며, 같은 계약을 다른 방식으로 구현하는 다양한 클래스에서 동일한 서비스를 다시 사용할 수 있으므로 이 옵션을 사용하는 것이 좋습니다.

Windows Communication Foundation 서비스는 Windows Communication Foundation 끝점을 하나 이상 정의함으로써 제공됩니다. 끝점은 주소, 바인딩 및 서비스 계약으로 정의됩니다. 주소는 서비스가 있는 위치를 정의합니다. 바인딩은 서비스와 통신하는 방법을 지정합니다. 서비스 계약은 서비스에서 수행할 수 있는 작업을 정의합니다.

일반적으로 서비스 계약은 다음과 같이 .NET 인터페이스에 System.ServiceModel.ServiceContractSystem.ServiceModel.OperationContract 특성을 추가함으로써 처음으로 정의됩니다.

[ServiceContract]
public interface IEcho
{
    [OperationContract]
    string Echo(string input);
}

System.ServiceModel.ServiceContract 특성은 인터페이스에서 Windows Communication Foundation 서비스 계약을 정의하도록 지정하고, System.ServiceModel.OperationContract 특성은 서비스 계약의 작업을 정의하는 인터페이스의 메서드가 있는 경우 해당 메서드를 나타냅니다.

서비스 계약은 일단 정의되면 다음과 같이 클래스가 서비스 계약을 정의하는 인터페이스를 구현하도록 함으로써 클래스에서 구현됩니다.

public class Service : IEcho
{
   
    public string Echo(string input)
    {
        return input;
    }
}

서비스 계약을 구현하는 클래스는 Windows Communication Foundation 용어로 서비스 형식이라고 합니다.

다음 단계는 주소와 바인딩을 서비스 형식과 연결하는 것입니다. 이 작업은 일반적으로 파일을 직접 편집하거나 Windows Communication Foundation에서 제공되는 구성 편집기를 사용하여 구성 파일에서 수행됩니다. 다음은 구성 파일 예제입니다.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <system.serviceModel>
      <services>
         <service name="Service ">
            <endpoint
               address="EchoService"
               binding="basicHttpBinding"
               contract="IEchoService "/>
         </service>
      </services>
   </system.serviceModel>
</configuration>

바인딩은 응용 프로그램과 통신하기 위한 프로토콜 집합을 지정합니다. 다음과 같이 일반 옵션을 나타내는 미리 정의된 여러 가지 바인딩이 있습니다.

이름 목적
BasicHttpBinding WS-Basic Profile 1.1과 Basic Security Profile 1.0을 지원하는 웹 서비스 및 클라이언트와의 상호 운용성
WSHttpBinding HTTP를 통한 WS-* 프로토콜을 지원하는 웹 서비스 및 클라이언트와의 상호 운용성
WSDualHttpBinding 이중 HTTP 통신(초기 메시지의 수신자가 초기 송신자에게 직접 응답하지 않고, 일정 기간에 걸쳐 WS-* 프로토콜을 따르는 HTTP를 통해 원하는 수의 응답을 전송할 수 있음)
WSFederationBinding HTTP 통신(명시적으로 식별된 자격 증명 제공자가 발행한 자격 증명에 따라 서비스의 리소스에 대한 액세스를 제어할 수 있음)
NetTcpBinding Windows Communication Foundation 소프트웨어 엔터티 간의 네트워크를 통한 안전하고 신뢰성 있는 고성능 통신
NetNamedPipeBinding 같은 컴퓨터에 있는 Windows Communication Foundation 소프트웨어 엔터티 간의 안전하고 신뢰할 수 있는 고성능 통신
NetMsmqBinding Windows Communication Foundation 소프트웨어 엔터티 간의 MSMQ를 통한 통신
MsmqIntegrationBinding Windows Communication Foundation 소프트웨어 엔터티와 다른 소프트웨어 엔터티 간의 MSMQ를 통한 통신
NetPeerTcpBinding Windows Communication Foundation 소프트웨어 엔터티 간의 피어-투-피어 네트워킹을 통한 통신

미리 정의된 바인딩인 System.ServiceModel.BasicHttpBinding은 ASP.NET 웹 서비스에서 지원되는 프로토콜 집합을 통합하고 있습니다.

Windows Communication Foundation 응용 프로그램용 사용자 지정 바인딩은 Windows Communication Foundation에서 개별 프로토콜을 구현하는 데 사용하는 바인딩 요소 클래스들의 컬렉션으로 간단히 정의할 수 있습니다. 새 바인딩 요소를 작성하여 추가 프로토콜을 나타낼 수 있습니다.

서비스 형식의 내부 동작은 behaviors라는 클래스 모음의 속성을 사용하여 조정할 수 있습니다. 다음 예제에서는 System.ServiceModel.ServiceBehavior 클래스를 사용하여 서비스 형식이 멀티스레드 형식이 되도록 지정합니다.

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple]
public class DerivativesCalculatorServiceType: IDerivativesCalculator

System.ServiceModel.ServiceBehavior와 같이 프로그래머가 설정할 수 있는 속성이 있는 일부 동작은 특성입니다. 관리자가 설정할 수 있는 속성이 있는 다른 동작은 응용 프로그램의 구성에서 수정할 수 있습니다.

서비스 형식 프로그래밍에서는 System.ServiceModel.OperationContext 클래스가 자주 사용됩니다. 이 클래스의 정적 Current 속성은 작업이 실행되고 있는 컨텍스트에 대한 정보로의 액세스를 제공합니다. 따라서 System.ServiceModel.OperationContext는 System.Web.HttpContext 및 System.EnterpriseServices.ContextUtil 클래스 모두와 비슷합니다.

호스팅

ASP.NET 웹 서비스는 클래스 라이브러리 어셈블리로 컴파일됩니다. 확장명이 .asmx인 서비스 파일이라는 파일이 제공됩니다. 이 파일에는 서비스에 대한 코드를 포함하고 있는 클래스 및 이 클래스가 있는 어셈블리를 식별하는 @ WebService 지시문이 있습니다.

<%@ WebService Language="C#" Class="Service,ServiceAssembly" %>

서비스 파일은 IIS의 ASP.NET 응용 프로그램 루트에 복사되며, 어셈블리는 이 응용 프로그램 루트의 \bin 하위 디렉터리에 복사됩니다. 그런 다음 응용 프로그램 루트에 있는 서비스 파일의 URL(Uniform Resource Locator)을 통해 이 응용 프로그램에 액세스할 수 있습니다.

Aaron Skonnard는 자신의 칼럼 (영문)에서 .NET Framework 2.0에서 제공되는 HttpListener 클래스를 사용하여 어떤 .NET 응용 프로그램에서든 IIS 외부에서 ASP.NET 웹 서비스를 호스트하는 방법에 대해 설명했습니다. 그러나 Skonnard는 이를 위한 작업은 '간단하지 않다'고 설명하고 있습니다. (영문)

Windows Communication Foundation 서비스는 IIS 5.1이나 6.0, IIS 7의 일부로 제공될 WAS(Windows Activation Service), 그리고 모든 .NET 응용 프로그램 내에서 즉시 호스트가 가능합니다. IIS 5.1 또는 6.0에서 서비스를 호스트하려면 이 서비스가 HTTP를 통신 전송 프로토콜로 사용해야 합니다.

IIS 5.1, IIS 6.0 또는 WAS에서 서비스를 호스트하려면 다음 단계를 따릅니다.

  1. 서비스 형식을 클래스 라이브러리 어셈블리로 컴파일합니다.
  2. 다음과 같은 서비스 형식을 식별하는 @ ServiceHost 지시문을 포함하는 서비스 파일을 확장명 .svc로 만듭니다.

    <%@ServiceHost language="c#" Service="MyService" %>

  3. 서비스 파일을 가상 디렉터리에 복사하고 어셈블리를 이 가상 디렉터리의 \bin 하위 디렉터리에 복사합니다.
  4. 구성 파일을 가상 디렉터리에 복사하고 파일 이름을 Web.config로 지정합니다.

그러면 응용 프로그램 루트에 있는 서비스 파일의 URL을 통해 해당 응용 프로그램에 액세스할 수 있습니다.

.NET 응용 프로그램에서 Windows Communication Foundation 서비스를 호스트하려면 서비스 형식을 이 응용 프로그램에서 참조하는 클래스 라이브러리 어셈블리로 컴파일하고, Windows Communication Foundation의 System.ServiceModel.ServiceHost 클래스를 사용하여 해당 서비스를 호스트하는 응용 프로그램을 프로그래밍합니다. 다음은 필요한 간단한 프로그래밍 예제입니다.

string httpBaseAddress = "http://www.woodgrove.com:8000/";
string tcpBaseAddress = "net.tcp://www.woodgrove.com:8080/";

Uri httpBaseAddressUri = new Uri(httpBaseAddress);
Uri tcpBaseAddressUri = new Uri(tcpBaseAddress);

Uri[] baseAdresses = new Uri[] {
    httpBaseAddressUri,
    tcpBaseAddressUri};

using(ServiceHost host = new ServiceHost(
typeof(Service), //"Service" is the name of the service type    baseAdresses))
{
    host.Open();

    [...] //Wait to receive messages
    host.Close();
}

이 예제에서는 Windows Communication Foundation System.ServiceModel.ServiceHost 생성에서 하나 이상의 전송 프로토콜에 대한 주소가 지정되는 방식을 보여 줍니다. 이러한 주소를 기준 주소라고 합니다.

Windows Communication Foundation 서비스의 끝점에 제공되는 주소는 끝점의 호스트에 대한 기준 주소를 기준으로 하는 상대 주소입니다. 호스트에는 통신 전송 프로토콜당 하나의 기준 주소가 있을 수 있습니다. 끝점의 주소는 호스트의 기준 주소 중 끝점의 통신 전송 프로토콜에 대한 기준 주소의 상대 주소입니다. 위에 있는 구성 파일의 예제에서 끝점으로 선택된 System.ServiceModel.BasicHttpBinding은 HTTP를 전송 프로토콜로 사용하므로 EchoService 끝점의 주소는 호스트의 HTTP 기준 주소의 상대 주소입니다. 위 예제에서 호스트의 경우 HTTP 기준 주소는 http://www.woodgrove.com:8000/입니다. IIS 또는 WAS에서 호스트되는 서비스의 기준 주소는 해당 서비스에 대한 서비스 파일의 URL입니다.

IIS 또는 WAS에서 호스트되고 전송 프로토콜로 HTTP만 사용하도록 구성된 서비스만 Windows Communication Foundation의 ASP.NET 호환 모드 옵션을 사용하도록 만들 수 있습니다. 이 옵션을 사용하려면 다음의 두 단계가 필요합니다.

  1. 프로그래머는 다음과 같이 System.ServiceModel.AspNetCompatbilityRequirements 특성을 서비스 형식에 추가하여 ASP.NET 호환 모드가 허용되는지 또는 필수인지 지정해야 합니다.

    [System.ServiceModel.AspNetCompatibilityRequirements(
            RequirementsMode=AspNetCompatbilityRequirementsMode.Require)]
    public class DerivativesCalculatorServiceType: IDerivativesCalculator

  2. 관리자는 다음과 같이 ASP.NET 호환 모드를 사용하도록 응용 프로그램을 구성해야 합니다.

    <configuration>
       <system.serviceModel>
          <services>
             [...]
          </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
       </system.serviceModel>
    </configuration>

또한 Windows Communication Foundation 응용 프로그램은 서비스 파일의 확장명을 .svc가 아닌 .asmx를 사용하도록 구성할 수 있습니다.

<system.web>
   <compilation>
      <compilation debug="true">
         <buildProviders>
            <remove extension=".asmx"/>
            <add extension=".asmx"
               type="System.ServiceModel.ServiceBuildProvider,
               Systemm.ServiceModel,
               Version=3.0.0.0,
               Culture=neutral,
               PublicKeyToken=b77a5c561934e089" />
         </buildProviders>
      </compilation>
   </compilation>
</system.web>

이 옵션을 사용하면 Windows Communication Foundation을 사용하도록 서비스를 수정할 때 .asmx 서비스 파일의 URL을 사용하도록 구성된 클라이언트를 수정하지 않아도 됩니다.

클라이언트 개발

ASP.NET 웹 서비스용 클라이언트는 .asmx 파일의 URL을 입력으로 제공하는 wsdl.exe 명령줄 도구를 사용하여 생성됩니다. Windows Communication Foundation에서 제공되는 이와 유사한 도구는 svcutil.exe입니다. 이 도구는 서비스 계약과 프록시 클래스에 대한 정의를 포함한 코드 모듈을 생성합니다. 또한 서비스의 주소와 바인딩을 포함한 구성 파일도 생성합니다.

원격 서비스의 클라이언트를 프로그래밍할 때 일반적으로 비동기 패턴에 따라 프로그래밍하는 것이 좋습니다. wsdl.exe 도구로 생성되는 코드는 기본적으로 항상 동기 패턴과 비동기 패턴을 모두 제공합니다. svcutil.exe 도구로 생성되는 코드는 둘 중 한 패턴을 제공할 수 있습니다. 이 도구는 기본적으로 동기 패턴을 제공합니다. 이 도구를 /async 스위치와 함께 실행하면 생성된 코드에서 비동기 패턴을 제공합니다.

ASP.NET의 wsdl.exe 도구로 생성되는 프록시 클래스의 이름이 기본적으로 Windows Communication Foundation의 svcutil.exe 도구로 생성되는 프록시 클래스의 이름과 일치할 것이라는 보장은 없습니다. 특히 System.Xml.Serialization.XmlSerializer를 사용하여 serialize되어야 하는 클래스의 속성 이름에는 기본적으로 svcutil.exe 도구로 생성되는 코드에서 Property 접미사가 제공되지만 wsdl.exe 도구에서는 그렇지 않습니다.

메시지 표현

ASP.NET 웹 서비스에서 송수신되는 SOAP 메시지 헤더는 사용자 지정할 수 있습니다. System.Web.Services.Protocols에서 클래스가 파생되어 헤더의 구조가 정의된 다음 System.Web.Services.SoapHeader 특성이 헤더의 존재를 나타내는 데 사용됩니다.

public class SomeProtocol : SoapHeader
{
    public long CurrentValue;
    public long Total;
}

[WebService]
public interface IEcho
{
    SomeProtocol ProtocolHeader
    {
       get;
   set;
    }

    [WebMethod]
    [SoapHeader("ProtocolHeader")]
    string PlaceOrders(PurchaseOrderType order);
}

public class Service: WebService, IEcho
{
    private SomeProtocol protocolHeader;
   
    public SomeProtocol ProtocolHeader
    {
      get
      {
        return this.protocolHeader;
      }
     
      set
      {
        this.protocolHeader = value;
      }
    }
   
    string PlaceOrders(PurchaseOrderType order)
    {
      long currentValue = this.protocolHeader.CurrentValue;
    }
}

Windows Communication Foundation에서는 다음과 같이 System.ServiceModel.MessageContract, System.ServiceModel.MessageHeader, System.ServiceModel.MessageBody 특성을 제공하여 서비스에서 송수신되는 SOAP 메시지의 구조를 설명합니다.

[DataContract]
public class SomeProtocol
{
    [DataMember]
    public long CurrentValue;
    [DataMember]
    public long Total;
}

[DataContract]
public class Item
{
    [DataMember]
    public string ItemNumber;
    [DataMember]
    public decimal Quantity;
    [DataMember]
    public decimal UnitPrice;
}

[MessageContract]
public class ItemMesage
{
    [MessageHeader]
    public SomeProtocol ProtocolHeader;
    [MessageBody]
    public Item Content;
}

[ServiceContract]
public interface IItemService
{
    [OperationContract]
    public void DeliverItem(ItemMessage itemMessage);
}

이 구문은 메시지 구조의 명시적인 표현을 나타내지만 메시지의 구조는 ASP.NET 웹 서비스 코드로 암시될 뿐입니다. 또한 ASP.NET 구문에서는 메시지 헤더가 위 예제의 ProtocolHeader 속성과 같은 서비스 속성으로 나타나지만 Windows Communication Foundation 구문에서는 메시지 속성으로 더욱 정확하게 나타납니다. Windows Communication Foundation에서는 끝점 구성에 메시지 헤더를 추가할 수도 있습니다.

<service name="Service ">
   <endpoint
      address="EchoService"
      binding="basicHttpBinding"
      contract="IEchoService ">
      <headers>
         <dsig:X509Certificate
            xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
               ...
         </dsig:X509Certificate>
      </headers>
   </endpoint>
</service>

이 옵션을 사용하면 클라이언트 또는 서비스에 대한 코드에서 기반 프로토콜 헤더를 참조할 필요가 없습니다. 끝점 구성 방식에 의해 헤더가 메시지에 간단히 추가됩니다.

서비스 설명

wsdl 쿼리를 사용하여 ASP.NET 웹 서비스의 .asmx 파일에 대해 HTTP GET 요청을 발행하면 ASP.NET에서 해당 서비스를 설명하는 WSDL을 생성하게 됩니다. 이 WSDL이 요청에 대한 응답으로 반환됩니다.

ASP.NET 2.0을 통해 서비스가 WS-I(Web Services-Interoperability Organization) Basic Profile 1.1과 호환되는지 확인하고 서비스가 WSDL과 호환된다는 문구를 넣을 수 있게 되었습니다. 다음과 같이 System.Web.Services.WebServiceBinding 특성의 ConformsTo와 EmitConformanceClaims 매개 변수를 사용하면 됩니다.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(
    ConformsTo = WsiProfiles.BasicProfile1_1,
    EmitConformanceClaims=true)]
public interface IEcho

ASP.NET에서 서비스에 대해 생성하는 WSDL은 사용자 지정할 수 있습니다. System.Web.Services.Description.ServiceDescriptionFormatExtension의 하위 클래스를 만들어 WSDL에 항목을 추가함으로써 사용자 지정할 수 있습니다.

IIS 5.1, 6.0 또는 WAS에서 호스트되는 HTTP 끝점이 있는 Windows Communication Foundation 서비스의 .svc 파일에 대해 wsdl 쿼리를 사용하여 HTTP GET 요청을 발행하면 Windows Communication Foundation에서 해당 서비스를 설명하는 WSDL로 응답하게 됩니다. .NET 응용 프로그램에서 호스트되는 서비스의 HTTP 기준 주소에 대해 wsdl 쿼리를 사용하여 HTTP GET 요청을 발행해도 같은 효과가 있습니다.

Windows Communication Foundation은 WS-MetadataExchange 요청에 대해서도 서비스를 설명하기 위해 생성하는 WSDL로 응답합니다. ASP.NET 웹 서비스는 WS-MetadataExchange 요청에 대한 지원을 기본적으로 제공하지 않습니다.

Windows Communication Foundation에서 생성하는 WSDL은 광범위하게 사용자 지정할 수 있습니다. System.ServiceModel.ServiceMetadataBehavior 클래스는 WSDL을 사용자 지정하기 위한 몇 가지 기능을 제공하며 System.ServiceModel.Design.IWsdlExporter 구현을 개발하여 완벽하게 제어할 수 있습니다. 또한 다음과 같이 WSDL을 생성하지 않고 지정된 URL에서 정적 WSDL 파일을 사용하도록 Windows Communication Foundation을 구성할 수도 있습니다.

<behaviors>
   <serviceBehaviors>
      <behavior name="DescriptionBehavior">
        <metadataPublishing
        enableMetadataExchange="true"
        enableGetWsdl="true"
        enableHelpPage="true"
        metadataLocation=
        "http://localhost/DerivativesCalculatorService/Service.wsdl"/>
      </behavior>
   </serviceBehaviors>
</behaviors>

예외 처리

ASP.NET 웹 서비스에서는 처리되지 않는 예외는 클라이언트에 SOAP 오류로 반환됩니다. 또한 System.Web.Services.Protocols.SoapException 클래스의 인스턴스를 명시적으로 발생시켜 클라이언트에 전송되는 SOAP 오류의 내용에 대한 제어 범위를 넓힐 수 있습니다.

Windows Communication Foundation 서비스에서는 중요한 정보가 예외를 통해 우발적으로 노출되지 않도록, 처리되지 않은 예외가 클라이언트에 SOAP 오류로 반환되지 않습니다. 디버깅을 위해 처리되지 않은 예외가 클라이언트에 반환되도록 하는 구성 설정이 제공됩니다.

Windows Communication Foundation 프로그래머는 의도적으로 클라이언트에 SOAP 오류를 반환하기 위해 generic 형식의 System.ServiceModel.FaultException<T> 인스턴스를 발생시킬 수 있습니다. 여기서 T는 데이터 계약이어야 합니다. 또한 프로그래머는 다음과 같이 System.ServiceModel.FaultContract 특성을 작업에 추가하여 작업에서 발생할 수도 있는 오류를 지정할 수 있습니다.

[DataContract]
public class MathFault
{   
    [DataMember]
    public string operation;
    [DataMember]
    public string problemType;
}

[ServiceContract]
public interface ICalculator
{
    [OperationContract]
    [FaultContract(typeof(MathFault))]
    int Divide(int n1, int n2);
}

이렇게 하면 가능한 오류가 서비스에 대한 WSDL에 알려지기 때문에 클라이언트 프로그래머가 작업에서 발생할 수 있는 오류를 정확히 예상하고 다음과 같이 적절한 catch 문을 작성할 수 있습니다.

try
{
    result = proxy.Divide(value1, value2);
}
catch (FaultException<MathFault> e)
{
    Console.WriteLine("FaultException<MathFault>: Math fault while doing "
      + e.Detail.operation
      + ". Problem: "
      + e.Detail.problemType);
}

상태 관리

ASP.NET 웹 서비스를 구현하는 데 사용되는 클래스는 다음과 같이 System.Web.Services.WebService에서 파생될 수 있습니다.

public class Service : WebService, IEcho
{
    public string Echo(string input)
    {
        return input;
    }
}

이러한 경우 System.Web.Services.WebService 기본 클래스의 Context 속성을 사용하여 System.Web.HttpContext 개체에 액세스하도록 클래스를 프로그래밍할 수 있습니다. System.Web.HttpContext 개체를 사용하여 Application 속성을 통해 응용 프로그램 상태 정보를, Session 속성을 통해 세션 상태 정보를 업데이트하고 검색할 수 있습니다.

ASP.NET에서는 System.Web.HttpContext의 Session 속성을 통해 액세스되는 세션 상태 정보가 실제로 저장되는 위치를 세부적으로 제어할 수 있습니다. 세션 상태 정보는 쿠키, 데이터베이스, 현재 서버의 메모리 또는 지정된 서버의 메모리에 저장될 수 있습니다. 저장 위치는 서비스의 구성 파일에서 선택됩니다.

Windows Communication Foundation은 상태 관리를 위한 확장 가능한 개체를 제공합니다. 확장 가능한 개체는 System.ServiceModel.IExtensibleObject<T>를 구현하는 개체입니다. 가장 중요한 확장 가능한 개체는 System.ServiceModel.ServiceHostBaseSystem.ServiceModel.InstanceContext입니다. 전자를 사용하면 같은 호스트에 있는 모든 서비스 형식의 모든 인스턴스에서 액세스 가능한 상태를 유지할 수 있으며, 후자를 사용하면 서비스 형식의 같은 인스턴스 내에서 실행되는 코드로 액세스 가능한 상태를 유지할 수 있습니다.

여기서 TradingSystem 서비스 형식에는 같은 프록시 인스턴스의 모든 호출이 서비스 형식의 같은 인스턴스로 라우팅되도록 지정하는 System.ServiceModel.ServiceBehavior 특성이 있습니다.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TradingSystem: ITradingService

DealData 클래스는 다음과 같이 서비스 형식의 같은 인스턴스 내에서 실행되는 코드로 액세스할 수 있는 상태를 정의합니다.

internal class DealData: IExtension<InstanceContext>
{
    public string DealIdentifier = null;
    public Trade[] Trades = null;
}

서비스 계약의 작업 중 하나를 구현하는 서비스 형식의 코드에서 DealData 상태 개체는 다음과 같이 서비스 형식의 현재 인스턴스에 대한 상태에 추가됩니다.

string ITradingService.BeginDeal()
{
    string dealIdentifier = Guid.NewGuid().ToString();
    DealData state = new DealData(dealIdentifier);
    OperationContext.Current.InstanceContext.Extensions.Add(state);
    return dealIdentifier;
}

그런 다음 이 상태 개체는 다음과 같이 서비스 계약의 작업 중 다른 하나를 구현하는 코드로 검색 및 수정이 가능하게 됩니다.

void ITradingService.AddTrade(Trade trade)
{
    DealData dealData =      OperationContext.Current.InstanceContext.Extensions.Find<DealData>();
    dealData.AddTrade(trade);
}

ASP.NET에서 System.Web.HttpContext 클래스의 상태 정보가 실제로 저장되는 위치를 세부적으로 제어할 수 있지만, 적어도 초기 버전의 Windows Communication Foundation에서는 확장 가능한 개체가 저장되는 위치를 제어할 수 없습니다. 이러한 점이 Windows Communication Foundation 서비스를 위해 ASP.NET 호환 모드를 선택하는 가장 정당한 이유가 됩니다. 구성 가능한 상태 관리가 필수적인 경우 ASP.NET 호환 모드를 채택함으로써 System.Web.HttpContext 클래스의 기능을 ASP.NET에서와 똑같은 방식으로 사용할 수 있으며 System.Web.HttpContext 클래스를 통해 관리되는 상태 정보가 저장되는 위치를 구성할 수도 있습니다.

보안

ASP.NET 웹 서비스의 보안을 유지하기 위한 옵션은 대개 IIS 응용 프로그램의 보안을 유지하는 옵션과 같습니다. Windows Communication Foundation 응용 프로그램은 IIS뿐만 아니라 .NET 실행 파일 내에서도 호스트될 수 있기 때문에 Windows Communication Foundation 응용 프로그램의 보안을 유지하기 위한 옵션은 IIS의 기능에 독립적으로 만들어야 했습니다. 그러나 ASP.NET 웹 서비스에 제공되는 기능은 ASP.NET 호환 모드에서 실행되는 Windows Communication Foundation 서비스에서도 사용할 수 있습니다.

보안: 인증

IIS는 응용 프로그램에 대한 액세스를 제어하는 기능을 제공하므로 이 기능을 통해 익명 액세스 또는 Windows 인증, 다이제스트 인증, 기본 인증, .NET Passport 인증 등 다양한 모드의 인증을 선택할 수 있습니다. Windows 인증 옵션을 사용하면 ASP.NET 웹 서비스에 대한 액세스를 제어할 수 있습니다. Windows Communication Foundation 응용 프로그램이 IIS 내에서 호스트되는 경우에는 이 응용 프로그램에 대한 익명 액세스를 허용하도록 IIS를 구성하여 다양한 다른 옵션과 함께 Windows 인증을 지원하는 Windows Communication Foundation 자체에서 인증을 관리할 수 있도록 해야 합니다. 기본 제공되는 다른 옵션에는 사용자 이름 토큰, X.509 인증서, SAML 토큰 및 InfoCard가 있으며 사용자 지정 인증 메커니즘도 정의할 수 있습니다.

ASP.NET 2.0의 System.Web.HttpContext 클래스에는 특정 종류의 저장소에서 인증된 사용자에 대한 정보를 읽는 방법을 아는 System.Web.Profile.Provider 클래스를 통해 이러한 정보를 저장소에서 자동으로 검색할 수 있는 Profile 속성이 있습니다. 사용자 지정 동작에서 System.Web.Profile.Provider 클래스를 사용하여 프로필 정보를 검색할 수 있지만 이러한 ASP.NET 2.0 메커니즘은 Windows Communication Foundation에서 ASP.NET 호환 모드인 경우를 제외하면 지원되지 않습니다.

보안: 가장

ASP.NET에서는 ASP.NET 웹 서비스가 특정 사용자로, 또는 현재 요청에서 제공된 사용자의 자격 증명으로 가장할 수 있도록 하는 ID 요소를 제공합니다. 이 요소를 사용하면 ASP.NET 호환 모드에서 실행되고 있는 Windows Communication Foundation 응용 프로그램에서 가장을 구성할 수 있습니다.

Windows Communication Foundation의 구성 시스템은 가장할 특정 사용자를 지정하기 위한 자체 ID 요소를 제공합니다. 또한 Windows Communication Foundation 클라이언트 및 서비스는 가장을 위해 별도로 구성할 수 있습니다. 클라이언트는 요청을 전송할 때 현재 사용자로 가장하도록 다음과 같이 구성할 수 있습니다.

<behaviors>
   <endpointBehaviors>
      <behavior name="DerivativesCalculatorClientBehavior">
         <clientCredentials>
            <windows allowedImpersonationLevel="Impersonation"/>
         </clientCredentials>
      </behavior>
   </endpointBehaviors>
</behaviors>

서비스 작업은 현재 요청에서 제공된 사용자의 자격 증명으로 가장하도록 다음과 같이 구성할 수 있습니다.

[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void Receive(Message input)

보안: ACL을 사용한 인증

ACL(액세스 제어 목록)을 사용하면 .asmx 파일에 대한 액세스를 제한할 수 있습니다. 그러나 Windows Communication Foundation .svc 파일에 대한 ACL은 ASP.NET 호환 모드인 경우를 제외하고는 무시됩니다.

보안: 역할 기반 인증

IIS Windows 인증 옵션을 ASP.NET 구성 언어로 제공되는 인증 요소와 함께 사용하면 사용자에게 지정된 Windows 그룹 기반의 ASP.NET 웹 서비스에 대한 역할 기반 인증을 쉽게 수행할 수 있습니다. ASP.NET 2.0에는 더 포괄적인 역할 기반 인증 메커니즘인 역할 공급자가 도입되었습니다.

모든 역할 공급자는 사용자에게 지정된 역할을 쿼리하기 위한 간단한 인터페이스를 구현하는 클래스이지만, 각 역할 공급자는 서로 다른 출처에서 해당 정보를 검색하는 방법을 인식하고 있습니다. ASP.NET 2.0은 Microsoft SQL Server 데이터베이스에서 역할 지정을 검색할 수 있는 역할 공급자와 Windows Server 2003 권한 부여 관리자에서 역할 지정을 검색할 수 있는 역할 공급자를 제공합니다.

실제로 역할 공급자 메커니즘은 Windows Communication Foundation 응용 프로그램을 포함한 모든 .NET 응용 프로그램에서 ASP.NET과 별개로 사용할 수 있습니다. 다음은 System.ServiceModel.ServiceAuthorization 동작을 통해 ASP.NET 역할 공급자를 사용하는 옵션이 선택되는 방식을 보여 주는 Windows Communication Foundation 응용 프로그램의 샘플 구성입니다.

<system.serviceModel>
  <services>
    <service name="Service.ResourceAccessServiceType"
     behaviorConfiguration="ServiceBehavior">
     <endpoint
       address="ResourceAccessService"
      binding="wsHttpBinding"
      contract="Service.IResourceAccessContract"/>
    </service>
  </services>
  <behaviors>
   <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <serviceAuthorization principalPermissionMode="UseAspNetRoles"/>
        </behavior>
      </serviceBehaviors>
   </behaviors>
</system.serviceModel>

보안: 클래임 기반 인증

Windows Communication Foundation의 가장 중요한 기술 혁신 중 하나는 클래임에 기반하여 보호되는 리소스에 대한 액세스 인증을 완벽하게 지원한다는 것입니다. 클래임은 형식, 권한 및 값으로 구성됩니다. 예를 들어 운전 면허증을 생각해 보십시오. 운전 면허증은 소지인에 대한 클래임 집합으로 구성되는데, 그 중 하나는 소지인의 생년월일입니다. 이 클래임의 형식은 생년월일이며, 값은 운전자의 생년월일입니다. 클래임이 소지인에게 부여하는 권한은 소지인이 클래임 값으로 수행할 수 있는 작업을 지정합니다. 운전자의 생년월일에 대한 클래임의 경우 권한은 단순한 소유입니다. 운전자는 해당 생년월일을 소유하고 있지만 변경하는 등의 작업은 할 수 없습니다. 역할은 단순히 클래임의 한 형식이기 때문에 클래임 기반 인증은 역할 기반 인증을 포함합니다.

클래임 기반 인증은 클래임 집합을 작업의 액세스 요구 사항과 비교하여 그 결과에 따라 작업에 대한 액세스 권한을 부여하거나 거부함으로써 수행됩니다. Windows Communication Foundation에서는 다음과 같이 System.ServiceModel.Description.ServiceAuthorizationBehaviorServiceAuthorizationManager 속성에 값을 다시 한 번 지정함으로써 클래임 기반 인증을 실행하는 데 사용할 클래스를 지정할 수 있습니다.

<behaviors>
  <serviceBehaviors>
    <behavior name='ServiceBehavior'>
   <serviceAuthorization
        serviceAuthorizationManagerType='Service.AccessChecker, Service' />
    </behavior>
  </serviceBehaviors>
</behaviors>

클래임 기반 인증을 실행하는 데 사용되는 클래스는 System.ServiceModel.ServiceAuthorizationManager에서 파생되어야 하며, 여기에서 다시 정의할 유일한 메서드는 AccessCheck() 메서드입니다. Windows Communication Foundation에서는 서비스의 작업이 호출될 때마다 이 메서드를 호출하여 System.ServiceModel.OperationContext 개체를 제공하며, 이 개체의 ServiceSecurityContext.AuthorizationContext 속성에는 사용자에 대한 클래임이 있습니다. 따라서 Windows Communication Foundation에서는 사용자가 인증을 위해 제공한 보안 토큰이 어떤 것이든 이 토큰으로부터 사용자에 대한 클래임을 조합하는 작업을 이미 완료한 상태이며, 이러한 클래임이 요청한 작업에 충분한지 여부를 평가하는 간단한 작업만 남게 됩니다.

Windows Communication Foundation이 어떤 종류의 보안 토큰으로든 자동으로 클래임을 조합한다는 점은 매우 중요한 혁신입니다. 클래임 기반 인증을 위한 코드를 인증 메커니즘과는 완전히 별개로 만들어 주기 때문입니다. 반면 ASP.NET에서 ACL 또는 역할을 사용하는 인증은 Windows 인증과 밀접하게 연결됩니다.

보안: 기밀성

ASP.NET 웹 서비스와 교환되는 메시지의 기밀성은 IIS의 응용 프로그램에서 HTTPS(Secure Hypertext Transfer Protocol)를 사용하도록 구성함으로써 전송 수준에서 보장할 수 있습니다. IIS에서 호스트되는 Windows Communication Foundation 응용 프로그램에 대해서도 마찬가지입니다. 한편 IIS의 외부에서 호스트되는 Windows Communication Foundation 응용 프로그램도 보안 전송 프로토콜을 사용하도록 구성할 수 있습니다. 더 중요한 것은 메시지가 WS-Security 프로토콜을 사용하여 전송되기 전에 메시지의 보안을 유지하도록 Windows Communication Foundation 응용 프로그램을 구성할 수 있다는 점입니다. WS-Security를 사용하여 메시지 본문의 보안만 유지하면 메시지가 기밀을 유지한 상태로 중간 단계를 거쳐 최종 목적지에 도달하도록 할 수 있습니다.

세계화

ASP.NET 구성 언어를 사용하면 개별 서비스의 culture를 지정할 수 있습니다. Windows Communication Foundation에서는 ASP.NET 호환 모드인 경우를 제외하고 이러한 구성 설정을 지원하지 않습니다. ASP.NET 호환 모드를 사용하지 않는 Windows Communication Foundation 서비스를 지역화하려면 서비스 형식을 culture별 어셈블리로 컴파일하고 각 culture별 어셈블리가 별도의 culture별 끝점을 갖도록 합니다.

내부 아키텍처

ASP.NET 웹 서비스

최신 ASP.NET 구현에서 HTTP 요청은 http.sys라는 Windows 커널의 HTTP 구현에서 System.Web.HttpWorkerRequest 개체 형식으로 수신됩니다. System.Web.HttpWorkerRequest 개체는 System.Web.HttpRuntime 개체에 수신되며, 이 개체는 System.Web.HttpWorkerRequest의 데이터로 System.Web.HttpContext 개체를 채웁니다. 요청은 System.Web.HttpRequest 개체인 Request 속성으로 System.Web.HttpContext 개체에 통합됩니다.

.asmx 파일로 배달된 HTTP POST 요청을 나타내는 System.Web.HttpRequest 개체는 기본적으로 System.Web.IHttpHandler 인터페이스를 구현하는 개체에 전달됩니다(Skonnard 2003, 2004). 이 개체는 System.Web.Services.Protocols.WebServiceHandlerFactory 클래스를 사용하여 만들어지며, ASP.NET 웹 서비스 요청 처리기라고도 합니다. Windows Communication Foundation은 ASP.NET 호환 모드로 구성되면 ASP.NET 웹 서비스 요청 처리기의 동작을 모방하는 System.Web.IHttpHandler의 구현을 제공합니다.

ASP.NET 웹 서비스 요청 처리기는 적어도 4가지 작업을 수행해야 합니다. 첫째, HTTP 요청에 통합된 SOAP 메시지를 개발자가 제공하는 SOAP E\extensions의 인스턴스에 전달해야 합니다(Meier, Vasireddy, Babbar 및 Mackman 2004). 둘째, 요청을 처리하기 위해 .asmx 파일에 참조된 클래스의 메서드 중 호출할 메서드를 결정해야 합니다. 셋째, 메서드에서 매개 변수로 예상하는 형식의 인스턴스로 요청이 deserialize되도록 해야 합니다. 넷째, 실제로 메서드를 호출하여 요청으로부터 deserialize한 매개 변수를 이 메서드에 전달해야 합니다.

SOAP 확장은 System.Web.Services.Protocols.SoapExtension에서 파생된 형식입니다. 이 확장은 SOAP 메시지가 처리를 위해 .asmx 파일에서 참조되는 클래스의 메서드로 전달되기 전에 요청에 있는 SOAP 메시지를 액세스하거나 수정하는 데 사용됩니다. 또한 이 확장은 응답 메시지를 액세스 및 수정할 수 있으며 서버뿐만 아니라 클라이언트에도 배포될 수 있습니다. SOAP 확장의 일반적 용도는 메시지 로깅 및 암호화입니다. SOAP 확장은 컴퓨터에 배포된 모든 서비스 또는 개별 서비스에 적용되거나, 사용자 지정 System.Web.Services.SoapExtension 특성을 사용하여 서비스의 특정 작업에 적용될 수 있습니다.

ASP.NET 웹 서비스 요청 처리기는 기본적으로 SOAPAction 헤더를 사용하여 요청을 처리하기 위해 호출할 클래스의 메서드를 결정합니다. 기본적으로 요청 처리기는 서비스의 네임스페이스와 작업의 이름 순서로 구성된 SOAPAction HTTP 헤더를 예상합니다. 서비스의 기본 네임스페이스는 http://tempuri.org/이며, 작업의 기본 이름은 해당 작업을 정의하는 메서드의 이름입니다. 다음은 HTTP 요청을 처리하는 예제입니다.

POST /asmxservice/service.asmx HTTP/1.1
User-Agent: Mozilla/4.0
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/Echo"
Host: localhost
Content-Length: 314
Expect: 100-continue
Connection: Keep-Alive

<?xml version="1.0" encoding="UTF-8" ?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<Echo xmlns="http://tempuri.org/">
<input>Hello, World
</input>
</Echo>
</soap:Body>
</soap:Envelope>

예제에서 HTTP 요청을 처리할 때 요청 처리기는 http://localhost/asmxservice/service.asmx에 있는 파일의 @_WebService 지시문에서 참조되는 클래스가 작업 이름이 Echo인 기본 네임스페이스를 가진 서비스를 정의한다고 예상합니다. 또한 이 요청 처리기는 기본적으로 해당 작업이 Echo라는 클래스의 메서드로 구현된다고 예상하며, 해당 메서드를 호출하여 요청을 처리하려고 합니다. 이러한 모든 동작은 다음과 같이 사용자 지정할 수 있습니다.

요청 처리기에서 SOAPAction HTTP 헤더를 사용하지 않고 SOAP 메시지 본문 요소의 정규화된 요소 이름을 사용하여 호출할 메서드를 식별하도록 할 수 있습니다. 앞의 HTTP 요청 예제에서 본문 요소는 다음과 같습니다.

<Echo xmlns="http://tempuri.org/">
[...]
</soap:Body>

정규화된 요소 이름은 http://tempuri.org/Echo입니다. 이 요소 이름은 SOAPAction HTTP 헤더와 같기 때문에 ASP.NET 웹 서비스 요청 처리기에서 메시지를 라우팅하는 방법을 결정하기 위해 이 이름을 어떻게 사용할 것인지는 명백합니다. 요청 처리기에서 SOAPAction HTTP 헤더가 아닌 SOAP 메시지 본문 요소의 정규화된 이름을 사용하도록 하려면 서비스를 구현하는 클래스에 System.Web.Services.Protocols.SoapDocumentService 특성을 적용하고 RequestElement를 라우팅 스타일로 지정합니다. 즉 다음과 같습니다.

[SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
public class Service : WebService, IEcho

또한 다음과 같이 System.Web.Services.Protocols.SoapDocumentMethod 특성의 RequestElementName 매개 변수를 사용하여 메서드 이름과는 다른 SOAP 메시지 본문 요소의 로컬 이름을 만들 수도 있습니다.

[WebMethod]
[SoapDocumentMethod(RequestElementName="OtherName")]
string Echo(string input);

그러면 ASP.NET 웹 서비스 요청 처리기에서 SOAP 메시지 본문 요소의 로컬 이름을 메서드 이름에 직접 일치시키지 않고 해당 메서드에 적용된 System.Web.Services.Protocols.SoapDocumentMethod 특성의 RequestElementName 매개 변수 값에 일치시킵니다.

다음과 같이 System.Web.Services.WebService 특성의 Namespace 매개 변수를 사용하여 ASP.NET 웹 서비스의 네임스페이스에 대한 기본값을 수정할 수 있습니다.

[WebService(Namespace = "http://www.woodgrove.com/2006/01/29/")]
public interface IEcho

여기서는 이 매개 변수의 값이 인터페이스에 적용된 System.Web.Services.WebService 특성에 대해 수정된 것으로 보이지만, 유감스럽게도 버그로 인해 실제로 매개 변수의 값을 변경해도 클래스가 아닌 인터페이스에 이 특성이 적용될 때는 아무런 효과가 없습니다.

작업 이름은 해당 작업을 구현하는 메서드의 이름과 다르게 만들 수 있습니다. 다음과 같이 System.Web.Services.WebMethod 특성의 MessageName 매개 변수 값을 지정하면 됩니다.

[WebMethod(MessageName="OtherName")]
string Echo(string input);

이 기능은 다음과 같이 ASP.NET 웹 서비스 요청 처리기의 다형적 메서드를 구별하는 데 사용할 수 있습니다.

[WebMethod(MessageName="OtherName")]
string Echo(string input);
[WebMethod]
string[] Echo(string[] inputs);

다음과 같이 System.Web.Services.Protocols.SoapDocumentMethod 특성을 메서드에 추가하여 이 특성의 Action 매개 변수에 대한 값을 제공할 수 있습니다.

[WebMethod]
[SoapDocumentMethod(Action="urn:echoing:echo")]
string Echo(string input);

이러한 모든 메서드에 대해 ASP.NET 웹 서비스 요청 처리기는 SOAPAction HTTP 헤더를 서비스의 네임스페이스와 작업 이름으로 분해하여 대상 메서드를 식별하려고 시도하지 않고, 단지 SoapDocumentMethod 특성의 Action 매개 변수에 대해 지정된 값과 일치시킵니다.

일단 ASP.NET 웹 서비스 요청 처리기에서 요청을 처리하기 위해 .asmx 파일에서 참조된 클래스의 메서드 중 호출할 메서드를 식별하면 해당 요청은 이 메서드에서 매개 변수로 예상하는 형식의 인스턴스로 deserialize되어야 합니다. 이를 위해 System.Xml.Serialization.XmlSerializer가 사용됩니다.

기본적으로 요청 처리기는 요청에 통합된 SOAP 메시지가 WSDL 1.1 사양의 문서 스타일과 일치한다고 가정합니다. 이러한 스타일의 메시지에는 임의의 스키마에 따라 구조화된 본문 요소가 있으며, 요청 처리기는 System.Xml.Serialization.XmlSerializer를 사용하여 해당 본문 요소를 .NET 형식으로 deserialize합니다. 다음은 작업 예제입니다.

[WebMethod]
PurchaseOrderConfirmationType PlaceOrders(PurchaseOrderType order);

위 작업에서 ASP.NET 웹 서비스 요청 처리기는 다음과 같은 문서 스타일의 SOAP 메시지를 예상합니다.

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:s1="urn:Woodgrove:2006:January:29" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://tempuri.org/"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <soap:Body>
      <tns:PlaceOrders>
         <s1:PurchaseOrder>
            <s1:Date>2006-01-31</s1:Date>
            <s1:LineItems>
               <s1:LineItem>
                  <s1:ItemNumber>1</s1:ItemNumber>
                  <s1:Quantity>1</s1:Quantity>
                  <s1:UnitPrice>50.00</s1:UnitPrice>
               </s1:LineItem>
            </s1:LineItems>
            <s1:Total>50.00</s1:Total>
         </s1:PurchaseOrder>
      </tns:PlaceOrders>
   </soap:Body>
</soap:Envelope>

여기서 요소 이름은 deserialize될 형식의 이름을 식별합니다. 이 메커니즘도 다음과 같이 사용자 지정할 수 있습니다.

  1. 형식의 이름이 SOAP 메시지의 구성 요소에 매핑되는 방식은 System.Xml.XmlElementSystem.Xml.XmlAttribute 특성을 메서드의 매개 변수에 적용함으로써 사용자 지정할 수 있습니다. System.Xml.XmlElement 특성은 다음과 같이 ASP.NET 웹 서비스 요청 처리기에서 형식을 deserialize할 것이라고 예상하는 XML 요소의 이름을 제어합니다.

    [WebMethod]
    PurchaseOrderType PlaceOrders
    (
        [XmlElement("OrderParameter")]
        PurchaseOrderType order
    );

    System.Xml.XmlAttribute 특성이 매개 변수에 추가된 경우 요청 처리기는 해당 매개 변수를 XML 요소가 아닌 XML 특성에서 deserialize할 것이라고 예상합니다.

  2. 요청에 통합된 SOAP 메시지가 문서 스타일과 일치할 것이라는 ASP.NET 웹 서비스 요청 처리기의 기본 예상을 변경하여 해당 SOAP 메시지가 WSDL 1.1 사양의 RPC 스타일과 일치할 것이라고 예상하도록 만들 수 있습니다. 전체 서비스에 대해 SoapRpcService 특성을 사용하거나, 개별 메서드에 대해 SoapRpcMethod 특성을 사용하여 이를 수행할 수 있습니다. 그러나 RPC 스타일 사용은 WS-I Basic Profile 1.1에 포함되지 않고, 따라서 상호 운용성을 낮추기 때문에 이 옵션은 사용하지 않는 것이 좋습니다.

Windows Communication Foundation

Windows Communication Foundation 클라이언트에 사용되는 서비스의 프록시 클래스는 이 클래스의 메서드로 전달되는 매개 변수를 System.ServiceModel.Channels.Message 개체로 serialize합니다. 그러면 해당 개체는 선택한 바인딩으로 수 및 성격이 결정되는 일련의 채널을 통해 전달됩니다. 이 채널은 일반적으로 해당 채널에서 구현하는 프로토콜에 따라 System.ServiceModel.Channels.Message 개체의 Headers 컬렉션에 System.ServiceModel.Channels.MessageHeader 개체를 추가합니다. 일련의 채널에서 마지막 채널은 항상 전송 채널입니다. 전송 채널은 바인딩으로도 성격이 결정되는 인코더를 사용하여 System.ServiceModel.Channels.Message 개체를 전송 채널에서 서버로 전송하는 바이트 스트림으로 serialize합니다. 서버의 수신기는 바이트 스트림을 받은 다음 인코더를 사용하여 바이트 스트림을 System.ServiceModel.Channels.Message 개체로 deserialize합니다. 그 다음 일반적으로 클라이언트의 채널과 일치하는 일련의 채널을 통해 해당 개체가 전달됩니다. 그런 다음 System.ServiceModel.Channels.Message 개체가 System.ServiceModel.Dispatcher.DispatchRuntime 개체로 전달됩니다. System.ServiceModel.Dispatcher.DispatchRuntime 개체는 호출될 서비스 형식의 메서드를 결정하고 System.ServiceModel.Channels.Message 개체의 데이터를 해당 메서드에서 매개 변수로 예상하는 형식의 인스턴스로 deserialize하고 해당 메서드를 호출합니다.

Windows Communication Foundation에서 데이터 전송을 전달하게 되는 채널의 선택과 작업을 서비스 바인딩을 통해 사용자 지정할 수 있을 뿐만 아니라 사용자 지정 채널도 손쉽게 추가할 수 있습니다. 또한 프록시 및 System.ServiceModel.Dispatcher.DispatchRuntime 작업도 이러한 동작을 통해 조정하거나 전체적으로 사용자 지정할 수 있습니다. 즉, ASP.NET에서는 웹 서비스 요청 처리기를 제어하기 위해 많은 특성을 제공하는 반면, Windows Communication Foundation에서는 이와 비슷한 특성 집합뿐만 아니라 프록시 및 System.ServiceModel.Dispatcher.DispatchRuntime을 제어하기 위한 사용자 지정 코드 형식의 스와핑 옵션도 제공합니다.

System.ServiceModel.Dispatcher.DispatchRuntime은 요청을 처리하기 위해 호출되는 서비스 형식의 메서드를 결정할 때 SOAPAction 헤더를 사용합니다. 기본적으로 Windows Communication Foundation 작업에 대한 SOAPAction 헤더는 서비스의 네임스페이스, 서비스 계약 이름, 작업 이름 순서로 구성됩니다. 서비스 계약의 기본 네임스페이스는 http://tempuri.org/입니다. 서비스 계약의 기본 이름은 해당 서비스 계약을 정의하는 데 사용되는 인터페이스 또는 클래스의 이름이며, 작업의 기본 이름은 해당 작업을 구현하는 메서드의 이름입니다. 다음은 작업 예제입니다.

[ServiceContract]
public interface IDerivativesCalculator
{
    [OperationContract]
    decimal CalculateDerivative(
        string[] symbols,
        decimal[] parameters,
        string[] functions);
}

위 작업의 경우 SOAPAction 헤더는 http://tempuri.org/IDerivativesCalculator/CalculateDerivative가 됩니다. 다음과 같이 네임스페이스와 서비스 계약의 이름은 System.ServiceModel.ServiceContract 특성의 Namespace 및 Name 매개 변수를 사용하여 기본 이름을 변경할 수 있으며, 작업 이름은 System.ServiceModel.OperationContract 특성의 Name 매개 변수를 사용하여 기본 이름을 변경할 수 있습니다.

[ServiceContract(Namespace="OtherNamespace",Name="OtherContractName"]
public interface IDerivativesCalculator
{
    [OperationContract(Name="OtherOperationName")]
    decimal CalculateDerivative(
        string[] symbols,
        decimal[] parameters,
        string[] functions);
}

System.ServiceModel.Message 개체의 데이터를 deserialize할 때 System.ServiceModel.Dispatcher.DispatchRuntime은 기본적으로 System.Runtime.Serialization.DataContractFormatter를 사용합니다. XML 요소의 이름을 deserialize되는 클래스와 일치시키기 위한 System.ServiceModel.DataContractSystem.ServiceModel.DataMember 특성의 Namespace 및 Name 매개 변수의 메커니즘에 관해서는 이미 설명했습니다.

다음과 같이 System.ServiceModel.XmlSerializerFormat 특성을 적용함으로써 System.ServiceModel.Dispatcher.DispatchRuntime에서 System.Xml.Serialization.XmlSerializer를 사용하도록 만들 수 있습니다.

[ServiceContract, XmlSerializerFormat]
public interface IEcho

이 특성은 서비스의 개별 작업에도 적용할 수 있습니다.

deserialization에 System.Runtime.Serialization.DataContractFormatter가 사용되고 있는 경우 System.ServiceModel.DataContractFormat 특성을 사용하여 데이터가 문서 스타일 과 RPC 스타일 중 어느 것으로 예상되는지 제어할 수 있습니다. 이 특성은 다음과 같이 서비스 계약 전체 또는 서비스 계약의 개별 작업에 적용할 수 있습니다.

[ServiceContract]
public interface IItemService
{
    [OperationContract]
    [DataContractFormat(Style=OperationFormatStyle.Rpc)]
    public void DeliverItem(ItemMessage itemMessage);
}

Windows Communication Foundation 채택을 위한 사전 준비: 향후 통합 간소화

현재 ASP.NET을 사용 중이고 앞으로 Windows Communication Foundation을 사용할 예정이라면 새 ASP.NET 웹 서비스가 Windows Communication Foundation 응용 프로그램과 올바르게 작동하도록 하기 위해 다음과 같은 지침을 따릅니다.

일반 권장 사항

새로운 모든 서비스에 대해 ASP.NET 2.0을 채택합니다. ASP.NET 2.0을 채택하면 물론 새 버전의 개선된 기능과 향상된 기능을 사용할 수 있으며 같은 응용 프로그램에서 ASP.NET 2.0 구성 요소를 Windows Communication Foundation 구성 요소와 함께 사용할 수 있게 됩니다.

프로토콜

WS-I Basic Profile 1.1에 대한 부합 여부를 검증하기 위한 ASP.NET 2.0의 새로운 기능을 사용합니다.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(
    ConformsTo = WsiProfiles.BasicProfile1_1,
    EmitConformanceClaims=true)]
public interface IEcho

이 프로필을 따르는 ASP.NET 웹 서비스는 일반적으로 상호 운용성이 더 높고, 미리 정의된 Windows Communication Foundation 바인딩인 System.ServiceModel.BasicHttpBinding을 통해 Windows Communication Foundation 클라이언트와 상호 운용이 가능합니다.

서비스 개발

메시지를 SOAPAction HTTP 헤더가 아닌 SOAP 메시지 본문 요소의 정규화된 이름을 기반으로 발송하려면 System.Web.Services.Protocols.SoapDocumentService 특성을 사용하지 마십시오. Windows Communication Foundation은 SOAPAction HTTP 헤더를 사용하여 메시지를 메서드로 발송합니다.

데이터 표현

System.Xml.Serialization.XmlSerializer에서 기본적으로 형식을 serialize하는 XML은 해당 XML의 네임스페이스가 명시적으로 정의되어 있는 경우 System.RuntimeSerialization.DataContractFormatter에서 형식을 serialize하는 XML과 의미상 동일하다는 점을 상기하십시오. 따라서 앞으로 Windows Communication Foundation을 채택할 예정이면서 현재 ASP.NET 웹 서비스에 사용할 데이터 형식을 정의하는 경우에는 다음을 수행하십시오.

  1. XML 스키마가 아닌 .NET 클래스를 사용하여 데이터 형식을 정의합니다.
  2. System.Serializable 특성 및 System.Xml.Serialization.XmlRoot 특성만 클래스에 추가하고, 후자를 사용하여 형식에 대한 네임스페이스를 명시적으로 정의합니다. .NET 클래스가 XML로 변환되는 방식을 제어하기 위해 System.Xml.Serialization 네임스페이스의 다른 특성을 추가하지 않습니다.

이러한 접근 방식을 채택하면 .NET 클래스가 전송을 위해 serialize되는 XML을 많은 부분 변경하지 않고도 System.Runtime.Serialization.DataContractSystem.Runtime.Serialization.DataMember 특성을 추가하여 나중에 .NET 클래스를 데이터 계약으로 만들 수 있습니다. 그러면 ASP.NET 웹 서비스에서 메시지에 사용되는 동일한 형식이 Windows Communication Foundation 응용 프로그램에서 데이터 계약으로 처리될 수 있으며, 이는 다른 이점과 함께 Windows Communication Foundation 응용 프로그램의 성능 향상이라는 이점을 제공합니다.

보안

IIS의 인증 옵션을 사용하지 마십시오. Windows Communication Foundation 클라이언트에서 지원하지 않습니다. Windows Communication Foundation은 표준 프로토콜에 기반한 더욱 풍부한 옵션을 제공하므로 서비스를 보안해야 하는 경우 즉시 Windows Communication Foundation을 채택해야 합니다.


Windows Communication Foundation 채택을 위한 사전 준비: 향후 마이그레이션 간소화

앞으로 새로운 ASP.NET 응용 프로그램을 Windows Communication Foundation으로 더 쉽게 마이그레이션하려면 앞서 설명한 권장 사항과 다음 권장 사항을 모두 따르십시오.

프로토콜

SOAP 1.2에 대한 ASP.NET 2.0 지원을 사용하지 않습니다.

<configuration>
    <system.web>
        <webServices >
            <protocols>
                <remove name="HttpSoap12"/>
            </protocols>     
        </webServices>
    </system.web>  
</configuration>

Windows Communication Foundation에서는 SOAP 1.1, SOAP 1.2와 같은 별개의 프로토콜을 따르는 메시지가 각각 별개의 끝점을 거치도록 요구하기 때문에 SOAP 1.2에 대한 ASP.NET 2.0 지원은 사용하지 않는 것이 좋습니다. ASP.NET 2.0 웹 서비스가 기본 구성대로 SOAP 1.1과 SOAP 1.2를 모두 지원하도록 구성되면 ASP.NET 웹 서비스의 기존 클라이언트 모두와 호환되는 원래 주소의 단일 Windows Communication Foundation 끝점으로 마이그레이션할 수 없습니다.

서비스 개발

  • Windows Communication Foundation을 사용하면 인터페이스 또는 클래스에 System.ServiceModel.ServiceContract 특성을 적용하여 서비스 계약을 정의할 수 있습니다. 이 특성을 인터페이스에 적용하면 원하는 수의 클래스에서 다양하게 구현할 수 있는 데이터 계약 정의가 생성되므로 클래스보다는 인터페이스에 적용하는 것이 좋습니다. ASP.NET 2.0은 클래스뿐만 아니라 인터페이스에도 System.Web.Services.WebService 특성을 적용하는 옵션을 지원합니다. 그러나 앞서 설명했듯이 ASP.NET 2.0에는 System.Web.Services.WebService 특성이 클래스가 아닌 인터페이스에 적용될 때는 이 특성의 Namespace 매개 변수가 아무런 효과도 없다는 결점이 있습니다. 일반적으로 System.Web.Services.WebService 특성의 Namespace 매개 변수를 사용하여 서비스의 네임스페이스에 대한 기본값(http://tempuri.org)을 수정하는 것이 좋기 때문에 인터페이스 또는 클래스에 System.ServiceModel.ServiceContract 특성을 적용하여 ASP.NET 웹 서비스를 계속 정의해야 합니다.
  • 인터페이스를 정의하는 메서드에 최소한의 코드만 사용합니다. 메서드의 작업을 다른 클래스에 위임하도록 합니다. 그러면 새로운 Windows Communication Foundation 서비스 형식에서도 상당한 작업을 해당 클래스로 간단하게 위임할 수 있습니다.
  • System.Web.Services.WebMethod 특성의 MessageName 매개 변수를 사용하여 서비스 작업에 명시적 이름을 제공합니다.

    [WebMethod(MessageName="ExplicitName")]
    string Echo(string input);

    ASP.NET에서 작업의 기본 이름은 Windows Communication Foundation에서 제공하는 기본 이름과 다르기 때문에 이렇게 하는 것이 중요합니다. 명시적 이름을 제공함으로써 기본 이름을 사용하지 않아도 됩니다.

  • 다형적 메서드로 ASP.NET 웹 서비스 작업을 구현하지 마십시오. Windows Communication Foundation에서는 다형적 메서드로 작업을 구현하는 것을 지원하지 않습니다.
  • System.Web.Services.Protocols.SoapDocumentMethod 특성을 사용하여 HTTP 요청을 메서드로 라우팅할 SOAPAction HTTP 헤더에 명시적 값을 제공합니다.

    [WebMethod]
    [SoapDocumentMethod(RequestElementName="ExplicitAction")]
    string Echo(string input);

  • 이 접근 방식을 취할 경우 ASP.NET에서 사용되는 기본 SOAPAction 값을 사용하지 않아도 되며 Windows Communication Foundation에서도 마찬가지입니다.
  • SOAP 확장을 사용하지 마십시오. SOAP 확장이 필요한 경우에는 고려하고 있는 SOAP 확장의 목적이 이미 Windows Communication Foundation에서 제공되는 기능인지 확인하십시오. 이 경우 당장은 Windows Communication Foundation을 채택하지 않는다는 방침을 재고하십시오.

상태 관리

서비스의 상태를 유지하지 마십시오. 상태를 유지하는 것은 응용 프로그램의 확장성을 떨어뜨리는 경향이 있을 뿐만 아니라, Windows Communication Foundation에서 ASP.NET 호환 모드를 통해 ASP.NET 메커니즘을 지원하지만 ASP.NET과 Windows Communication Foundation의 상태 관리 메커니즘은 매우 다릅니다.

예외 처리

서비스에서 송수신할 데이터 형식의 구조를 디자인할 때 클라이언트로 전달하려는 서비스에서 발생할 수 있는 다양한 종류의 예외를 표시하기 위한 구조도 디자인합니다.

[Serializable]
[XmlRoot(
    Namespace="ExplicitNamespace", IsNullable=true)]
public partial class AnticipatedException {
   
    private string anticipatedExceptionInformationField;
   
    public string AnticipatedExceptionInformation {
        get {
            return this.anticipatedExceptionInformationField;
        }
        set {
            this.anticipatedExceptionInformationField = value;
        }
    }
}

다음과 같이 해당 클래스에 자신을 XML로 serialize할 수 있는 기능을 부여합니다.

public XmlNode ToXML()
{
    XmlSerializer serializer = new XmlSerializer(
        typeof(AnticipatedException));
    MemoryStream memoryStream = new MemoryStream();
    XmlTextWriter writer = new XmlTextWriter(
        memoryStream, UnicodeEncoding.UTF8);
    serializer.Serialize(writer, this);
    XmlDocument document = new XmlDocument();
    document.LoadXml(new string(
        UnicodeEncoding.UTF8.GetChars(memoryStream.GetBuffer())).Trim());
    return document.DocumentElement;
}

그러면 해당 클래스를 사용하여 명시적으로 발생된 System.Web.Services.Protocols.SoapException 인스턴스에 자세한 내용을 제공할 수 있습니다.

AnctipatedException exception = new AnticipatedException();
exception.AnticipatedExceptionInformation = "...";
throw new SoapException(
   "Fault occurred",
   SoapException.ClientFaultCode,
   Context.Request.Url.AbsoluteUri,
   exception.ToXML());

이러한 예외 클래스는 다음과 같이 Windows Communication Foundation의 System.ServiceModel.FaultContract<T>에서 즉시 다시 사용할 수 있습니다.

throw new FaultException<AnticipatedException>(anticipatedException);

보안

  • ASP.NET 2.0 프로필을 사용하지 마십시오.
  • 서비스에 대한 액세스를 제어하기 위해 ACL을 사용하지 마십시오.
  • ASP.NET 2.0 역할 공급자를 사용하여 서비스의 리소스에 대한 액세스를 인증하는 방법을 고려합니다.
Windows Communication Foundation 채택

ASP.NET 웹 서비스와의 공존

ASP.NET으로 개발된 기존 응용 프로그램을 계속 유지하면서 새로운 개발에는 Windows Communication Foundation을 사용하는 방법을 선택할 수 있습니다. Windows Communication Foundation은 모든 시나리오에서 .NET 응용 프로그램과의 원활한 통신을 위한 최적의 선택이 되도록 고안되었기 때문에 ASP.NET으로는 수행할 수 없었던 방식으로 다양한 소프트웨어 통신 문제를 해결하는 표준 도구로 사용할 수 있습니다.

새로운 Windows Communication Foundation 응용 프로그램은 기존 ASP.NET 웹 서비스와 동일한 컴퓨터에 배포될 수 있습니다. 이러한 ASP.NET 웹 서비스에서 버전 2.0 이전의 .NET 버전을 사용하는 경우 ASP.NET IIS 등록 도구를 사용하여 새 Windows Communication Foundation 응용 프로그램을 호스트할 IIS 응용 프로그램에 .NET Framework 2.0을 선택적으로 배포할 수 있습니다. ASP.NET IIS Registration Tool (Aspnet_regiis.exe) (영문) 문서를 참조하십시오. 이 도구에는 IIS 6 관리 콘솔에 기본적으로 제공되는 직관적인 사용자 인터페이스가 있습니다.

Windows Communication Foundation은 사용하여 ASP.NET 호환 모드에서 실행되도록 구성된 Windows Communication Foundation 서비스를 IIS의 기존 ASP.NET 웹 서비스 응용 프로그램에 추가함으로써 기존 ASP.NET 웹 서비스에 새 기능을 추가할 수 있습니다. ASP.NET 호환 모드 덕분에 새로운 Windows Communication Foundation 서비스의 코드는 System.Web.HttpContext 클래스를 통해 기존 ASP.NET 코드와 동일한 응용 프로그램 상태 정보를 액세스 및 업데이트할 수 있으며 동일한 클래스 라이브러리를 공유할 수 있습니다.

ASP.NET 웹 서비스와 클라이언트를 Windows Communication Foundation으로 마이그레이션

Windows Communication Foundation 클라이언트는 ASP.NET 웹 서비스를 사용할 수 있습니다. System.ServiceModel.BasicHttpBinding으로 구성되는 Windows Communication Foundation 서비스는 ASP.NET 웹 서비스 클라이언트에서 사용할 수 있습니다. ASP.NET 웹 서비스는 Windows Communication Foundation 응용 프로그램과 공존할 수 있으며, Windows Communication Foundation을 사용하여 기존 ASP.NET 웹 서비스에 기능을 추가할 수도 있습니다. Windows Communication Foundation과 ASP.NET 웹 서비스를 함께 사용할 수 있는 이러한 모든 방식을 고려해 보면 ASP.NET 웹 서비스를 Windows Communication Foundation으로 마이그레이션해야 하는 필연적인 이유는 거의 없습니다.

마이그레이션이 필요한 것으로 보이는 일부 경우에서도, 단순히 한 기술에서 다른 기술로 코드를 마이그레이션하는 것은 대부분 올바른 접근 방식이 아니라는 점을 신중하게 고려하십시오. 새로운 기술을 채택하는 이유는 이전 기술로는 따라갈 수 없는 새로운 요구 사항을 충족하기 위한 것이며, 이러한 경우 새로 확장된 요구 사항을 충족하는 새로운 솔루션을 디자인하는 것이 올바른 방법입니다. 새 디자인은 기존 시스템에서의 경험과 해당 시스템이 디자인된 이후 축적된 지식을 활용할 수 있습니다. 또한 새 디자인은 단순히 새 플랫폼에서 이전 디자인을 재생산하는 것이 아니라 새 기술의 완전한 기능을 충분히 고려할 것입니다. 새 디자인의 주요 요소를 프로토타이핑하면 대개 새 시스템에서 기존 시스템의 코드를 다시 사용하는 방법을 명확하게 알 수 있습니다.

단순히 ASP.NET 웹 서비스에서 Windows Communication Foundation으로 이식하는 것이 올바른 솔루션으로 생각되는 일부 경우에 대한 몇 가지 진행 방법 지침이 있습니다. 서비스를 마이그레이션하는 방법과 클라이언트를 마이그레이션하는 방법에 대한 권장 사항이 있습니다.

ASP.NET 웹 서비스를 Windows Communication Foundation으로 마이그레이션

  1. 서비스에 대한 포괄적인 테스트 집합이 있는지 확인합니다.
  2. 서비스에 대한 WSDL을 생성하고 해당 서비스의 .asmx 파일과 같은 폴더에 복사본을 저장합니다.
  3. .NET 2.0을 사용하도록 ASP.NET 웹 서비스를 업그레이드합니다. 먼저 .NET Framework 2.0을 IIS의 응용 프로그램에 배포한 다음 Visual Studio 2005를 사용하여 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/webprojectsVS05.asp?frame=true(영문) 문서의 설명에 따라 코드 변환 프로세스를 자동화함으로써 업그레이드를 수행합니다. 테스트 집합을 실행합니다.
  4. System.Web.Services.WebService 특성의 Namespace 및 Name 매개 변수의 값이 아직 제공되지 않았으면 이 매개 변수들에 명시적 값을 제공합니다. System.Web.Services.WebMethod 특성의 MessageName 매개 변수에도 같은 작업을 수행합니다. 또한 요청을 메서드로 라우팅하는 SOAPAction HTTP 헤더에 명시적 값이 아직 제공되지 않았으면 다음과 같이 System.Web.Services.Protocols.SoapDocumentMethod 특성으로 Action 매개 변수의 기본값을 명시적으로 지정합니다.

    [WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
    public class Adder
    {
        [WebMethod(MessageName = "Add")]
        [SoapDocumentMethod(Action = "http://tempuri.org/Add")]
        public double Add(SumInput input)
        {
            double sum = 0.00;
            foreach (double inputValue in input.Input)
            {
                sum += inputValue;
            }
            return sum;
        }
    }

  5. 테스트 집합을 실행합니다.
  6. 클래스의 메서드 본문에 있는 코드를 원래 클래스에서 사용하도록 만들어진 별개의 클래스로 옮깁니다.

    [WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
    public class Adder
    {
        [WebMethod(MessageName = "Add")]
        [SoapDocumentMethod(Action = "http://tempuri.org/Add")]
        public double Add(SumInput input)
        {
            return new ActualAdder().Add(input);
        }
    }

    internal class ActualAdder
    {
        internal double Add(SumInput input)
        {
            double sum = 0.00;
            foreach (double inputValue in input.Input)
            {
                sum += inputValue;
            }
            return sum;
        }
    }

  7. 테스트 집합을 실행합니다.
  8. Windows Communication Foundation 어셈블리인 System.ServiceModelSystem.Runtime.Serialization에 대한 참조를 ASP.NET 웹 서비스 프로젝트에 추가합니다.
  9. Windows Communication Foundation의 svcutil.exe 도구를 실행하여 WSDL에서 Windows Communication Foundation 프록시 클래스를 생성합니다. 생성된 클래스 모듈을 솔루션에 추가합니다.
  10. 앞 단계에서 생성된 클래스 모듈에는 인터페이스 정의가 포함됩니다.

    [System.ServiceModel.ServiceContractAttribute()]
    public interface AdderSoap
    {
        [System.ServiceModel.OperationContractAttribute(
          Action="http://tempuri.org/Add",
          ReplyAction="http://tempuri.org/Add")]
        AddResponse Add(AddRequest request);
    }
    Modify the definition of the ASP.NET Web service class so that the class is defined as implementing that interface:
    [WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
    public class Adder: AdderSoap
    {
        [WebMethod(MessageName = "Add")]
        [SoapDocumentMethod(Action = "http://tempuri.org/Add")]
        public double Add(SumInput input)
        {
            return new ActualAdder().Add(input);
        }

       
        public AddResponse Add(AddRequest request)
        {
            return new AddResponse(new AddResponseBody(this.Add(request.Body.input)));
        }
    }

  11. 프로젝트를 컴파일합니다. 9단계에서 생성된 코드 때문에 일부 형식 정의가 중복되는 오류가 있을 수 있습니다. 일반적으로 형식에 대한 기존 정의를 삭제하여 이러한 오류를 수정합니다. 테스트 집합을 실행합니다.
  12. System.Web.Services.WebService, System.Web.Services.WebMethod, System.Web.Services.Protocols.SoapDocumentMethod 특성 등 ASP.NET 고유의 특성을 제거합니다.

    public class Adder: AdderSoap
    {
        public double Add(SumInput input)
        {
            return new ActualAdder().Add(input);
        }

       
        public AddResponse Add(AddRequest request)
        {
            return new AddResponse(new AddResponseBody(this.Add(request.Body.input)));
        }
    }

  13. Windows Communication Foundation 서비스 형식이 될 클래스를 구성하여 ASP.NET 웹 서비스가 다음 항목을 사용하는 경우 Windows Communication Foundation의 ASP.NET 호환 모드를 요구하도록 합니다.
    • System.Web.Services.HttpContext 클래스
    • ASP.NET 프로필
    • .asmx 파일의 ACL
    • IIS 인증 옵션
    • ASP.NET 가장 옵션
    • ASP.NET 전역화
    [System.ServiceModel.AspNetCompatibilityRequirements(
           RequirementsMode=AspNetCompatbilityRequirementsMode.Require)]
    public class Adder: AdderSoap

  14. 원래의 .asmx 파일 이름을 .asmx.old로 바꿉니다.
  15. 서비스에 대한 Windows Communication Foundation 서비스 파일을 만들고, 이 파일의 확장명을 .asmx로 지정한 다음 IIS의 응용 프로그램 루트에 저장합니다.

    <%@Service Class="MyOrganization.Adder" %>
    <%@Assembly Name="MyServiceAssembly" %>

  16. 서비스에 대한 Windows Communication Foundation 구성을 Web.config 파일에 추가합니다. BasicHttpBinding을 사용하고, 앞 단계에서 만든 .asmx 확장명의 서비스 파일을 사용하고, 자체적으로 WSDL을 생성하지 않고 2단계에서 생성된 WSDL을 사용하도록 서비스를 구성합니다. 또한 위 10단계에서 필요하다고 판단된 경우 ASP.NET 호환 모드를 사용하도록 서비스를 구성합니다.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
       <system.web>
          <compilation>
             <buildProviders>
    <remove extension=".asmx" />
                <add extension=".asmx"
                   type=
    "System.ServiceModel.ServiceBuildProvider, System.ServiceModel, Version=2.0.0.0,
                Culture=neutral,
                PublicKeyToken=b77a5c561934e089" />
             </buildProviders>
          </compilation>
       </system.web>
       <system.serviceModel>
          <services>
             <service name="MyOrganization.Adder "
                      behaviorConfiguration="AdderBehavior">
    <endpoint
    address=""
    binding="basicHttpBinding"
    contract="AdderSoap "/>
             </service>
          </services>
          <behaviors>
               <serviceBehaviors>
             <behavior name="AdderBehavior">
                <metadataPublishing
                   enableMetadataExchange="true"
                   enableGetWsdl="true"
                   enableHelpPage="true"
    metadataLocation=
    "http://MyHost.com/AdderService/Service.wsdl"/>
             </behavior>
            </serviceBehaviors>
          </behaviors>
          <serviceHostingEnvironment
    aspNetCompatibilityEnabled ="true"/>
       </system.serviceModel>
    </configuration>

  17. 구성을 저장합니다.
  18. 프로젝트를 컴파일합니다.
  19. 테스트 집합을 실행합니다.

ASP.NET 웹 서비스 클라이언트를 Windows Communication Foundation으로 마이그레이션

  1. 클라이언트에 대한 포괄적인 테스트 집합이 있는지 확인합니다.
  2. Visual Studio 2005를 사용하여 클라이언트 응용 프로그램을 .NET 2.0으로 업그레이드합니다. 테스트 집합을 실행합니다.
  3. 클라이언트 프로젝트에서 ASP.NET 프록시 코드를 제거합니다. 이 코드는 대개 wsdl.exe 도구를 사용하여 생성된 모듈에 있습니다.
  4. svcutil.exe 도구를 사용하여 Windows Communication Foundation 프록시 코드를 생성합니다. 이 코드를 클라이언트 프로젝트에 추가하고 구성 출력을 클라이언트의 기존 구성 파일에 병합합니다.
  5. 응용 프로그램을 컴파일합니다. 기존 ASP.NET 프록시 형식에 대한 참조를 새 Windows Communication Foundation 프록시 형식에 대한 참조로 바꾸어 컴파일 오류를 수정합니다.
  6. 테스트 집합을 실행합니다.
요약

ASP.NET 웹 서비스 도구는 단지 웹 서비스를 빌드하기 위한 것이지만, Windows Communication Foundation은 소프트웨어 엔터티들이 서로 통신하도록 설정되어야 하는 모든 환경에서 사용할 수 있는 도구를 제공합니다. 웹 서비스 개발 프로젝트에서도 Windows Communication Foundation은 ASP.NET 웹 서비스에서 지원하는 것보다 더 많은 웹 서비스 프로토콜을 지원합니다. 이러한 프로토콜은 신뢰할 수 있는 세션과 트랜잭션을 비롯한 여러 가지 장점을 수반하는, 더욱 정교한 솔루션을 제공합니다. 대부분의 경우 권장되는 작업 과정은 기존 ASP.NET 웹 서비스 응용 프로그램을 계속 유지하면서 새로운 개발에는 Windows Communication Foundation을 채택하는 것입니다. 이러한 작업 과정은 Windows Communication Foundation의 이점을 누리면서 기존 응용 프로그램을 마이그레이션해야 하는 비용을 절약해 줍니다. 새 Windows Communication Foundation 응용 프로그램은 기존 ASP.NET 웹 서비스를 사용할 수 있으며 기존 ASP.NET 응용 프로그램과 공존할 수 있습니다. Windows Communication Foundation의 ASP.NET 호환 모드 덕분에 Windows Communication Foundation을 사용하여 기존 ASP.NET 응용 프로그램에 새로운 작업 기능을 프로그래밍할 수도 있습니다.

출처 : 한국 마이크로소프트 MSDN (2006년 7월)

Posted by 상현넘™

댓글을 달아 주세요