WCF Troubleshooting (1)

WCF 2010.11.19 09:00 Posted by 오태겸(RuAA)
 
와우~ 정말 오랜만에 포스팅을 하네요 ^^;;
여러 가지 일 때문에 포스팅을 하지 못했는데 이제 다시 힘을 내서 그 동안 못했던 포스팅을 해보도록 하겠습니다.
어느새 계절은 겨울이 되어 날씨가 많이 추워졌는데, 모두 건강 주의 하시길 바랍니다.

이번 포스팅 부터는 WCF 를 이용하여 서비스를 구현하는데 있어 발생할 수 있는 여러 가지 예외나 에러를 어떻게 처리할 수 있는지에 대한 얘기를 해 볼까 합니다.

WCF 서비스와 같이 분산 어플리케이션을 개발할 때는 항상 에러나 예외의 원인을 찾거나 디버깅하기가 조금 애매하죠. 로컬에서 개발할 때에는 문제가 없다가도 실제 서버에 배포를 하고 나면 발생하는 예외나 에러는 개발자를 난감하게 만들기도 합니다.

그래서~ 이번 포스팅 부터는 WCF 에서 발생하는 예외를 어떻게 핸들링 할 수 있는지, 그리고 에러에 대한 진단을 하기 위해 어떤 방법을 사용할 수 있는지에 대한 내용으로 진행을 해볼까 합니다.


Fault Message 와 Exception

.NET 어플리케이션은 에러가 발생했을 때, 에러에 대한 내용을 알리기 위해 Exception 클래스를 사용합니다. 이는 물론, WCF에서도 예외가 아닙니다. 다만, 이러한 에러의 내용을 클라이언트에 전달하기 위해서 Fault Message 를 사용한다는 것이 조금 다르긴 하죠.
쉽게 얘기해서, 서비스에서 클라이언트로 어떤 데이터를 넘겨줄 때 직렬화를 사용하는 것처럼, 에러 메시지,, 그러니깐 Exception 오브젝트도 직렬화를 한 후에 클라이언트로 전달한다고 생각하면 된다는 것입니다.

Fault Message는 다음과 같은 XML 형태로 클라이언트로 전달되는데, 이는 표준 SOAP Fault 메시지(Ver 1.2)의 스키마와 같습니다.

<Body>
   <Fault>
      <Code>
         <Value>s:Sender</Value>
      </Code>
      <Reason>
         <Text xml:lang="en-US">…</Text>
      </Reason>
      <Detail>
         <ErrorInfo xmlns="http://Service1">
            <Info>…</Info>
         </ErrorInfo>
      </Detail>
   </Fault>
</Body>


각 element에 대한 자세한 설명은 다음의 링크에서 확인하시면 될 것 같습니다. (http://www.w3.org/TR/soap12-part1/#soapfault) 근데, 자료가 영문이라 보기 싫으신 분들 있으실 겁니다. 저도 그렇지만~ 일단, 글이 너무 많으면 부담되서…(그것도 영문… OTL)

그래서, 다음과 같이 간단하게 정리를 해보았습니다.

Element

설명

Fault

Fault 메시지의 root element

Code

예외가 발생한 원인을 나타낸다.

Reason

예외의 자세한 내용을 보여준다.

Detail

예외의 추가적인 정보를 나타낸다.


이 정도만 체크하고, 다음으로 넘어가기로 하죠~ 엣헴~ ;;;;

기본적으로 WCF 서비스에서 예외가 발생하면, 자세한 정보가 담겨있는 Fault 메시지가 전달되는 대신에 다음과 같이 Detail element가 빠져있는 Fault 메시지를 전달합니다.

<s:Fault>
   <s:Code>
      <s:Value>s:Receiver</s:Value>
      <s:Subcode>
         <s:Value xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault
         </s:Value>
      </s:Subcode>
   </s:Code>
   <s:Reason>
   <s:Text xml:lang="en-US">The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug&gt; configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.</s:Text>
   </s:Reason>
</s:Fault>


Text element의 내용을 자세히 보면, WCF 서비스 개발을 하면서 한번쯤 봤을 법 한 메시지가 적혀있는 것을 확인 할 수 있습니다. 이 Fault 메시지는 닷넷에서 처리할 수 없는 범주의 예외가 발생했거나, 예외 정보를 공개하지 않도록 설정되어 있는 경우에 생성되는 메시지 입니다.

기본적으로 WCF 서비스는 예외 정보를 공개하지 않도록 설정되어 있으므로, 개발을 하는 동안 자세한 예외 정보를 받기를 원한다면 이 설정 값을 바꿔주어야 합니다.

서비스의 환경 설정 파일(web.config/app.config) 에서 behavior element 밑에 있는 serviceDebug element 의 includeExceptionDetailInFaults의 속성값을 true로 바꿔주면 되는 것이죠.

<behaviors>
   <serviceBehaviors>
      <behavior name="MyBehavior">
         <serviceMetadata httpGetEnabled="true"/>
         <serviceDebug includeExceptionDetailInFaults="true"/>
      </behavior>
   </serviceBehaviors>
</behaviors>

 
이와 같이 설정을 한 후에 예외를 발생시켜 볼까요? 서비스에 두 숫자를 받아 나눗셈을 한 결과를 리턴하는 "Divide" 라는 메서드를 만들었습니다. 그리고, 간단하게 예외를 발생시키기 위해서, 0으로 다른 숫자를 나누도록 파라미터를 넘겨주었습니다. 그러면, 당연히 DivideByZeroException 이 발생하겠죠~

그랬더니~ 다음과 같은 Fault Message 를 클라이언트로 전송해 주는 것을 볼 수 있었습니다.
아~ 참고로 이 메시지를 확인하기 위해서 저는 Fiddler(피들러)를 사용했습니다.

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
   <s:Header>
      <a:Action s:mustUnderstand="1">
         http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/
dispatcher/fault
      </a:Action>
      <a:RelatesTo>urn:uuid:8e5a89f4-b765-45e3-b343-26c07fde57dd</a:RelatesTo>
   </s:Header>
   <s:Body>
      <s:Fault>
         <s:Code>
            <s:Value>s:Receiver</s:Value>
            <s:Subcode>
               <s:Value xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">
                 
a:InternalServiceFault
               </s:Value>
           
</s:Subcode>
         </s:Code>
         <s:Reason>
            <s:Text xml:lang="en-US">Attempted to divide by zero.</s:Text>
         </s:Reason>
         <s:Detail>
            <ExceptionDetail xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
               <HelpLink i:nil="true"/>
               <InnerException i:nil="true"/>
               <Message>Attempted to divide by zero.</Message>
               <StackTrace>
at Wcf_TroubleShooting.Service1.Divide(Int32 numerator, Int32 denominator) in C:\Users\RuAA\documents\visual studio 2010\Projects\Wcf-TroubleShooting\Wcf-TroubleShooting\Service1.svc.cs:line 19&#xD;
at SyncInvokeDivide(Object , Object[] , Object[] )&#xD;
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]&amp; outputs)&#xD;
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc&amp; rpc)&#xD;
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
               </StackTrace>
               <Type>System.DivideByZeroException</Type>
            </ExceptionDetail>
         </s:Detail>
   </s:Fault>
   </s:Body>
</s:Envelope>



딱 봐도 아시겠지만, 이 전에 봤던 Fault Message에 비해 확실히 더 자세한 내용을 담고 있는 것을 볼 수 있습니다.

사실 이러한 내용들을 다 알 필요는 없습니다. 닷넷으로 클라이언트를 개발한다면 다른 예외와 마찬가지로 try/catch 문을 이용하여 예외를 처리할 수 있을 테니까 말이죠~ 하지만, 개인적으로 이런 기본적인 내용들도 중요하다고 생각하는지라 하나의 포스팅을 통해 정리를 해보았습니다.

아직 할 얘기들이 많지만 너무 길어지면 지루해질 것 같아 여기서 줄일려고 합니다.

다음 포스팅에선 FaultContract 에 대한 설명과 함께 이를 이용해서 에러에 대한 메세지를 정의하고, 이를 클라이언트에 전달하는 방법, 그리고 그외에 에러를 핸들링 하는 방법에 대한 이야기를 써 볼까 합니다.

다음 포스팅을 기대하시는 분은 없으실 거라 예상이 되어 기대해달란 말을 하긴 어려울 것 같지만, 최대한 아주 아주 빠른 시일 내에 업데이트 할 수 있도록 하겠습니다. ^^
저작자 표시 비영리 변경 금지
신고