WCF의 기본 <Contract> - Data Contract

WCF 2010.02.09 09:00 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


 

티스토리 툴바