WCF Hosting(2) - ASP.NET 호환성(Compatibility)

WCF 2010. 7. 12. 09:00 Posted by 알 수 없는 사용자

바야흐로, 여름입니다.
아ㅡ 정말 이놈의 귀차니즘 덕분에 너무 띄엄띄엄 포스팅이 되는 것에 대해 죄송하다는 말씀 먼저 드려야할 것 같습니다.
여름이라 더워서 그렇다고 핑계대지 않을게요, 휴가 시즌이라 놀고 싶어서 그렇다고 핑계대지 않을게요~ ;;;
잡담은 여기서 줄이고, 힘을 내어, 이번 포스팅을 시작해 보겠습니다. 레츠 고우~
 
지난 포스트의 주제는 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 분들이 존경스러워 지는군요,,ㅎ) 그래도 포기하면 안되겠죠,, ㅎ

다음 포스팅 때 뵙겠습니다. 꾸벅~