SimpleIsBest.NET

유경상의 닷넷 블로그

HTTP 압축 : 웹 서비스 클라이언트

by 블로그쥔장 | 작성일자: 2005-11-17 오후 4:10:00
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.
지난 포스트들에서 HTTP 압축의 원리 IIS에서 HTTP 압축을 설정하는 방법, 그리고 커스텀 HTTP 압축 모듈을 소개한 바 있습니다. 그러면서 닷넷에서 웹 서비스 호출을 압축하기 위해 닷넷 클라이언트 측에서 특별한 코드가 필요하다는 것을 살짝 언급하고 도망갔었지요. 이제서야 관련 포스트를 올립니다. 뭐 더 빨리 글을 쓸 수도 있었지만, 아무도 재촉하는 분이 없으셔서... 게으른 제가 스스로 뭔가를 하겠습니까요...

사실 이 포스트도 가진 시간에 비해 빨리 쓸 수 있겠다 싶어서 쓰는 거죠. 사실 제가 요즘 무기력증 비슷한거에 빠져서요... 날씨가 추워지니 만사가 귀찮고 놀고만 싶네요... 아우... 프로젝트 하나를 1년여 동안 컨설팅하니 지겹기도 하고... 열심히 도와줬는데도 계속 새로운 요구만 나오고... 짜증도 나고... 뭐 그렇습니다... -_-;

HTTP Compression : Web Service Client

HTTP 압축을 사용하면 성능이 향상될 수 있다는 것은 지난 포스트에서 밝힌 바 있다. 그렇다면 웹 서비스 역시 HTTP에 기반하고 있으므로 HTTP 압축을 적용하면 웹 서비스 호출 역시 성능이 향상 될 수 있을까? 당연 빤스 되겠다. 웹 서비스에 HTTP 압축을 적용하면 네트워크 대역폭을 절약할 수 있을 뿐더러 네트워크 상에서 전송할 데이터의 양이 줄어 들기 때문에 성능적인 향상을 기대할 수 있다. 물론 이러한 성능 향상은 지난 포스트에서도 강조한 바 대로 1-2명이 사용하는 경우에는 오히려 압축과 압축해제에 소요되는 오버헤드가 네트워크 전송량을 줄임으로써 오는 잇점을 초과할 수도 있다. 하지만 수십, 수백명이 사용하는 웹 서비스라면 HTTP 압축은 전반적인 응답 시간(response time)을 향상 시켜줄 것이다(이점에 대한 상세한 논의는 필자의 HTTP 압축 (1) : 성능 향상을 위한 다른 접근 기법 이란 포스트를 참조하기 바란다).

Web Service Clients

웹 서비스 서버에 HTTP 압축을 적용하는 것은 일반 웹 사이트에 HTTP 압축을 적용하는 것과 별반 다를 바가 없다. IIS 가 기본 제공하는 HTTP 압축 기능을 사용할 수도 있고, 필자가 작성한 커스텀 HTTP 압축 모듈을 사용해도 좋다. HTTP 프로토콜 입장에서 보면 그 내용이 HTML 이냐 XML 이냐의 차이점만이 존재할 뿐이지 압축하는 것은 동일하기 때문이다.

문제는 압축된 웹 서비스 XML을 받아보는 클라이언트에서 발생한다. 필자의 "HTTP 압축과 웹 액세스 API" 포스트를 읽어본 독자라면 금방 이해가 갈 터인데... AJAX 나 기타 자바 스크립트에서 웹 서비스 호출을 할 때 사용하는 XMLHTTP 객체(MSXML2.XMLHttp)는 WinInet을 사용하고 WinInet은 기본적으로 압축된 HTTP Response를 압축해제 할 능력을 갖고 있다. 반면 닷넷 프레임워크에서 웹 서비스 호출에 사용되는 HttpWebResponse 클래스는 WinInet 이 아닌 SOCKET을 직접 사용하기 때문에 압축된 HTTP Response를 압축해제 하는 방법을 알고 있지 못하다. 따라서 서버 측에서 웹 서비스 호출 결과를 압축해 버리면 닷넷 웹 서비스 클라이언트는 항상 XML을 파싱 할 수 없다는 다음과 같은 예외가 발생한다.

처리되지 않은 예외: System.Xml.XmlException: ' ', 16진수 값 0x1F은(는) 잘못된 문자입니다. 줄 1, 위치 1
at System.Xml.XmlScanner.ScanContent()
at System.Xml.XmlTextReader.ParseRoot()
at System.Xml.XmlTextReader.Read()
...... 이하 생략.....

압축된 웹 서비스 결과를 수신했을 때 닷넷 클라이언트가 발생하는 예외

요는, 닷넷 웹 서비스 클라이언트가 압축을 이해하고 압축해제 하는 코드가 필요하다는 것이다.

.NET Web Service Client

일반적인 웹 호출이라면 지난 포스트의 예제 코드 처럼 HttpWebResponse 클래스의 Stream을 직접 압축해제 할 수도 있겠지만, 웹 서비스 클라이언트는 VS.NET의 웹 서비스 참조 기능에 의해 생성되는 프록시를 통해서 HttpWebResponse 객체가 사용되고 이 객체가 반환하는 Stream 객체를 한꺼번에 압축해제 하는 것이 아니라 XML 파서에 의해 조금 조금 읽어 가면서 파싱 한다. 이 때문에 직접 HttpWebResponse의 결과 Stream을 압축해제하는 코드를 마땅히 집어 넣기 어렵게 되어 버린다.

이러한 난제를 해결하기 위해서는 먼저 닷넷의 웹 서비스 클라이언트에 대한 이해가 필요하다. 일반적으로, 웹 서비스를 호출할 때 VS.NET의 "웹 참조"를 수행하거나 wsdl.exe 유틸리티를 이용하여 웹 서비스에 대한 클라이언트 프록시 클래스를 생성하는 것이 일반적이다. 이 때 VS.NET 혹은 wsdl.exe는 프록시 클래스를 생성하는데 이 프록시 클래스의 베이스 클래스가 System.Web.Services.Protocols 네임스페이스의 SoapHttpClientProtocol 클래스이다. 사실 프록시 클래스는 단순히 매개변수를 SoapHttpClientProtocol 클래스에게 넘겨주는 역할만을 할 뿐, 실제적인 웹 서비스 호출은 이 클래스에서 수행되고 있다.

SoapHttpClientProtocol 클래스는 웹 서비스 호출을 SOAP(Simple Object Access Protocol) 프로토콜에 의거하여 XML로 변환하는 작업(Serialization)을 수행하며 HttpWebRequest 클래스를 이용하여 작성한 XML을 HTTP 프로토콜을 통해 웹 서비스 서버에 POST 한다. 그리고 웹 서비스 서버로 부터 XML 결과를 담은 HTTP Response를 HttpWebRequest 클래스를 통해 수신하고 파싱하여(Deserialization) 결과 값을 반환한다. 웹 서비스 호출을 위해서 SoapHttpClientProtocol 클래스는 HttpWebRequest 객체와 HttpWebResponse 객체를 생성하고 그것을 사용한다는 말이 되겠다.

Approach : Using Custom Web Service Proxy

다행이도 SoapHttpClientProtocol 클래스는 HttpWebRequest와 HttpWebResponse 객체를 클라이언트가 변경할 수 있는 기회를 준다. 이 클래스의 GetWebRequest 메쏘드와 GetWebResponse 메쏘드는 protected virtual 메쏘드로서 이 메쏘드를 오버라이드하여 HttpWebRequest/HttpWebResponse 객체에 변경을 가할 수 있다. 혹은 HttpWebRequest/HttpWebResponse 객체가 아닌 다른 WebRequest/WebResponse 객체를 반환할 수도 있다. 무슨 말인고 하니... SoapHttpClientProtocol 클래스는 WebRequest/WebResponse 클래스에서 파생된 임의의 클래스를 사용하기 때문에 반드시 HttpWebRequest/HttpWebResponse 클래스(이 둘은 WebRequest/WebResponse에서 파생된 클래스이다)가 아니어도 된다는 얘기가 되겠다. 물론 이러한 메쏘드 오버라이드는 클래스 상속이 필요하므로 VS.NET 혹은 wsdl.exe 유틸리티가 생성해 주는 웹 서비스 프록시를 상속 받는 커스텀 프록시 클래스를 정의 해야만 한다.

namespace WsHttpCompress.Localhost_Aspx {
// 일부 attribute 생략...
[System.Web.Services.Protocols.WebServiceBindingAttribute(
/* 매개변수 생략... */)]
public class ReturnDataSet : Protocols.SoapHttpClientProtocol {
    
public ReturnDataSet() {
        
this.Url "http://localhost/aspx/webservice/returndataset.asmx";
    
}

    [System.Web.Services.Protocols.SoapDocumentMethodAttribute(
/* 매개변수 생략... */ )]
   
// 일부 attribute 생략...
    
public DataSet GetProducts() {
        
object[] results = this.Invoke("GetProducts"new object[0]);
        return 
((DataSet)(results[0]));
    
}

   
// 기타 생략.......
}
}
// end of namespace

리스트 1. VS.NET 혹은 wsdl.exe 유틸리티가 생성해 주는 웹 서비스 프록시 코드 예제

리스트1은 자동 생성된 프록시 클래스를 보여주고 있다. 이 프록시 클래스를 상속받는 커스텀 프록시 클래스는 리스트2와 같이 정의할 수 있다. 그리고 이 클래스에서 SoapHttpClientProtocol 클래스의 GetWebRequest 메쏘드와  GetWebResponse 메쏘드를 오버라이드 하여 WebRequest 객체 혹은 WebResponse 객체에 대한 변경을 가할 수 있음을 알 수 있을 것이다.

class CustomProxy : WsHttpCompress.Localhost_Aspx.ReturnDataSet
{
   
// SoapHttpClientProtocol.GetWebRequest 메쏘드 오버라이드
    
protected override WebRequest GetWebRequest(Uri uri)
    {
        HttpWebRequest req 
(HttpWebRequest)base.GetWebRequest(uri);
       
// 다음 처럼 request 객체에 대한 변경을 가할 수 있다.
        
req.KeepAlive = false;
        return 
req;
    
}

    
// SoapHttpClientProtocol.GetWebResponse 메쏘드 오버라이드
    
protected override WebResponse GetWebResponse(WebRequest request)
    {
        HttpWebResponse response 
(HttpWebResponse)base.GetWebResponse(request);
       
// 여기에서 response 객체에 대한 변경을 가할 수 있다.
        return 
response;
    
}

    
// SoapHttpClientProtocol.GetWebResponse 메쏘드 오버라이드 (비동기 버전)
    
protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
    {
        HttpWebResponse response 
(HttpWebResponse)base.GetWebResponse(request, result);
       
// 여기에서 response 객체에 대한 변경을 가할 수 있다.
        return 
response;
    
}
}

리스트2. 커스텀 웹 서비스 프록시 예제

리스트2의 GetWebRequest 메쏘드 오버라이드는 HttpWebRequest 객체의 KeepAlive 속성을 false 로 설정함으로써 TCP 세션이 즉시 종료하도록 설정하고 있다. GetWebRequest 메쏘드의 예제 처럼 베이스 클래스에서 받아온 객체의 속성을 변경하는 것도 가능하지만 전혀 새로운 객체를 만들어 반환해도 된다. 즉, 베이스 클래스를 호출하지 않고 완전히 새로운 HttpWebRequest 객체의 인스턴스를 만들어 반환해 주어도 무방하다는 것이다. 이 때 반환하는 객체는 WebRequest 클래스에서 파생된 어떤 클래스이건 관계 없다.

Implementation : Web Service Call Decompression in .NET

이젠 웹 서비스 호출을 압축하는 방법에 대해 대충 감이 좀 잡힐 것이다. 압축을 수행하기 위해서는 HTTP 압축의 원리에서 설명한 것처럼 먼저 HTTP Request 메시지의 Accept-Encoding 헤더를 설정할 필요가 있다. 이 작업은 SoapHttpClientProtocol 클래스의 GetWebRequest 메쏘드를 오버라이드 하여 이 헤더를 설정해 줄 수 있다.

class CustomProxy : WsHttpCompress.Localhost_Aspx.ReturnDataSet
{
    
// 압축 여부
    
private bool _DoCompress = false;

    
// 생성자. 웹 서비스에 HTTP 압축을 사용할 것인가 결정
    
public CustomProxy(bool doCompress)
    {
        _DoCompress 
doCompress;
    
}

    
// SoapHttpClientProtocol.GetWebRequest 메쏘드 오버라이드
    // HTTP 압축을 사용한다면 WebRequest에 Accept-Encoding 헤더 값을 설정한다.
    
protected override WebRequest GetWebRequest(Uri uri)
    {
        HttpWebRequest req 
(HttpWebRequest)base.GetWebRequest(uri);
        
// 압축이 요구되면 Accept-Encoding을 수행한다.
        
if (_DoCompress) {
            req.Headers[
"Accept-Encodi}ng""gzip";
        
}
        req.KeepAlive 
= false;
        return 
req;
    
}

   
// 이하 생략...
}

리스트3. HTTP 헤더에 Accept-Encoding 헤더 설정

이제 CusomProxy 클래스를 통해 웹 서비스를 호출하고, 웹 서비스 서버가 HTTP 압축을 하도록 설정되어 있다면, 웹 서비스 결과는 압축되어 반환될 것이다. 이제 문제는 압축된 HTTP Response를 압축해제 해야 하는 일만 남았다. 그런데... 이것이 좀 복잡하다. 앞서 언급한 대로 한방에 결과를 압축해제 하여 XML을 파싱하는 것이 아니라 SoapHttpClientProtocol 클래스는 WebResponse 클래스의 GetResponseStream() 메쏘드를 통해 조금씩 읽어 가면서 파싱한다는 것이다. 따라서 리스트2에서 처럼 단순히 베이스 클래스의 GetWebResponse 호출결과에 어떤 변경을 가해서 반환하는 것으론 제대로 작동하지 않는다.

이 때문에 새로운 WebResponse 클래스로서 DecompressHttpWebResponse 클래스를 필요로 하는데, 이 클래스는 베이스 클래스, 즉 SoapHttpClientProtocol 클래스의 GetWebResponse 가 반환한 클래스를 포함(containment)하는 클래스로 GetResponseStream 메쏘드가 단순한 스트림이 아닌, 압축을 해제하는 스트림을 반환하도록 만드는 것이다.

// 압축된 HTTP Response를 이해하고 압축을 해제하는 WebRespone의 파생 클래스
public class DecompressHttpWebResponse : System.Net.WebResponse, IDisposable
{
    
// HttpWebResponse 객체 Aggregation
    
private HttpWebResponse _Response;
    private 
Stream _OriginalStream;
    private 
GZipInputStream _ZipStream;

    public DecompressHttpWebResponse(HttpWebResponse response)
    {
        _Response 
response;
        
_OriginalStream response.GetResponseStream();
        
_ZipStream = null;
    
}

    
// WebResponse.GetResposneStream() 메쏘드 구현
    
public override System.IO.Stream GetResponseStream()
    {
        
// Content-Encoding 값에 의해 압축 여부를 판단하고 압축을 풀 것인가를 결정한다.
        
string contentEncoding _Response.ContentEncoding;
        if 
(contentEncoding == "gzip") {
            _ZipStream 
= new GZipInputStream(_OriginalStream);
            return 
_ZipStream;
        
}
        
else {
            
return _OriginalStream;
        
}
    }

    
// WebResponse.Close() 메쏘드 구현
    
public override void Close()
    {
        
if (_ZipStream != null) {
            _ZipStream.Close()
;
        
}
        _Response.Close()
;
    
}

    
// WebResponse.ContentLength 속성 구현
    
public override long ContentLength
    {
        
get return _Response.ContentLength}
        
set { _Response.ContentLength = value; }
    }

    
// WebResponse의 나머지 속성들은 모두 ContentLength 속성 구현처럼
    // aggregation 해 놓은 HttpWebResponse 의 속성을 반환해 주면 된다.
    // (ContentType, WebHeaderCollection, ResponseUri 속성)


    
// IDisposable.Dispose() 메쏘드 구현
    
void System.IDisposable.Dispose()
    {
        (_Response 
as IDisposable).Dispose();
    
}
}

리스트4. 압축을 해제하는 DecompressHttpWebResponse 클래스 구현

리스트4는 압축을 해제하는 DecompressHttpWebResponse 클래스 구현을 보여주고 있다. 이 클래스는 추상(abstract) 클래스인 WebResponse 클래스를 상속한다. 그리고 내부에 HttpWebResponse 클래스를 포함하고 있어서 WebResponse 클래스의 파생클래스가 반드시 구현해야 하는 메쏘드, 속성들을 모두 내부의 HttpWebResponse 객체의 값을 넘겨준다. 다만, GetResponseStream 메쏘드의 구현은 압축 해제를 하기 위해 ContentEncoding 헤더의 값을 판단하여 HTTP Response가 압축되어 있다면 압축을 풀기 위해 SharpZipLib 라이브러리의 GzipInputStream 클래스(gzip 압축을 푸는 스트림 클래스)를 반환해주도록 되어 있다.

이제 DecompressHttpWebResponse 클래스를 작성했으니, CustomProxy 클래스의 GetWebResponse 오버라이드의 구현은 손쉽게 작성할 수 있다.

// 베이스 클래스에서 반환한 HttpWebResponse 객체를 바탕으로 하여 DecompressHttpWebResponse 객체를
// 생성하여, 이것을 반환함으로써 압축이 해제되도록 한다.

protected override 
WebResponse GetWebResponse(WebRequest request)
{
    HttpWebResponse response 
(HttpWebResponse)base.GetWebResponse(request);
    
DecompressHttpWebResponse newResponse = new DecompressHttpWebResponse(response);
    return 
newResponse;
}

// 비동기 버전 역시 구현은 동일하다. (테스트는 못해봤다. 배째... -_-;)
protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
{
    HttpWebResponse response 
(HttpWebResponse)base.GetWebResponse(request, result);
    
DecompressHttpWebResponse newResponse = new DecompressHttpWebResponse(response);
    return 
newResponse;
}

리스트5. GetWebResponse 메쏘드 구현

리스트2의 코드에서 GetWebResponse 메쏘드 구현을 리스트5와 같이 바꾸면 CustomProxy 클래스는 완성된다. CustomProxy 클래스를 통해 이제 웹 서비스 호출이 압축되더라도 압축을 해제할 능력이 생긴 것이다 !

Usage

이제 클라이언트 코드는 VS.NET 혹은 wsdl.exe 가 생성해 준 웹 서비스 프록시 클래스를 사용하지 않고 압축 해제 능력이 있는 CustomProxy 클래스를 사용하면 된다. CustomProxy 클래스는 DecompressHttpWebRequest 클래스와 더불어 웹 서비스 결과가 압축되어 있다면(서버가 압축 설정이 되어 결과를 압축한 경우) 압축을 해제할 것이고, 그렇지 않다면 압축을 해제 하지 않는다. 클라이언트 예제 코드는 리스트6 과 같다.

CustomProxy svc = new CustomProxy();
DataSet ds svc.GetProducts();
Console.WriteLine("Result Record Count = {0}", ds.Tables[0].Rows.Count);

리스트6. CustomProxy 클래스 사용

Summary & Consideration

지금까지 설명한 CustomProxy 클래스와 DecompressHttpWebResponse 클래스는 닷넷으로 웹 서비스 클라이언트를 작성할 때 효과적으로 HTTP 압축을 처리한다. 반면 큰 단점이 있는데, 그것은 웹 서비스마다 커스텀 프록시를 작성해 주어야 한다. 이게 뭐 큰 문제냐 라고 생각하면 대략 난감하다. 대형 프로젝트를 웹 서비스로 구축해 본 경험이 있다면 그런 소리를 하지 않을 테니 말이다. 대형 프로젝트는 웹 서비스, 즉 다수의 .asmx 파일을 갖고 있다. 그리고, 이 asmx 파일마나 웹 서비스 프록시가 사용되므로 수십 수백개의 .asmx 파일을 가진 시스템은 커스텀 프록시 역시 수십, 수백 개를 만들어야 한다는 점이다. (오, 쉣 !) 더욱 문제 골치아프게 하는 것은... 하나의 웹 서비스를 여러 어셈블리(DLL)에서 호출한다면, 커스텀 프록시 역시 두 어셈블리에서 공유되어 한다. 즉, 커스텀 프록시를 별도의 어셈블리에 모아둘 필요가 생기는 것이다. 만약, 커스텀 프록시를 별도의 어셈블리에 모아둔다면, 과연 몇 개의 어셈블리에 나누어 커스텀 프록시를 모아 둘 것이가? 하나의 어셈블리에 모두 몰아 때려 놔도 되는가(오오... 단순 무식... 때론 단순 무식이 좋기도 하다...)? 나눈 다면 어떻게 나누는 것이 좋은가?

이러한 여러 사항들이 어셈블리의 참조 관계를 복잡하게 할 뿐더러, 여러 개발자가 개발에 참여한다면, 공유되는 커스텀 프록시를 사용하기 위한 여러 가지 규칙들을 고려해야만 한다. 이러한 것들을 고려하지 않고 무작정 프록시 클래스들만 찍어 내고, 마구 참조해서 사용한다면 재귀 참조(circular referenece) 같은 문제가 발생할 수도 있다.

또 한가지 필자가 이 글에서 제시한 웹 서비스 압축에 관련된 코드는, 새로이 프로젝트를 할 때는 유용할지 몰라도 이미 개발되어 운영 중인 프로젝트에 대해서는 유용하지 않을 수 있다. 왜냐하면 커스텀 프록시를 만드는 것은 둘째치고, 웹 서비스 호출을 수행하는 클라이언트 코드를 모두 수정해야 하기 때문이다. 이미 구축된 시스템이 크면 클 수록 작업량이 많아지는 대략 난감해 지는 시츄에이션으로 돌변한다.

클라이언트 코드를 수정하지 않고 HTTP 압축을 웹 서비스에 적용하는 해결책은 없냐고? "불가능은 없다... 대략 귀찮을 뿐..." 방법이 있긴 하지만... 이것이 필자와 우리 회사의 영업상의 비밀이기에 이 글을 통해 알려줄 수 없다는 것이 애석할 뿐이다.

약 올리냐고? 뭐 그렇게 생각해도 어쩔 수 없고... -_-;;

한 두 시간 짬이 나서 글을 쓴다는 것이 또.... 길어 졌다.... 블로그를 때려 치든가 해야징... 일안하고 딴짓 하느라 눈치보기 바쁘다... 된~장~



Comments (read-only)
#re: HTTP 압축 : 웹 서비스 클라이언트 / 임대열 / 2006-07-25 오후 7:44:00
잘 봤습니다.

그런데 제가 그대로 따라 해보는데 웹서비스를 호출하면 System.NotImplementedException 이 발생합니다.

이유가 뭔지 잘 모르겠습니다.
#re: HTTP 압축 : 웹 서비스 클라이언트 / 블로그쥔장 / 2006-07-25 오후 10:30:00
임대열님// 안녕하세요... NotImplementedException이 발생하는 위치를 잘 살펴보시고,
또한 호출 스택(call stack)을 잘 살펴 보시면 어디서 그러한 예외가 발생하는지 아실 수 있습니다.
그걸로 부터 원인 파악을 해 보십시요.
단순히 ~ 예외가 난다는 말로는 저도 왜 NotImplementedException이 발생했는지 원인 파악을 할 수 없습니다. -_-;
#re: HTTP 압축 : 웹 서비스 클라이언트 / 임대열 / 2006-07-26 오전 9:31:00
네. ^^;

일단 현재 파악한 상황으로는 어떤 웹서비스 메소드를 호출하여도 같은 예외가 발생합니다.

아마 제가 뭔가를 잘못한거 같습니다.

제가 이글을 잘 이해하고 그대로 따라 했는지 궁금합니다.

웹서비스 참조시 Visual Studio 2003에서 자동으로 만들어진 Reference.cs 파일의 프록시 클래스 선언은

public class Service1 : System.Web.Services.Protocols.SoapHttpClientProtocol {
...
}
이런식으로 되어 있습니다.

그래서 다른 클래스를 추가해서 이 클래스를 위의 클래스를 상속받게 만들었습니다.

구현은 똑같이 따라 했습니다.

public class UBISTWebservice : WebReference.Service1
{
// 압축 여부
private bool _DoCompress = false;

// 생성자. 웹 서비스에 HTTP 압축을 사용할 것인가 결정
public UBISTWebservice(bool doCompress)
{
_DoCompress = doCompress;
}

// SoapHttpClientProtocol.GetWebRequest 메쏘드 오버라이드
// HTTP 압축을 사용한다면 WebRequest에 Accept-Encoding 헤더 값을 설정한다.
protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(uri);
// 압축이 요구되면 Accept-Encoding을 수행한다.
if (_DoCompress)
{
req.Headers["Accept-Encoding"] = "gzip";
}
req.KeepAlive = false;
return req;
}

protected override WebResponse GetWebResponse(WebRequest request)
{
HttpWebResponse response = (HttpWebResponse)base.GetWebResponse(request);
DecompressHttpWebResponse newResponse = new DecompressHttpWebResponse(response);
return newResponse;
}

protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
{
HttpWebResponse response = (HttpWebResponse)base.GetWebResponse(request, result);
DecompressHttpWebResponse newResponse = new DecompressHttpWebResponse(response);
return newResponse;
}
}

그리고 DecompressHttpWebResponse 클래스를 위의 구현대로 그대로 클래스를 생성해서 추가 했습니다.

그리고 클라이언트에서 다음과 같은 식으로 사용했습니다.

UBISTWebservice service=new UBISTWebservice(true);
service.Method()

여기에서 Method() 부분을 여러개로 바꾸어 봤는데 계속 똑같은 Exception 이 발생합니다.

제가 제대로 이해하고 구현을 한건가요?

수고스럽겠지만 그 부분만이라도 답변을 해주시면 고맙겠습니다.
#re: HTTP 압축 : 웹 서비스 클라이언트 / 블로그쥔장 / 2006-07-26 오전 10:23:00
흠... 구현은 제대로 하신것 같은데요...
DecompressHttpWebResponse 클래스의 구현을 이 글에 나와 있는대로 하셨나요?
리스트4는 전체 코드 구현이 아닙니다. 일부 구현들은 생략되어 있습니다만...
Exception의 호출스택을 잘 살펴보십시요. 어디에서 코드의 어느 부분에서
NotImplementedException 이 발생했는지 파악하는 것이 중요합니다.
NotImplementedException 은 주로 오버라이드를 해야할 메쏘드를 오버라이드 하지 않은 경우에 주로 발생하곤 합니다.
또한, Fiddler를 구동시키셔서 웹 서비스 호출이 실제로 이루어 지는지 확인해 보십시요.
일단 웹 서비스가 결과를 반환한 이후에 예외가 발생한다면
프록시의 GetWebResponse 부분 이후(DecompressHttpWebResponse 클래스 포함)에서 오류가 발생한 것입니다.
디버거를 이용하여 GetWebRequest와 GetWebResponse에 Break point를 설정해 보시고 추적을 해보시는 것도
좋은 방법입니다.

건투를 빕니다.
#re: HTTP 압축 : 웹 서비스 클라이언트 / 임대열 / 2006-07-26 오전 11:13:00
네 에러가 나는 원인을 알았습니다.

DecompressHttpWebResponse 구현이 다 되어있던 것이 아니었군요.

나머지를 구현해놓으니 에러가 발생하지 않습니다.

감사합니다. ^^
#re: HTTP 압축 : 웹 서비스 클라이언트 / 블로그쥔장 / 2006-07-26 오전 11:22:00
해결이 되셨다니 다행입니다. 참고로 닷넷 프레임워크 2.0을 사용하는 경우에는
간단한 속성 하나 설정으로 압축을 사용하실 수 있습니다.
관련 글은 다음 링크를 사용하십시요.

http://www.simpleisbest.net/archive/2006/06/27/678.aspx