EndPoint 의 추가
이젠 개략적으로나마 WCF를 이용하여 서비스를 개발하고 클라이언트를 구현하는 방법을 알게 되었을 것이다. 약간 더 욕심을 내서 WCF의 다양한 기능을 좀 더 살펴보도록 하자. 이번에는 [그림 2]에서 보였듯이 하나의 서비스가 여러 개의 서비스 종점을 갖는 경우를 구현해 보도록 하자.
서비스가 여러 종점을 갖도록 하는 것은 뭐 별로 어려울 것이 없다. 단순히 서비스 호스트에 ServiceEndPoint를 하나 더 추가해주는 것일 뿐이다. 종점을 추가하기 위해서는 적어도 이미 추가해 놓은 종점과는 다른 종점이 되어야 할 것이다. 기존에 추가된 종점에 비해 새로이 추가되는 서비스 종점은 서비스 계약이 다르던가, 주소가 다르던가 혹은 바인딩이 달라야만 한다. 이는 서비스 호스트의 리스너(listener)가 종점을 구별하기 위한 최소의 조건이기 때문이다. 서비스 종점을 호스트에 추가하는 것 외에 서비스 계약이 변경된다던가 서비스 구현을 바꾸어야 할 일은 전혀 없다는 점도 주목할 만 하다.
우리의 예제에서는 다른 바인딩을 사용해 보도록 하겠다. BasicHttpBinding이 HTTP 프로토콜을 사용하는 가장 기본적인 바인딩이라고 하면 NetTcpBinding은 TCP/IP을 기반으로 하여 WCF의 트랜잭션, 콜백, 보안 등의 모든 기능을 다 갖추고 있으면서도 성능 역시 좋은 바인딩이다. 단점이라면 서비스와 클라이언트가 모두 WCF 기반이어야 하기 때문에 상호운영성(interoperability)이 떨어진다는 점 정도일 것이다.
백문이 불여일견이라 했다. 실제 코드를 보고 확인을 해보자. [리스트 3]의 호스트 구현을 다음과 같이 수정하면 새로운 서비스 종점을 추가할 수 있다.
리스트 5. NetTcpBinding을 사용하는 서비스 종점 추가
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(HelloWorldWCFService),
new Uri("http://localhost/wcf/example/helloworldservice"),
new Uri("net.tcp://localhost/wcf/example/helloworldservice"));
host.AddServiceEndpoint(
typeof(IHelloWorld), // service contract
new BasicHttpBinding(), // service binding
""); // relative address
host.AddServiceEndpoint(
typeof(IHelloWorld), // service contract
new NetTcpBinding(), // service binding
""); // relative address
host.Open();
Console.WriteLine("Press Any key to stop the service");
Console.ReadKey();
host.Close();
}
[리스트 5]에서 주목할 부분은 ServiceHost 클래스의 인스턴스를 생성할 때 설정하는 서비스의 베이스 주소가 두 개라는 점이다. HTTP 프로토콜을 사용하는 바인딩은 인터넷 URI를 사용하는 반면 NetTcpBinding을 사용하는 경우 URI의 스킴(scheme)이 net.tcp로 시작하기 때문에 별도의 베이스 주소를 명시한 것이다 . [리스트 5]에서 또 한가지 주목할 부분은 AddServiceEndpoint 메쏘드를 한번 더 호출하여 NetTcpBinding을 사용하는 서비스 종점을 추가한 것이다.
NetTcpBinding은 별도로 TCP 포트를 지정해 주지 않으면 808 포트를 사용하도록 되어 있다
이제 서비스가 2개의 종점을 가지고 있으므로 HTTP를 기반으로 하는 클라이언트의 호출과 TCP를 기반으로 하는 클라이언트의 호출을 모두 수신할 수 있게 되었다. 클라이언트가 TCP를 이용하여 서비스를 호출하고자 한다면 ServiceEndpoint 객체를 생성할 때 NetTcpBinding을 사용해야 하며 서비스 종점의 주소 역시 TCP 트랜스포트에 맞도록 조정해 주어야 한다. [리스트 4]의 클라이언트 코드에 TCP를 사용하는 코드를 추가하여 수정해 보자.
리스트 6. HTTP와 TCP를 모두 사용하는 클라이언트 예제
static void Main(string[] args)
{
InvokeUsingHTTP();
InvokeUsingTCP();
}
static void InvokeUsingHTTP()
{
Uri uri = new Uri("http://localhost/wcf/example/helloworldservice");
ServiceEndpoint ep = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IHelloWorld)),
new BasicHttpBinding(),
new EndpointAddress(uri));
ChannelFactory<IHelloWorld> factory = new ChannelFactory(ep);
IHelloWorld proxy = factory.CreateChannel();
string result = proxy.SayHello();
(proxy as IDisposable).Dispose();
Console.WriteLine(result);
}
static void InvokeUsingTCP()
{
Uri uri = new Uri("net.tcp://localhost/wcf/example/helloworldservice");
ServiceEndpoint ep = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IHelloWorld)),
new NetTcpBinding(),
new EndpointAddress(uri));
ChannelFactory factory = new ChannelFactory(ep);
IHelloWorld proxy = factory.CreateChannel();
string result = proxy.SayHello();
(proxy as IDisposable).Dispose();
Console.WriteLine(result);
}
[리스트 6]의 InvokeUsingTcp 메쏘드에서 NetTcpBinding이 사용되었으며 서비스의 주소 역시 [리스트 5]에서 사용한 TCP 기반의 주소가 사용되었음을 확인하도록 하자.
자, 그렇다면 하나의 서비스가 2개 이상의 종점이 필요한 이유에 대해 잠시 생각을 해보자. [리스트 6]과 같이 하나의 클라이언트가 2개의 바인딩을 사용할 이유는 전혀 없다고 봐도 무방하다. 대개의 경우 클라이언트는 하나의 바인딩을 사용하여 서비스를 호출하기 마련이기 때문이다. 굳이 복잡하게 바인딩과 주소를 바꾸어가며 서비스를 호출할 이유는 아무리 억지를 부리더라도 찾기 어렵지 않은가?
서비스는 다양한 클라이언트의 호출을 수신하고 처리할 능력을 갖추는 것이 좋다. 그것이 WCF 기반의 닷넷 클라이언트이건 WCF가 배제된 닷넷 클라이언트이건 심지어 닷넷이 아닌 C/C++ 혹은 Java 클라이언트이건 말이다. 또한 WCF 서비스가 공용 인터넷을 통해 서비스를 제공할 수도 있기 때문에 방화벽을 손쉽게 통과하여 서비스 호출이 이루어 질 수 있는 것 역시 중요하다. 이 정도로 힌트를 주었으면 서비스가 왜 2개 이상의 서비스 종점을 제공하는 것이 의미를 갖는지 알 수 있을 것이다.
인트라넷 상의 WCF를 기반으로 하는 클라이언트는 상호운영성이나 방화벽 통과 보다는 성능적인 측면과 풍부한 기능을 갖는 프로토콜과 바인딩을 사용하는 것이 유리함은 두 말할 나위도 없다. 따라서 이러한 클라이언트는 성능 면에서 우수한 NetTcpBinding을 사용하는 것이 가장 좋을 것 아닌가? 반면에, 방화벽 바깥 세상에서 인트라넷 상에 존재하는 서비스를 호출하고자 한다면 방화벽 통과가 용이한 HTTP 프로토콜을 쓰는 것이 좋다. 비록 인트라넷 내에 존재하는 클라이언트일지라도 닷넷 플랫폼이 아닌 Java 클라이언트는 WCF가 사용하는 TCP 기반의 NetTcpBinding을 이해하지 못할 것이 너무나 자명하므로 순수 XML과 HTTP를 사용하는 BasicHttpBinding을 사용해야 할 것이다. 이러한 이유들 때문에 필요에 따라서 서비스가 다양한 바인딩을 사용하는 2개 이상의 종점을 제공하는 것이 충분한 의미를 갖게 되는 것이다. [그림 15]는 이와 같은 상황을 잘 보여주고 있다.
그림 15. 다양한 클라이언트와 서비스 종점의 관계
Service Address
NetTcpBinding이 등장했으니 서비스 주소에 대해 좀 더 자세히 이야기할 때가 왔다. 서비스의 주소는 바인딩이 어떤 트랜스포트(transport)를 사용하는가에 따라 달라진다. 갑자기 등장한 트랜스포트란 용어에 당황할 필요는 없다. 트랜스포트란 XML SOAP 메시지를 네트워크 상에서 전송해주는 매개체 프로토콜을 말한다. WCF에서 지원하는 트랜스포트는 HTTP, TCP, 명명된 파이프, MSMQ, P2P(Peer-to-Peer) 프로토콜이 있다. 이들 트랜스포트 외에도 UDP나 SMTP를 사용하는 트랜스포트를 사용하도록 커스터마이징이 가능하지만 상당한 코드를 작성해야만 한다. Windows SDK에는 UDP를 사용하는 예제 코드가 있으므로 관심 있는 독자들은 참고하기 바란다(미리 경고하지만 상당히 어려운 예제 코드이다).
HTTP 트랜스포트를 사용하는 경우 서비스의 주소는 http로 시작하게 되며, TCP의 경우 net.tcp로, 명명된 파이프는 net.pipe로, MSMQ의 경우 net.msmq로, P2P의 경우 net.p2p로 시작하는 주소를 서비스의 주소로 사용하게 된다. 서비스가 어떤 트랜스포트를 사용할 것인가에 대한 결정은 어떤 바인딩을 사용하느냐에 달려있다. 바인딩은 자신이 사용할 트랜스포트를 고정하고 있으므로 어떤 바인딩을 사용하는가에 따라 SOAP 메시지 전송에 사용될 트랜스포트가 결정된다. 다음 표는 WCF에서 기본으로 제공하는 바인딩들과 이들이 사용하는 트랜스포트를 보여준다.
트랜스포트
|
바인딩
|
Scheme
|
HTTP(S)
|
BasicHttpBinding, WSHttpBinding,
WSDualHttpBinding, WSFederationBinding
|
http 혹은 https
|
TCP
|
NetTcpBinding
|
net.tcp
|
Named Pipe
|
NetNamedPipeBinding
|
net.pipe
|
MSMQ
|
NetMsmqBinding, MsmqIntegrationBinding
|
net.msmq
|
P2P
|
NetPeerTcpBinding
|
net.p2p
|
이처럼 서비스 주소는 서비스의 종점이 사용하는 바인딩에 의존적으로 어떤 스킴(scheme)의 URI를 사용할 것인가가 결정되며 그 주소 설정 방법이 결정된다는 점을 알아두자. 구체적인 예를 들어보자면, NetNamedPipeBinding을 사용하는 서비스의 주소는 net.pipe://servername/mypipe/service.svc, net.pipe://localhost/wcf/example/helloworldservice 등이 될 수 있겠다. 각 트랜스포트에 따라서 서비스의 URI를 지정하는 몇 가지 규칙이 존재하지만 이들에 대해서는 5장에서 각 바인딩을 다룰 때 상세하게 이야기하도록 하겠다.
서비스를 구현하면서 서비스의 주소를 명시할 때는 서비스 호스트에 설정하는 베이스 주소로부터 상대적인 경로(relative path)를 줄 수도 있고, 베이스 주소와 무관하게 절대 URI를 명시할 수도 있다. 상대적인 경로를 사용하고자 하는 경우, ServiceHost 클래스의 생성자의 매개변수로서 베이스 주소를 명시하고 서비스의 종점을 추가할 때 명시된 베이스 주소로부터 상대 경로를 사용하면 된다. 예들 들어 다음과 같은 코드가 있을 때,
ServiceHost host = new ServiceHost(typeof(HelloWorldWCFService),
new Uri("http://localhost/wcf/example"));
host.AddServiceEndpoint(typeof(IHelloWorld), new BasicHttpBinding(),
"testservice.svc");
이 서비스에 추가된 종점의 서비스 주소는 http://localhost/wcf/example/testservice.svc 가 된다.
절대 경로를 URI로 사용하고자 한다면 서비스 종점을 추가 할 때 URI 주소 값으로 절대 경로를 사용하기만 하면 된다. 서비스 종점의 주소가 절대 경로로서 명시되면 베이스 주소는 무시되며 서비스 종점이 제공하는 주소를 사용하게 된다. 또한 모든 서비스의 종점이 절대 경로를 사용한다면 서비스 호스트에게 베이스 주소를 제공하지 않아도 된다. 베이스 주소를 제공하지 않을 때는 null 값을 사용하면 ArgumentNullException 이 발생하므로 다음과 같이 베이스 주소를 나타내는 매개변수를 생략해야만 한다.
// no base address
ServiceHost host = new ServiceHost(typeof(HelloWorldWCFService));
// ArgumentNullException condition !!!
ServiceHost host = new ServiceHost(typeof(HelloWorldWCFService), null);
베이스 주소는 호스트에서 호스팅 하는 서비스의 각 종점들이 사용하는 트랜스포트 마다 별도로 줄 수 있다. 즉, 서비스 호스트가 BasicHttpBinding을 사용하는 종점과 NetTcpBinding을 사용하는 종점을 모두 가지고 있다면 ServiceHost 클래스의 생성자를 호출할 때 HTTP 트랜스포트를 위한 베이스 주소와 TCP 트랜스포트를 위한 베이스 주소를 모두 줄 수 있다는 것이다. 주어진 베이스 주소의 순서와 상관 없이 WCF 런타임은 이들 베이스 주소를 트랜스포트 별로 기록해 둘 것이며 서비스 종점이 추가될 때 종점이 사용하는 바인딩에 따라 적절한 베이스 주소를 선택하여 사용한다. [리스트 5]에서 ServiceHost 객체를 생성할 때 http 와 net.tcp 두 개의 베이스 주소를 사용한 것을 볼 수 있을 것이다. 이는 BasicHttpBinding 과 같이 HTTP를 사용하는 서비스 종점과 NetTcpBinding 처럼 TCP를 사용하는 서비스 종점을 위한 베이스 주소이다.
베이스 주소를 사용하지 않고 절대 주소만을 사용하도록 [리스트 5]를 수정하면 다음과 같이 바꾸어 볼 수 있겠다. ServiceHost 클래스의 생성자에 어떤 Uri 객체도 매개변수로 넘겨주지 않았으며, AddServiceEndpoint 메쏘드 호출에서 서비스 주소를 모두 절대 주소로 사용했음에 주목하자. 하지만 이렇게 모든 서비스 종점에 절대 주소를 사용하는 것은 권장하지 않는다. 베이스 주소를 사용하면 서비스를 운영 환경에 배포할 때 베이스 주소만을 운영 환경에 맞추어 주면 되지만, 서비스 종점에 절대 주소를 사용하면 모든 서비스 종점의 주소를 운영 환경에 맞추어 수정해 주어야 하기 때문이다.
ServiceHost host = new ServiceHost(typeof(HelloWorldWCFService));
host.AddServiceEndpoint(
typeof(IHelloWorld), // service contract
new BasicHttpBinding(), // service binding
"http://localhost/wcf/example/helloworldservice");
host.AddServiceEndpoint(
typeof(IHelloWorld), // service contract
new NetTcpBinding(), // service binding
"net.tcp://localhost/wcf/example/helloworldservice");
바인딩에 대한 첨언
앞서 간략히 설명했던 바인딩에 대해서도 조금만 더 이야기 해보자. WCF에서 바인딩을 몇 마디로 설명하기란 쉽지 않은 일이다. 그래도 굳이 이야기 하자면 서비스와 클라이언트가 네트워크를 통해 통신하기 위해 필요한 트랜스포트 종류, 프로토콜 설정, 보안과 같은 특정 기능의 사용 여부 등을 명세 하는 통신 설명자(descriptor) 정도로 이야기 할 수 있겠다. 어려운 말은 그만하고, 쉽게 이야기 하자면 서비스와 클라이언트가 네트워크 상에서 어떻게 통신할 것을 기술하는 것이 되겠다.
네트워크 통신을 할 때 통신의 주체가 되는 양 종점(endpoint)은 다양한 고려사항과 옵션을 가지기 마련이다. TCP, UDP, HTTP 등 프로토콜 중 어떤 프로토콜을 사용할 것인가가 가장 기본적이며 전형적인 고려사항 중 하나일 것이다. 프로토콜을 결정한 후에도 네트워크 수준의 보안을 적용할 것인가도 결정해야 하며, 데이터를 주고 받을 때 사용할 데이터 포맷 역시 고민해야 한다. 좀 더 고급 기능을 요구하는 통신이라면 전송 측에서 보낸 데이터를 수신 측에서 수신했는가를 확인하고 싶을 수도 있다. 즉, 데이터 송수신에 대한 신뢰도를 보장하는 기능을 요구할 수 있다는 것이다. 필요에 따라서는 전송 측에서 시작한 트랜잭션(transaction)이 수신 측까지 전파되어 전송 측과 수신 측이 하나의 분산 트랜잭션으로 묶이기를 원할 수도 있다.
이처럼 네트워크 통신 상에서 요구되는 다양한 선택 사항과 몇몇 기능들을 WCF는 이미 프레임워크 수준에서 구현해 놓았기 때문에 개발자는 이들을 조합하여 웹 서비스 메시지 전송에 적용하면 되는 것이다. 이 때 WCF가 제공하는 트랜스포트 프로토콜, 보안, 메시지 인코딩(message encoding), 메시지 수준의 보안, 신뢰할 수 있는 메시징(reliable messaging), 트랜잭션 흐름(transaction flow) 등의 통신 요소들을 조합해 놓은 것을 WCF에서는 바인딩이란 용어를 사용한다고 보면 되겠다. [그림 16]은 이러한 바인딩의 개념을 보여주고 있다. [그림 16]에서 한 가지 놓쳐서는 안될 것은 서비스와 클라이언트가 동일한 바인딩을 사용해야만 한다는 점이다. 너무도 당연한 것이 통신을 수행하는 양측이 서로 네트워크 선택사항을 동의하지 않는다면 통신이 제대로 되지 않을 것이기 때문이다. 한 사람은 중국어를, 다른 사람은 아프리카 콩고 말을 한다면 당연히 의사 소통이 되지 않을 것 아닌가?
그림 16. 바인딩의 개념
바인딩은 특정 기능이 그 바인딩에 포함되어 있는가에 대한 여부와 각 기능이 선택할 수 있는 옵션을 포함한다. 예를 들어, 어떤 바인딩은 분산 트랜잭션을 아예 사용하지 못하는 반면 또 어떤 바인딩은 분산 트랜잭션을 사용할 수 있지만 다양한 분산 트랜잭션 프로토콜 중 하나만을 지원하거나 두 개 이상의 분산 트랜잭션 프로토콜 중 하나를 선택할 수도 있다. 이처럼 바인딩은 여러 바인딩 요소(binding element)들 중 몇 개의 조합으로 구성되고 각 바인딩 요소에는 그 바인딩 요소에서 선택할 수 있는 선택 사항들이 또 존재한다는 것이 되겠다.
필자의 설명이 좀 어려운가? 보다 구체적으로 예를 들어 보자. BasicHttpBinding은 HTTP 트랜스포트를 사용하는 기본적인 바인딩으로써 분산 트랜잭션이나 신뢰할 수 있는 메시징을 사용할 수 없다. 반면 NetTcpBinding은 트랜잭션과 신뢰할 수 있는 메시징을 모두 지원한다. 그리고 BasicHttpBinding과 NetTcpBinding은 메시징 인코딩을 사용하지만 BasicHttpBinding은 메시지 인코딩 옵션 중 텍스트 인코딩 혹은 MTOM(Message-Transmission Optimization Mechanism) 인코딩 중 하나를 선택할 수 있고 NetTcpBinding은 바이너리 인코딩 만을 사용하도록 구성 되어 있다.
기본적으로 WCF에서 바인딩은 서비스에서 사용할 수 있는 다양한 기능들의 조합일 뿐이므로 이론적으로 다양한 조합이 나올 수 있으며 실제로 개발자가 자신이 필요한 기능들만을 조합하여 사용자 정의 바인딩(custom binding)을 만들 수도 있다. 하지만 상당한 노력을 요구하는 커스텀 바인딩을 매번 만들어야 한다면 대단한 낭비가 아닐 수 없다. 따라서 WCF에서는 가장 많이 사용될 것으로 예상되는 기능들을 조합하여 대표적인 바인딩을 기본적으로 제공한다. 이들이 바로 BasicHttpBinding, WSHttpBinding, WSDualHttpBinding, WSFederationBinding, NetTcpBinding, NetNamedPipeBinding, NetMsmqBinding, MsmqIntegrationBinding, NetPeerTcpBinding으로써 표준 바인딩 혹은 시스템 바인딩이라고도 한다(표 1 참조).
표 1. WCF 표준 바인딩들
바인딩 |
간략한 설명 |
BasicHttpBinding
|
WS-I 프로파일을 만족하는 웹 서비스와 호환되는 HTTP 기반의 기본 웹 서비스 가장 상호호환성이 높지만 신뢰할 수 있는 메시징, 트랜잭션 등의 기능은 사용할 수 없다. 이 바인딩은 ASP.NET 웹 서비스와 WSE(Web Service Extension)과의 호환성을 갖는다.
|
WSHttpBinding
|
다양한 웹 서비스 표준인 WS-*를 대부분 만족하는 풀 스펙의 HTTP 기반 바인딩으로써 보안, 트랜잭션, 신뢰할 수 있는 메시징을 모두 지원한다. 웹 서비스 표준들을 준수하므로 이들을 준수하는 다른 웹 서비스 혹은 웹 서비스 클라이언트들과 호환된다.
|
WSDualHttpBinding
|
WSHttpBinding에 일부 제약이 가해지지만 서비스가 클라이언트를 호출하는 콜백(callback) 기능을 갖춘 바인딩이다. 상호 운영성은 그다지 높지 않다.
|
WSFederationBinding
|
WS-Federation 표준을 구현하는 바인딩으로써 서로 다른 인증 시스템을 갖춘 서비스들이 서로를 인증하고 메시지에 대한 보안을 설정하기 위해 제공되는 바인딩이다.
|
NetTcpBinding
|
TCP 트랜스포트를 사용하는 바인딩으로써 WCF가 제공하는 모든 기능을 사용할 수 있으면서도 성능적으로 우수한 바인딩이다. 기존의 닷넷 리모팅에 비유할 수 있지만 XML 메시지를 사용한다는 점이 크게 다르다. 클라이언트와 서비스가 모두 WCF를 사용해야 하므로 상호운영성이 높지 않다.
|
NetNamedPipeBinding
|
단일 컴퓨터에서 사용하는 것을 목적으로 제공되는 바인딩으로써 가장 빠른 성능을 내는 바인딩이다.
|
NetMsmqBinding
|
트랜스포트로서 MSMQ를 사용하는 바인딩으로써 클라이언트와 서비스가 네트워크에 연결되어 있지 않더라도 SOAP 메시지가 전달될 수 있는 진정한 비동기 메시지 전달 능력을 갖춘 바인딩이다.
|
MsmqIntegrationBinding
|
레거시 MSMQ서버 및 클라이언트와 통신하기 위해 제공되는 바인딩이다.
|
NetPeerTcpBinding
|
P2P 네트워킹을 사용하는 바인딩으로써 서비스와 클라이언트가 P2P 통신이 가능하게 해준다.
|
이 표에서 언급된 바인딩들 외에도 닷넷 프레임워크 3.5에서는 WS2007HttpBinding, WS2007FederationBinding이 제공되지만 이들은 WSHttpBinding 및 WSFederationHttpBinding이 최신 버전의 WS-* 표준을 구현하도록 설정한 것이므로 기본적인 바인딩 특징 및 사용법은 동일하다.
다시 예를 들어보자. BasicHttpBinding은 트랜잭션, 신뢰할 수 있는 메시징 기능은 사용할 수 없으며 메시지 수준의 보안은 제한적으로만 사용할 수 있을 뿐이다. 서비스와 클라이언트가 BasicHttpBinding을 사용하기로 했다면 트랜잭션이나 신뢰할 수 있는 메시징 기능을 제외하기로 서로 합의를 본 것이다. 이제 남은 것은 BasicHttpBinding이 제공하는 기능들 중에서 선택 사항들에 대해 서비스와 클라이언트가 합의를 볼 차례이다. 예를 들어 메시지 인코딩 기능을 사용하는 것은 양측이 합의 했지만 텍스트 인코딩을 사용할 것인지 MIME 인코딩을 사용할 지는 서로 합의를 봐야 한다는 것이다.
[그림 17]은 BasicHttpBinding에 대한 구체적인 예를 보여주고 있다. [그림 17]에서 서비스와 클라이언트가 합의를 본 바인딩은 메시지 수준의 보안 기능은 사용할 수는 있지만 다양한 옵션 중 “사용하지 않음”을 선택했고 메시지 인코딩은 텍스트 인코딩을, 트랜스포트 보안은 SSL을, 트랜스포트는 HTTP를 사용하기로 한 것이다. 트랜잭션, 신뢰할 수 있는 메시징은 BasicHttpBinding에서 사용할 수 없는 기능이므로 그림에 포함되지 않았음은 말할 나위도 없다.
그림 17. BasicHttpBinding 과 바인딩 설정
대충 바인딩에 대한 감이 오는가? 그렇다면 지금까지 우리가 예제 코드에서 사용한 바인딩은 어떻게 된 것일까? 우리가 지금까지 보아온 예제코드는 달랑 바인딩 객체들(BasicHttpBinding과 NetTcpBinding)를 생성하는 것으로 끝나지 않았는가? 그렇다. 바인딩은 기본 설정 값들을 가지고 있기 때문에 별도의 설정을 수행하지 않아도 기본 설정을 사용하여 작동하도록 되어 있다. 예를 들어 BasicHttpBinding은 메시지 수준 보안은 “사용하지 않음”이 기본값이며 메시지 인코딩은 텍스트 인코딩이 기본값이다. 또한 트랜스포트 수준의 보안 역시 “사용하지 않음”이 기본 설정이다. 반면 NetTcpBinding은 메시지 수준 보안이 “사용하지 않음”이 기본값이며 메시지 인코딩은 바이너리 인코딩이, 트랜스포트 수준의 보안은 Windows 보안을 사용하는 것이 기본값이다. 따라서 지금까지 우리는 바인딩의 기본 설정만을 이용한 것이 되겠다.
다음 코드 조각은 서비스 호스트에 서비스 종점을 명시할 때 BasicHttpBinding의 메시지 인코딩을 MIME 기반의 MTOM 인코딩을 사용할 것을 설정하고 있다 . BasicHttpBinding 객체를 먼저 생성한 후에 MessageEncoding 속성을 설정하는 것에 주목하기 바란다.
ServiceHost host = new ServiceHost(typeof(HelloWorldWCFService));
BasicHttpBinding binding = new BasicHttpBinding();
binding.MessageEncoding = WSMessageEncoding.Mtom;
host.AddServiceEndpoint(
typeof(IHelloWorld), // service contract
binding, // service binding
"http://localhost/wcf/example/helloworldservice");
......
이 서비스를 호출하는 클라이언트 역시 동일하게 BasicHttpBinding을 사용해야 함은 물론이요, 메시지 인코딩 옵션을 MTOM으로 해주지 않으면 안 된다. 다음 코드 조각은 클라이언트 측의 바인딩 설정을 보여 주고 있다. 서비스 호스트와 비슷하게 바인딩 객체를 생성한 후에 필요한 설정을 수행하고 있음이 지금까지 우리가 보아온 예제 코드와 다른 점임을 살펴보도록 하자.
BasicHttpBinding binding = new BasicHttpBinding();
binding.MessageEncoding = WSMessageEncoding.Mtom;
Uri uri = new Uri("http://localhost/wcf/example/helloworldservice");
ServiceEndpoint ep = new ServiceEndpoint(
ContractDescription.GetContract(typeof(IHelloWorld)),
binding,
new EndpointAddress(uri));
......
각 바인딩이 어떤 기능을 지원하고 MessageEncoding 속성처럼 어떤 옵션 사항 중 하나를 선택할 수 있는지를 여기서 모두 설명할 수는 없다. 앞으로 이 책의 전반에 걸쳐 여러 시스템 바인딩들의 특징, 제약 등을 설명하게 될 것이며 특히 5장에서 바인딩들의 기능과 각 바인딩의 옵션 사항들이 어떤 것들이 있는가를 상세하게 설명할 것이다.
Summary
지금까지 눈물 나게 간단한 Hello World 서비스를 작성해 보았다. 그리고 이 예제 서비스를 통해 WCF의 여러 가지 기본 개념들인 서비스 종점, 주소, 바인딩 계약, 서비스 호스트와 같은 아주 기초적인 WCF 프로그래밍에 대해서도 다방면으로 살펴보았다. 이제 WCF가 무엇을 하는 것인지에 대한 감이 어느 정도 왔으리라 생각한다. 비록 간단한 예제이지만 주소, 바인딩, 계약을 사용하는 WCF 프로그래밍 모델의 기초를 소개하였다. 이 장에서는 간단한 예제를 통해 매우 기초적인 예제를 설명했지만 이 예제로써 WCF의 기본적인 사항들을 모두 설명하기에는 부족하다. 다음 강좌부터 좀더 진보된 WCF 예제를 다루어 봄으로써 WCF가 제공하는 풍부한 기능에 대해 조금 더 살펴보도록 하자.