login register Sysop! about ME  
qrcode
    최초 작성일 :    2009년 01월 06일
  최종 수정일 :    2009년 01월 06일
  작성자 :    Loner
  편집자 :    Loner (유경상)
  읽음수 :    29,718

강좌 목록으로 돌아가기

필자의 잡담~

강좌가 조금 늦었네요. 죄송합니다. ^^
이전 WCF 강좌에서 이어지는 내용입니다.
다음 강좌는 프록시 자동 생성에 관한 이야기가 올라올 예정입니다.
감사합니다.

WSDL과 서비스 메타 정보

지금까지 필자가 살펴본 Hello World 서비스의 구현은 적은 코드를 가지고 다양한 기능을 제공하는 웹 서비스와 클라이언트를 작성하게 해주는 것이었다. 하지만 여전히 서비스와 클라이언트의 구현에서 불만 사항이 남아 있다. 특히, 클라이언트 측의 구현 사항에 대해 심각한 불만 사항이 존재하는데 그것은 다름 아닌 서비스 코드를 클라이언트가 직접 “참조” 해야만 한다는 점이다. 즉, ChannelFactory와 프록시를 생성하기 위해서는 반드시 IHelloWorld 인터페이스를 클라이언트 코드가 참조해야 하지만 이 인터페이스의 선언은 서비스 구현 코드, 즉 HelloWorldService 프로젝트 내에 존재한다는 것이다. 그리고 이 프로젝트에는 IHelloWorld 인터페이스뿐만 아니라 서비스의 실제 구현을 맡고 있는 HelloWorldWCFService 클래스의 구현까지도 포함되어 있지 않은가?

클라이언트를 구현하기 위해 서비스의 계약 인터페이스에 대한 정보가 필요하고 이를 위해서 서비스의 구현을 담는 어셈블리를 클라이언트에 배포해야 한다면 내부 구현을 클라이언트에게 알리지 않는다는 서비스 지향 아키텍처(SOA; Service Oriented Architecture)에 위반될 뿐 더러, 자칫 잘못하면 서비스 구현 기술에 대한 중요한 기밀이 외부에 새어 나갈 수도 있다는 말이 되겠다. 간단한(?) 해결책으로 HelloWorldService 프로젝트를 다시 두 개의 프로젝트로 나누고 한 프로젝트에는 IHelloWorld 인터페이스에 대한 선언만을, 또 한 프로젝트에는 IHelloWorld 인터페이스의 구현인 HelloWorldWCFService 클래스를 넣어두는 것을 생각해 볼 수 있겠다. 물론 클라이언트에게는 인터페이스 만이 포함된 어셈블리를 배포해야 할 것이다.

비록 인터페이스만이 포함된 어셈블리만을 배포하는 방식으로 구현을 숨기는 방법을 사용한다 하더라도 문제는 여전히 남아 있다. WCF 클라이언트에게는 그런 방식으로 클라이언트 구현이 가능하도록 한다 할지라도 WCF가 아닌 일반 닷넷 클라이언트나 닷넷이 아닌 ASP, JAVA, C/C++ 클라이언트에게도 닷넷 어셈블리를 배포할 수 있을까? 그들에게 닷넷 어셈블리는 아무짝에도 쓸모 없는 바이너리 코드 덩어리일 뿐이다. 따라서 서비스는 자신의 인터페이스 계약을 클라이언트에게 알려줄 다른 방법이 필요하며, 이 방법은 서비스의 플랫폼이나 구현 기술, 프로그래밍 언어에 무관해야 할 것이고 절대 다수의 클라이언트들이 이 방법을 이해하고 서비스의 인터페이스 계약이 어떠한 것인지 알아 들을 수 있는 표준적인 방법이어야만 한다.

WSDL(Web Service Description Language)이 바로 서비스의 계약을 표준적인 방법으로 클라이언트에게 알리기 위한 웹 서비스 표준이다. WSDL은 웹 서비스가 어떤 메쏘드를 가지고 있고 이 메쏘드의 매개변수와 반환 값이 무엇인가를 XML을 통해 알려줄 뿐만 아니라 웹 서비스에 접근하는 방법으로 HTTP GET, HTTP POST, SOAP 등 중에서 어느 것이 지원되는가도 알려준다. 클라이언트는 서비스의 WSDL로 부터 서비스가 어떤 메쏘드를 제공하는지 매개변수의 타입은 어떠한지 알아낼 수 있으므로 서비스 호출에 필요한 모든 정보들을 알아낼 수 있는 것이다. [리스트 12]는 Hello World 서비스에 대한 WSDL의 일부를 보여주고 있다.

리스트 12. Hello World 서비스에 대한 WSDL의 일부

<?xml version="1.0" encoding="utf-8" ?>
<wsdl:definitions targetNamespace="http://www.simpleisbest.net/wcf/helloworld" ...>
    <wsdl:types>
        <xsd:schema targetNamespace="...">
        ......
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="IHelloWorld_SayHello_InputMessage">
        <wsdl:part name="parameters" element="tns:SayHello" />
    </wsdl:message>
    <wsdl:message name="IHelloWorld_SayHello_OutputMessage">
        <wsdl:part name="parameters" element="tns:SayHelloResponse" />
    </wsdl:message>
    <wsdl:portType name="IHelloWorld">
        <wsdl:operation name="SayHello">
            <wsdl:input wsaw:Action="..."
                        message="tns:IHelloWorld_SayHello_InputMessage" />
            <wsdl:output wsaw:Action="..."
                        message="tns:IHelloWorld_SayHello_OutputMessage" />
        </wsdl:operation>
    </wsdl:portType>
</wsdl:definitions>

[리스트 12]는 Hello World 서비스의 계약인 IHelloWorld 인터페이스가 SayHello 메쏘드를 가지고 있고 이 메쏘드를 호출하기 위한 SOAP 메시지의 스키마와 SayHello 메쏘드가 반환하는 SOAP 메시지의 스키마에 대한 정보를 포함한다. 이들 스키마 정보들로부터 SayHello 메쏘드의 매개변수가 무엇인지 그리고 반환 값의 타입이 무엇인지 알아낼 수 있으며 이로부터 다시 서비스의 메쏘드인 SayHello에 대한 닷넷 메쏘드 프로토타입(prototype; 함수의 이름과 매개변수 그리고 반환 타입을 함수 프로토타입이라 한다)을 생성해 낼 수 있는 것이다.

웹 서비스를 처음 접하는 개발자라면 [리스트 12]의 XML을 이해하려 들지 않는 것이 좋다. 비록 XML 이란 것이 사람이 읽기 쉬운 텍스트 기반의 마크업(Markup) 언어라고 할지라도 WSDL 정도 되면 복잡한 참조와 XML 네임스페이스 때문에 가독성(readability)이 크게 떨어질뿐더러 사람이 이해하기란 매우 어려워진다. 또한 WSDL 자체의 스키마 정의도 잘 알아야만 [리스트 12]를 이해할 수 있으므로 이제 막 WCF를 배우기 시작하는 독자들에겐 당연히 이해가 안될 것이다. 사실 WSDL을 잘 이해하고 있으면 분명 도움이 되지만 필수 조건은 아니다. 개발자는 WSDL이 무엇이며 어떤 역할을 하는지를 잘 이해하고 WSDL을 통해 무엇을 할 수 있는지 정도만 잘 이해 하도록 하자.

클라이언트가 서비스를 성공적으로 호출하기 위해서는 서비스가 어떤 메쏘드를 가지고 있는지 그리고 그 메쏘드의 매개변수, 반환 값은 어떤지에 대한 인터페이스 정보가 있어야 한다. 뿐만 아니라 서비스가 사용하는 트랜스포트, 메시지 인코딩과 같은 바인딩 속성에 대한 정보도 클라이언트에게 알려주어야 한다. 이처럼 서비스 호출을 위한 다양한 정보들을 서비스 메타 데이터(Metadata)라고 하고 서비스에 대한 메타데이터는 WSDL을 통해 다양한 플랫폼을 가진 클라이언트에게 알려줄 수 있다. 또한 WCF는 서비스의 메타 데이터를 제공하기 위해 서비스에 미리 정의되어 있는 종점(endpoint)를 추가하여 메타데이터를 제공하는 방법도 제공한다. 전통적인 HTTP GET을 통해 WSDL을 제공하는 방법과 WCF에서 미리 구현되어 있는 MEX(Metadata Exchange) 종점을 추가하는 방법 중 하나를 선택하거나 두 기능을 모두 제공할 수 있다. MEX 종점에 대해서는 추후에 메타 데이터를 상세히 다루는 6장에서 다시 살펴보도록 하자.

그림 1. WSDL과 MEX를 이용한 서비스 메타 데이터 게시

WCF에서 WSDL을 제공하기 위해서는 약간의 작업을 요구한다. 첫 번째 방법은 오프라인으로 WSDL을 생성하는 방법으로써 Windows SDK 에 포함된 svcutil.exe 이란 유틸리티를 사용하면 된다. 이 유틸리티에 서비스 호스트에 대한 어셈블리 혹은 서비스 구현에 대한 어셈블리를 매개변수로 다음과 같이 명시하면 WSDL 파일(.wsdl)과 관련 스키마(XSD) 파일(.xsd)을 생성해 준다.

svutil.exe HelloWorldService.dll

위와 같이 생성한 .wsdl 파일과 .xsd 파일을 클라이언트에게 배포하거나 코드를 생성하는데 사용할 수 있다. 구체적으로 이들 파일을 사용하여 클라이언트 코드를 생성하는 방법은 조금 후에 살펴보기로 하자.

Platform SDK vs. Windows SDK

일반적으로 지금까지 플랫폼 SDK란 이름으로 제공되던 다양한 C/C++을 위한 라이브러리 및 헤더는 Windows XP, Windows 2003 등의 특정 플랫폼 및 그 하위 플랫폼에 대한 개발환경을 제공하고 있었다. 닷넷 프레임워크를 위한 SDK는 별도로 닷넷 프레임워크 SDK 란 이름으로 별도로 제공되었고 이들 플랫폼 SDK와 닷넷 프레임워크 SDK는 Visual Studio 내에 포함되곤 했다.

닷넷 프레임워크 3.0이 등장하면서 이러한 SDK는 하나로 합쳐졌으며 Windows SDK란 이름으로 배포되고 있다. Windows SDK에는 C/C++ 기반의 윈도우 프로그램을 작성하기 위한 헤더, 라이브러리, 심지어는 C/C++ 컴파일러까지 포함되어 있으며 닷넷 프레임워크 2.0 및 WCF, WPF, WF, CardSpace 를 위한 유틸리티 및 예제를 포함한다. Windows SDK를 통해 개발할 수 있는 플랫폼은 Windows Vista, Windows 2003 SP1, Windows XP SP2, Windows 2000 이며 IE7에 대한 프로그램까지 개발이 가능하다.

Windows SDK가 Visual Studio 2005보다 나중에 나온 것이고 아직 이 SDK가 Visual Studio에 포함되어 있지 않으므로 별도로 다운로드 하여 설치해 주어야 한다. Visual Studio 2008에는 Windows SDK의 일부가 포함되어 있지만 가급적 Windows SDK를 별도로 다운로드 받아 설치해 주는 것이 좋다. WCF를 위한 구체적인 개발환경에 대한 내용은 부록을 참고하기 바란다.

이렇게 WSDL을 생성하는데 유틸리티까지 써가면서 생성할 필요가 있을까? ASP.NET 웹 서비스처럼 기본적으로 WCF 런타임이 온라인 상에서 WSDL을 제공해주는 기능은 없을까? 물론 있다. 하지만 보안상의 이유로 서비스에 대한 정보 공개를 최소화하기 위해 WSDL 자동 생성은 기본적으로 해제(disable) 되어 있다.

온라인 상에서 HTTP GET 을 통해 WSDL을 읽어 갈 수 있도록 하기 위해서는 다음과 같은 configuration 설정을 해 주어야만 한다.

리스트 13. HTTP GET을 통해 WSDL 생성을 위한 configuration 설정

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
        ......
        </bindings>
        <services>
            <service name="HelloWorldService.HelloWorldWCFService"
                    behaviorConfiguration="HelloWorldService">
                    ......
            </service>
        </services>
        <behaviors>
            <serviceBehaviors>
                <behavior name="HelloWorldService">
                    <serviceMetadata httpGetEnabled="true"/>
                </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>

여기에서 새로이 등장하는 개념이 서비스 행동(service behavior)이란 것이다. WCF에서 Behavior 란 것은 클라이언트와 서비스가 동의한 인터페이스와 무관하게 서비스가 내부적으로 사용하는 작동 방식을 지칭한다. 예를 들어 어떤 웹 서비스가 여러 클라이언트의 동시 호출을 처리하기 위해 내부적으로 쓰레드 풀을 쓴다고 가정해 보자. 이 때 클라이언트는 서비스가 내부적으로 쓰레드 풀을 쓰는지 단일 쓰레드를 쓰는 지는 관심사 밖이며, 또한 서비스 계약 인터페이스에 이러한 내부 구현 여부가 명시되지도 않는다. 사실 우리가 Google 이나 E-bay에서 제공하는 웹 서비스를 사용할 때 그 내부 구현이 C++인지 PHP 인지 알게 무엇인가? 우리가 알아야 할 것은 이 웹 서비스가 제공하는 메쏘드와 매개변수가 무엇이고 그 메쏘드의 수행결과가 무엇인가, 즉 서비스 인터페이스가 중요한 것이다.

Behavior는 WCF의 서비스 혹은 서비스의 메쏘드의 내부 구현에 관계된 사항을 제어할 때 사용되는 용어이며, 특정 서비스가 내부 작동 방식에 대한 설정이 필요한 경우 configuration 을 사용하거나 소스 코드 내에 ServiceBehavior와 같은 특성(attribute)를 사용하여 서비스의 행동 방식을 결정할 수 있다. WCF는 쓰레드 관리, 인스턴스 관리, 디버깅 등 다양한 서비스의 내부 구현에 관련된 다양한 Behavior 들을 제공하고 있으며, 서비스의 각 메쏘드 별로 트랜잭션 사용 여부 등을 결정하는데 적용되는 OperationBehavior 역시 제공하고 있다. 이들 Behavior 들에 대해서는 이 책의 전반에 걸쳐 지속적으로 등장하게 될 것이며 13장에서 상세하게 다시 언급될 것이다.

이 장에서 처음 등장한 서비스 Behavior는 서비스가 자동으로 WSDL을 제공할 것인가를 결정하는 ServiceMetadataBehavior 클래스이다. 이 Behavior가 서비스에 지정되면 WCF 런타임은 서비스를 위한 WSDL을 런타임에 자동을 생성해 줄 수 있다. 이 Behavior를 사용하는 것만으로 자동으로 생성된 WSDL을 클라이언트에게 제공할 수는 없다. 이 Behavior 클래스의 HttpGetEnabled 속성을 true로 설정해야만 HTTP GET 방식으로 WSDL을 제공할 수 있게 된다. 다시 이해하기 쉽게 말하자면 웹 브라우저에서 WSDL의 XML을 눈으로 확인하고자 하면(HTTP GET 방식), HttpGetEnabled 속성이 true 이어야 한다는 말이 되겠다. 웹 브라우저 외에도 곧 설명할 서비스 프록시 생성기(generator)를 사용하기 위해서도 이 속성의 값은 true로 설정되어야 한다. 왜냐면 프록시 생성기가 WSDL을 알아내기 위해 HTTP GET 방식으로 WSDL을 조회하기 때문이다.

[리스트 13]과 같이 configuration을 사용하여 HttpGetEnabled 속성을 바꾸고자 한다면 먼저 <behavior> 요소를 추가해 넣어야 한다. 그리고 <serviceMetadata> 요소의 httpGetEnabled 속성의 값을 true로 설정해 주면 된다. 그리고 서비스가 이 Behavior 의 적용을 받기 위해서는 서비스를 선언하는 <service> 요소에 behaviorConfiguration 속성을 추가하고 적용하고자 하는 Behavior의 이름을 명시하면 된다. 간단하지 않은가?

WCF에서는 configuration으로 수행하는 설정이라면 모두 코드로도 표현이 가능하다. ServiceHost 클래스의 Description 속성으로 제공되는 ServiceDescription 객체는 서비스가 가진 Behavior 들에 대한 컬렉션을 제공한다. 이 컬렉션은 Behaviors 속성으로 제공되며 이 속성은 주어진 Behavior에 대한 타입을 근거로 객체을 찾아주는 Find 메쏘드와 새로운 Behavior를 추가할 수 있는 Add 메쏘드를 가지고 있다. 따라서 코드를 통해 적용할 Behavior 객체를 만들고 이를 ServiceHost에 추가하면 해당 Behavior를 적용할 수 있는 것이다.

public class ServiceHost : ServiceHostBase
{
    public ServiceDescription Description { get; }
    ......
}

public class ServiceDescription
{
    public KeyedByTypeCollection<IServiceBehavior> Behaviors { get; }
    ......
}

public class KeyedByTypeCollection<TItem> : KeyedCollection<Type, TItem>
{
    public T Find<T>();
    ......
}

[리스트 14]는 코드를 통해 Behavior를 추가하거나 기존 설정을 변경하는 전형적인 예제를 보여주고 있다. [리스트 14]에서 관심을 가질 부분은 ServiceMetadataBehavior 객체가 Behaviors 컬렉션에 이미 존재하는가 살펴보는 코드이다. Configuration의 <serviceMetadata> 요소에 의해 ServiceMetadataBehavior 가 이미 설정되었다면 이 객체는 Behaviors 컬렉션 내에 이미 존재할 것이고 이미 존재하는 Behavior에 대해 동일한 Behavior가 추가되면 오류가 발생하기 때문이다. 의도하지는 않았지만 [리스트 14]의 코드는 ServiceBehavior 들에 대한 configuration의 설정을 오버라이드 하는 예제도 된다. Configuration에서 HttpGetEnabled 속성을 false로 지정하더라도 [리스트 14]의 코드에 의해 HttpGetEnabled 속성은 true로 오버라이드 되게 된다. 앞서 configuration 오버라이드에 대한 설명은 충분히 했으므로 더 이상 반복하진 않겠다.

리스트 14. WSDL에 대한 HTTP GET을 enable 시키는 코드

// 다음 코드에서 configuration을 읽어 ServiceHost를 초기화 한다.
ServiceHost host = new ServiceHost(typeof(HelloWorldWCFService));

// ServiceMetadataBehavior 설정
ServiceMetadataBehavior behavior =
    host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (behavior == null) {
    behavior = new ServiceMetadataBehavior();
    host.Description.Behaviors.Add(behavior);
}
behavior.HttpGetEnabled = true;

......

HTTP GET에 대한 설정이 끝났으면 이제 서비스는 클라이언트 요구에 의해 WSDL을 제공할 수 있게 되었다. HTTP GET 방식에 의한 WSDL은 서비스의 주소 뒤에 ?wsdl 이란 쿼리 문자열(query string)을 추가함으로써 얻을 수 있다. 우리의 간단한(?) Hello World 서비스에 대한 WSDL 주소는 다음과 같다.

http://localhost/wcf/example/helloworldservice?wsdl

이 URL을 웹 브라우저에 직접 입력하면 현란한 XML 덩어리를 볼 수 있을 것이며 이 XML은 다음에 설명할 프록시 자동 생성에 사용될 수 있는 WSDL 이다.

이렇게 자동적으로 서비스에 대한 메타 데이터를 생성하여 제공하면 되었지 왜 WCF는 svctuil.exe 를 사용하여 .wsdl 파일과 .xsd 파일들을 생성하는 기능을 만들어 놓았을까 생각해 보자. 세상은 넓고 다양한 상황이 존재할 수 있다는 점을 잊지 말자. 인터넷 상의 웹 서비스는 불특정 다수의 클라이언트에게 서비스를 제공하는가 하면 특정 몇몇 클라이언트에게만 서비스를 제공하는 경우도 있다. 불특정 클라이언트에게 서비스를 제공한다 함은 클라이언트가 누가 될지 모른다는 얘기가 되므로 서비스에 대한 메타 데이터를 제공하기 위해서는 HTTP GET이나 MEX(Metadata Exchange)를 통해 클라이언트가 능동적으로 서비스 메타 데이터를 조회하도록 하는 것이 좋을 것이다.

반대로 특정 몇몇 클라이언트에게만 서비스를 제공할 때는 아무나 서비스의 메타 데이터를 조회해 가는 것은 여러모로 찜찜하다. 비록 인증 등의 방법으로 아무나 서비스를 호출하지 못하도록 스스로를 보호하겠지만 악의적인 사람이 WSDL을 통해 서비스의 트랜스포트, 주소, 네트워크 포트와 같은 정보를 알아낼 수 있다. 또한 서비스의 메쏘드들에 대한 인터페이스 역시 공개되므로, HTTP GET과 같이 공개적인 방법으로 메타 데이터를 제공하는 것은 “해킹하는데 필요한 정보를 무료로 가져가세요”라고 하는 것과 같은 꼴이 된다. 바로 이러한 이유에서 특정 클라이언트들에게만 WSDL 파일을 SDK 란 형식을 빌어 제공하거나 기타 다른 방법으로 WSDL 파일을 제공하는 방법을 사용하는 것이 좋을 수 있다. 이 때 svcutil.exe 유틸리티를 이용하여 WSDL 파일을 생성하고 생성된 WSDL을 한정된 클라이언트에게만 제공할 수 있겠다. 클라이언트가 WCF를 사용한다면 아예 다음에 설명할 프록시 생성 방법을 이용하여 프록시를 생성하여 DLL을 제공하는 것 역시 좋은 접근 방법이라 할 수 있겠다.


authored by


 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})