SimpleIsBest.NET

유경상의 닷넷 블로그

BUG: 웹 서비스에서 DataTable 사용하기

by 블로그쥔장 | 작성일자: 8/10/2006 5:49:00 AM
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.

어떤 분이 질문을 하셔서 테스트를 하다가 알아낸 내용입니다. 닷넷 프레임워크 2.0에서는 DataTable 객체를 Serialize 할 수 있게 되어서 DataTable을 웹 서비스에서 직접적으로 사용할 수 있게 되었다고 되어 있습니다(닷넷 프레임워크 2.0에서 ADO.NET의 새로운 기능 참조). 하지만 이 말은 100% 맞다고 할 수 없습니다. Visual Studio 2005의 버그로 인해 데이터 테이블을 직접 사용하는 웹 서비스를 사용하는데 문제가 발생합니다. 이 글은 이러한 문제를 살펴보고 이를 Work Around (피해가는) 방법을 알아보도록 하겠습니다.

질문을 받은 지 2주 정도 되었지만 이제서야 글을 쓰네요... 요즘은 귀차니즘 때문이 아닙니다. 정말로 요즘은 마니 바뿌답니다... 믿거나 말거나 지만... -_-;

Bug: Using DataTable in XML Web Service

닷넷 프레임워크 2.0의 새로운 기능들을 자랑(?)하는 What's New 자료 중에서 ADO.NET 2.0의 새로운 기능을 기술하는 문서를 살펴보면 여러 가지 새로운 기능 중에 DataTable의 기능이 여러 모로 향상되었음을 알 수 있다. 시간 관계상 이들을 다 언급할 수는 없고... 그 중 DataTable이 DataSet에 대해 독립적으로 Serialize(직렬화)할 수 있다고 되어 있다. 그래서 웹 서비스 같은 곳에서 DataSet 뿐만 아니라 DataTable을 독립적으로 사용할 수 있다고 되어 있다.

Introduction

이 말은 사실이다. 간단한 리스트1의 코드를 살펴보면 복잡할 거라곤 하나도 없는 아주 간단한 웹 서비스 구현이다. 이 웹 서비스의 달랑 하나 존재하는 TestMethod가 DataTable을 반환하고 있음을 노려봐야 한다.

<%@ WebService Language="C#" Class="TestService" %>

//

// 데이터 테이블을 반환하는 아조 간단한 웹 서비스.

// code behind 고 나발이고도 필요 없을 정도로 간단타.

// (ASP.NET 1.x와 2.0에서 모두 작동함)

//

using System;

using System.Web;

using System.Web.Services;

using System.Web.Services.Protocols;

using System.Data;

using System.Data.SqlClient;

 

[WebService(Namespace = "http://simpleisbest.net/example")]

public class TestService : System.Web.Services.WebService {

 

    [WebMethod]

    public DataTable TestMethod()

    {

        SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Products",

            "SERVER=(local);Trusted_Connection=true;DATABASE=Northwind");

        DataSet ds = new DataSet();

 

        adapter.Fill(ds);

        return ds.Tables[0];

    }

}

리스트1. DataTable을 반환하는 간단한 웹 서비스

리스트1의 코드를 ASP.NET 1.x에서 수행하면 얄짤없이 화면1과 같이 NotSupportedException이 발생한다. 이는 닷넷 프레임워크 1.x 에서 DataTable이 XML로 Serialize 되지 않았음을 알 수 있다.

하지만 이 코드를 ASP.NET 2.0에서 돌려보면 언제 그랬냐는 듯이 아주 잘 작동하며 결과 역시 테이블의 내용을 아조 잘 보여준다. 이로써 닷넷 프레임워크 2.0에서 ADO.NET의 새로운 기능이란 문서에서 주장하듯 DataTable이 Serialize 된다는 것이 구라가 아님을 확인할 수 있다.


화면1. 닷넷 프레임워크 1.x에서 DataTable을 사용하면 나타나는 졸라 무정한 예외 메시지

Problem When Writing Client

여기까지만 테스트 했다면 누구도 태클을 걸지 않을 것이다. 필자도 여기까지만 테스트하고 말았어야 했다. 클라이언트를 구현해 본답시고 웹 서비스 클라이언트를 만든 것이 화근 이였다. 보통 때 처럼 Console 어플리케이션을 생성하고 아무 생각 없이 웹 참조를 수행했다. 그리고 웹 서비스를 호출하는 코드를 작성했는데... 아무리 봐도 VS 2005 가 생성해 준 웹 서비스 프록시 클래스의 TestMethod() 메쏘드의 리턴 타입이 이상한 것 아닌가?

    /// <remarks/>

    [System.Web.Services.Protocols.SoapDocumentMethodAttribute(... 어쩌고 저쩌고...)]

    public TestMethodResponseTestMethodResult TestMethod()

    {

        object[] results = this.Invoke("TestMethod", new object[0]);

        return ((TestMethodResponseTestMethodResult)(results[0]));

    }

리스트2. VS 2005 혹은 wsdl.exe 가 생성해 준 웹 서비스 프록시의 TestMethod() 메쏘드

리스트2는 VS 2005가 생성해 준 웹 서비스 프록시 클래스인 TestService 클래스의 TestMethod() 메쏘드이다. 이 메쏘드의 리턴 타입은 놀랍게도 DataTable이 아닌 열라 이상하게 생긴 TestMethodResponseTestMethodResult 클래스이다.

이 메쏘드를 호출하는 코드를 작성하고 수행해 보면 결과값은 DataTable이 아닌 엉뚱한 XML 노드 몇개를 받게 된다. 또한 TestMethodResponseTestMethodResult 이란 클래스를 DataTable로 캐스팅(casting; 형변환)할 수 있는 것도 아니다. 그렇다면 당췌 클라이언트는 웹 서비스가 리턴해 준 DataTable 객체를 어케 받아낼 수 있단 말인가?

분기 탱천한 필자... 미친척하고 TestMethod() 의 TestMethodResponseTestMethodResult 타입 대신 DataTable로 바꾸어 보았다. 리스트3과 같이 말이다.

    /// <remarks/>

    [System.Web.Services.Protocols.SoapDocumentMethodAttribute(... 어쩌고 저쩌고...)]

    public DataTable TestMethod()

    {

        object[] results = this.Invoke("TestMethod", new object[0]);

        return ((DataTable)(results[0]));

    }

리스트3. 리턴 타입을 수정한 메쏘드

우씨 그랬더니 떡하니 DataTable이 리턴 되는 것이 아닌가? 이는 명백히 웹 서비스 프록시를 생성해 주는 VS 2005 혹은 wsdl.exe가 DataTable 타입에 대해 제대로 된 프록시 코드를 생성해 주지 못하는 버그로 보인다.

Work Around

앞서 언급한 버그를 피해가는 가장 좋은 방법은 DataTable을 사용하지 않고 예전처럼 DataSet을 사용하는 것이다. 나중에 설명하겠지만 DataTable을 사용한다고 해서 성능이 좋아질 것이라는 막연한 기대는 하지 말자. 하나의 테이블에 대해서 DataSet을 사용하건 DataTable을 사용하건 그 성능 차이는 거의 없다.

패 죽여도 DataTable을 꼭 써야겠다면(고집 무쟈게 세네... -_-;), VS 2005 혹은 wsdl.exe가 생성해 준 프록시 코드를 졸라 찾아 다니면서 리턴 타입 혹은 매개변수 타입을 DataTable로 수정해 주면 된다. 정말 이렇게 해야만 할까? 열심히 생성된 프록시 코드를 수정했다가, 웹 서비스 참조를 갱신(update)라도 하면 수정해 놓은 코드는 한 순간에 날라가 버리기 때문에 이 방법은 피하고 싶을 것이다.

C# 2.0에 추가된 partial 클래스 기법을 사용하거나, 상속을 받아서 overload 하는 방법은 없을까? 필자가 이렇게 저렇게 다 해본 결과 모두 다 실패였다. 필자가 아는 한 생성된 코드를(VS 2005의 경우 Reference.cs 파일) 수정하는 방법 밖에 없다.

마지막으로 MS가 이에 대한 수정할 때까지 기다리는 것이다. 언제 서비스 팩이 나올지 모르겠지만 그때는 이 문제가 해결되지 않을까? 아님 말고... (필자는 걍 DataSet 을 계속 사용할 것이다).

DataTable vs. DataSet in XML Web Service

앞서 잠깐 언급했지만 필자가 웹 서비스에서 DataTable을 사용하는 것을 미련 없이 포기할 수 있었던 것은 다 이유가 있어서 이다. 필자가 그냥 숏 꼴리는 대로 결정한 것은 아니고... 웹 서비스 상에서 DataTable을 사용할 때와 DataSet을 사용할 때의 성능 테스트를 수행한 결과 둘의 차이가 없었기 때문이다. 이제 필자가 수행한 성능 테스트의 결과를 독자들에게 공개하고자 한다. 애초에는 그래프까지 보여주면서 성능 비교를 하려고 했으나 귀차니즘이 몰려오고(쓰바 지금 새벽 6시다. 출근해야 하는데... 필자는 이젠 완전히 조뙤따...) 별 의미도 없기 때문에 간단히 말로 때우기로 한다.

테스트에 사용한 서버는 필자의 데스크톱 PC 로서Pentium 2.8GHz Dual Core (D820), 2GB 메모리, Windows 2003 SP1, IIS 6.0 이였다. 그리고 스트레스를 생성할 클라이언트는 필자의 노트북으로서 Pentimum 1.8GHz Mobile, 1.5 GB 메모리, Windows 2003 SP1 이다. 두 컴퓨터는 100Mbps 네트워크로 연결되어 있으며, 테스트에 사용한 테스트 툴은 VS 2005 Team Suite에 포함된 테스트 툴 이였다.

그리고 DataTable을 반환하는 웹 서비스를 호출하기 위해 VS 2005가 생성해준 프록시 코드에서 리턴 타입을 DataTable로 바꾸어서 웹 프록시를 반복적으로 호출하는 형태를 취하였음을 밝혀 둔다.

Returning Single Table

첫 번째 테스트는 Northwind 데이터베이스의 Products 테이블 하나를 DataSet에 넣어 반환할 때와 DataTable로 반환할 때 성능 비교를 수행해 보았다.

결론부터 말하자면 둘의 성능 차이는 1% 내외로 비슷했다. 단순히 DataTable이 빠를 것이라고 생각했다면 큰 오산이다. 그리고 여기서 또 한가지 뽀인뜨는 DataSet을 반환했을 때 XML과 DataTable의 반환했을 때의 XML이 거의 차이가 나지 않는다는 점이다. 직접 독자들이 살펴봐도 알겠지만 처음 한 두 줄을 제외하곤 판박이처럼 같다. 직접 복잡하게 스트레스 테스트를 해보지 않아도 이 두 XML을 비교해 봐도 성능이 비슷할 것이라는 예상을 할 수 있었고 실제 스트레스 테스트 결과도 그러하게 나왔다.

Returning Multiple Table

두 번째 테스트는 2개의 테이블(Products 테이블과 Customers 테이블)을 DataSet에 넣어 반환하는 경우와 DataTable의 배열로 반환하는 경우에 대한 테스트이다. 2개 이상의 테이블을 반환할 때 DataSet이 나은가 아니면 DataTable의 배열이 나은가에 대한 호기심에 함 테스트해본 것이다.

어느 것이 더 빨랐을까? 놀랄 것도 없이 둘의 차이는 1% 내외의 차이를 보였을 뿐 성능은 거의 비슷하게 나왔다.

Test Summary

테스트가 엄정한 환경에서 빡세게 진행된 것은 아니지만, 웹 서비스 상에서 DataTable을 반환하는 것과 DataSet을 반환하는 것의 성능차이는 거의 없었다. 이로써 버그까지 가지고 있는 DataTable을 굳이 웹 서비스에 사용할 필요가 없다는 것이 필자의 주장이 되겠다.

Summary

닷넷 프레임워크 2.0에서 DataTable이 DataSet과는 독립적으로 Serialize가 가능해 짐으로써 웹 서비스 상에서 DataSet 이 아닌 DataTable을 웹 서비스의 매개변수 혹은 리턴 타입으로 사용할 수 있게 되었다. 웹 서비스는 훌륭하게 DataTable을 Serialize 해 주지만 VS 2005와 wsdl.exe가 생성해 주는 웹 서비스 프록시는 DataTable을 제대로 인식하지 못해서 잘못된 결과를 반환하는 코드를 생성해 준다. 이는 VS 2005와 wsdl.exe의 버그로 추측되며 이러한 상황을 유도한 MS는 쑛잡고 반성해야 할 것이다.

이러한 버그를 피해 가기 위해서는 생성된 웹 서비스 프록시 코드를 수정하여 DataTable이 제대로 반환되도록 하면 된다. 하지만 이러한 방법은 웹 서비스가 수정되어 프록시 코드를 다시 생성해야 할 때에 변경한 부분이 유지되지 않고 사라져 버린다는 단점이 있다.

필자의 개인적인 성능 테스트 결과 웹 서비스가 DataSet을 반환하거나 DataTable을 반환할 때 둘의 성능적인 차이는 거의 없었으며, DataSet과 DataTable이 생성해 내는 XML 역시 매우 흡사하였다. 따라서 현재까지의 상황에서 버그까지 가지고 있는 DataTable 보다는 DataSet을 웹 서비스에 사용하는 것이 유리하다는 것이다. 물론 추후에 MS가 DataTable의 Serialization 성능을 향상시키고 VS 2005 및 wsdl.exe의 버그(?)를 수정한다면 이야기는 달라질 수 있다.



Comments (read-only)
#re: BUG: 웹 서비스에서 DataTable 사용하기 / 역시 기대를 충만시키는 글입니다. / 8/10/2006 3:14:00 PM
저는 그냥 버그려니 하고 접어버렸는데
이렇게 테스트와 해결책을 주시셔 감사합니다.

저도 열심히 공부하고자하는 마음이 충만해집니다.

다시 한번 감사드립니다.

PS: 제가 드린 두번째 질문은 아직도 해결책을 못찾고 있습니다.
개발하는데 아주 중요하지 않지만 그냥 오기로 열심히 자료를 찾고 예제도 만들어 보지만
제 실력이 미천한 관계로 안되는군요(염치가 없어서 해결해달라고는 못하고 시간나시면 ...)
#역시 기대를 충만시키는 글입니다. 2 / 위시 / 8/11/2006 3:22:00 PM
감사합니다..이번에도 머리 꽉~채우고 갑니다...^^
주인장님의 내공에 놀라움을 금치못하면서....^^
#re: BUG: 웹 서비스에서 DataTable 사용하기 / 김한수 / 8/14/2006 9:49:00 AM
저도 한때는 위와 같은 방법으로 사용을 했었습니다. 하지만 데이터가 많아지니, 성능에 문제가 많더군요.
MSDN에도 해당 성능 테스트가 나와있지만, 가능하면 오브젝트 배열로 넘겨주라고 하더군요.
물론 코딩이 좀 더 빡시기는 한데, 눈에 띄는 성능향상이 있었습니다.
그래서 저는 요즘 Dataset 대신에 오브젝트 배열로 리턴값을 넘기고 있습니다.
코딩이야 개발자가 고생하면 되지만, 속도느린건... 도저히 용서가 안되서리. ㅡㅡ;
#re: BUG: 웹 서비스에서 DataTable 사용하기 / name / 8/28/2006 8:13:00 PM
이거 댓글 지우는게 없네요...ㅋㅋ
간단한 질문 하나!!!
asp로 쇼핑몰 잘 돌아가고 있습니다. 근데...굳이 닷넷 2.0으로 변경해야할
이유가 뭘까요? 물론 안 해도 되겠죠.
제 말은 변경했을때...좋은점이 뭘까? 하는 야그입니다...^^;
#나름대로 보충 설명해봤습니다. ^^ / 정성태 / 9/8/2006 2:49:00 PM
요즘 갑자기 바빠서 다른 분들 블로그도 제대로 못 읽고 다녔습니다. ^^ 이 글도 오늘에서야 읽게 되어서... 관련해서 보충 설명하는 토픽 한번 써봤습니다. ^^
http://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&pageno=0&detail=1&wid=338

그럼... 주인장님... 또 좋은 토픽 기대하겠습니다. ^^
#re: BUG: 웹 서비스에서 DataTable 사용하기 / 블로그쥔장 / 9/12/2006 9:35:00 AM
좋은 내용 입니다. 멋지군요... 역시 성태씨야...
여러가지 문제를 해결할 수 있는 SchemaImporterExtension에 대한 정보를 주신 성태씨께 심심한 감사를...
#re: BUG: 웹 서비스에서 DataTable 사용하기 / 김한수 / 9/18/2006 2:29:00 PM
.NET에서는 위에 처럼 작업하면 될 것 같은데요.
그럼 만약 java 등에서 해당 웹서비스를 부를 경우 문제가 되지 않을까요?
기본 타입이 아니다보니 뭔가 다른 짓을 할 것 같은 느낌이... 뇌리를 빡 스치네요.. ^^;
#re: BUG: 웹 서비스에서 DataTable 사용하기 / 블로그쥔장 / 9/18/2006 3:25:00 PM
Java가 클라이언트라면 DataTable 뿐만 아니라 DataSet 역시 동일한 문제를 유발합니다.
이 경우, 결과 XML을 직접 파싱해야 합니다.
이는 Java 뿐만 아니라 ASP 등 unmanaged code 도 동일하게 적용되는 상황입니다.
#re: BUG: 웹 서비스에서 DataTable 사용하기 / 이도한 / 9/26/2006 1:41:00 AM
우이씨... DataTable 반환형이 생겨서 좋다 싶었는데... 기냥 막연하게 좋다라고 생각했던것 같네요. 이런 저의 TypeOf("생각").Replace() 하겠습니다. ^^
좋은 글 감사합니다. 자야 되는데 글에 주욱 주욱 빠져 듭니다. X 됐다. 내일 출근 어카나... -_-''
#re: BUG: 웹 서비스에서 DataTable 사용하기 / 진성욱 / 3/27/2007 11:20:00 PM
좋은 글 보고 갑니다~^^
검색하니 구글에서 1등으로 나오네요 ㅋ