Visual Studio 2010 공식 팀 블로그 @vsts2010

Posted by 오태겸(RuAA)
여름의 정점을 지나가고 있는 듯 합니다. 날씨가 무진장 덥네요,,
장마가 끝나면 폭염이라는데,, ㅡ,.ㅡ;;;
식상한 멘트이긴 하겠지만, 이런 날일 수록 정말 건강이 중요한 것 같습니다.
덥다고 에어컨 바람만 쐬면 냉방병 걸리고, 밤에 잘때도 너무 에어컨 틀어놓으면 감기 걸리니깐,,
아프지 않는게 최고죠~ 특히, 저 같이 혼자 자취하는 자취남들은,, ^^
아무쪼록 건강하세요~


이번 아티클의 주제는 Windows Service를 이용한 호스팅입니다.

윈도우즈 서비스에 대해서 모르는 분이 계실까요?
네~ 계실 수 있죠,, 그래서 간략하게 윈도우즈 서비스에 대해 설명을 하고 넘어가도록 하겠습니다.

What is Windows Service?

NT 서비스라고 알려져 있기도 한 윈도우즈 서비스는 자체의 Windows 세션에서 실행되며, 윈도우즈가 구동을 하고 있는 동안 계속 동작을 해야하는 이러한 작업이 필요할 때, 윈도우즈 서비스의 형태로 구현하여 사용할 수 있습니다. 또한, 윈도우즈 서비스는 윈도우즈의 시작과 함께 자동으로 시작할 수 있기 때문에, 서버가 동작함과 동시에 항상 동작해야하는 응용 프로그램이라면 윈도우즈 서비스로 구현하는 것을 고려해 볼 필요가 있을 듯 합니다.

윈도우즈 서비스의 경우는 따로 UI를 가지고 있지 않기 때문에, 윈도우즈 사용자 또는 관리자가 윈도우즈 서비스의 구동 상태를 상세하게 확인할 수는 없습니다. 다만, 윈도우즈에 기본으로 제공되는 서비스 제어 관리자를 통해 서비스의 시작, 중지, 일시정지 등을 컨트롤 할 수 있습니다. (아래 그림은 서비스 제어 관리자의 모습입니다.)


그림을 보니 윈도우즈 서비스가 어떠한 것들을 말하는 것인지 알겠죠? ^^ (역시, 백문이 불여일견,, 엣헴~)

Windows 서비스 응용프로그램에 대한 소개와 개발 방법에 대해 좀 더 자세한 정보를 원하시는 분은 다음 링크를 참고하시기 바랍니다.

"Windows 서비스 응용 프로그램 소개"

이제 본격적으로 이 윈도우즈 서비스를 이용하여 WCF 서비스를 호스팅하는 방법에 대해 적어보겠습니다.
고고씽~

Windows Service를 이용한 WCF 서비스 호스팅

WCF 서비스를 호스팅하는 방법에는 IIS 호스팅, WAS 호스팅, 그리고 셀프 호스팅으로 나뉘어진다고 얘기했었습니다.

그럼, Windows Service를 이용한 호스팅은 어디에 속할까요?
네~!! 당연히 셀프 호스팅에 속합니다. 그리고, 셀프 호스팅에 속한다는 말은 직접 ServiceHost 클래스를 이용하여 호스팅을 구현 해야 한다는 말이기도 합니다.

결국, 여기서 제가 하고 싶은 말은 이것입니다.
 "Windows Service를 이용하여 WCF 서비스를 호스팅하기 위해서는 ServiceHost 클래스를 이용하여 서비스 호스팅하는 부분을 직접 구현해야 한다."
 
WCF 서비스를 호스팅하기 위한 특별한 코드가 필요한 것은 아닙니다. 여타의 다른 윈도우즈 서비스를 개발하는 것과 같이 개발을 하고, WCF 서비스를 호스팅하는 코드만 추가를 해주면 된다는 것입니다~!!
아마, 윈도우즈 서비스를 한번이라도 개발 해 보신 분은 어렵지 않게 개발을 할 수 있을 것 같습니다.

우선, 윈도우즈 서비스 응용프로그램을 만들기 위해 Visual Studio에서 새 프로젝트를 생성할 때, "Windows 서비스" 템플릿을 선택합니다.


프로젝트를 생성하면 Service1.cs 와 Program.cs 파일이 생성되어 있음을 확인할 수 있습니다.
Program.cs 파일은 이 응용 프로그램의 진입점을 가지고 있으며, 실제 서비스가 동작을 할 때의 코드는 Service1.cs 파일에 구현을 하면 됩니다.

그리고, Service1.cs 파일을 확인하면 Service1 클래스가 선언되어 있으며, 이 클래스는 ServiceBase 클래스를 상속 받는 것을 확인할 수 있습니다.
그리고, Service1 클래스에는 기본적으로 OnStart와 OnStop 메소드가 재 정의(override)되어 있는데 메소드의 이름으로 짐작할 수 있겠지만, 각각 서비스가 시작할 때 와 서비스가 멈췄을 때의 동작을 수행하는 메소드입니다.

이 메소드 외에, OnContinue, OnPause, OnShutdown 메소드를 재정의하여 서비스를 일시정지에서 다시 시작했을 때, 서비스를 일시정지 했을 때, 그리고 컴퓨터가 종료될 때의 동작을 구현할 수 있습니다.

이번 아티클의 주제는 Windows 서비스를 이용한 WCF 서비스의 호스팅이니 만큼, Windows 서비스 구현에 대한 자세한 내용은 앞서 걸어놓은 MSDN의 링크로 대신하고 넘어가겠습니다.
(Windows 서비스 응용 프로그램의 구현에 대한 자세한 내용을 모르셔도 아래 내용을 따라 하시면, 아마 무사히 WCF 서비스를 호스팅 할 수 있으실겁니다. )

음,, 일단 우리가 만들 Windows 서비스의 이름을 먼저 변경해보도록 하겠습니다. 이름을 변경하지 않아도 별 상관은 없지만 우리가 만든 서비스란 것을 알아보기 위해서 변경하는게 낫겠죠~ 서비스의 이름을 변경하는 것은 어렵지 않습니다. Service1 클래스의 생성자에서 바꿔줘도 되고, Service1.Designer.cs 파일을 확인하면 Service1 의 partial 클래스가 정의되어 있는데, 이곳에 위치한 InitializeComponent 메소드 내에서 변경해줘도 됩니다. 저는 InitializeComponent 메소드 내에서 다음과 같은 코드로 서비스의 이름을 지정 해주었습니다.

private void InitializeComponent()

{

     components = new System.ComponentModel.Container();

  this.ServiceName = "RuAA WCF Service"; // 서비스의 이름 변경

}


이제 본격적으로 WCF 서비스를 호스팅 해보도록 하겠습니다.
우선, WCF 서비스에서 사용할 ServiceContract와 DataContract 들을 선언해주어야 겠죠. 다음과 같은 인터페이스와 클래스들을 선언해주었습니다.

// ServiceContract 정의
[
ServiceContract]

public interface IProductService

{

    [OperationContract]

    Product GetProductInfo(int id);

}


// DataContract 정의
[
DataContract]

public class Product

{

    [DataMember]

    public int ID;

    [DataMember]

    public string Name;

    [DataMember]

    public string Company;

    [DataMember]

    public int Price;

}

// 서비스 구현

public class ProductService : IProductService

{

    List<Product> productList;

 

    public ProductService()

    {

        productList = new List<Product>();

        productList.Add(new Product {

            ID = 1,

            Name = "ABC Chocolate",

            Company = "RuAA Inc.",

            Price = 5300

        });

    }

 

    public Product GetProductInfo(int id)

    {

        var item = (from p in productList

                    where p.ID == id

                    select p).FirstOrDefault();

 

        return item;

    }

}

항상 그랬지만, 서비스의 역할은 심플합니다. 복잡한 서비스를 만드는게 목표는 아니잖아요~ ㅎ

이제 이렇게 구현된 서비스를 호스팅하는 일 만을 남겨두었습니다.

앞에서도 밝혔듯이 Windows 서비스에서 WCF 서비스를 호스팅 하는 것은 Self Hosting 에 포함되는 것이기 때문에 ServiceHost 클래스를 직접 구현해야 합니다.

근데 이 코드를 어디에 위치해야 할까요? 예상하신 분들이 분명 계실겁니다.
바로 바로 바로~~!! Service1 클래스의 OnStart 메소드입니다. 

또한, 생각해야 할 것이 있습니다.
Windows 서비스가 멈추었을 때, 당연히 WCF 서비스의 호스팅을 멈추어줘야 겠죠. 그래서 OnStop 메소드 내부에 WCF 서비스의 호스팅을 멈추게 하는 코드도 포함이 되어야 할 것입니다.

Service1 클래스를 다음과 같은 코드로 구현해보았습니다.

// ServiceHost 클래스의 인스턴스를 클래스의 멤버로 선언하여 Service1 클래스의 메소드에서 접근이 가능하도록 한다.
private
ServiceHost svcHost;

// 윈도우즈 서비스가 시작할 때의 동작을 구현한다.

protected
override void OnStart(string[] args)

{

    // ServiceHost 인스턴스 생성
    string baseUrl = "http://10.30.101.84:9090/ProductService";

    this.svcHost = new ServiceHost(typeof(ProductService), new Uri(baseUrl));

    // 엔드포인트 추가
   
this.svcHost.AddServiceEndpoint(typeof(IProductService),

        new BasicHttpBinding(),

        "");

           

    // 메타 데이터 엔드포인트를 위한 Behavior 설정
    ServiceMetadataBehavior metaBehavior = new ServiceMetadataBehavior();

    metaBehavior.HttpGetEnabled = true;

    svcHost.Description.Behaviors.Add(metaBehavior);

 

    // 메타 데이터 엔드포인트 추가

    this.svcHost.AddServiceEndpoint(typeof(IMetadataExchange),

        MetadataExchangeBindings.CreateMexHttpBinding(),

        "mex");

 

    svcHost.Open();  // WCF 서비스 오픈

    ServiceEndpoint endpoint = this.svcHost.Description.Endpoints[0];

 

    // 윈도우즈 이벤트 로그에 정보를 남긴다.

    EventLog.WriteEntry(endpoint.Contract.Name + " Started"

        + " listening on " + endpoint.Address

        + " (" + endpoint.Binding.Name + ")",

        System.Diagnostics.EventLogEntryType.Information);

}

 

// 윈도우즈 서비스가 멈췄을 때의 동작을 구현한다.

protected override void OnStop()

{

    this.svcHost.Close();

    EventLog.WriteEntry("RuAA Service Stopping", EventLogEntryType.Information);

}
 
위의 코드에서 그렇게 어려운 점은 보이지 않습니다. Self Hosting을 해보셨던 분이라면 말이죠~
혹시나, Self Hosting을 해보지 못하신 분이 있다면, 첫 WCF 만들기 아티클을 참고해주시기 바랍니다.

각 주요 코드에 주석도 남겨놨으니 따로 긴 설명은 필요없을 듯 합니다. ^^

이제, 서비스의 구현은 모두 끝이 났습니다. 하지만, 우리가 만든 이 Windows 서비스를 컴퓨터에 설치하기 위해서는 설치 관리자가 필요합니다. (설치 관리자에 대한 자세한 설명은 이곳으로~)
Service1.cs 파일의 디자이너 보기에서 마우스 우측 클릭한 후 나타나는 메뉴에서 "설치 관리자 추가"를 선택합니다.


그러면 프로젝트에 ProjectInstaller.cs 라는 파일이 생기는데, 이 클래스의 디자인 뷰를 확인하면,  serviceProcessInstaller1, serviceInstaller1 이라는 이름의 컨트롤들이 포함되어 있는 것을 확인할 수 있습니다.

이 컨트롤들의 속성을 다음 그림과 같이 수정해보겠습니다.



이제서야, 정말 Windows 서비스를 설치할 모든 준비가 끝이 났습니다.
이 Windows 서비스를 설치하기 위하여 Visual Studio 명령 프롬프트를 실행시킵니다. 이때, 관리자 권한으로 실행시켜 주셔야 합니다. 그렇지 않으면 Windows 서비스가 제대로 설치가 되지 않는 경우가 있더라구요~ ㅎ

그리고, 이 프로젝트 폴더의 bin/Debug 폴더로 이동한 후에 다음 그림과 같이, Installutil 이란 명령어를 이용하여 우리가 만든 Windows 서비스를 설치합니다. (Windows 서비스 설치에 대한 자세한 설명은 이곳으로~!!)
참고로, 설치 파일은 프로젝트를 빌드한 후에 생성된 exe 파일입니다.


위의 그림처럼 "트랜잭트 설치가 완료되었습니다." 란 메세지가 떨어졌다면 아무 에러없이 Windows 서비스가 설치되었다는 말입니다. 그럼, 서비스 제어 관리자에서 확인해 보도록 하겠습니다.


네,, Windows 서비스가 올라와있는 것을 볼 수 있네요~ 그럼 시작 버튼을 클릭하여 Windows 서비스를 시작할 수 있습니다. 그리고, Windows 서비스가 시작하면서 우리가 구현한 WCF 서비스가 호스팅되겠죠,, ㅎㅎ

그럼, 이 WCF 서비스가 제대로 호스팅 되고 있는 것인지 확인을 해보아야 할겁니다,,
역시나, 지금까지 그래왔듯이 콘솔 어플리케이션을 이용하여 확인해보도록 하겠습니다.

솔루션에 콘솔 어플리케이션 프로젝트를 추가하고, 이 프로젝트에 서비스 참조를 시켜주었습니다. 이때 너무나도 당연하겠지만, 서비스의 주소는 ServiceHost 인스턴스에 추가 시켜준 엔드 포인트의 주소를 넣어주셔야 합니다.ㅎ


위의 그림처럼, 서비스를 제대로 찾으면~ 모든 것이 OK!!! 입니다. ㅎㅎ

이 서비스를 이용한 콘솔 어플리케이션 코드는 생략해도 되겠죠?? ㅎㅎ (이번 글이 너무 길어진 것 같아,, ^^;;;;)
그래서~~~ 결과 화면만 보여드리겠습니다 ㅎㅎ



후아~ 결과가 잘 나오는 군요,, ^^
이로써, 이번 포스팅도 무사히(?) 끝을 낼 수 있게 되었습니다. ㅎㅎㅎ

이번 포스팅은 Windows 서비스에 대한 설명과 구현에 대한 내용을 함께 적다보니 조금 길어진 것 같습니다. 물론 한번이라도 Windows 서비스 응용 프로그램을 구현해보신 분이라면 아는 내용들이겠지만, 혹시나 모르시는 분도 있을 것 같아 자세한 내용까지 설명드리지 못했지만 최소한의 내용을 포함시켰습니다.

조금 내용이 길어졌지만 끝까지 읽어주신 분들께 심심한 감사의 인사를 드리며, 저는 이만 퇴근(?)하겠습니다 ㅎㅎ
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. Windows NT 서비스를 이용하여 접목할 수 있는 실제 사례가 인상 깊네요. 강좌 잘 읽었습니다. :-)

Posted by 오태겸(RuAA)

바야흐로, 여름입니다.
아ㅡ 정말 이놈의 귀차니즘 덕분에 너무 띄엄띄엄 포스팅이 되는 것에 대해 죄송하다는 말씀 먼저 드려야할 것 같습니다.
여름이라 더워서 그렇다고 핑계대지 않을게요, 휴가 시즌이라 놀고 싶어서 그렇다고 핑계대지 않을게요~ ;;;
잡담은 여기서 줄이고, 힘을 내어, 이번 포스팅을 시작해 보겠습니다. 레츠 고우~
 
지난 포스트의 주제는 WAS 호스팅이었습니다. 이번 주제는 조금 다르긴 하지만 지난 포스트에 이어서 Hosting과 관련된 내용을 적어볼까 합니다.

WCF 서비스를 호스팅하기 위해 가장 쉬운 방법이 무엇인지 다들 아시죠?
제 개인적인 생각인지는 모르겠지만, Visual Studio를 사용하여 WCF 서비스를 만든다면, 아마도~ 가장 쉬운 호스팅 방법은 IIS 호스팅일 것입니다. 솔루션 만들고 별 수정없이 바로 호스팅이 가능하니깐요,,

갑자기 왜 IIS 호스팅에 대한 얘기를 꺼내냐구요?
음,, 오늘 제가 꺼낼 이야기가 IIS 호스팅일 때 WCF 서비스에 ASP.NET의 몇 가지 특성을 적용할 수 있는 방법에 대한 내용을 적으려다 보니... 네!! 결국, 제가 하고 싶은 얘기는 이번 포스팅에서 나오는 방법들이 IIS 호스팅을 바탕으로 한다는 것을 명심(?)해 달라는 것입니다. ㅎㅎ

닷넷 웹 서비스 와 WCF 서비스

WCF 서비스 얘기를 할 때, 가장 비교를 많이 하는 것이 아마 .NET 웹 서비스 일 듯 합니다. (지금도 이 닷넷 웹 서비스에 대해 얘기를 하려 하구요~ ㅎ)

닷넷 웹 서비스와 WCF 서비스의 가장 큰 차이는 무엇일까요?
구현하는 방법에 대한 차이도 있겠지만, 그것보단 WCF 서비스가 HTTP 프로토콜 이외의 프로토콜(net.tcp, net.pipe, MSMQ)을 이용하여 접근이 가능하게 호스팅할 수 있다는 점일 것입니다. (WAS를 이용한 호스팅 참조)

닷넷 웹 서비스는 WCF 서비스와는 다르게 HTTP 프로토콜만 지원합니다. 그리고 이는, ASP.NET HTTP 파이프라인을 따르고 있습니다.

"아ㅡ ASP.NET 파이프 라인은 또 뭔가요?" 라고 원망 섞인 소리가 여기까지 들리는 것 같습니다. 저도 아직 실력이 미천한 개발자라 자세히 설명드릴 수는 없습니다.
간단하게 설명 드리자면, ASP.NET 에서 Http 프로토콜을 이용하여 들어오는 요청(request)과 응답(response)을 처리하기 위한 파이프라인입니다. 즉, 어떤 요청에 대해서 어떻게 필터링을 수행하고, 어떤 어플리케이션을 호출할 것인지를 처리하며, 파이프라인을 통해서 그에 대한 응답을 전송하는 것입니다.

자세한 내용을 알고 싶은 분은 다음을 참고 하시면 될 것 같습니다.
Securely Implement Request Processing, Filtering, and Content Redirection with HTTP Pipelines in ASP.NET

닷넷 웹 서비스는 이렇게 ASP.NET HTTP 파이프라인을 사용하기 때문에 많은 ASP.NET 의 특징을 함께 사용할 수 있다는 장점을 가지고 있습니다. 이러한 장점에는 다음과 같은 것들이 포함 됩니다. 

  • Authentication
  • Url/File authorization
  • Impersonation
  • Session state
  • Request cache
  • Globaliztion
  • Etc

목록을 보니 인증과 권한에 대한 것, 그리고 세션과 관련한 것들이 있네요~

이제 WCF 서비스 얘기를 해볼까요?
WCF 서비스는 닷넷 웹 서비스와는 다르게 non-HTTP 프로토콜들을 지원해줍니다. 그리고, 이러한 장점을 위하여 프로토콜에 독립적인 디자인을 사용하게 된 것입니다.

조금 둘러서 얘기를 했지만, 제가 하고 싶은 말은 이것입니다. 
"닷넷 웹 서비스와는 다르게 WCF 서비스는 ASP.NET HTTP 파이프 라인을 따르지 않는다!!"


음,, 그렇군요. WCF 서비스는 ASP.NET HTTP 파이프 라인을 따르지 않는군요... 앗~ 그렇다면 닷넷 웹 서비스의 경우 ASP.NET HTTP 파이프 라인을 따랐기에 여러가지 ASP.NET의 특성을 사용할 수 있었는데,, 그럼, WCF 서비스에서는 ASP.NET의 특성들을 사용할 수 없는걸까요??

네~!! 기본적으로는 그렇습니다.
하!지!만!!! ASP.NET 에는 여러 유용한 특성들이 존재했기에 이를 완전히 버리기는 아까웠을겁니다. WCF 서비스를 위해서 같은 특성들을 다시 만들기 보다는 기존에 있던 것들을 가져다 쓰는 방향으로 개발하고 싶었겠지요~(제 개인적인 생각입니다. 아니면 말구요~ ㅎ) 

어떤 이유인지는 확실치 않지만, 어찌됐든 중요한 것은, WCF 서비스에서도 기존의 ASP.NET 특성들을 (전부는 아니고 일부분의 특성들을) 사용할 수 있는 방법을 제공해주고 있다는 것입니다. 단, HTTP 프로토콜을 사용하는 WCF 서비스에서만요~

이번에도 역시 둘러둘러~ 이제서야 본론으로 들어온 것 같습니다 ^^
그럼, 이제 본격적으로 WCF 서비스에서 ASP.NET의 유용한 기능들을 사용하기 위한 방법에 대해서 얘기해보도록 하겠습니다.


WCF 서비스에서 Session 사용하기

이번 포스팅에서는 ASP.NET의 특징들 중에서 간단하게 Session을 사용하는 예제를 구현해보려 합니다. (차후에 WCF의 보안에 대해 포스팅을 할 때에는 Impersonation 과 관련한 예제도 보여드릴 수 있을 것 같습니다.)
여기서 Session은 WCF 의 인스턴스를 생성할 때의 모드인 InstanceContextMode.Session 과는 전혀 무관합니다. 다들 알고 계시리라 생각하지만 혹시나 싶어서요~ ㅎ

우선, ASP.NET의 특징들을 사용하기 위해서는 크게 두 가지의 설정이 필요합니다.

첫 번째, Application Level 에서의 설정이 필요한데, 이는 WCF 서비스 프로젝트의 web.config에서 <system.serviceModel> 의 자식 요소인 <serviceHostingEnvironment> 요소의 속성 aspNetCompatibilityEnabled 의 값을 true로 명시하여 설정할 수 있습니다. 

두 번째로, Service Level 에서의 설정이 필요합니다. 이는 WCF 서비스를 구현하는 클래스에 AspNetCompatibilityRequirements 특성을 통해 설정을 할 수 있습니다.

코드를 보면서 하나씩 해보도록 하죠~

WCF 솔루션을 하나 만듭니다. 좀 편하게 작업을 하기 위해서 셀프 호스팅보다는 Visual Studio 에서 제공해주는 "WCF 서비스 응용 프로그램" 템플릿을 사용하도록 하겠습니다.

그리고, 다음과 같이 서비스 계약을 위한 interface와 WCF 서비스에서 사용할 개체인 Product 클래스를 정의했습니다.

[ServiceContract]

public interface IProductService

{

    [OperationContract]

    Product GetProduct(string ticker);

}

 

[DataContract]

public class Product

{

    [DataMember]

    public string Name;

    [DataMember]

    public int calls;

    [DataMember]

    public double price;

    [DataMember]

    public string RequestedBy;

}


이 다음에 할 일은 당연히 서비스를 구현하는 것이겠죠.
이 서비스에서는 session을 사용하여 현재 메서드가 호출되는 횟수를 기록, 유지하도록 했습니다.

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]

public class ProductService : IProductService

{

    public Product GetProduct(string ticker)

    {

        Product p = new Product();

        int nCalls = 0;

        // I .

        if (HttpContext.Current.Session["cnt"] != null)

            nCalls = (int)HttpContext.Current.Session["cnt"];

        HttpContext.Current.Session["cnt"] = ++nCalls;

 

        p.Name = "Caramel Latte";

        p.calls = nCalls;

        p.price = 2500;

        p.RequestedBy = "RuAA";

          

        return p;

    }

}


이 코드에서 다시 한번 유의해서 보아야 할 부분은 역시 ProductService 클래스 위에 선언 된 AspNetCompatibilityRequirements 특성입니다. 그 값을 Required 로 주었네요~
이 부분은 앞에서 ASP.NET 특성들을 사용하기 위한 설정 중 Service Level에서의 설정에 해당하는 것이었습니다.

그럼, 이제 Application Level에서의 설정을 해주어야 겠군요. 
web.config 파일을 다음과 같이 수정합니다.

<system.serviceModel>

  <behaviors>

    <serviceBehaviors>

      <behavior>

        <serviceDebug includeExceptionDetailInFaults="true"/>

      </behavior>

    </serviceBehaviors>

  </behaviors>

  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

</system.serviceModel>


밑줄 쳐 있는 부분을 주목하시면 되겠습니다~ ㅎ
이 부분을 추가함으로써 첫 번째 Application Level 에서의 설정까지 완료한 것입니다.
설정이라고 하기엔 너무 간단한가요? ㅎ

이제, 이 서비스를 검증해보아야 겠죠,, 세션을 잘 유지하는지,,
매번 그래왔듯이 콘솔 어플리케이션을 이용하여 클라이언트를 간단히 만들어 보겠습니다.

서비스 참조를 하고, 다음과 같은 코드를 작성하였습니다. 

static void Main(string[] args)

{

    ProductServiceClient client = new ProductServiceClient();

    Product p = null;

    for (int i = 0; i < 5; i++)

    {

        p = client.GetProduct(i.ToString());

        Console.WriteLine(" : {0}", p.calls.ToString());

        Console.WriteLine(" : {0}", p.Name);

        Console.WriteLine(" : {0} ", p.price.ToString());

        Console.WriteLine(" : {0}", p.RequestedBy);

        Console.WriteLine();

    }

}


이 코드에선 그렇게 중요한 부분이 없습니다. 단순히 서비스의 GetProduct 메서드를 연속해서 5번 호출을 해주는 것 밖엔,, 복잡한건 싫으니깐 이렇게 간단히~ ㅎ

그리고 실행을 해보도록 하죠~
 

앗~ 뭔가 이상합니다. 우리가 예상했던 그런 결과가 나오지 않는군요. 세션이 유지가 되었다면 호출 횟수의 값이 1씩 증가하여 1~5의 값을 보여주어야 할텐데 말이죠.......

이런 결과가 나오는 이유는 바로,, Session을 사용하기 위해서는 클라이언트에서 쿠키를 허용해주어야 하는데,  기본 HTTP 바인딩인 basicHttpBinding 과 wsHttpBinding이 기본적으로 쿠키를 허용하지 않기 때문입니다.
HTTP 바인딩에서 쿠키를 허용해주기 위해선 config 파일에서 binding 태그의 allowCookies 속성의 값을 true 로 바꿔주시면 됩니다. 다음과 같이 말이죠~

<basicHttpBinding>

    <binding name="BasicHttpBinding_IProductService" closeTimeout="00:01:00"

        openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"

        allowCookies="true" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"

        maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"

        messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"

        useDefaultWebProxy="true">

    ……

</ basicHttpBinding>


이제는 모든 설정이 완벽하게 끝이 난 것 같군요.
다시 실행을 해 보겠습니다.


아~ 이제 예상했던대로 결과 값이 나오는 것 같군요.. ㅎ

문득, 서비스의 InstanceContextMode의 값이 바뀌면 어떻게 될지 궁금해지지 않으신가요? ㅎ
한번 직접 해보시면 알겠지만 이 세션은 InstanceContextMode의 값(PerSession, PerCall, Single)이 무엇이 되든지 간에 유지됩니다. (다들 한번씩 해보시길~ ^^)

이번 포스팅은 여기까지하고 줄이도록 하겠습니다.

사실 이 포스팅은 제가 월드컵이 시작할 때 같이 시작했었는데, 결국 월드컵이 끝날 때 같이 끝나게 되었네요,,
이 놈의 귀차니즘 덕분에 포스팅이 항상 늦게 올려져서,, 정말 죄송한 마음밖엔 없는 것 같습니다.
일을 하면서 포스팅을 한다는 것 자체가 쉬운 일이 아님을 깨달았습니다.(MVP 분들이 존경스러워 지는군요,,ㅎ) 그래도 포기하면 안되겠죠,, ㅎ

다음 포스팅 때 뵙겠습니다. 꾸벅~
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. asp.net compatibility mode를 사용하게 되면 System.Web.HttpApplication , System.Web.HttpContext 를 이용해서 행할수 있는 대부분의 작업이 가능해집니다. 이 부분에 대한 설명이 없는 부분이 다소 아쉽네요 ^^;

    • 오태겸 2010/07/12 17:40

      아쉬운 부분을 보완해서 loveciel 님이 한번 포스팅 해주시지요~ ㅎ

  2. 비밀댓글 입니다

    • 오태겸 2010/07/13 17:28

      비밀댓글이라 저한테 보이지 않네요,, 무슨 내용이길래 비밀로 댓글을 다셨는지,, 너무 궁금하게 만드시는데요~ ^^;;

  3. 항상 좋은 글 감사합니다. 질문이 있는데요.. WCF로 채팅 프로그램 만들고 있습니다. 클라이언트간의 파일 전송을 하려고 하는데..(네이트온처럼..)

    채팅이나 쪽지 같은경우는 서비스를 거쳐서 통신을 하게 했는데.. 파일전송도 서비스를 거치면 부하가 많이 걸릴듯하고.. 클라이언트간에 직접 파일전송을 하려고합니다.. 혹시 어떤 방식으로 하면 되는지 힌트 좀 얻을 수 있을까요?

    혹은 참고할 주소나 예제가 있으면 좀 부탁드립니다. on2god@naver.com

    감사~~~

    • 오태겸 2010/07/19 09:51

      보통 클라이언트간에 직접 파일 전송을 위해서 P2P 프로토콜을 사용합니다. 저도 이 프로토콜들에 대한 자세한 정보를 알지는 못하지만 여러 프로토콜이 있는 것으로 알고 있습니다. P2P 프로토콜에 대한 자료를 찾고, 공부해서 적용해보면 좋은 경험이 될 수 있을 것 같네요~ ^^

Posted by Mr.Fear
필자를 포함해서 여러명의 개발자들은 WCF 서비스를 이용할때 당췌 알수 없는 이유의 오류때문에 무한 야근을 한 경험이 있을거라고 확신한다. 이번엔 그중에 하나이면서 , 지금까지 명확하게 설명되지 않았던 오류 하나를 살펴보고자 한다.

<exception>
This collection already contains an address with scheme http. There can be at most one address per scheme in this collection.
Parameter name: item


한글로는?

이 컬렉션에는 스키마 http을(를) 가진 주소가 이미 있습니다. 이 컬렉션에서 스키마당 주소는 하나만 존재할 수 있습니다.
매개 변수 이름: item
설명: 현재 웹 요청을 실행하는 동안 처리되지 않은 예외가 발생했습니다. 스택 추적을 검토하여 발생한 오류 및 코드에서 오류가 발생한 위치에 대한 자세한 정보를 확인하십시오.

예외 정보: System.ArgumentException: 이 컬렉션에는 스키마 http을(를) 가진 주소가 이미 있습니다. 이 컬렉션에서 스키마당 주소는 하나만 존재할 수 있습니다.
매개 변수 이름: item



분명

로컬 호스트에서는 넘흐넘흐 잘 돌아갔던 우리 프로그램. 서버에 올려봤더니 요지부동. 절대 동작할 생각을 안하는 우리 WCF 서비스. 당신은 과연 이 메시지를 본적이 있는가?

이 문제는 하나의 WCF 서비스는 오로지 1개의 호스트로만 서비스되야 하는 규칙이 존재하기 때문이다.

Microsoft 는 이 문제에 대해 처음 해결책을 제시하였다.(이글은 IE에서 클릭했을때만 보입니다.)
http://blogs.msdn.com/rampo/archive/2008/02/11/how-can-wcf-support-multiple-iis-binding-specified-per-site.aspx

그러나 이글은 www.loveciel.com 이란 바인딩으로 접근했을때는 A 라는 메서드를 호출하게 할수 있는 시나리오로 , 사용자가 원하던 방식인 멀티 도메인으로 호출했을때 동일한 호출을 기대하는것은 아니었다.

그리고 그에 따른 방식을 사용자가 제안하였을때 Microsoft 의 대답은 다음과 같았다.

Lets say ServiceA is bound to Services.xxx.com and ServiceB is bound to xxx.com.

I am trying to understand why ServiceB cannot call directly call services.xxx.com. The baseAddressPrefixfilter workaround should further help to host these two services under the same site.

If you did want two base address today the only workaround is to have two copies of the service.


뭐 간단히 말하면 , 여러개의 호스트로 WCF서비스를 호출하는건 안되고 , 정 그렇게 하고 싶으면 두개의 카피를 만들라는 이야기였다. 그리고 이 Microsoft 측은 이 이슈를 종료시켜 버렸다.

그리고 프로그래머는 분노했다 -_-;;

WTF? "By design"? This is a blocking issue for us, and we are now turning back to ASMX handlers, thanks WCF Team! You did a great job by putting in fundamental "design" flaws in WCF.

But oh well, MS connect is useless anyway. They don't take this feedback seriously... THE workaround is to not use WCF and go back to ASMX handlers. Period.


WTF 는 What the fuck? 의 줄임말이다. 처음 이 댓글을 봤을때 얼마나 웃었는지 ㅎㅎ


결과적으로 이 이슈는 3.5에서는 해결될수 없는 이슈이다. 본인의 경우는 ssl을 통하는 사이트가 있었는데 , 이번에 아이폰 인증 모듈이 들어가면서 이 ssl증서를 사용해야할 일이 생겼는데 , 기존사이트는 이 ssl 을 미러링으로 사용하였기 때문에 상당히 많은 host 주소가 하나의 IIS에 등록이 되어 있었다. 그러한 이유로 , baseaddress를 지정하는 방법으로 해당 이슈를 해결할수 있었다. 그러나 crossdomain 에 민감한 ajax 나 flash 등에서는 여러개의 도메인을 하나의 머신에서 쓰는게 상당히 중요한 이슈이기 때문에 ajax 와 실버라이트에서 가장 유용하게 쓰이는 WCF 는 이 이슈를 해결해야 할 필요가 있었다.

결론적으로? 이 이슈는 WCF 4.0 에서 해결이 되었다 ^^

WCF 4.0 에서는 해당 Attribute 를 지정하면 간단하게 멀티 도메인을 허용할수 있게 된다.
웹기반의 WCF 서비스에서만 동작한다

<serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> 


이 일련의 에피소드는 Microsoft 라는 조직내에서 개발자의 니즈를 처리하는 방식을 보여준다. 물론 그 과정에서 다소간의 오류가 의견충돌이 일어날수도 있지만 , 사용자에 입장에서 얼마나 생각하는지에 따라 사용자가 그 기업을 바라보는 시각이 달라짐을 보여준다고 할수 있다. 비록 WCF 3.5에서 해당 오류를 해결하진 못했지만 , 긴 시간동안 사용자의 불만에 대해 고민하고, 결국 수용하는 Microsoft 의 모습을 나는 칭찬해주고 싶다.

참고자료 : https://connect.microsoft.com/wcf/feedback/details/322896/cant-host-wcf-service-in-a-website-with-multiple-identities?wa=wsignin1.0
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 재미있는 일화네요. 강좌 잘 읽었습니다. :-)

Posted by 오태겸(RuAA)

이제 포스팅 할 때마다 오랜만에 글 쓴다는 말하기도 미안해지네요,, 한 달만에 WCF에 관한 포스팅을 하게 되었습니다. 꾸준히 하겠다는 말을 하기도 민망하지만,, 어찌됐든, 이런 민망함을 뒤로하고 본론으로 들어가 보도록 하겠습니다. 

이번 포스팅의 주제는 Hosting 입니다.
호스팅은 WCF 서비스를 클라이언트에서 사용할 수 있게끔 해주는 중요한 작업이죠,, 이정도는 다들 알고 계실거라 생각합니다.

WCF는 여러 가지의 방법으로 호스팅을 할 수 있는 장점이 있습니다. 이는 제가 처음 WCF를 소개할 때도 언급했던 내용이었구요. 그래서, 지금부터는 WCF 서비스를 호스팅할 수 있는 방법과 호스팅할 때 사용할 수 있는 기능에 대해서 알아볼까 합니다.

호스팅에 대한 내용을 하나의 포스팅에 담기에는 내용이 조금 많은 것 같아서 두, 세번으로 나뉘어 포스팅 하도록 하겠습니다. (이렇게 말해놓고 다음 포스팅이 또 한달을 넘긴다면,, 다들 욕(?)하시겠죠, ^^;;)

지금까지 8번의 포스팅을 진행하면서 제가 예제로 사용했던 WCF 서비스들은 모두 Self Hosting이었습니다.

"응?? Self Hosting 이라뇨? 그건 뭔가요? 먹는건가요?"

라고,, 궁금해 하실 수도 있을 것 같습니다.
Selft Hosting이란,, 특별한 무언가가 있는 것은 아니구요, System.ServiceModel 네임스페이스에 정의 되어있는 ServiceHost 클래스를 사용하여 직접 서비스를 호스팅한 것을 의미합니다.

아시겠죠? 지금까지 제가 사용했던 예제들은 모두 콘솔 어플리케이션 내에서 직접 ServiceHost 클래스를 이용하여 서비스를 호스팅했으니 모두 Self Hosting이 되는 것입니다.
물론, 윈도우 폼 프로그램, WPF 프로그램 그리고, 윈도우 서비스를 이용하여 WCF 서비스를 호스팅할 수 있는데, 이것들 역시 셀프 호스팅(Self Hosting) 인 것입니다.

그럼, 셀프 호스팅을 제외하고 어떤 호스팅 방법이 있을까요?
WCF 서비스를 한번이라도 개발하신 분이라면 아마 사용했을 법한 IIS 호스팅이 있습니다. 그리고 IIS7에서 사용할 수 있는 WAS 호스팅이 있습니다.

IIS 호스팅은 웹 서비스 처럼 IIS를 이용한 호스팅이라 특별한 것도 없고, WCF 서비스를 호스팅하기 위한 가장 쉬운 방법이기도 하구요, 그래서 따로 포스팅을 하지 않아도 될 것 같습니다.

그래서, 이번 포스팅은 WAS 호스팅에 대해서 좀 더 자세히 알아보려 합니다.

WAS(Windows Process Activation Service) 를 이용한 호스팅

WAS는 Windows Process Activation Service의 약자로 IIS 7.0의 기본 구성 요소로서 HTTP 이외의 프로토콜(TCP, MSMQ, Named Pipes)을 사용한 서비스를 호스팅할 수 있게 해주는 역할을 수행합니다.
WAS에 대한 자세한 내용을 알고 싶다면 다음 링크를 참조 하시기 바랍니다. (WAS로 HTTP를 초월한 WCF 서비스 확장)

그럼, 간단하게 WAS가 어떻게 HTTP 이외의 프로토콜을 사용할 수 있게 하는지 알아볼까요?
다음은 WAS 의 아키텍처를 간략하게 표현한 그림입니다.


서버는 서버로 어떤 요청이 들어왔을 때, 그 요청을 처리할 수 있는 수신기를 가지고 있으며, 이 수신기(Listener)는 각 요청의 프로토콜에 맞는 수신기 어댑터(TCP 수신기 어댑터, MSMQ 수신기 어댑터, Named Pipe 수신기 어댑터)로 요청을 보냅니다. 수신기와 WAS 사이에는 수신기 어댑터 인터페이스가 존재하는데, 이를 이용해 각 프로토콜의 수신기 어댑터는 전달받은 요청을 WAS로 보낼 수 있게 되는 거죠. 그리고 WAS 에서는 각 요청에 맞는 응용 프로그램 인스턴스를 생성하기 위해 작업자 프로세스로 요청을 전달하게 되는 것입니다.

이러한 일련의 작업들을 통해 WAS는 HTTP 이외의 프로토콜을 이용한 서비스를 가능하게 하는 것입니다.
설명이 조금 복잡한 것 같지만, 대충 어떻게 돌아가는지는 아시겠죠? ^^;;

그럼, WAS를 이용하여 호스팅을 해보도록 하겠습니다.

WAS는 Vista 이상의 Windows 에서는 IIS를 설치할 때 기본으로 함께 설치가 됩니다. 이 때는 HTTP 수신기 어댑터가 설치 되구요, .NET 3.5 이상의 버전이 설치될 때 TCP, MSMQ, named pipe 수신기 어댑터가 설치 됩니다.

WAS를 이용한 서비스를 만들기 위해 가장 먼저 해야할 것은 IIS를 이용한 호스팅을 하는 WCF 서비스 솔루션을 만드는 것입니다. WAS를 이용한 서비스를 만드는 특별한 방법이 있는 것은 아니며, 기본적으로 WAS는 IIS 7.0의 요소이기 때문에 우선 IIS를 이용한 서비스를 만드는 것이 가장 먼저 해야할 일인 것입니다.

Visual Studio 2010에서 다음 그림처럼 "WCF 서비스 응용 프로그램" 솔루션을 선택합니다.


이렇게 새로운 솔루션을 생성하면, IIS를 이용한 WCF 서비스를 만들 수 있습니다. 기본적으로 Service1.svc 라는 파일이 만들어지는데 이 파일이 WCF 서비스의 로직이 담기는 핵심 파일입니다.(솔루션의 구조는 다음 그림과 같은 모습을 하고 있습니다.)


여기서 서비스의 기능을 바꾸려면 어떤 파일을 수정해야 할까요? 바로 찾을 수 있으시겠죠? IService1.cs 와 Service1.svc.cs 파일을 바꾸어야 합니다. 물론, 이번 포스팅에서의 주제는 WAS를 이용한 호스팅이기 때문에 자동으로 만들어지는 서비스의 로직을 굳이 바꿀 필요는 없을 것 같습니다.

그래~서~!! 저는 따로 서비스의 기능을 바꾸지 않고 자동으로 만들어진 서비스를 그대로 이용해 보겠습니다. 절대 귀찮아서 그러는 것은 아닙니다!! (응? ;;)

솔루션을 만든 후 바로 Ctrl+F5 키를 눌러서 실행을 시켜보도록 하죠~ ㅎ

ASP.NET 솔루션을 실행시킨 것 처럼 ASP.NET Development Server가 시작되고, 웹 브라우저를 이용해 svc 파일을 탐색해 보면 다음과 같이 WCF 서비스 안내 페이지를 만날 수 있을 것입니다.


IIS를 이용한 호스팅은,,. 정말 너무나도 쉽습니다. ServiceHost 클래스를 사용하지도 않고, 자동으로 만들어진 파일 그대로 실행을 해도 무리없이 실행이 되는 것을 알 수 있습니다.

오늘 포스팅의 목적인 WAS를 이용한 호스팅을 구현하기 위해 몇가지 설정을 해보도록 하겠습니다.
바로 이전에 실행했던 서비스는 당연한 이야기겠지만, IIS를 이용한 호스팅이었고, 이는 HTTP 프로토콜을 사용하고 있습니다. 

WAS 호스팅을 실행하기 위해서 우선 서비스 솔루션을 ASP.NET Development Server 가 아닌 IIS에서 실행이 되도록 바꾸어야 합니다. 그래서 솔루션 속성창 "웹" 메뉴에서 "IIS 웹 서버 사용"으로 설정해줍니다. (아래 그림 참조)


IIS 관리자를 실행시키고, 이 사이트가 돌아갈 수 있도록 새로운 사이트를 만들어 주는 것도 잊으시면 안됩니다~ 이 내용은 다들 알고 계시리라 생각하고 생략하겠습니다 ^^

WAS를 이용한 호스팅의 목적은 HTTP 프로토콜 이외의 다른 프로토콜을 사용하기 위함입니다. 그래서 저는 이번 예제에서 TCP 프로토콜을 사용할 수 있는 서비스를 만들어보려 합니다. TCP 프로토콜을 사용할 수 있도록 하기 위해선 IIS 관리자에서 설정을 변경해주어야 할 것이 몇 개 있습니다.

IIS 관리자에서 해당하는 사이트를 선택하고 오른쪽에 위치한 "고급설정"을 클릭하여 설정 창을 띄웁니다. 고급설정 창에서 "사용할 수 있는 프로토콜"에 아래 그림과 같이 "net.tcp"를 추가하여 줍니다.



그리고, 한 가지 더 설정해주어야 할 것이 있는데, 해당 사이트와 특정 프로토콜에 대한 바인딩입니다. 사이트 작업 메뉴에서 "바인딩"을 클릭하여 사이트 바인딩 창을 띄웁니다. 그리고, 추가 버튼을 클릭하여 다음 그림과 같이 net.tcp 프로토콜에 대한 바인딩 정보를 입력하여 줍니다.


여기까지 설정이 완료되면, WAS를 이용하여 호스팅을 하기 위한 IIS 사이트 설정이 모두 끝이 납니다. 어렵지 않죠? ^^

이제, 마지막으로 앞에서 만든 WCF 서비스에 TCP 프로토콜을 사용할 수 있도록 netTcpBinding을 사용하는 엔드포인트를 추가시켜주어야 합니다. WCF 서비스 솔루션에 있는 web.config 파일을 열어보죠.

Visual Studio 2010을 이용해 서비스를 만드셨다면, web.config의 내용이 조금 달라졌다고 느끼실 것 같습니다. 사실, 내용이 많이 달라진 것은 아니고, 그 전 버전에 비해 web.config의 내용이 아주 많이 간략화되었습니다. 매우 매우 심플해졌죠. 이 내용에 대해서 나중에 따로 포스팅을 할 기회가 있을 것이라 생각됩니다.

엔드 포인트를 다음과 같이 포함시켜 보겠습니다.

<system.serviceModel>

    <services>

      <service name="WAS_Hosting.Service1">

        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />

        <endpoint binding="netTcpBinding" contract="WAS_Hosting.IService1" />

      </service>

    </services>
    ...
</system.serviceModel>


service 태그의 name 속성과 endpoint 태그의 contract 속성은 WCF 서비스의 네임스페이스와 클래스 이름을 잘 확인하여 설정해주셔야 합니다.

IMetadataExchange를 컨트랙트로 사용하는 엔드포인트도 같이 설정을 해주는 걸 볼 수 있습니다. 이는 서비스의 메타 데이터에 접근할 수 있는 엔드 포인트란 것을 알 수 있으실 겁니다. 이에 대한 내용은 다음 포스팅을 참조해 주시면 될 것 같습니다. (첫 WCF 서비스 만들기 2)

자~ 이제 모든 설정이 끝났습니다. 여기까지 따라오시느라 수고하셨습니다~ ^^

이 서비스가 제대로 서비스가 되는지 확인해보기 위해 콘솔 프로젝트를 새로 만들고 서비스 참조를 통해 우리가 만들었던 WCF 서비스를 참조해보도록 하겠습니다.

IIS 관리자에서 저는 net.tcp에 대한 바이딩을 할 때 포트번호를 8081로 설정했었습니다. 따라서 그림에서 볼 수 있듯이 "net.tcp://localhost:8081/Service1.svc" 주소로 WCF 서비스에 접근할 수 있습니다.


서비스를 참조한 후에 서비스를 사용하는 방법은 특별한 것이 없습니다. 예전에 셀프 호스팅 서비스를 사용했던 것과 같은 방법으로 사용하면 되기에 클라이언트 쪽 코드는 생략하고, 결과 화면만 첨부하겠습니다.


WCF 서비스가 아주 잘 동작하는 것을 확인할 수 있습니다. 유후~ ^^

이번 포스팅의 내용은 여기까지 입니다.
쓸데없이 내용이 길어진 것 같다는 생각도 들지만, 어쨌든 여기까지 읽어 주셔서 너무 감사드리구요~
다음번 포스팅때 다시 뵙도록 하겠습니다. ^^
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. Hi,
    Thank you for your posting.
    It's very brief and right on to the point.

Posted by 오태겸(RuAA)
안녕하세요~ ^^
오늘 너무 오랜만에 WCF 포스팅을 하게 되었습니다. 자주자주 인사를 드려야 하는데 그렇게 하지 못해 너무 죄송합니다.
오늘 감기 기운 덕분에 약을 먹어놨더니 정신이 헤롱헤롱 하네요 ^^;;
글이 조금 이상하더라도 하해(?)와 같은 마음으로 이해해주시길 바랄께요~

이번 글에서는 serialization(직렬화)에 대해 얘기를 해볼까 합니다.

우선, serialization이 무엇인지 알아야 하겠죠?
컴퓨터 용어에서 serialization은 종종 하나의 오브젝트를 바이트의 배열로 전환(converting)시키는 것을 의미합니다. 하지만 WCF 에서의 serialization은 이러한 의미로 사용되지는 않구요, 하나의 오브젝트(or 닷넷 클래스)를 XML Information Set(XML Infoset) 으로 전환시킨다는 의미로 사용됩니다.

아직은 조금 이해하기가 어렵나요? 음,, 아마 이번 포스팅을 끝까지 읽으면 이해가 되실 겁니다. ^^
이제부터 WCF에서의 Serialization에 대해 조금 더 자세하게 얘기해보겠습니다.

WCF에서는 아래와 같은 네 가지의 serialization을 위한 클래스를 제공합니다.

  • DataContractSerializer
    : WCF 에서 사용하는 기본 serializer 를 제공한다.
  • NetDataContractSerializer
    : 타입에 대한 추가적인 정보를 제공하는 serializer
  • XmlSerializer
    : .NET 2.0에서 제공되었던 serialization을 수행한다.
  • DataContractJsonSerializer
    : serialization 포맷으로 JSON을 제공한다.

이번 포스팅에서는 이러한 클래스들이 직렬화를 했을 때 어떤 결과물을 보여주는지에 대해 써볼까 합니다. 이러한 내용들이 WCF 서비스를 만들때 직접적인 도움이 되진 않겠지만, WCF 서비스를 이해하고, 목적에 맞는 서비스를 만들기 위한 커스터마이징을 하기 위해선 도움이 될 수 있을 것입니다.

DataContractSerializer

DataContractSerializer는 WCF에서 사용하는 기본 serialization을 수행합니다. (이후 부터는 serialization 대신 "직렬화"를 사용하겠습니다.) 이 serializer를 사용하기 위해서는 직렬화가 되는 클래스에 [DataContract] 특성을 지정하여 주면 됩니다. 이 방법은 WCF 서비스를 만들때 많이 사용했던 방법이기에 다들 익숙하리라 생각됩니다.

DataContractSerializer 클래스가 어떤 형태로 직렬화를 수행하는지 알아보도록 하겠습니다. 우선, 다음과 같은 클래스를 생성합니다.

[DataContract]

public class Employee

{
     public Employee(int employeeID, string firstName, string lastName
     {

         this.EmployeeID = employeeID;

         this.FirstName = firstName;

         this.LastName = lastName;

     }

 

    [DataMember]

    public int EmployeeID { get; set; }

 

    [DataMember]

    public string FirstName { get; set; }

 

    [DataMember]

    public string LastName { get; set; }

}


그리고, 콘솔 어플리케이션에서 다음과 같은 코드를 작성합니다.

using System.Runtime.Serialization;

using System.IO;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            Employee e = new Employee(101, "Tae kyeom", "Oh");

            FileStream writer = new FileStream("sample.xml", FileMode.Create);

 

            DataContractSerializer ser = new DataContractSerializer(typeof(Employee));

            ser.WriteObject(writer, e);

            writer.Close();

        }

    }

}


이 코드를 실행을 시켜보면~ 프로젝트의 bin\Debug 폴더안에 sample.xml 파일이 생성됩니다.
코드는 간단하기에 이해하는데 큰 어려움은 없을 것 같지만, 간단히 설명하자면,,, Employee 클래스 인스턴스를 생성하고, 이 오브젝트를 XML 형태로 직렬화를 시킵니다. 그리고 이에 대한 내용을 sample.xml 파일에 썼구요. 그리고, 직렬화 할 때는, DataContractSerializer 클래스를 사용했습니다

그럼, sample.xml 파일의 내용을 확인 해보겠습니다.

네~ 다음과 같은 내용을 보여주고 있네요, 딱 봐도 Employee 인스턴스가 가지고 있는 데이터를 XML로 표현해주고 있다는 것을 알 수 있습니다.

여기서 다시 한번 더 되새겨봐야 할 것은 WCF에서 [DataContract] 특성이 적용된 클래스의 경우 서비스에서 클라이언트로 전달될 때, 위 모습과 같은 형태로 전달된다는 것입니다.


XmlSerializer

XmlSerializer는 이 전 닷넷 버전에서도 지원해주었던 클래스이며, 이는 ASP.NET 웹 서비스에서 사용하던 직렬화 방법을 제공해줍니다.
이러한 직렬화 방법을 WCF 에서도 사용할 수 있으며, 이는 ASP.NET 웹 서비스와 호환이 가능하다는 장점이 있죠. 또한, 이것은 웹 서비스를 WCF로의 전환하는 것이 그리 어려운 일이 아니라는 것을 의미하기도 합니다.^^

XmlSerializer는 public 접근자의 기본 생성자, 필드, 그리고 프로퍼티를 직렬화 시켜줍니다.

Employee 클래스를 다음과 같이 조금 수정해보았습니다.
public 기본 생성자가 필요하기에 추가시켜주었구요, [DataContract]와 [DataMember] 특성을 뺐습니다.

public class Employee

{
    public int EmployeeID { get; set; }

 

    public string FirstName { get; set; }

 

    public string LastName { get; set; }

 

    public Employee()

    {

    }

 

public Employee(int employeeID, string firstName, string lastName)

{
           
this.EmployeeID = employeeID;

            this.FirstName = firstName;

            this.LastName = lastName;

}

}


그리고, 이 클래스를 XmlSerializer 를 이용하여 직렬화 시켜보겠습니다.


using System.IO;

using System.Xml.Serialization;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            Employee e = new Employee(101, "Tae kyeom", "Oh");

            FileStream writer = new FileStream("sample.xml", FileMode.Create);

 

            XmlSerializer ser = new XmlSerializer(typeof(Employee));

            ser.Serialize(writer, e);

            writer.Close();

        }

    }

}


코드는 아주 간단하죠. DataContractSerializer 클래스를 이용했던 코드와 별반 다를건 없구요, 단지 직렬화 할때 XmlSerializer 클래스를 사용했습니다. 그리고 이 클래스엔 Serialize 메소드를 이용해 직렬화를 수행하죠.

sample.xml 파일을 확인해보면 아래와 같은 모습을 하고 있는 것을 알 수 있을 것입니다.


결과로 생성 된 xml 파일 역시 앞 예제에서의 결과 파일과 많이 다르진 않지만, 네임스페이스가 조금 바뀐 것을 확인할 수 있습니다.

아,, 참고로~ WCF에서 XmlSerializer를 사용할 수 있게끔 하기 위해선 서비스 계약(Service Contract)에서 [XmlSerializerFormat] 특성을 적용해주면 됩니다.


DataContractJsonSerializer

DataContractJsonSerializer는 직렬화의 결과로 JSON 형태를 제공합니다.
JSON 형태의 경우 XML 보다 데이터의 양이 적은 장점이 있고, 이 직렬화를 사용하면, 자바 스크립트를 이용하여 서비스를 호출할 수 있어, 웹 어플리케이션에서 서비스를 쉽게 활용할 수 있다는 장점이 있습니다.

WCF 에서는 REST 서비스를 구현하는 경우 이러한 DataContractJsonSerializer를 사용합니다.

앞 예제에서 사용한 Employee 클래스를 DataContractJsonSerializer를 이용하여 직렬화 시켜보도록 하겠습니다.

using System.IO;

using System.Runtime.Serialization.Json;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            Employee e = new Employee(101, "Tae kyeom", "Oh");

            FileStream writer = new FileStream("sample.txt", FileMode.Create);

 

            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Employee));

            ser.WriteObject(writer, e);

            writer.Close();

        }

    }

}


앞 예제와는 다르게 직렬화 결과를 xml 파일이 아닌 텍스트 파일로 썼습니다. 그리고 DataContractJsonSerializer 클래스를 사용하여 직렬화를 수행했습니다.

다음은 그 결과입니다.


멋지게 JSON 형태로 그 결과를 보여주고 있네요. ㅎ


오늘의 포스팅은 여기까지 하려 합니다. (몸이 좋지 않다는 핑계로 성급히 마무리 하려는,, ㅡ,.ㅡ;;;)

오늘 포스팅의 내용은 WCF 서비스를 만드는 팁도 아니고, 서비스를 만드는데 직접적인 영향을 미치는 것은 아니었던것 같습니다. 하지만, 이 부분은 앞에서도 언급했지만, WCF 서비스에 대한 이해를 위해서 알아두어야 하는 부분이라 생각하기에 포스팅을 감행(?)했습니다. 비록, 실무적으로 많은 도움이 되진 못하겠지만 이러한 기본적인 지식도 중요한 부분이니깐요~ ^^

다음 포스팅에서는 WCF 에서 제공하는 기본 직렬화 방법이 적절하지 않을때 커스터마이징을 수행하는 방법에 대해 알아보도록 하겠습니다.

감사합니다.
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 질문이 있는데요. ^^

    본문에 보면,

    "
    하지만 WCF 에서의 serialization은 이러한 의미로 사용되지는 않구요, 하나의 오브젝트(or 닷넷 클래스)를 XML Information Set(XML Infoset) 으로 전환시킨다는 의미로 사용됩니다.
    "

    라고 언급이 되어 있는데, ... 그렇다면 DataContractJsonSerializer 의 출력 결과물이 XML Infoset이라고 볼 수 있다는 것인가요?

    • ㅎㅎ날카로운 지적이십니다. 당연히 DataContractJsonSerializer 의 결과물은 XML이 아닙니다. 따라서 XML Infoset이라고 볼 수 없겠죠,, 이 클래스만은 예외라고 보시면 될 듯 합니다. ^^;;

    • 엔돌핀 2010/07/29 13:27

      좋은글 담아갑니다~

  2. wcf는 처음하는데, 개념잡는데 많은 도움이 됩니다.
    앞으로의 유익한 포스팅도 많이 기대하고 있겠습니다.
    감사합니다.

  3. 정말 많은 도움이 됐습니다. 감사합니다!

Posted by 오태겸(RuAA)
지난 포스팅에 이어서 WCF 서비스의 동시성에 대해 이야기 해보겠습니다.
다른 설명 하지 않고, 지난 포스팅에서 했던 것 처럼 예제를 우선 보고 얘기를 진행해볼까 합니다.

Implementing a Singleton

단 하나의 서비스 인스턴스만이 생성되며, 인스턴스에서 동작하는 스레드 역시, 단 하나만 생성되게 하는 경우에 대해 먼저 살펴보겠습니다.
저번 포스팅에서 사용했던 서비스 클래스의 코드를 다음과 같이 굵은 글씨체 부분만을 수정하여 서비스를 실행해 보시기 바랍니다.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,

            ConcurrencyMode = ConcurrencyMode.Single)]

class ProductService : IProductService

{

    ProductService()

    {

        Console.WriteLine("{0}: !!", DateTime.Now);

    }
          ... 생략 ...
}

이 코드를 실행하면 다음과 같은 결과를 확인할 수 있을 것입니다.

[서버]


[클라이언트]


결과가 여러분이 예상했던 것과 일치하나요?? ㅎ

이 경우 역시 서비스의 인스턴스는 서버 측 결과 화면을 통해 클라이언트의 요청의 수와는 관계없이 하나 만이 생성됨을 알 수 있습니다.
그리고, 클라이언트에서 서비스를 호출하는 방식으로 비동기 방식을 사용했던 것 기억하실겁니다. 결과 그림만을 봐서는 잘 모를 수도 있지만, 이 때문에 클라이언트 측 결과 화면을 보면, 지연시간 없이(각 호출에 대한 결과를 받지 않아도) 서비스를 연속적으로 세 번 호출함을 볼 수 있습니다.
하지만, 이 호출에 대한 결과는 각각 5초간의 지연시간을 두고 화면에 출력하는데, 이는 서버측 결과 화면을 보면 그 이유를 알 수 있습니다. 만약, 서비스 인스턴스가 여러 개의 스레드를 만들어 요청을 처리했다면, 클라이언트의 각 요청에 대한 결과를 거의 동시에 받을 수 있겠지만, 이 경우에는 하나의 스레드 만이 동작하기 때문에 각 요청을 한번에 하나씩 처리할 수 있어 이러한 결과를 얻을 수 있는 것이죠. 참고로, 각 요청에 대한 처리는 FIFO(First In First Out)의 순서로 동작합니다.

서비스 인스턴스에서 단 하나의 스레드 만이 동작한다는 것은 서버 측 결과에서 Thread ID 값이 3으로 동일한 것을 봐도 증명이 가능합니다. 가끔, 이 thread id의 값이 각 요청마다 다른 값이 나올 수도 있습니다. 그렇다고 잘못된 결과값은 아닙니다. ConcurrencyMode.Single한번에 하나의 스레드만이 동작한다는 것을 명시하는 거지, 단 하나의 스레드만이 만들어진다라는 의미는 아니거든요~,, 약간 헷갈릴 수도 있는 부분인 것 같으니, 꼭 명심해주세요,, ㅎ

이 경우처럼 싱글 인스턴스, 싱글 스레드는 한번에 하나의 클라이언트 요청만을 처리할 수 있기 때문에 throughput을 감소시킨다는 단점이 있지만, 반면에 시스템 자원(resource)에 동시 접근 같은 문제가 일어나지 않아서, 이에 대한 추가적인 관리가 필요하지 않다는 장점도 있습니다.

Session-Level Instances

이번에는 세션 모드가 적용되었을 때, 서비스의 인스턴스가 어떻게 생성되는지 한번 살펴보도록 하겠습니다.

우선, 서비스에서 세션을 지원하도록 하기 위해 서비스 계약의 특성값을 다음과 같이 수정합니다.

[ServiceContract(Namespace = "http://RuAAService.co.kr/",

                     SessionMode = SessionMode.Required)]

interface IProductService

{

    [OperationContract]

    Product GetProduct();

}


SessionMode에 Required 값을 적용하여 이 서비스가 세션 모드를 지원해준다는 것을 명시해 줍니다.
다음은 ServiceBehavior 특성에서 InstanceContextMode의 값을 PerSession 으로, ConcurrencyMode의 값을 Multiple 로 수정을 해줍니다. 그리고 GetProduct 메서드가 한 인스턴스에서 몇 번 호출이 이루어지는지를 체크하기 위해 n_Calls 라는 이름의 필드를 추가해 주었습니다. lockThis 필드는 n_Calls 필드의 값을 증가시킬 때 다른 스레드에서 동시에 n_Calls의 값을 바꾸지 못하도록 하기 위한 목적으로 선언해주었습니다.


[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,

            ConcurrencyMode = ConcurrencyMode.Multiple)]

class ProductService : IProductService

{

    object lockThis = new object();

    private int n_Calls = 0;

       
       ... 중간 생략 ...

   
    public
Product GetProduct()

    {

        Console.WriteLine("{0} : GetProduct , Thread Id {1}", 
                            DateTime
.Now, Thread.CurrentThread.ManagedThreadId);

        Thread.Sleep(1000);

 

        Product p = new Product();

        p.ProductId = 1234;

        p.ProductName = "ABC Chocolate";

        p.Price = 1500.0;

        p.Company = "Lotteee";

        p.CreateDate = DateTime.Parse("2010-01-22");

 

        lock (lockThis)

        {

            p.calls = ++n_Calls;

        }

 

        return p;

    }

}


코드를 다 수정하셨으면, 결과를 확인해보도록 하겠습니다.

당연히, 서버 측 콘솔 어플리케이션을 실행 시키신 후에, 클라이언트 어플리케이션을 실행시켜야겠죠,,^^
아,, 이런~ 저와 같은 방법으로 수정을 한 후에 실행 시키면, 아마 예외가 발생하실겁니다.

예외의 이유는 간단합니다. Service Contract에서 세션 모드의 값을 Required로 설정을 해놓았는데, 실제 서비스를 호스팅할 때 세션을 지원하지 않는 BasicHttpBinding을 사용했기 때문입니다. 따라서, BasicHttpBinding을 WSHttpBinding 으로 수정해주시면 이 예외는 발생하지 않을 것입니다.

자~ 이 부분 수정을 다 하셨다면, 다시 실행을 해보도록 하겠습니다.

세션모드의 지원을 확인하기 위해서 클라이언트 어플리케이션을 두 개 연속해서 실행을 시킵니다.

다음은 서버 어플리케이션의 실행 화면입니다.


인스턴스가 두 개 생성된 것을 확인할 수 있네요,, 왜 인스턴스가 두 개 생성되었을까요?? 당연히 클라이언트 어플리케이션이 두 개 실행이 되었기 때문입니다. 세션을 지원하는 서비스이니깐요,,

자~ 다음은 두 클라이언트 어플리케이션의 실행화면입니다.





각 클라이언트는 호출한 횟수가 1~3인 것을 확인할 수 있습니다. 이것은 각 클라이언트 마다의 서비스 인스턴스가 따로 데이터를 유지한다는 것을 의미하는 것이죠. 이해 되셨죠?? ^^

이번 포스팅은 이것으로 마무리 하려 합니다.
이번에도 포스팅이 조금 늦었습니다. 바로 하려고 했는데 이게 마음처럼 쉽지가 않네요. ^^;;

어찌됐든, 다음 포스팅때 뵙도록 하겠습니다. ㅎ
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 좋은 글 감사합니다.

Posted by 오태겸(RuAA)

봄이 오고 있네요,, 
날씨가 많이 따뜻해졌고, 해도 부쩍 길어졌음을 느낍니다.
최대한 빠른 시일 내에 포스팅을 하려 했는데, 그동안 무기력증(?)에 빠져있다보니,,
하는거 없이 시간만 보내버렸네요,, ^^;;
봄이 찾아온 만큼 새로운 마음가짐으로 다시 시작해보겠습니다. 아자~!

이번 포스팅의 주제는 WCF의 Behaviors 중에서 서비스의 동시성(Concurrency)을 컨트롤 할 수 있는 Behavior 입니다.

Behavior는 서비스가 동작할 때(그러니깐 런타임 시) 동작에 영향을 끼치는 클래스들로, 서비스 클래스의 특성으로 지정하거나, 환경 설정파일을 통해 지정할 수 있습니다.

Behavior와 관련된 여러 가지 내용 중 이번 포스팅에선 동시성(Concurrency)에 대해서 얘기해보도록 하겠습니다.

동시성이라 함은, 여러 task 들이 동시에 동작하는 것을 말합니다.
동시성은 다들 아시겠지만, 그리고 아주 당연하게도 시스템의 throughput(출력률)에 큰 영향을 끼칩니다. 일정 시간동안 처리할 수 있는 작업의 양이 커지기 때문이죠.

WCF 에서는 동시성을 컨트롤할 수 있는 두 종류의 behavior 가 있습니다. 바로 “InstanceContextMode”“ConcurrencyMode” 입니다.

InstanceContextMode는 생성되는 서비스의 인스턴스를 조절할 수 있는 behavior로 다음과 같은 세 종류의 값으로 설정할 수 있습니다.

  • Single : 이 값은 서비스로 들어오는 모든 요청을 하나의 인스턴스에서 처리하도록 설정합니다.
  • PerCall : 서비스로 들어오는 요청마다 서비스의 인스턴스가 만들어지도록 하기 위한 설정입니다.
  • PerSession : 클라이언트 세션마다의 서비스 인스턴스를 생성하기 위한 설정이며, 만약 세션을 사용하지 않는 채널일 때, 이 값으로 설정이 된다면, PerCall과 같은 방식으로 동작합니다.

그리고, InstanceContextMode의 기본값은 PerSession으로 따로 어떠한 값도 설정되어 있지 않은 경우엔 세션 수에 따라 서비스 인스턴스가 생성됩니다.

WCF에서 동시성을 조절할 수 있는 또 다른 모드인 ConcurrencyMode는 하나의 서비스 인스턴스 내에서 동작하는 스레드를 통한 동시성을 컨트롤하는 behavior입니다.
다음은 ConcurrencyMode에서 설정할 수 있는 값에 대한 설명입니다.

  • Single : 하나의 서비스 인스턴스 내에 오로지 하나의 스레드만이 동작하도록 설정하는 값입니다. 따라서, 이 값으로 설정되어 있는 경우엔 스레딩 문제를 고려하지 않아도 된다는 장점이 있습니다.
  • Reentrant : 이 설정 역시 하나의 서비스 인스턴스에서 하나의 스레드만이 동작하도록 하는 설정값입니다. 하지만, 이 설정값이 Single과 다른 점은 하나의 스레드가 동작하는 도중에 다른 작업이 처리될 수 있다는 것입니다. 이 작업의 처리가 완료되면 이 전의 작업이 계속해서 동작됩니다.
  • Multiple : 하나의 서비스 인스턴스에서 하나 이상의 스레드가 동작할 수 있도록 하는 설정입니다. 이 값으로 설정되어 있는 경우엔 여러 개의 스레드에서 서비스 개체를 변경할 수 있기 때문에 항상 동기화와 상태 일관성을 처리해 주어야 합니다.

ConcurrencyMode와 InstanceContextMode의 값을 적절하게 조합하면, 서비스의 기능에 맞게 동시성과 인스턴스 관리를 할 수 있습니다. 지금 이러한 내용을 글로 써내려가봤자 설명하기도 힘들고, 받아들이기도 힘이 들겁니다. 따라서 이러한 내용은 역시 실제 코드를 작성하고, 결과를 보면서 이해하는게 가장 쉽고 빠른 방법이겠죠 ^^

네~ 이제 InstanceContextMode와 ConcurrencyMode의 값을 적절하게 조합하여 서비스에 적용하는 실습을 해보도록 하겠습니다.

우선, 가장 먼저 세션을 사용하지 않는 환경에서 InstaceContextMode와 ConcurrencyMode의 기본값을 사용한 서비스를 구현해보겠습니다. InstanceContextMode의 기본값은 PerSession 이며, ConcurrencyMode의 기본값은 Single 입니다. 이 기본값은 따로 설정해주지 않아도 적용된다는거 아시죠? ㅎ

다음은 서비스를 구현한 클래스의 코드 입니다.

class ProductService : IProductService

{

    ProductService()

    {

        Console.WriteLine("{0}: 서비스의 새로운 인스턴스 생성!!", DateTime.Now);

    }

 

    public Product GetProduct()

    {

        Console.WriteLine("{0} : GetProduct 호출, Thread Id {1}", DateTime.Now,
Thread.CurrentThread.ManagedThreadId);

        Thread.Sleep(5000);

 

        Product p = new Product();

        p.ProductId = 1234;

        p.ProductName = "ABC Chocolate";

        p.Price = 1500.0;

        p.Company = "Lotteee";

        p.CreateDate = DateTime.Parse("2010-01-22");

 

        return p;

    }

}


저번 포스팅에서 사용했던 서비스의 코드를 살짝 수정 해보았습니다.
서비스 클래스 생성자를 만들어 단순하게 인스턴스가 생성되었다는 메시지를 출력해주는 코드를 추가하였구요, GetProduct 메서드 내에서는 현재 스레드의 ID 값을 출력해주는 코드를 추가하였습니다.

다음은 이 서비스를 호출하는 클라이언트 코드입니다. 코드를 보시면 아시겠지만 클라이언트에서 서비스 메서드를 비동기로 호출하고 있습니다. 혹시 WCF 서비스를 비동기로 호출하는 클라이언트를 만들어보시지 않은 분이 계시면 제가 예전에 올렸던 포스팅을 참고해주시기 바랍니다. (http://ruaa.tistory.com/entry/async-call) 자세한 설명은 없지만 대충은 이해하실 수 있으실겁니다 ^^;;

namespace MySvcAsyncClient

{

    class Program

    {

        static int c = 0;

        static void Main(string[] args)

        {

            ProductServiceClient proxy = new ProductServiceClient();

            for (int i = 0; i < 3; i++)

            {

                Console.WriteLine("{0}: GetProduct 메서드 호출", DateTime.Now);

                proxy.BeginGetProduct(GetProductInfoCallback, proxy);

                Thread.Sleep(100);

                Interlocked.Increment(ref c);

            }

            while (c > 0)

            {

                Thread.Sleep(100);

            }

        }

 

        static void GetProductInfoCallback(IAsyncResult ar)

        {

            ProductInfo productInfo = ((ProductServiceClient)ar.AsyncState)
                                           .EndGetProduct(ar);

            Console.WriteLine("{0} : ProductName : {1}",
                                        
DateTime.Now, productInfo.Name);

            Interlocked.Decrement(ref c);

        }

    }

}


Main 메소드 내에서는 for 문을 사용하여 3번 반복하여 GetProduct 메소드를 비동기로 호출하고 있으며, 각각의 비동기 호출에 의한 작업이 끝이 나면 AsyncCallback 대리자인 GetProductInfoCallback 메소드가 호출되며, 서비스에서 받은 Product 데이터를 화면에 출력해줍니다.

이렇게 코드를 작성하고 나면, 역시 결과가 궁금해 질겁니다. 다음은 이 코드에 대한 결과 화면입니다.

[서버]


[클라이언트]


클라이언트 측 결과 화면을 보면 동시에 서비스의 메소드를 세번 호출하는 것을 확인할 수 있습니다. 그리고 6초 정도의 시간 후에 차례대로 결과값을 가져와서 출력하는 것을 볼 수 있습니다.

서비스 측 결과 화면을 확인해 보면, 각 호출마다 생성자를 통해 새로운 인스턴스를 생성하고, 인스턴스 내에 하나의 스레드를 통해 GetProduct 메소드를 호출하는 것을 확인할 수 있습니다.

여기서 잠깐 의문이 들지도 모르겠습니다. 제가 분명, InstanceContextMode의 기본값은 PerSession 이라고 했는데 왜 서버에선 클라이언트의 호출마다 새로운 인스턴스를 생성한 것일까요?

답은 아주 간단합니다. 서비스를 호스팅할 때 사용했던 binding의 종류가 BasicHttpBinding 이었던 것 기억하시나요? BasicHttpBinding의 경우엔 세션을 사용하지 않기 때문에, 이 경우엔 실제로 InstanceContextMode.PerCall 과 같은 형식으로 동작하게 되는 것입니다.

기본값으로 설정한 경우를 알아봤으니, 이번엔 두 모드의 값을 바꿔서 서비스에 적용해보겠습니다.

인스턴스는 모든 호출에 대해 하나만 생성하도록하고, 스레드의 갯수는 하나 이상으로 만들 수 있게끔 설정한 후에 결과값을 살펴보죠~

앞에서 한번 언급했지만 서비스의 Behavior를 적용하는 방법은 서비스 클래스에 특성으로 설정하는 방법과 config 파일에 설정하는 방법이 있습니다. 여기서는 클래스에 특성으로 설정하는 방법을 사용해보겠습니다.

서비스 클래스의 코드를 다음과 같이 굵은 글씨로 적용된 부분만을 추가해보죠~

class ProductService : IProductService

{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,

            ConcurrencyMode=ConcurrencyMode.Multiple)]

    ProductService()

    {

        Console.WriteLine("{0}: 서비스의 새로운 인스턴스 생성!!", DateTime.Now);

    }

 

    ... 생략 ...

}


이렇게만 수정한 후에 솔루션을 실행시켜 보면, 클라이언트 측 화면은 변화가 없지만 서버 측 결과 화면은 다음과 같이 변화된 것을 확인하실 수 있으실겁니다.



달라진 점이 무엇인지 보이시죠? ^^

네,, 맞습니다. 인스턴스가 하나만 생성되었다는 점이죠. 아~ 그러고보니 동작한 스레드의 ID 값들이 모두 다른 것도 보이네요. 이 말은 곧, 하나의 인스턴스에 여러 개의 스레드가 생성되었다는 것을 의미하는 것이겠죠. 앞에서 설정했던 InstanceContextMode의 값과 ConcurrencyMode의 값이 어떻게 서비스의 동시성에 적용되었는지 이해가 가실겁니다.

이 외에도 서비스의 동시성에 적용할 수 있는 두 모드의 조합이 더 있지만, 다음 포스팅에서 더 다루도록 하겠습니다. 글도 길어졌고, 아직 담아야 할 내용도 많으니깐요.
이번에는 정말 다음 포스팅때 까지 많이 걸리지 않을 것입니다. 약속드릴께요~ ^^;;

제 포스팅에 항상 댓글 남겨주시고 응원해주시는 분들께 감사 드리며, 또 너무 오랜만에 글을 남겨 죄송한 마음도 듭니다. 제가 잠깐 주춤하긴 했지만, 앞으로는 계속 꾸준한 모습 보여드리려 노력하겠습니다. ^^

감사합니다.
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 붉은이리 2010/05/03 15:04

    항상 좋은 내용 감사합니다...

    많이 도움이 됩니다...ㅎㅎ

  2. coolpixer 2010/06/22 11:22

    글 잘 보고 있습니다.
    다만 위의 내용 가운데

    "우선, 가장 먼저 세션을 사용하지 않는 환경에서 InstaceContextMode와 ConcurrencyMode의 기본값을 사용한 서비스를 구현해보겠습니다. InstanceContextMode의 기본값은 Single 이며, ConcurrencyMode의 기본값은 PerSession 입니다. 이 기본값은 따로 설정해주지 않아도 적용된다는거 아시죠? ㅎ"

    이 부분에서 InstanceContextMode와 ConcurrencyMode의 설정값이 뒤바뀐 것으로 보입니다.

Posted by 오태겸(RuAA)
2010년도 어느새 한달이 지나가고 두번째 달이 되었습니다. 한달 한달이 왜 이렇게도 빨리 지나가는지... 할 일은 많은데 시간만 무심하게 지나가는 것 같습니다... 드래곤 볼의 "시간의 방" 같은 곳이 있었으면 하는 허무한(?) 생각도 들고.. ㅎ

이번 포스팅은 저번 주제에 이어서 Contract 중 Data Contract에 대해 이야기 해볼까 합니다.

간단히 얘기하면, Data Contract는 WCF 서비스에서 사용하는 개체에 대한 정보를 클라이언트에서 인지할 수 있게끔 XSD 형태로 매핑시켜주는 역할을 합니다.
저번 포스팅에서 충분히 설명했듯이 클라이언트에서는 서비스에 대한 정보를 WSDL을 통해 얻게 됩니다. 이때, 서비스에서 사용하는 개체에 대한 정보 역시 이 WSDL을 통해 전달되는데, 이렇게 XSD 형태로 매핑시켜주는 역할을 하는 것이 Data Contract 입니다.

그럼, 일단 코드를 한번 보고 설명을 이어 나가겠습니다. 역시, 말보다는 코드를 봐야 "아~ 이게 Data Contrat 구나~" 하실겁니다,, ㅋ

저번 포스팅에서 만들었던 서비스에 다음과 같이 Product라는 이름의 새로운 클래스를 추가하였습니다.

using System.ServiceModel;

using System.Runtime.Serialization;

 

namespace MyService

{

    [DataContract]

    public class Product

    {

        [DataMember]

        public int ProductId;

 

        [DataMember]

        public string ProductName;

 

        [DataMember]

        public string Company;

 

        [DataMember]

        public double Price;

 

        [DataMember]

        public DateTime CreateDate;

    }

}

ServiceContract 속성을 줬듯이 DataContract 속성(attribute)은 서비스에서 사용할 새로운 클래스에 지정해 주면 됩니다. 그리고 이 클래스의 필드들에겐 DataMemer 속성을 줬습니다.

이제 이 클래스를 서비스에서 사용하도록 기존 서비스의 코드를 다음과 같이 살짝 바꾸었습니다.

[ServiceContract(Namespace = "http://RuAAService.co.kr/")]

interface IProductService

{

    [OperationContract]

    Product GetProduct();

}

class ProductService : IProductService

{

    public Product GetProduct()

    {

        Product p = new Product();

        p.ProductId = 1234;

        p.ProductName = "ABC Chocolate";

        p.Price = 1500.0;

        p.Company = "Lotteee";

        p.CreateDate = DateTime.Parse("2010-01-22");

 

        return p;

    }

}


사용하는 방법은 특별히 따로 존재하지 않습니다. 다른 클래스들 처럼 그냥 사용하면 되는 것입니다. 여기서 조금 주목해야할 점은 GetProduct 메소드의 반환 타입이 우리가 새로 생성한 Product 클래스라는 것입니다.

Product  클래스는 서비스에서 생성한 클래스이기 때문에 클라이언트에선 알 수 없는 타입이겠죠. 서비스를 이용하는 프로그램이 아닐때는 그냥 참조 추가를 이용해 dll 파일을 참조하여 다른 클래스를 사용할 수 있지만, 서비스를 사용할 땐 이 방법을 쓸 수는 없습니다.

이러한 문제(?)를 해결하는 방법이 바로 wsdl에 이 클래스에 대한 정보를 추가하여 클라이언트에서 인지할 수 있게 하는 것인데, 이걸 Data Contract가 해주게 되는 것입니다.

그럼, 이 클래스에 대한 정보를 닷넷 컴파일러가 어떤 형태의 xsd로 매핑시켜주는지 눈으로 확인해 보겠습니다.

서비스를 실행시킨 상태에서 브라우저를 이용하여 http://localhost:8000/ProductService?wsdl=wsdl0 위치로 이동해 봅니다. 그럼, <wsdl:types> 라는 엘리먼트를 볼 수 있고, 그 밑에 서비스에서 사용하는 여러 가지 스키마에 대한 정보를 명시한 엘리먼트를 확인할 수 있습니다.


이 스키마들 중에 http://localhost:8000/ProductService?xsd=xsd2 위치로 이동해보겠습니다. 그럼 다음과 같은 내용을 확인할 수 있을 것입니다.


complexType 엘리먼트는 우리가 생성한 클래스의 이름을 나타내고 있고, 그 밑으로는 클래스의 멤버들에 대한 정의가 엘리먼트로 포함되어 있는 것을 확인할 수 있습니다.

이제 어떻게 클라이언트에서 이러한 클래스에 대해 인지할 수 있는지,, 아시겠죠? ^^

아~ 참고로 클래스의 필드들을 DataMember 속성을 이용해 노출을 했었는데, 만약 필드에 DataMember 속성을 적용시키지 않으면 아예 노출이 안된다는 것을 유념해주시기 바랍니다.
그리고, 필드들의 한정자가 public 이든, private 이든 이 역시 상관없다는 것도 알아두시면 좋을 것 같습니다. 오로지 DataMember 속성이 적용되었는지만 신경을 써주시면 됩니다.

DataContract 와 DataMember는 몇 개의 프로퍼티(property)를 가지고 있습니다.

DataContract는 Name과 Namespace 프로퍼티를 가지고 있는데, 이는 ServiceContract가 가지고 있는 프로퍼티와 같은 역할을 하는 녀석들이기 때문에 따로 긴 설명을 필요없을 듯 합니다.

그리고, DataMember 는 Name, Order, IsRequired 라는 프로퍼티들을 가지고 있습니다.
Name은 클라이언트에 노출되는 멤버의 이름을 명시적으로 설정하는 역할을 하구요, Order는 클라이언트에 멤버들이 노출될 때 그 순서를 정하는 역할을 수행합니다. (이 순서를 명시적으로 설정하지 않으면 기본적으로 멤버들은 알파벳 순으로 xsd에 나타나게 됩니다.) 사실 .NET 어플리케이션 끼리는 이 순서가 그리 중요하지 않습니다. 하지만 다른 플랫폼의 어플리케이션 간의 상호 운용성을 고려할 땐 중요할 수가 있습니다. 클라이언트로 부터 메시지를 받았을 때, 이 순서가 다르면 서버에선 인식할 수가 없게 되어버리거든요.
IsRequired는 멤버가 적어도 한번은 포함이 되어야 하는지에 대한 정의를 내려주는 역할을 합니다. 이 프로퍼티의 값이 true인 경우엔 설정된 멤버는 메시지에 적어도 한번은 꼭 포함되어야 함을 의미하는 것이죠~

그럼, 이러한 속성들을 적용했을 때, 어떻게 xsd로 표현되는지 한번 보도록 하겠습니다.
Product 클래스를 다음과 같이 살짝 수정해보았습니다.


[DataContract (Namespace="http://RuAAService.co.kr/Product", Name="ProductInfo")]

public class Product

{

    [DataMember(Name = "ID", Order = 1, IsRequired = true)]

    public int ProductId;

 

    [DataMember(Name = "Name", Order = 2, IsRequired = true)]

    public string ProductName;

 

    [DataMember(Order = 3)]

    public string Company;


   
[
DataMember(Name = "Value", Order = 4, IsRequired = true)]

    public double Price;

 

    [DataMember(Order = 5, IsRequired = true)]

    public DateTime CreateDate;

}

이제 xsd를 어떻게 확인하시는지 아시죠? xsd를 확인해 보면 다음과 같이 나타남을 확인할 수 있으실 겁니다.


complextType 엘리먼트에 name 속성의 값이 바뀌고, 네임스페이스의 값도 바뀌고,, 여러가지가 바뀐 것을 확인할 수가 있습니다. 프로퍼티 설정과 xsd를 비교해가면서 어떻게 적용되는지 유심히 살펴보시기 바랍니다. ^^

Company 멤버의 경우 IsRequired 속성을 따로 명시해주지 않았는데, 이 경우엔 minOccrus=0 으로 표현되는 것을 볼 수가 있습니다. 이게 기본값이며, 포함되지 않아도 됨을 나타내주는 것이죠~

이렇게 해서 Data Contract에 관한 기본적인 내용은 끝이 난 것 같습니다.

마지막으로, 수정한 이 서비스를 이용하도록 클라이언트 코드 역시 수정을 조금 해보구요~ 결과 화면을 보는 것으로 이번 포스팅을 마치도록 하겠습니다.

수정한 클라이언트의 코드는 다음과 같습니다.

static void Main(string[] args)

{

    // 프록시 클래스 인스턴스 생성

    ProductServiceClient myService = new ProductServiceClient();

    // 서비스 Operation 호출

    ProductInfo product = myService.GetProduct();

 

    Console.WriteLine("Product ID : {0}", product.ID);

    Console.WriteLine("Product Name: {0}", product.Name);

    Console.WriteLine("Made by :{0}", product.Company);

    Console.WriteLine("Price : {0:c}", product.Value);

    Console.WriteLine("Created Date : {0}", product.CreateDate.ToShortDateString());

}

이 코드에 대한 추가적인 설명은 필요없겠죠? 이제 다들 "이까이꺼~" 하실테니깐,, ^^

수정을 모두 완료한 후에 서비스와 클라이언트를 동시에 실행시키면 다음과 같은 결과 화면을 확인하실 수 있으실 겁니다. 


네~ 무사히 서비스에서 Product 클래스에 대한 데이터를 받아왔고, 이 데이터를 화면에 잘 뿌려주는군요...

아직까지는 WCF의 기본이 되는 내용에 대해서만 포스팅을 하고 있기에 그렇게 어렵지 않으실 거라 생각합니다.
저도 WCF에 대해 마스터하지 못하였지만, WCF를 이제 시작하는 분들께 조금이라도 도움이 되었으면 하는 마음으로 포스팅을 하고 있는 것이라, 아주 기본이 되는 내용들을 주제로 진행하고 있습니다.

이렇게 하나씩 채워나가다 보면, 언젠가 좀 더 복잡한 주제로 포스팅을 할 수 있는 시간도 올꺼라 생각합니다.
너무 조급해하지 마시고 따뜻한 시선으로 지켜봐주셨으면 합니다.
(뭐,, 질책이나 조언도 감사한 마음으로 받겠습니다 ^^)

그럼, 다음 포스팅때 뵙겠습니다 ^^
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 저렙프로그래머 2010/02/09 09:43

    일빠네요 ㅎㅎ 강좌 잘 보고 있습니다. 친절한 강좌 감사드립니다.

  2. 잘 보고 있습니다. ^^ 다음 강좌도 빨리 나오면 좋겠네요. ^^

  3. 안녕하세요. 감사히 보고 있는데
    .NET 3.5 (2008)에서는 DataContract가 없는데 혹시 다른 방식으로 구현이 가능한지요?

    • .NET 3.5 버전에서도 DataContract는 존재합니다. System.Runtime.Serialization을 참조 추가하시고 using 구문을 이용해서 네임스페이스를 추가하시면 사용할 수 있으실거에요~ ^^

Posted by 오태겸(RuAA)
지금까지 세번의 포스팅으로 WCF의 기초에 대해 알아보았습니다.
기초를 뗏으니, 이제 기본으로 넘어가야죠~ ^^

WCF의 기본 중에서 가장 먼저 알려드리고 싶어 꺼내 든 주제는 Contract 입니다.

Contract의 사전적 의미는,, "계약" 이죠.
(네이X 사전에서 찾아보니 계약이란 의미 외에 살인 청부, 줄어들다, 수축하다,, 등의 의미가 있군요,,)

그럼, "계약"의 사전적 의미는 무엇일까요? (약간 글이 엉뚱한 방향으로 흐르는 것 같지만,, ^^;;)
역시, 네이X 국어 사전에서 찾아본 결과,, 
"관련되는 사람이나 조직체 사이에서 서로 지켜야 할 의무에 대하여 글이나 말로 정하여 둠" 이라고 합니다.

잘 아시겠지만 계약은 약속이랑 비슷하지만 약속 보다는 좀 더 강력한 의미로 쓰입니다.(법적 효력도 있죠,,)

다시, 본론으로 돌아와서,, 그럼, WCF 에서의 Contract 는 무엇을 말하는 걸까요?
네,, 다들 예상 하셨겠지만, WCF 에서의 Contrat 역시 사전적 의미와 비슷하게 쓰이며, WCF 서비스와 클라어언트 사이에 어떤 계약을 정의할 때 사용하는 것을 말합니다.

좀 더 자세하게 설명을 하자면, 이 Contract 라는 것은 서비스와 클라이언트가 서로 통신할 때 사용하는 메세지의 명세(specification)를 정의하는 것을 말합니다.

WCF 는 총 세가지 타입의 Contract가 있으며, 다음은 이 타입들에 대한 정의입니다.

  • Service contract : 서비스에 의해 구현되는 기능들에 대해 설명하며, 서비스 계약(service contract)으로 정의 된 .NET 타입의 클래스는 WSDL의 services, port types의 엘리먼트로 매핑됩니다. 서비스 계약과 함께 Operation contract는 서비스 계약 내에서 정의되며, 서비스의 동작(operation)을 설명합니다.

  • Data contract : 서비스가 통신(communication)을 하는데 사용하는 데이터 구조를 나타냅니다. 이는 CLR 타입을 XSD(XML Schema Definitions) 로 매핑해주는 역할을 수행하는데, WCF 가 통신할 때 사용되어지는 데이터들을 어떻게 직렬화(serialization) 또는 역직렬화(deserialization)를 수행하는지를 설명합니다.

  • Message contract : 메세지 계약은 CLR 타입을 SOAP 메세지로 매핑해주며, SOAP 메세지의 포맷을 설명합니다. 메세지 계약은 SOAP 헤더에서 바디까지 컨트롤할 수 있게 해줍니다.

예전 포스팅에서 몇 번 언급한 점이기 때문에 다들 아실거라 생각하는데,, WCF 서비스는 많은 시스템과의 상호 운용성(interoperability)을 높이기 위해 WSDL을 사용합니다. 위에서 설명한 세 가지의 Contract 들은 WCF 서비스나 서비스에서 사용하는 여러 데이터들을 WSDL 또는 SOAP의 요소들로 매핑 시키는 역할을 수행합니다. 이 말은 곧, 서비스와 통신하기 위한 메세지의 포맷을 정의한다는 말이기도 하지요.(앞에서 언급했듯이,,)

이제 Contract의 정의에 대해선 조금 이해가 가시지요? ^^

그럼, 이러한 Contract들이 어떻게 쓰이는지 하나씩 살펴보겠습니다.
우선, Service Contract 와 Operation Contract에 대해 알아보구요, 한 두번에 걸쳐서 Data Contract와 Message Contract에 대해서도 알아보도록 하겠습니다.

서비스 계약은 지금까지 몇번 언급했던 것 같습니다.
다시 한번 더 얘기하자면, 서비스 계약은 서비스가 제공하는 여러 기능(동작)들을 정의하는 인터페이스이며, 서비스 자체를 정의한다고 생각하면 될듯 합니다. 
그리고, 서비스에서 제공하는 기능들을 외부로 노출(?)하기 위해 사용하는 Operation Contract가 있습니다. 이는 Service Contract 내부에서 정의됩니다.

백문이 불여일견이니, 코드를 한번 보도록 하죠. 음,, 예전에 만들었던 코드를 다시 살펴 보면서 이해하는게 좋을 것 같네요 ^^

[ServiceContract]
interface IProductService
{
    [OperationContract]
    string GetFirstName(string empID);
}


우리가 만들었던 첫 WCF 서비스의 코드 중 일부입니다.
서비스는 ServiceContract 특성을 인터페이스에서 선언하고, 그 메소드에 OperationContract 특성을 선언해줌으로써, 서비스 계약을 정의해주었습니다.
기억하시겠지만, 실제 구체적인 서비스의 기능은 Service Contract가 선언된 인터페이스를 상속 받아 구현해야 했었죠.

그럼, 이렇게 서비스가 정의됐을 때 WSDL이 어떻게 만들어지는지 살펴보겠습니다.
우리가 작성했었던 첫 WCF 서비스를 동작시킨 상태에서 웹 브라우저를 이용해 http://localhost:8000/ProductService?wsdl 로 접근해 보면 다음과 같은 화면을 확인할 수 있습니다.


이것이 바로 WCF 서비스가 제공하는 WSDL입니다.
앞에서 언급했듯이 Service Contract로 정의 되어진 정보는 WSDL의 "service", "port type" 엘리먼트로 매핑된다고 하였습니다. 또한 Operation Contract로 정의된 부분은 "operation" 엘리먼트로 매핑된다고 했었죠,, 
이것 역시 다음과 같이 확인할 수 있었습니다.





이렇게 WCF를 이용하여 만들어진 서비스는 WSDL로 제공되어지며, 클라이언트에서 서비스를 사용할 수 있게 되는 것입니다.

그리고, ServiceContract 특성 클래스에는 Namespace 속성을 제공합니다. 이 속성은 WSDL과 SOAP 메세지의 Namespace의 값을 명시적으로 설정할 수 있게 합니다.

다음과 같이 기존의 서비스를 조금 수정해 보았습니다.

[ServiceContract(Namespace="http://RuAAService.co.kr/")]
interface IProductService
{
    [OperationContract]
    string GetFirstName(string empID);
}


그리고, 다시 이 서비스의 WSDL을 확인해보면, 서비스의 네임스페이스가 다음과 같이 바뀌어져있는 것을 확인할 수 있습니다.



자~ Service Contract에 대한 설명은 여기까지 입니다.
이번에는 실습보다는 이론적인 설명이 위주였습니다. 그래서 조금 재미가 없을 수도 있을 것 같네요 ㅎ
하지만, 이론도 중요하다는 것,, 아시죠? ^^
아무 생각없이 닷넷 인터페이스 만들고 ServiceContract 특성을 주는 것 보다 이러한 작업으로 서비스가 어떻게 클라이언트에 노출되는지를 알고 있는 것이 서비스를 구성하고, 클라이언트를 개발하는데 있어서 많은 도움을 줄 것이라 확신합니다.

이 글 역시, 많은 분들에게 조금이라도 도움이 되었으면 하는 바람을 가지면서 이만 줄이겠습니다 ^^
다음 포스팅때 뵙죠~ ㅎ
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 좋은 글 너무 감사합니다.

Posted by 오태겸(RuAA)
요즘, 날씨가 심상치 않습니다. 세계적으로 여기저기서 한파, 폭설로 난리도 아니더군요~
몇몇의 과학자들은 지구가 예전 온도를 찾기 위한 자정작용을 하고 있는 것이라 하는데,,
어쨌든 지구가 정상이 아니란 건 사실인 것 같습니다.
설마,, 2012년에 정말 지구가 멸망하는건 아니겠죠? ^^;;;;;

WCF 세번째 이야기를 시작하겠습니다~ ^^
이번에는 지난 포스팅때 다 하지 못한 내용에 대해 이야기를 해볼까 합니다.
지난 포스팅에서는 Console 어플리케이션을 이용하여 간단한 WCF 서비스를 구현하였었죠,,
서비스를 구현하였으니, 이제 이 서비스를 이용할 수 있는 클라이언트를 구현해야 할 때입니다.

클라이언트도 Console 어플리케이션을 이용해보도록 하겠습니다. 지난번 서비스를 만들었던 같은 솔루션내에 새로운 프로젝트를 추가합니다. (새로운 솔루션을 만들어도 상관은 없습니다 ^^) 아래 그림과 같이 저는 "MyServiceClient"라는 이름의 콘솔 어플리케이션 프로젝트를 추가 하였습니다.


다음으로 클라이언트 프로젝트에 지난번 만들었던 서비스를 참조 추가해야합니다. 서비스를 참조 추가하는 방법은,, 다들 예상 했겠지만,, 아!주! 쉽습니다. ㅎ
비주얼 스튜디오 Solution Explorer의 클라이언트 프로젝트에서 마우스 오른쪽 클릭을 해주면, 다음과 같은 메뉴가 나타납니다. 여기서 "Add Service Reference"를 선택합니다.


그러면, 서비스를 참조 추가할 수 있는 창이 뜹니다. 이 창의 Address 텍스트 박스에"http://localhost:8000/ProductService" 를 입력하고, "Go" 버튼을 클릭합니다. (방금 입력한 주소는 서비스 엔드포인트 주소입니다. 기억나시죠?? ㅎ) 이렇게 하면~ 서비스를 찾는 메시지가 뜨고 얼마 뒤 다음 그림과 같이 서비스를 찾았다는 메시지가 뜨는 것을 확인할 수 있습니다.
참~!! 이때, 아주 당연한 얘기지만, 서비스가 실행 중이어야 합니다. ctrl + F5 를 눌러 서비스를 실행하는 콘솔 창을 띄워놓은 상태에서 서비스를 찾아야 합니다.


Namespace 텍스트 박스엔 "ServiceReference1"이라고 적혀있지만, 이는 개발자 마음대로 변경 가능합니다. 그래서, 저는 "MyService"라고 수정 후에 "OK" 버튼을 클릭하였습니다.

이 작업을 모두 완료하면 Solution Explorer의 모습이 다음 그림과 같이 조금 바뀌게 됩니다.


"Service Reference" 라는 폴더가 새로 생겼고, 그 밑에 방금 등록했던 "MyService"가 추가 되어있는 것을 확인할 수 있습니다.
이로써, 클라이언트에서 서비스를 사용할 준비는 거의 끝났습니다. 너무 쉬운가요? ^^ 

그렇습니다. 아주 쉽죠,, 이렇게 개발자가 쉽게 작업을 할 수 있는 건 역시 대부분의 일을 비주얼 스튜디오가 자동으로 해주는 일이 많기 때문입니다.

비주얼스튜디오가 자동으로 해주는 일은 바로, 서비스와 통신할 수 있는 proxy 클래스를 생성 해 주는 것입니다. 이는 예전 닷넷 웹서비스를 참조 했을 때, proxy 클래스를 생성해 주던 것과 아주 비슷합니다. 여기서 이에 대한 자세한 얘기는 생략하려 합니다. 저도 자동으로 생성되는 개체에 대해선 모르는 것이 많기도 하고, 여기서 이런 얘기까지 하는건 조금 초점이 빗나가는 이야기가 될 것 같기도 하거든요,,^^;;

어쨌든, 서비스와 통신을 할 수 있는 준비가 거의 끝났다는데 큰 의미가 있는 것 같습니다. ㅎㅎ

아ㅡ 본격적으로 서비스를 이용하기 전에 한가지 더 얘기를 하고 넘어가야할 것이 있습니다.
비주얼스튜디오에서 쉽게 서비스를 참조 추가할 수 있었던 또 다른 이유,, 바로 Metadata Exchange Endpoint 입니다.

WCF의 Metadata라 함은, WCF 서비스와 통신하는 방식을 설명한 것이라고 간단하게 생각하시면 됩니다. 그러면 MEX(Metadata Exchange Endpoint) 가 무엇인지 대충 감이 오시나요?

네,, 추측하신대로 이러한 메타데이터를 교환하기 위한 엔드포인트를 말합니다. 클라이언트에서 이 MEX로 요청 메시지(request message)를 보내면, MEX 에서는 서비스가 가지고 있는 엔드포인트에 대한 내용과 메시지의 포맷에 대한 내용을 WSDL 형태로 클라이언트에 전송해 주는 것입니다.

비주얼 스튜디오에서 "서비스 참조 추가"를 실행하였을 때, 프록시 클래스와 config 파일을 생성해 주는 것은 모두 MEX에서 보내준 WSDL을 이용한 것이죠~

MEX에 대해서까지 설명을 들으니, 클라이언트와 서비스가 통신하는 방법에 대한 그림이 대충 그려지지 않으시나요?ㅎ

그럼, MEX를 정의하지 않으면 WCF 서비스와 통신하지 못할까요?

꼭 그렇지만은 않습니다. 물론, 비주얼 스튜디오에서 지원해주는 "서비스 참조 추가"를 이용하진 못합니다.
하지만, svcutil.exe 라는 명령어를 이용하여 프록시 클래스를 생성해 줄 수 있고, 이를 통해 서비스와 통신할 수 있습니다. 이 명령어에 대해서도 정리할 수 있는 시간을 가질 예정이지만, 지금 당장은 그렇게 자세히 알 필요는 없을 듯 합니다. 우리에겐 Visual Studio 라는 훌륭한 툴이 있으니깐요~ ㅎ 
(개발자는 너무 툴에 종속적이면 좋지 않다 생각하고는 있지만,,, ^^;;;)

이제, 드디어,, 서비스에 정의 되어 있는 메소드를 호출하고 사용해 볼 시간이 왔습니다.

using MyServiceClient.MyService;

namespace MyServiceClient
{
    class Program
    {
        static void Main(string[] args)
        {
            ProductServiceClient proxy = new ProductServiceClient();
            string strResult = proxy.GetFirstName("WCF");

            Console.WriteLine(strResult);
        }
    }
}

위의 코드는 클라이언트 Console 어플리케이션의 Program.cs 파일 내용입니다.

using 구문을 이용하여 포함시킨 MyServiceClient.MyService 네임스페이스는 비주얼 스튜디오에 의해 생성된 프록시 클래스의 네임스페이스입니다.
이 네임스페이스에서 상위 네임스페이스는 서비스에 정의 된 네임스페이스에 "Client"가 붙은 형태로 생성되며, 하위 네임스페이스는 서비스 참조 추가를 할 때 입력하여 준 것과 동일한 형태로 생성되어 집니다.

그리고, ProductServiceClient 클래스는 비주얼 스튜디오에 의해 생성된 프록시 클래스 입니다. 이 클래스를 이용하면 서비스에 정의되어 있는 메소드들을 클라이언트에서 맘껏 사용할 수 있게 되는 것입니다. 아래와 같은 방식으로 말이죠~ ^^
string strResult = proxy.GetFirstName("WCF");

이로써, 클라이언트에 작성할 것도 모두 다 했습니다.

그럼, 실행을 한번 시켜 보도록 하죠~

현재, 솔루션에서 서비스 프로젝트와 클라이언트 프로젝트가 모두 동작해야 되기 때문에 솔루션의 속성을 조금 변경할 필요가 있습니다. 
아래 그림에서 볼 수 있듯이, 솔루션 속성 창에서 "Multiple startup projects"를 선택하고 두 개의 프로젝트 모두 "Start" 하도록 값을 변경하여 줍니다.


이제 실행만 하면 됩니다. 과감하게~ F5 키를 눌러 실행을 시켜 보죠~
아래와 같은 화면이 나타납니다. 두둥~~!!


결과 화면을 보고 실망하셨나요?? ^^;;
지금까지, 길~게 설명한 것에 비하면 결과는 아주 보잘것 없습니다. 하지만, 실망(?)하지 마시기 바랍니다 ^^;
처음부터 이 서비스는 아주 아주 간단한 서비스라고 말씀 드렸었고, 이 간단한 서비스를 이용해 우리는 WCF 에 대한 큰 그림을 그릴 수 있었으니깐, 결코 실망스러운 결과는 아닙니다.

이렇게 해서 WCF의 기초에 대해선 모두 끝이 났습니다.
글을 이용하지 않고, 옆에서 말로 설명을 했다면, 금방 끝날 수 있는 내용을 두 개의 포스팅에 걸쳐 설명을 했습니다. 
이렇게 하고 나니, 글을 이용해 정보를 전달한다는 것이 결코 쉬운 일이 아니란 것을 새롭게 깨닫게 되면서, 파워 블로거분들이 존경 스러워 지네요 ^^

포스팅을 시작한지 얼마되지 않아 많이 부족하지만, 꾸준히 하다보면 저도 다른 분들처럼 멋진 글을 쓸 수 있는 날이 오겠죠~ㅎㅎ 그때까지 포기하지 않고 열심히 하겠습니다 ^^

그럼, 다음 포스팅때 뵙겠습니다~
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 강의 잘 봤습니다~ 감사합니다^^

  2. 강의 잘 보고있습니다. ^^ 많이 배워가네요..

  3. 감사합니다 많은 정보 얻고 갑니다~

  4. 이렇게 글로 써주시니 이해가 가는군요.
    말로 설명을 들었더라면 이해가 잘 안가도 끄덕거려야 했을겁니다.
    그리고선, 정작에는 깨닫지 못하는 거죠.

  5. 좋은 정보 감사히 잘보고 갑니다.
    질문좀 하나 해도 될런지요?
    서버쪽에 WCF OperationContract로 쿼리를 작성하고
    클라이언트를 실버라이트 프로그램으로 서비스참조로 받아서 사용하려고 하는데요;;
    클라이언트쪽에 자동생성되는 함수들이 어떤형식으로 생성이 되는건지..
    그리고 자유롭게 사용하기위해서어찌해야 될런지;;
    답을 얻을수있는 공부해볼만한것들좀 조언좀 해주세요
    부탁드립니다.

    • 자동 생성되는 클래스들을 보고 싶다면 프로젝트 폴더 밑에 "Service References" 폴더를 보시면 확인할 수 있으실겁니다.
      그런데 서비스를 자유롭게 사용한다는 말이 어떤 말인지,, 명확하게 말씀을 해주시면 더 자세히 도와드릴 수 있을 것 같습니다. ^^;;

Posted by 오태겸(RuAA)
새해가 밝았습니다. (조금 늦었지만,, ;;;;)
올해는 60년 만에 찾아 온  백 호랑이의 해라고 하죠~ 
식상한 멘트일 것 같지만, 백 호랑이의 기운을 받아 올 한해 모두 바라는 일 이루시길 바라겠습니다. ^^

오늘은 WCF 두 번째 시간으로 WCF의 기본에 대해 알아보고, 간단한 WCF 서비스를 만들어보도록 하겠습니다.

지난 포스팅에서 WCF가 웹 서비스와 비슷하게, 분산 어플리케이션을 쉽게 개발할 수 있는 .NET 기술이라고 설명했었습니다.
분산 어플리케이션은 많은 분들이 아시겠지만, 특정 기능을 수행하는 서버 프로그램과 그리고 이 서버 프로그램을 호출하는 클라이언트로 이루어져 있는 이러한 구조를 가집니다. (쉽게 웹 서비스를 사용하는 어플리케이션 구조를 생각하면 될 것 같습니다.)

물론, WCF도 예외는 아닙니다. WCF는 크게 클라이언트와 서버 프로그램,, 그러니깐 WCF 서비스로 나눌 수 있습니다. (이제부터는 서버 쪽 WCF 프로그램을 WCF 서비스(또는 서비스)라고 지칭하겠습니다.) 

이러한 구조에서 클라이언트와 서비스는 메세지를 주고 받기 위해 아래 그림과 같이 Endpoint 를 제공합니다.


Endpoint라,, 약간 생소한 단어죠? ㅎ,,  endpoint는 WCF 에서 새롭게 정의되어진 단어로서 서비스에 접근할 수 있는 interface라 생각하면 될 것 같습니다. Endpoint를 "종단점"이라고 해석하던데, 개인적으로는 종단점보다는 엔드포인트(endpoint)가 더 와닿는 것 같아서 잘 안씁니다 ㅎ

엔드포인트는 Address, Binding, Contract 세가지의 구성 요소로 이루어집니다. (이를 "WCF 서비스의 ABC" 라고 기억하시면 됩니다.)

그럼, 각 요소들에 대해 좀 더 자세한 설명을 해보겠습니다.

  • Address (Where) : Address는 말 그대로 주소입니다. 서비스가 어디에 있는지에 대한 정보를 나타내는 것으로 메세지가 보내어져야 하는 곳을 말합니다. 메세지 전송을 위해 HTTP 프로토콜을 사용한다면 address는 "http://myserver/myservice/" 와 같은 모습을 할 것이며, 만약, TCP 프로토콜을 사용한다면 "net.tcp://myserver:8080/myservice" 와 같이 표현될 수 있습니다.
  • Binding (How)BindingChannel을 정의합니다. Channel이란 메시지를 주고 받기 위한 여러 프로토콜을 명세하는 것을 말하는데, 여기에는 메시지를 전송하기 위한 전송 프로토콜, 보안의 적용 여부 그리고, 트랜잭션의 지원 여부 등이 포함됩니다. 전송 프로토콜의경우 HTTP, TCP, Named Pipes, MSMQ 등을 지원하여 서비스의 목적이나 환경에 따라서 적절하게 사용할 수 있다는 장점이 있습니다.
  • Contract (What)Contract는 서비스의 operation에 필요한 메시지의 포맷을 정의합니다. 좀 더 쉽게 설명하자면, 메시지의 포맷이란 결국 서비스에서 제공하는 메소드와 이 메소드의 매개변수를 정의한 것입니다.(서비스가 제공하는 기능들을 정의한 것이라고 볼 수도 있겠네요~^^) 실제로, 이후에 WCF 서비스를 만들어보면 알겠지만 이 Contract에는 서비스가 제공하는 메소드를 정의한 인터페이스가 매핑되는 것을 볼 수 있을 것입니다.

위의 그림을 조금 발전시킨 그림입니다. WCF 서비스와 클라이언트는 특수한 상황이 아닌 경우 양방향 통신을 하기 때문에 클라이언트 역시 엔드포인트를 가지고 있어야 합니다. 물론, 서비스와 클라이언트의 엔드포인트는 각각 A,B,C를 정의해야 겠죠~!!

서비스를 개발하고, 엔드포인트까지 정의가 끝이 나고 나면, 이제 이 서비스를 호스팅(Hosting)해야 합니다. 마지막으로 호스팅 작업을 해줘야 서비스는 외부에서 접근할 수 있는 상태가 됩니다.

WCF는 서비스를 호스팅 하기 위해 여러 가지의 방법을 제공합니다. 가장 보편적인 방법으로 IIS Hosting이 있구요, 이 외에 Windows Service를 이용한 Hosting과 윈폼 또는 콘솔 어플리케이션을 이용해서 호스팅을 수행할 수도 있습니다
(호스팅 부분만 다루더라도 내용이 많기 때문에 여기서는 간단하게 설명하고 넘어가도록 하겠습니다. 물론, 차후에 이 주제로 포스팅을 할 예정이니 Don’t worry about that~!! ^^;;)

여기서도 WCF의 장점이 팍팍 느껴지지 않으시나요? ^^ 
기존 웹 서비스의 경우에는 항상 IIS를 사용해야 했었고, 그 덕분에 http 프로토콜 만을 이용할 수 밖에 없었죠. 하지만, WCF 서비스는 여러 방식을 통해 호스팅 할 수 있는 장점을 가짐으로써, 적시적소에 서비스를 적용할 수 있는 큰 장점을 제공해 주는 것입니다. 더군다나, 이러한 것들을 개발자가 쉽게 개발할 수 있도록 해주었다는 것 역시 WCF의 큰 장점입니다. (보면 볼수록 너무나도 매력적인 플랫폼이에요~ ^^*)

일단, 기본적인 사항은 여기까지 설명을 하구요, 나머지는 실제로 간단한 WCF 서비스를 만들어보면서 추가적으로 설명을 하겠습니다.
다음으로 넘어가기 전에, 아래와 같은 내용을 다시 한번 되새김질(?) 하고 넘어가도록 하죠~ ^^

서비스를 개발하기 위해선,,
  • 서비스가 제공하는 기능들을 정의 한 Contract 가 존재해야 한다.
  • 서비스에 접근하기 위한 엔드포인트가 필요하며, 엔드포인트는 Address, Binding, Contract 로 구성되어 있다.
  • 서비스를 노출하기 위해 서비스를 호스트 해야하며, WCF는 호스팅을 수행하기 위한 여러가지 방식을 제공해 준다.

이제~ 간단한 WCF 서비스를 개발해 볼까요? 


간단한 WCF 서비스 개발하기

앞에서도 설명을 했었지만, WCF 서비스는 호스팅을 위한 여러 방법이 존재합니다. 처음으로 만드는 이 WCF 서비스는 여러 방법 중 Console 어플리케이션을 이용한 방법으로 간단하게 만들어볼까 합니다. (이 방법이 가장 심플하니깐요~ 흣,,)
여기서는 아주*10000 간단한 서비스를 만들어볼꺼구요, “WCF 서비스가 어떤 모습을 하고 있는지,,” 정도만 알고 넘어가시면 될 듯 합니다.

우선, VS2010에서 Console Application 프로젝트를 생성합니다. (아,, 개발 언어는 C#을 사용할 것입니다. 앞으로도 쭈~욱~~ )
그리고, 프로젝트에 아래 그림과 같이 각각 IProductService.cs, ProductService.cs 라는 이름을 가진  두 개의 파일을 추가 하였습니다.


IProductService는 인터페이스며, 서비스의 계약을 정의합니다. 서비스의 계약은 한마디로 서비스가 제공하는 여러 메소드를 정의하는 인터페이스라고 생각하시면 됩니다. 그리고, ProductService는 IProductService를 상속받아 실제 메소드를 구현하는 클래스입니다.

파일을 추가한 후에, 프로젝트에 System.ServiceModel.dll 어셈블리를 참조 추가합니다.
(System.ServiceModel.dll 에 WCF 서비스를 위한 대부분의 클래스들이 포함되어 있습니다. 앞으로도 계속 쓰이게 될 중요한 어셈블리죠~ ^^)

이제, 서비스를 개발 할 준비가 다 되었습니다. 먼저, IProductService 인터페이스 부터 구현해보겠습니다. 
(아래 코드에는 생략되었지만, using 문을 사용하여 System.ServiceModel 네임스페이스를 사용하도록 해주는 것도 잊지 마세요~)

[ServiceContract]
interface IProductService
{
    [OperationContract]
    string GetFirstName(string empID);
}

너무 간단한가요? ㅎ,, 
서비스의 계약을 정의해 주는 일은 봐서 알겠지만 아주 쉽습니다. 클래스에 "ServiceContract" 특성을 지정하고, 그 메소드에는 "OperationContract" 특성을 지정해 주는 것만으로도 계약을 정의해 줄 수 있습니다.이 외에 여러 옵션들을 줄 수 있지만, 차후에 다루게 될 것이니 이것만으로 넘어가겠습니다.
(다시 한번 강조하지만, 이번 WCF 서비스는 아주 간단한 서비스라는거~ 아시죠?? ^^)

다음으로 ProductService 클래스를 구현해보겠습니다.

class ProductService: IProductService
{
    public string GetFirstName(string empID)
    {
        return"Steve";
    }
}


음,, ProductService도 아주 아주 간단합니다. (제가 민망할 정도로,,~ ^^;;)
차후에 이 서비스를 수정해서 쓰긴 하겠지만, 지금은 별 다른 기능없이 단순하게 "Steve" 라는 문자열을 반환해주는 메소드를 구현하였습니다.
사실, 구현 부분은 지금 상황에서 그렇게 중요하지 않습니다.

서비스의 계약과 관련한 구현은 다 되었습니다. 이제 해야할 작업은 이 서비스를 호스팅하는 일이죠~
Console 어플리케이션을 이용하여 호스팅을 하기 위해서 Program.cs 에 관련 코드를 다음과 같이 추가 하였습니다.

using System.ServiceModel;
using System.ServiceModel.Description;

namespace MyService
{
    class Program
    {
        static void Main(string[]args)
        {
            Uri baseUri = new Uri("http://localhost:8000/ProductService");
            using(ServiceHost serviceHost = new ServiceHost(typeof(ProductService), baseUri))
            {
               //Add Service Endpoint
               serviceHost.AddServiceEndpoint(typeof(IProductService), new BasicHttpBinding(), "");
               
               //Add Metadata Behavior
               ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior();
               mexBehavior.HttpGetEnabled = true;
               serviceHost.Description.Behaviors.Add(mexBehavior);

               //Add Metadata Exchange Endpoint
               serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), 
                                              MetadataExchangeBindings.CreateMexHttpBinding(),"mex");
               serviceHost.Open();


               Console.WriteLine("Press <ENTER> to terminate WCFService.\n");
               Console.ReadLine();
            }
        }
    }
}

호스트 구현은 조금 코드가 길죠,, ^^;
사실, 서비스 자체만 호스팅 하는 코드는 그렇게 길지 않습니다. 하지만, 서비스에 Metadata Exchange Endpoint를 추가하려다 보니 코드가 조금 길어졌습니다. 

이 코드에서의 핵심은 ServiceHost 클래스입니다. ServiceHost 객체를 이용하면, WCF 서비스를 호스팅 할 수 있다는 사실을 마음 속 아니, 머리 속 깊이 넣어주세요~

ServiceHost 클래스의 생성자는 두 개의 파라미터를 받습니다.  첫번째 파라미터는 호스팅 하려는 클래스의 타입이며, 두번째 파라미터는 Base Uri 입니다. Base Uri는 말 그대로 서비스의 기본 주소(address)를 나타냅니다.

ServiceHost 객체를 생성한 후에는 이 개체에 AddServiceEndpoint 메소드를 이용하여 endpoint를 추가하여 줍니다.
AddServiceEndpoint 메소드는 기본적으로 세 개의 파라미터를 필요로 하는데, 이 파라미터가 앞에서 설명했던 Address, Binding, Contract 입니다.

Contract는 서비스의 계약을 정의 했던 IProductService 인터페이스를 지정하구요, BindingBasicHttpBinding 클래스를 사용하였습니다. BasicHttpBinding 클래스는 WCF에서 기본적으로 제공하는 여러 Binding 개체 중에 하나로 이름에서 알 수 있듯이 기본적인 Http 프로토콜을 사용합니다. 그리고, 마지막 Address 는 빈 문자열을 주었습니다. 이렇게 빈 문자열을 주게 되면 endpoint의 주소는 ServiceHost 개체를 만들 때 사용했던 Base Uri와 동일한 값을 가지게 됩니다.

Endpoint 추가까지 끝이 나면 호스트를 수행할 준비가 모두 끝납니다. 이제 ServiceHost 개체의 Open 메소드를 사용하여 서비스를 시작할 수 있는데, Open 메소드는 각 채널에 맞는 리스너(listener)를 별도의 스레드로 수행을 하여 클라이언트의 요청을 처리할 수 있게 합니다.

(혹시,, 제가 Metadata Exchange Endpoint 에 대한 설명은 살짝 넘어갈려고 한 걸 눈치 채셨나요? ^^;; MEX 에 대해선 다음 포스팅 때 꼭 설명을 하도록 할께요~ 한번만 눈 감아주시길,, ㅋ)

서비스 구현은 모두 끝이 났습니다. 한번 실행해 볼께요~

실행을 하면 다음과 같은 Console 창이 뜹니다.


이 창이 무사히 뜨면, 서비스가 아무 탈 없이 잘 동작하고 있다는 말입니다.
음,, 이것만 봐서는 믿음이 안 갈 수 있으니, 한번 검증해 보죠~ ㅎ,, 

이 콘솔 창을 그대로 띄워 놓은 상태에서 브라우저를 열고 주소창에 http://localhost:8000/ProductService (이 주소는 ServiceHost 개체를 생성할 때 입력했던 Base Uri 인거,, 기억하시죠? ^^) 를 입력해보겠습니다~
다음과 같은 내용을 확인할 수 있습니다. 짜잔~


이 화면은 어디서 많이 본 것 같죠?? 네,, 예전 .NET 웹 서비스를 만든 후 실행했을 때의 화면과 비스무리(??) 합니다~ ^^
(서비스가 잘 동작하고 있군요,, ㅎ)

이로써, 첫 WCF 서비스 만들기가 끝이 났습니다. 유후~~

이제 Client를 만들어야 되는데,,(흠,,) 글이 너무 길어진 것 같은,,;;;;; 
어쩔 수 없군요~ Client 구현은 다음으로,, (MEX 설명과 함께~ ㅎㅎ)

빠른 시일 내에 다음 포스팅을 올리도록 할테니, 너무 노여워 하지 말아주세요~ (기다리시는 분이 있을 것 같진 않지만,, ^^;;)

또 다시 강조하지만, 이번 포스팅의 목적은 "WCF 서비스는 어떤 식으로 구현하는가?" 에 있습니다.
따라서, WCF에 대해 전체적인 그림(?)을 그릴 수 있는 내용이 되었으면 해서 Binding 이나 Behavior 등 자세한 설명은 하지 않았습니다.
세부적인 설명들은 앞으로 차차 하게 될 것이니깐요,, (가야 할 길은 멀고도 험하죠,, ^^;;)

어쨌든 이 자료가 WCF를 시작하는 분들께 많은 도움이 되었으면 합니다.
그럼 다음 포스팅때 뵙겠습니다~ ^^
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. 좋은 글 감사합니다. 기다리는 사람 있습니다.

    • 헛,, 제가 더 감사합니다. 관심을 가져 주시니 더욱 열심히 하겠습니다. 감사합니다 ^^

  2. 홀리준 2010/01/09 05:04

    WCF 를 배우고 있던차에 좋은 강좌 잘 보고 있습니다.
    wcf를 이용한 다중 게시판 서비스같은 응용강좌도 있었으면 좋겠네요^^;

    • 관심 가져주셔서 감사합니다~ ^^
      차후에 응용강좌도 꼭 하도록 하겠습니다.
      감사합니다~

  3. 이제 막 WCF를 공부하고 있는데. 잘 보고 갑니다.
    다음 글도 많이 기다려지네요. ^^

  4. 맨날맑음 2010/01/16 13:57

    WPC 강좌 잘보고있습니다. 자세하고 알기쉽게 써주셨네요^^
    후속편들도 기대됩니다^^ 좋은하루되세요

  5. WCF 강좌 잘 보고 있습니다. 이해가 잘 되네요 감사합니다~^^

  6. 전길호 2010/01/22 08:54

    오늘 처음 이 글을 읽었네요.. 엄청난 도움이 되고 있습니다. 그런데 저는 오류가 뜨네요..
    본문대로 작성하고 추가하고 컴파일까진 문제없이 되는데 프로그램을 실행하니까 오류가 뜨면서 오류내용을 콘솔창에 보여 줍니다.
    제가 완전초보라.. 어떤문젠지 알려주세요...ㅜㅜ
    제가 쓴건 비쥬얼 2008입니다.

  7. 이제 막 WCF를 공부하는 사람입니다. 글 올리신걸 보고 많이 배워갑니다.
    감사합니다~ 추후의 포스팅도 기다리고 있겠습니다.

  8. 김새롬 2010/02/05 10:36

    웹서비스도 안 만들어본 상태에서..
    WCF 로 서비스를 만들어야 하는 상황인데..
    처음부터 차근 차근 설명해 주셔서 정말 잘 보고 따라하고 있습니다.~^^
    정말 초보의 입장에서 쉽게 볼 수 있게 가르쳐 주셔서 이해가 쉽네요~
    앞으로도 부탁드려요~ㅎㅎ

  9. 좋은 글 감사드립니다. WCF 처음 접하는데 글쓴님의 글이 많은 도움이 되네요. 감사합니다.

  10. WCF에 대해서 알기쉽게 설명해주셔서 감사합니다.
    많이 배우고 갑니다.

  11. 김승정 2010/06/22 10:11

    안녕하세요.
    좋은글 잘 보고 있습니다.
    헌데,컴파일은 잘 되는데, 실행하면 아래와 같은 오류가 발생을 하네요.
    첨 시작하느거라 모가몬지 잘 모르겠습니다.
    혹시,도움 주실수 있으시다면, 아래내용 참고해주세요.
    수고하세요.

    에러내용:
    동일한 형식의 항목이 이미 컬렉션이 있으므로 값을 컬렉션에 추가할 수 없습니다. 'System.ServiceModel.Description.ServiceMetadataBehavior'. 이 컬렉션은 각 형식의 인스턴스를 하나만 지원합니다.
    매개 변수 이름: item

    에러부분: serviceHost.Description.Behaviors.Add(mexBehavior);

    • 오태겸 2010/06/22 18:26

      에러부분과 에러내용을 보면 메타데이터 교환을 위한 엔드포인트가 중복해서 추가가 되었기 때문인 것 같습니다.

      제가 실습한 내용에서는 엔드포인트를 모두 코드상에서 추가를 해주었는데 김승정님은 아마도 app.config 라던지 설정파일에서 메타데이터 엔드포인트를 추가하는 부분이 포함되어 있을 듯 합니다.

      코드상에서 메타데이터 엔드포인트를 추가하는 부분을 없애시든지 환경설정 파일에서 엔드포인트 추가하는 부분을 삭제하시면 문제없이 동작할 듯 합니다 ^^

Posted by 오태겸(RuAA)

안녕하십니까? 이번에 새롭게 vs2010 team 에 합류하게 된 오태겸이라고 합니다. 
앞으로, WCF를 주제로 한 포스팅을 맡아서 활동을 하게 되었습니다. 
부족한 점이 많지만 열심히 활동 할테니 이쁘게(?) 봐주셨으면 합니다. ^^;;

이제, 첫 포스팅을 시작하도록 하겠습니다.

첫 포스팅의 주제는 “WCF란 무엇인가?” 입니다.

WCF는 “Windows Communication Foundation”의 약자로서, 이 이름만 보더라도 왠지 ‘여러 가지 Communication을 지원하기 위한 기술인 것 같다,,’ 라는 예상을 하게 됩니다.
네!! 맞습니다. WCF는 여러 가지 컴포넌트(응용프로그램, 서비스 등,,)와의 Communication을 쉽게 구현할 수 있도록 해주는, Microsoft에서 새롭게(이미 몇 년 지났지만,,^^;;) 개발한 기술입니다.

한마디로 정의하자면, WCF는 분산 어플리케이션 개발과 플랫폼에 상관없는 높은 상호 운용성을 지원하는 어플리케이션을 쉽게 개발할 수 있도록 해주는 기반 기술입니다.

분산 어플리케이션이란 단어는 이제 너무 친숙한 단어가 되었으며,  꽤 오랜 시간에 걸쳐 이러한 분산 어플리케이션을 쉽게 구현하기 위한 여러 가지 기술들이 개발되어왔습니다. 이러한 노력에 의해 CORBA(Common Object Request Broker Architecture), DCOM(Distributed Component Object Model), Web Service 등의 기술들이 개발되어 적용되어 왔으며, 특히, Web Service는 이 전의 기술에 비해 인터넷을 기반으로 높은 상호 운용성을 지원했기 때문에 많은 곳에서 활용되어 왔습니다.

Web Service가 많은 곳에서 환영(?)을 받고, 활용될 수 있었던 이유에는 아마도 XML이라는 인터넷 표준과 이를 기반으로 한 SOAP이라는 표준 프로토콜 사용에 있었다고 생각합니다. 앞에서도 언급했지만, 인터넷과 이러한 표준 기술들을 사용함으로써 좀 더 쉽게 높은 상호 운용성을 지원하는 분산 어플리케이션을 구현할 수 있었기 때문입니다.

저는 개인적으로, “Web Service를 대체할 만한 기술이 나오는 것은 당분간 어렵다-!” 라고 생각했었습니다. 하지만 Microsoft 에선 이런 저의 생각을 보기 좋게 뻥~! 차버렸습니다. 저의 생각이 너무 짧았던 거죠 (역시 난 아직 멀었어,, ㅡㅠ)

WCF와 처음 만났을 때(그리 오래되진 않았지만), WCF의 확장성과 상호 운용성에 조금 놀라기도 하고, 신기해 하기도 했었던 기억이 납니다.

이제 WCF의 장점에 대해 몇 가지 설명을 해보도록 하겠습니다.

우선, WCF의 클라이언트 및 서비스는 기본적으로 SOAP을 사용하여 통신하며, 서비스의 메타데이터를 전달하기 위한 방식으로 WSDL을 사용합니다. 이는 Web Service와 동일한 방식이며, 앞에서 언급한 Web Service가 널리 활용될 수 있었던 이유와 동일한 장점을 가지고 있습니다.

또한, 기존의 Microsoft의 분산 어플리케이션 개발 기술을 통합하여 사용할 수 있다는 장점도 가지고 있습니다.
예를 들어 WCF 와 기존의 .NET Web Service는 모두 SOAP을 사용하기 때문에 WCF 기반 응용 프로그램은 .NET Web Service에 구축된 응용 프로그램과 직접 상호 운용할 수 있습니다. 그리고, WCF 기반 응용 프로그램은 MSMQ(Microsoft Message Queue)를 이용하여 통신을 할 수 있기 때문에 기존의 MSMQ 인터페이스를 사용하여 구축된 응용 프로그램과도 직접 상호 운용할 수 있습니다.

이러한 장점 이외에 HTTP 통신 뿐만 아니라, TCP 통신을 사용할 수도 있다는 장점 등 WCF는 많은 장점을 가지고 있는 기술입니다.
앞으로, WCF에 대해 포스팅을 해 가면서 WCF의 많은 장점들을 보여드릴 예정이니 많은 관심과 격려 부탁 드리겠습니다 ^^*

음,, 글만 생각 나는대로 적다보니 조금 지루해지는 것 같습니다.
WCF의 아키텍처를 간단한 설명과 함께 그림으로 보여드리고 이번 첫 포스팅을 마칠까 합니다.

 

위 그림은 WCF의 아키텍처를 보여주고 있습니다.

WCF 를 처음 접하시는 분이라면, 이 그림을 보고 한번에 이해하기는 좀 어려울 것이라 생각합니다. 하지만, WCF에 대해 조금 알게 된다면 그렇게 이해하기 어려운 그림은 아닐 듯 합니다. (지금 이해하기 어렵다고 걱정할 필요는 없단 말이죠 ^^)

WCF의 아키텍처 중 계약 계층은 서비스에서 제공하는 실제 메서드를 정의하거나 서비스와 통신할 때 사용되는 메시지 또는 데이터를 정의하는 부분으로 서비스의 기본이 되는 계층입니다.

메시징 계층은 채널로 구성되며, 채널은 크게 전송 채널과 프로토콜 채널의 두 가지 유형으로 구분되어집니다.
전송채널은 메시지를 전송하기 위한 전송 프로토콜에 관한 채널을 정의하며, HTTP, TCP, 명명된 파이프 및 MSMQ가 있습니다.
프로토콜 채널은 메시지를 처리하기 위한 프로토콜을 구현합니다. WS-Security와 WS-Reliability 채널을 이용하여 메시지의 보안과 신뢰적인 전달을 위한 추가적인 기능을 정의할 수 있습니다.

활성화 및 호스팅 계층은 서비스가 호스팅될 수 있는 방식을 정의합니다. IIS 7의 WAS를 이용한 호스팅, 실행 파일로 호스팅 하는 자체 호스팅, Windows 서비스를 이용한 호스팅, 그리고 COM+ 구성 요소를 이용한 호스팅 등 여러 가지 방식으로 서비스를 호스팅할 수 있습니다.

WCF 아키텍처에 대해 짧게 설명을 했는데, 이 아키텍처만 보더라도 WCF의 기능에 어떤 것이 있는지 조금은 예상할 수 있을 듯합니다.
WCF는 Web Service와 마찬가지로 SOAP과 WSDL 같은 표준을 사용하여 플랫폼에 상관없는 분산 어플리케이션 개발을 쉽게 지원하지만, Web Service와는 다르게 전송 프로토콜이라던지, 호스팅을 하는 방식이라던지, 여러 가지 기능들을 제공하여 Web Service 보다 더 큰 확장성과 상호 운용성을 지원하는 기술이라 생각하면 될 것 같습니다.

아- 이것으로 첫 번째 포스팅을 마무리할까 합니다.
사실, WCF가 아직까진 국내에 많이 활용되고 있는 것 같진 않습니다. 자료도 그렇게 많지 않은 것 같구요. 제가 꾸준히 포스팅을 하고 제가 남겨놓은 자료가 단, 한 사람에게라도 좋은 자료가 될 수 있다면 기분 좋을 것 같습니다.
앞으로는 WCF의 기초에 대한 내용으로 포스팅을 하고, 그 이후에 WCF 4.0에 대한 내용, 그리고 WCF의 활용에 관한 내용으로 포스팅을 할 예정입니다. 많은 관심과 격려 부탁 드립니다 ^^

첫 포스팅이라 어떻게 진행을 해야 할지,, 감도 안잡히고, 어설퍼 보이는 부분이 한 둘이 아닐 듯 합니다. 아직 모르는 것도 많고, 부족한 것도 많기 때문에 지금 당장 전문가 다운 모습을 보이긴 어렵겠지만, 욕심 부리지 않고 꾸준하게 정진하는게 가장 바람직한 모습일 것 같습니다.

그럼, 다음 포스팅때 뵙겠습니다 ^^


크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

  1. Thank you for your posting.

  2. 좋은 정보 감사 드려용 ~

  3. 좋은 정보 감사합니다~