'Language'에 해당되는 글 463건

  1. 2016.12.15 arguments 객체
  2. 2016.12.13 참조, 예제 사이트
  3. 2016.12.12 WSDL(Web Services Description Language)
  4. 2016.12.07 XML, SOAP, WSDL, UDDI
  5. 2016.09.09 Domain-Specific Language
  6. 2016.09.09 DSL(Domain Specific Language) 이해하기
  7. 2016.09.09 자바 람다식(Lambda Expressions in Java)
  8. 2016.09.09 Metaprogramming
  9. 2016.09.09 그루비의 동적 객체지향 - 1
  10. 2016.09.08 그루비의 동적 객체지향 - 2

arguments 객체

Language/JAVASCRIPT 2016. 12. 15. 16:32

arguments 객체는 함수에 전달된 인수에 해당하는 Array같은 객체입니다.

구문

arguments

설명

arguments 객체는 모든 함수 내에서 이용 가능한 지역 변수입니다. arguments 객체를 사용하여 함수 내에서 함수의 인수를 참조할 수 있습니다. 이 객체는 함수에 전달된 각 인수를 위한 항목(entry)을 포함합니다, 첫 번째 항목의 인덱스가 0에서 시작하는. 예를 들어, 함수에 인수 셋이 전달된 경우, 다음과 같이 참조할 수 있습니다:

arguments[0]
arguments[1]
arguments[2]

arguments는 설정될 수도 있습니다:

arguments[1] = 'new value';

arguments 객체는 Array가 아닙니다. Array와 비슷하지만 length 빼고는 어떤 Array 속성도 없습니다. 예를 들어, pop 메서드가 없습니다. 그러나 실제 Array로 변환될 수 있습니다:

var args = Array.prototype.slice.call(arguments);

arguments를 실제 Array로 변환하기 위해 Array.from() 메서드 또는 전개 연산자를 사용할 수도 있습니다:

var args = Array.from(arguments);
var args = [...arguments];

arguments에 slice를 사용하면 일부 JavaScript 엔진(예를 들어 V8 - 자세한 정보는 여기)에서 최적화를 막습니다. 그게 신경 쓰이면, 대신에 arguments 객체를 차례로 반복하여 새로운 배열을 구성해 보세요. 대안은 함수로서 멸시된(despised) Array 생성자를 사용하는 것입니다:

var args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments));

당신이 형식상 받기로 선언된 것보다 많은 인수로 함수를 호출하는 경우 arguments 객체를 사용할 수 있습니다. 이 기법은 가변 인수가 전달될 수 있는 함수에 유용합니다. 함수에 전달된 인수의 수를 결정하기 위해 arguments.length를 쓰세요, 그 뒤에 arguments 객체를 사용하여 각 인수를 처리하세요. 함수 signature에 매개변수의 수를 결정하기 위해서는, Function.length 속성을 쓰세요.

속성

arguments.callee
현재 실행 중인 함수를 가리킵니다.
arguments.caller 
현재 실행 중인 함수를 호출한 함수를 가리킵니다.
arguments.length
함수에 전달된 인수의 수를 가리킵니다.
arguments[@@iterator]
arguments의 각 인덱스 값을 포함하는 새로운 Array Iterator 객체를 반환합니다.

여러 문자열을 연결하는 함수 정의하기

이 예는 여러 문자열을 연결하는 함수를 정의합니다. 함수의 유일한 형식 인수는 연결할 항목을 구분하는 문자를 지정하는 문자열입니다. 함수는 다음과 같이 정의됩니다:

function myConcat(separator) {
  var args = Array.prototype.slice.call(arguments, 1);
  return args.join(separator);
}

이 함수에 인수를 얼마든지 전달할 수 있으며 리스트 내 항목처럼 각 인수를 사용하여 리스트를 만듭니다.

// "red, orange, blue" 반환
myConcat(", ", "red", "orange", "blue");

// "elephant; giraffe; lion; cheetah" 반환
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// "sage. basil. oregano. pepper. parsley" 반환
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

HTML 리스트를 만드는 함수 정의하기

이 예는 리스트 HTML을 포함하는 문자열을 만드는 함수를 정의합니다. 함수의 유일한 형식 인수는 리스트가 정렬되지 않은(bulluet(글 머리 기호)가 붙는) 경우 "u" 또는 정렬된(번호가 매겨진) 경우 "o"인 문자열입니다. 함수는 다음과 같이 정의됩니다:

function list(type) {
  var result = "<" + type + "l><li>";
  var args = Array.prototype.slice.call(arguments, 1);
  result += args.join("</li><li>");
  result += "</li></" + type + "l>"; // end list

  return result;
}

이 함수에 인수를 얼마든지 전달할 수 있고, 표시된 유형의 리스트에 항목으로 각 인수를 추가합니다. 예를 들면:

var listHTML = list("u", "One", "Two", "Three");

/* listHTML은:

"<ul><li>One</li><li>Two</li><li>Three</li></ul>"

*/

나머지, 기본 및 비구조화된 매개변수

arguments 객체는 나머지 매개변수기본 매개변수 또는 비구조화된 매개변수와 함께 사용될 수 있습니다.

function foo(...args) {
  return arguments;
}
foo(1, 2, 3); // { "0": 1, "1": 2, "2": 3 }

그러나, 비엄격 함수에서는 mapped arguments 객체는 함수가 어떤 나머지 매개변수기본 매개변수 또는 비구조화된 매개변수든 포함하지 않는 경우에만 제공됩니다. 예를 들어, 기본 매개변수를 사용하는 다음 함수에서는, 100 대신에 10이 반환됩니다:

function bar(a=1) {
  arguments[0] = 100;
  return a;
}
bar(10); // 10

이 예에서, 어떤 나머지 매개변수기본 매개변수 또는 비구조화된 매개변수가 없는 경우에는, 100이 반환됩니다:

function zoo(a) {
  arguments[0] = 100;
  return a;
}
zoo(10); // 100

출처 - https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/arguments

'Language > JAVASCRIPT' 카테고리의 다른 글

참고 강좌  (0) 2016.12.23
pply(), call() 메서드  (0) 2016.12.15
구글맵(차트)  (0) 2014.12.12
prototypejs  (0) 2013.11.13
마우스 커서 포지션 이동  (0) 2013.05.22
:

참조, 예제 사이트

Language/XML 2016. 12. 13. 09:56

http://tcpschool.com/xml/intro


http://www.w3schools.com/xml/default.asp

'Language > XML' 카테고리의 다른 글

WSDL(Web Services Description Language)  (0) 2016.12.12
XML, SOAP, WSDL, UDDI  (0) 2016.12.07
XML 스키마  (0) 2013.07.03
:

WSDL(Web Services Description Language)

Language/XML 2016. 12. 12. 17:20

저자 (알파벳 순):

Erik Christensen, Microsoft

Francisco Curbera, IBM

Greg Meredith, Microsoft

Sanjiva Weerawarana, IBM

Copyrightⓒ 2001 AribaInternational Business Machines CorporationMicrosoft

개요

WSDL은 문서 지향적 또는 프로시저 지향적인 정보를 포함한 메시지에서 작동하는 종점 집합으로서의 네트워크 서비스를 설명하는 XML 형식입니다. 이러한 작동 및 메시지에 대해 대략적으로 설명한 다음, 구체적인 네트워크 프로토콜 및 메시지 형식으로 연결하여 종점에 대해 정의합니다. 관련된 구체적인 종점은 추상 종점(서비스)과 결합되어 있습니다. WSDL은 통신에 사용되는 메시지 형식 또는 네트워크 프로토콜과는 상관 없이 종점 및 종점의 메시지를 설명하도록 확장할 수 있습니다. 하지만, 이 문서에 설명된 바인딩만이 SOAP 1.1, HTTP GET/POST 및 MIME과 함께 WSDL을 사용하는 방법을 설명합니다.

이 WSDL 언어 버전은 종점의 컴퍼지션 및 결합을 설명하는 프레임워크를 포함하지 않는 첫 번째 단계입니다. 계약을 설명하는 전체 프레임워크는 메시지를 받거나 보내는 순서 규칙과 같이 서비스의 동작을 표현하는 방법과 서비스를 작성하는 방법을 포함합니다. 서비스의 컴퍼지션은 모든 형식을 지원해야 하지만 런타임에 교환되고 바운드되는 서비스 참조와 함께 전달되는 참조를 허용해야 합니다. 특히, 후자는 런타임 계약 협상의 중요한 요소이며 참조 동작을 캡처하고 서비스를 중개합니다.

WSDL 스펙 작성자들은 (1)서비스를 작성하는 프레임워크 및 (2)서비스의 동작을 설명하는 프레임워크를 포함하는 WSDL의 수정된 버전 및/또는 추가 문서를 적시에 게시하고 있습니다.

상태

여기에서는 Ariba, IBM 및 Microsoft 내에서 서비스의 설명과 관련한 현재 고려 사항을 보여 주며 NASSL, SCL 및 SDL(이 분야에서의 이전 제안)의 개념을 통합합니다.

목차

1. 소개 1. 소개 
1.1 WSDL 문서 예제 1.1 WSDL 문서 예제 
1.2 국제 규칙 1.2 국제 규칙 
2. 서비스 정의 2. 서비스 정의 
2.1 WSDL 문서 구조 2.1 WSDL 문서 구조 
2.1.1 문서 명명 및 연결 2.1.1 문서 명명 및 연결 
2.1.2 제작 스타일 2.1.2 제작 스타일 
2.1.3 언어 확장성 및 바인딩 2.1.3 언어 확장성 및 바인딩 
2.1.4 설명서 2.1.4 설명서 
2.2 형식 2.2 형식 
2.3 메시지 2.3 메시지 
2.3.1 메시지 부분 2.3.1 메시지 부분 
2.3.2 추상적인 메시지와 구체적인 메시지 2.3.2 추상적인 메시지와 구체적인 메시지 
2.4 포트 형식 2.4 포트 형식 
2.4.1 단방향 작업 2.4.1 단방향 작업 
2.4.2 요청-응답 작업 2.4.2 요청-응답 작업 
2.4.3 검색-응답 작업 2.4.3 검색-응답 작업 
2.4.4 알림 작업 2.4.4 알림 작업 
2.4.5 작업 내 요소의 이름 2.4.5 작업 내 요소의 이름 
2.4.6 작업 내 매개 변수 순서 2.4.6 작업 내 매개 변수 순서 
2.5 바인딩 2.5 바인딩 
2.6 포트 2.6 포트 
2.7 서비스 2.7 서비스 
3. SOAP 바인딩 3. SOAP 바인딩 
3.1 SOAP 예제 3.1 SOAP 예제 
3.2 SOAP 바인딩이 WSDL을 확장하는 방법 3.2 SOAP 바인딩이 WSDL을 확장하는 방법 
3.3 soap:binding 3.3 soap:binding 
3.4 soap:operation 3.4 soap:operation 
3.5 soap:body 3.5 soap:body 
3.6 soap:fault 3.6 soap:fault 
3.7 soap:header 3.7 soap:header 
3.8 soap:address 3.8 soap:address 
4. HTTP GET 및 POST 바인딩 4. HTTP GET 및 POST 바인딩 
4.1 HTTP GET/POST 예제 4.1 HTTP GET/POST 예제 
4.2 HTTP GET/POST 바인딩이 WSDL을 확장하는 방법 4.2 HTTP GET/POST 바인딩이 WSDL을 확장하는 방법 
4.3 http:address 4.3 http:address 
4.4 http:binding 4.4 http:binding 
4.5 http:operation 4.5 http:operation 
4.6 http:urlEncoded 4.6 http:urlEncoded 
4.7 http:urlReplacement 4.7 http:urlReplacement 
5. MIME 바인딩 5. MIME 바인딩 
5.1 MIME 바인딩 예제 5.1 MIME 바인딩 예제 
5.2 MIME 바인딩이 WSDL을 확장하는 방법 5.2 MIME 바인딩이 WSDL을 확장하는 방법 
5.3 mime:content 5.3 mime:content 
5.4 mime:multipartRelated 5.4 mime:multipartRelated 
5.5 soap:body 5.5 soap:body 
5.6 mime:mimeXml 5.6 mime:mimeXml 
6. 참조 6. 참조 
A 1. URI에 대한 참고 A 1. URI에 대한 참고 
A 1.1 XML 이름 공간 및 스키마 위치 A 1.1 XML 이름 공간 및 스키마 위치 
A 1.2 상대 URI A 1.2 상대 URI 
A 1.3 URI 작성 A 1.3 URI 작성 
A 2. 네트워크 형식의 WSDL 예제 A 2. 네트워크 형식의 WSDL 예제 
A 2.1. 예제 1 A 2.1. 예제 1 
A 3. 확장성 요소의 위치 A 3. 확장성 요소의 위치 
A 4. 스키마 A 4. 스키마 
A 4.1 WSDL 스키마 A 4.1 WSDL 스키마 
A 4.2 SOAP 바인딩 스키마 A 4.2 SOAP 바인딩 스키마 
A 4.3 HTTP 바인딩 스키마 A 4.3 HTTP 바인딩 스키마 
A 4.4 MIME 바인딩 스키마 A 4.4 MIME 바인딩 스키마 

1. 소개

통신 프로토콜 및 메시지 형식이 웹 커뮤니티에서 표준화됨에 따라, 통신을 어느 정도 구조적인 방법으로 설명하는 것이 점차 가능해지고 중요해지게 되었습니다. WSDL은 네트워크 서비스를 메시지를 교환할 수 있는 통신 종점의 컬렉션으로 설명하는 XML 문법을 정의하여 이러한 필요성을 해결합니다. WSDL 서비스 정의는 분산 시스템에 대한 설명서를 제공하고 응용 프로그램 통신과 관련된 자세한 정보를 자동화할 수 있도록 합니다.

WSDL 문서는 services를 네트워크 종점의 컬렉션 또는 ports로 정의합니다. WSDL에서 종점 및 메시지의 추상 정의는 구체적인 네트워크 구축 또는 데이터 형식 바인딩과는 구분되며 이러한 특성은 추상 정의를 재사용 가능하도록 합니다. 즉, messages는 교환되는 데이터의 추상 설명이고, port types은 operations의 추상 컬렉션입니다. 특정 포트 유형에 대한 구체적인 프로토콜과 데이터 형식 지정은 재사용 가능한 binding을 구성합니다. 포트는 네트워크 주소를 재사용 가능한 바인딩에 연결하여 정의되고, 포트의 컬렉션은 서비스를 정의합니다. 따라서 WSDL 문서는 네트워크 서비스의 정의에서 다음과 같은 요소를 사용합니다.

  • Types XSD와 같은 특정 형식 시스템을 사용하는 데이터 형식 정의에 대한 컨테이너.

  • Message 통신할 데이터에 대한 추상적이고 형식화된 정의.

  • Operation 서비스가 지원하는 동작에 대한 추상적인 설명.

  • Port Type 하나 이상의 종점에서 지원하는 추상적인 작업 집합.

  • Binding 특정 포트 유형에 대한 구체적인 프로토콜 및 데이터 형식 지정.

  • Port 바인딩과 네트워크 주소가 결합되어 정의되는 단일 종점.

  • Service 관련된 종점의 컬렉션.

이러한 요소들은 2절에서 자세하게 설명합니다. WSDL이 새로운 형식 정의 언어를 도입하는 것은 아니라는 것을 알아두십시오. WSDL은 메시지 형식을 설명하는 다양한 형식 시스템의 필요성을 인식하여 정식의 형식 시스템으로 XML 스키마 규격(XSD)[11]을 지원합니다. 그러나 현재의 메시지 형식을 포함하여 앞으로 사용하게 될 모든 메시지 형식을 설명하는 데 있어 단일 형식 시스템 문법을 기대하기 어렵기 때문에, WSDL은 확장성을 통해 다른 형식의 정의 언어를 사용할 수 있도록 합니다.

또한 WSDL은 일반적인 binding 메커니즘을 정의합니다. 이 메커니즘은 특정 프로토콜이나 데이터 형식 또는 구조를 추상 메시지, 작업 또는 종점에 연결하는 데 사용되고 추상 정의를 재사용할 수 있도록 합니다.

이 규격은 주요 서비스 정의 프레임워크와 함께 다음 프로토콜 및 메시지 형식에 대해 특정 바인딩 확장을 소개합니다.

1.1 WSDL 문서 예제

다음은 주식 시세를 제공하는 간단한 서비스에 대한 WSDL 정의 예제를 보여 줍니다. 이 서비스는 HTTP상에 SOAP 1.1 프로토콜을 사용하여 구축된 GetLastTradePrice라고 하는 단일 작업을 지원합니다. 요청은 문자열 형식의 증권 시세 표시기 기호를 입력으로 받아, 부동 소수점 형식의 가격을 반환합니다. 이 정의에 사용된 요소에 대한 자세한 설명은 2절(주요 언어) 및 3절(SOAP 바인딩)에서 볼 수 있습니다.

이 예제는 SOAP 인코딩 대신 고정 XML 형식을 사용합니다(SOAP 인코딩을 사용하는 예제는 예제 4를 참조하십시오).

예제 1 HTTP를 통한 SOAP 1.1 요청/응답

<?xml version="1.0"?>
<definitions name="StockQuote" 
targetNamespace="http://example.com/stockquote.wsdl"
          xmlns:tns="http://example.com/stockquote.wsdl"
          xmlns:xsd1="http://example.com/stockquote.xsd"
          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
          xmlns="http://schemas.xmlsoap.org/wsdl/">
   <types>
       <schema targetNamespace="http://example.com/stockquote.xsd"
              xmlns="http://www.w3.org/1999/XMLSchema">
           <element name="TradePriceRequest">
              <complexType>
                  <all>
                      <element name="tickerSymbol" type="string"/>
                  </all>
              </complexType>
           </element>
           <element name="TradePrice">
              <complexType>
                  <all>
                      <element name="price" type="float"/>
                  </all>
              </complexType>
           </element>
       </schema>
   </types>
 
   <message name="GetLastTradePriceInput">
        <part name="body" element="xsd1:TradePrice"/>
    </message>
    <message name="GetLastTradePriceOutput">
        <part name="body" element="xsd1:TradePriceResult"/>
    </message>
    <portType name="StockQuotePortType">
        <operation name="GetLastTradePrice">
           <input message="tns:GetLastTradePriceInput"/>
           <output message="tns:GetLastTradePriceOutput"/>
        </operation>
    </portType>
    <binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetLastTradePrice">
           <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
           <input>
               <soap:body use="literal" namespace="http://example.com/stockquote.xsd"
                          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
           </input>
           <output>
               <soap:body use="literal" namespace="http://example.com/stockquote.xsd"
                          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
           </output>
        </operation>
    </binding>
    <service name="StockQuoteService">
        <documentation>첫 번째 서비스</documentation> 
        <port name="StockQuotePort" binding="tns:StockQuoteBinding">
           <soap:address location="http://example.com/stockquote"/>
        </port>
    </service>
</definitions>

1.2 국제 규칙

1. 이 문서에 있는 키워드인 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 및 "OPTIONAL" 는 RFC-2119 [2]에 설명된 바와 같이 해석됩니다.

2. 다음 이름 공간 접두사는 이 문서 전반에 걸쳐 사용됩니다

접두사

이름 공간 URI

정의

wsdl

http://schemas.xmlsoap.org/wsdl/

WSDL 프레임워크에 대한 WSDL 이름 공간.

soap

http://schemas.xmlsoap.org/wsdl/soap/

WSDL SOAP 바인딩에 대한 WSDL 이름 공간.

http

http://schemas.xmlsoap.org/wsdl/http/

HTTP GET & POST 바인딩에 대한 WSDL 이름 공간.

mime

http://schemas.xmlsoap.org/wsdl/mime/

MIME 바인딩에 대한 WSDL 이름 공간.

soapenc

http://schemas.xmlsoap.org/soap/encoding/tous.gif

SOAP 1.1 [8]에 의해 정의된 인스턴스 이름 공간.

soapenv

http://schemas.xmlsoap.org/soap/envelope/tous.gif

SOAP 1.1 [8]에 의해 정의된 인스턴스 이름 공간.

xsi

http://www.w3.org/1999/XMLSchema-instancetous.gif

XSD[10]에 의해 정의된 인스턴스 이름 공간.

xsd

http://www.w3.org/1999/XMLSchema tous.gif

XSD [10]에 의해 정의된 스키마 이름 공간.

tns

(다양함)

“this namespace”를 의미하는 (tns) 접두사는 현재 문서를 참조하는 규칙으로 사용됩니다.

(기타)

(다양함)

다른 모든 이름 공간 접두사는 단지 예제일 뿐입니다. 특히, “http://example.com”으로 시작하는 URI는 어느 정도 응용 프로그램 종속적이거나 컨텍스트 종속적인 URI[4]를 나타냅니다.

3. 이 규격은 비공식 구문에 사용되어 WSDL 문서의 XML 문법을 설명합니다.

  • 구문은 XML 인스턴스로 나타나지만, 값은 값이라기 보다는 데이터 형식을 나타냅니다.

  • 문자는 "" (0 또는 1), "*" (0 또는 그 이상의 숫자), "+" (1 또는 그 이상의 숫자)와 같이 요소 및 특성에 추가됩니다.

  • <element.../> 또는 <element...>에서처럼 "..."로 끝나는 요소 이름들은 컨텍스트에 관계 없는 요소/특성이 생략된다는 것을 나타냅니다.

  • 굵게 되어 있는 문법은 이전 문서에 소개되지 않았거나 해당 예제에서 특별히 중요한 부분입니다.

  • <-- extensibility element -->는 XSD 이외인 경우 ##과 같이 어떤 "다른" 이름 공간에서 온 요소에 대한 자리 표시자입니다.

  • 위에 정의된 XML 이름 공간 접두사는 정의되는 요소의 이름 공간을 나타내는 데 사용됩니다.

  • <xml로 시작되는 예제에는 이 규격을 준수하는 충분한 정보가 있습니다. 다른 예제는 단편적이므로 규격에 맞도록 지정해야 되는 추가 정보가 필요합니다.

XSD 스키마는 WSDL 문법의 공식적인 정의로서 제공됩니다(A4항목을 참조 하시기 바랍니다).

2. 서비스 정의

이 절에서는 WSDL 언어의 주요 요소를 설명합니다. SOAP, HTTP 및 MIME의 바인딩 확장은 3절, 4절 및 5절에서 설명하였습니다.

2.1 WSDL 문서 구조

WSDL 문서는 단순한 정의의 집합입니다. 루트에 definition 요소가 있고 그 안에 정의가 있습니다. 문법은 다음과 같습니다.

<wsdl:definitions name="nmtoken" targetNamespace="uri">
    <import namespace="uri" location="uri"/>*
 
    <wsdl:documentation.../> 
    <wsdl:types> 
        <wsdl:documentation.../>
        <xsd:schema.../>*
        <-- extensibility element --> *
    </wsdl:types>
    <wsdl:message name="nmtoken> *
        <wsdl:documentation.../>
        <part name="nmtoken" element="qname" type="qname"/> *
    </wsdl:message>
    <wsdl:portType name="nmtoken">*
        <wsdl:documentation.../>
        <wsdl:operation name="nmtoken">*
           <wsdl:documentation.../> 
           <wsdl:input name="nmtoken" message="qname">
               <wsdl:documentation.../>
           </wsdl:input>
           <wsdl:output name="nmtoken" message="qname">
               <wsdl:documentation.../> 
           </wsdl:output>
           <wsdl:fault name="nmtoken" message="qname"> *
               <wsdl:documentation.../> 
           </wsdl:fault>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="nmtoken" type="qname">*
        <wsdl:documentation.../>
        <-- extensibility element --> *
        <wsdl:operation name="nmtoken">*
           <wsdl:documentation.../> 
           <-- extensibility element --> *
           <wsdl:input name="nmtoken"> 
               <wsdl:documentation.../> 
               <-- extensibility element --> 
           </wsdl:input>
           <wsdl:output name="nmtoken"> 
               <wsdl:documentation.../>
               <-- extensibility element --> *
           </wsdl:output>
           <wsdl:fault name="nmtoken"> *
               <wsdl:documentation.../> 
               <-- extensibility element --> *
           </wsdl:fault>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="nmtoken"> *
        <wsdl:documentation.../>
        <wsdl:port name="nmtoken" binding="qname"> *
           <wsdl:documentation.../> 
           <-- extensibility element -->
        </wsdl:port>
        <-- extensibility element -->
    </wsdl:service>
    <-- extensibility element --> *
</wsdl:definitions>

서비스는 다섯 개의 중요한 요소를 사용하여 정의됩니다.

  • types는 교환된 메시지를 설명하는 데 사용된 데이터 형식 정의를 제공합니다.

  • message는 전송되는 데이터의 추상 정의를 나타냅니다. 메시지는 논리적인 부분으로 이루어져 있으며 각 부분은 특정 형식 시스템 내의 정의와 연관되어 있습니다.

  • portType은 추상 작업의 집합이며 각 작업은 입력 메시지 및 출력 메시지를 참조합니다.

  • binding은 특정 portType에 의해 정의된 메시지 및 작업에 대해 구체적인 프로토콜과 데이터 형식을 지정합니다.

  • port는 바인딩에 대해 주소를 지정하기 때문에 단일 통신 종점을 정의합니다.

  • service는 관련된 포트의 집합을 집계하는 데 사용됩니다.

2.2절에서 2.7절은 이러한 요소를 설명합니다. 이 절의 나머지 부분은 문서 명명, 문서 정의 참조, 언어 확장 사용 및 컨텍스트 설명서 추가에 대해 WSDL로 소개된 규칙을 설명합니다.

2.1.1 문서 명명 및 연결

WSDL 문서는 간단한 형태의 설명서로 제공되는 NCNAME 형식의 선택적인 name 특성으로 지정될 수 있습니다. 또한 URI 형식의 targetNamespace 특성을 지정할 수도 있습니다. URI는 상대 URI일 수 없습니다.

WSDL은 import 문을 사용하여 문서 location과 namespace를 연결할 수 있도록 합니다.

<definitions...>
    <import namespace="uri" location="uri"/> *
</definitions>

WSDL 정의 참조는 QName tous.gif 을 사용하여 만들어집니다. WSDL 문서에 포함된 다음과 같은 정의 형식이 참조될 수도 있습니다.

  • WSDL 정의: service, port, message, bindings, 및 portType

  • 기타 정의: 추가 정의가 확장성을 통해 추가되는 경우, 이 정의는 QName 연결을 사용합니다.

    위에 나열된 WSDL 정의 형식은 자체 이름 범위를 가지고 있습니다. 즉, port 이름과 message 이름은 충돌하지 않습니다. 이름 범위내에 있는 이름은 WSDL 문서 내에서 고유해야 합니다.

    WSDL에서 QNames 확인은 XML 스키마 규격[11]으로 설명된 QNames 확인과 유사합니다.

2.1.2 제작 스타일

import 요소를 사용하여 서비스 정의의 서로 다른 요소들을 별도의 문서로 분리하여 필요할 때 가져올 수 있습니다. 이 기능은 서비스의 추상화 수준에 따라 정의를 분리하여 서비스 정의를 더 명확하게 작성할 수 있도록 도와 줍니다. 또한 모든 종류의 서비스 정의를 재사용하는 기능을 최대화합니다. 따라서 이러한 방법으로 구조화된 WSDL 문서는 사용 및 유지 관리가 쉽습니다. 아래의 예제 2는 예제 1에 나타난 서비스를 정의하는 제작 스타일의 사용 방법을 보여 줍니다. 여기서는 정의를 데이터 형식 정의, 추상 정의 및 특정 서비스 바인딩의 세 개 문서로 분리하였습니다. 이 메커니즘의 사용은 이 규격에 정의된 언어 요소만 사용하는 예제에 명시적으로 나타난 정의에만 제한되지는 않습니다. 추가 언어 확장을 기반으로 하는 다른 형식의 정의는 유사한 방법으로 인코드되고 다시 사용될 수 있습니다.

예제 2. 예제 1    서비스에   대한   대체   제작   스타일

http://example.com/stockquote/stockquote.xsd

<?xml version="1.0"?>
<schema targetNamespace="http://example.com/stockquote/schemas"
       xmlns="http://www.w3.org/2000/10/XMLSchema">
    <element name="TradePriceRequest">
        <complexType>
            <all>
                <element name="tickerSymbol" type="string"/>
            </all>
        </complexType>
    </element>
    <element name="TradePrice">
        <complexType>
            <all>
                <element name="price" type="float"/>
            </all>
        </complexType>
    </element>
</schema>

http://example.com/stockquote/stockquote.wsdI

<?xml version="1.0"?>
<definitions name="StockQuote" 
targetNamespace="http://example.com/stockquote/definitions"
          xmlns:tns="http://example.com/stockquote/definitions"
          xmlns:xsd1="http://example.com/stockquote/schemas"
          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
          xmlns="http://schemas.xmlsoap.org/wsdl/">
   <import namespace="http://example.com/stockquote/schemas"
           location="http://example.com/stockquote/stockquote.xsd"/>
   <message name="GetLastTradePriceRequest">
        <part name="body"element="xsd1:GetLastTradePrice"/>
   </message>
   <message name="GetLastTradePriceResponse">
        <part name="body"element="xsd1:GetLastTradePriceResult"/>
   </message>
   <portType name="StockQuotePortType">
        <operation name="GetLastTradePrice"> 
           <input message="tns:GetLastTradePriceRequest"/>
           <output message="tns:GetLastTradePriceResponse"/>
       </operation>
   </portType>
</definitions>

http://example.com/stockquote/stockquoteservice.wsdI

<?xml version="1.0"?>
<definitions name="StockQuote" 
targetNamespace="http://example.com/stockquote/service"
          xmlns:tns="http://example.com/stockquote/service"
          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
          xmlns:defs="http://example.com/stockquote/definitions"
          xmlns="http://schemas.xmlsoap.org/wsdl/">
   <import namespace="http://example.com/stockquote/definitions"
           location="http://example.com/stockquote/stockquote.wsdl"/>
 
    <binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
         <soap:binding style="document"/>
         <operation name="GetLastTradePrice">
            <soap:operation soapAction="http://my.org/GetLastTradePrice"/>
         </operation>>
    </binding>
 
    <service name="StockQuoteService"> 
        <documentation>첫 번째 서비스</documentation> 
        <port name="StockQuotePort" binding="tns:StockQuoteBinding">
           <soap:address location="http://my.org/stockquote"/>
        </port>
   </service>
</definitions>

2.1.3 언어 확장성 및 바인딩

WSDL에서 바인딩이라는 용어는 프로토콜 또는 데이터 형식 정보와 message, operation 또는 portType과 같은 추상 엔티티를 연결하는 과정을 나타냅니다. WSDL는 WSDL로 정의된 다양한 요소 아래 특정 기술(확장성 요소)을 나타내는 요소를 허용합니다. 이러한 확장성은 일반적으로 특정 프로토콜 또는 메시지 형식에 대한 바인딩 정보를 지정하는 데 사용되지만 이러한 사용에 제한되지 않습니다. 확장성 요소는 WSDL 이름 공간과는 다른 XML 이름 공간을 사용해야 합니다. 확장성 요소가 나타날 수 있는 문서에서 특정 위치는 부록 A3에 자세히 설명되어 있습니다.

확장성 요소는 일반적으로 몇몇 특정 기술 관련 바인딩을 지정하는 데 사용됩니다. 통신에 기술 관련 바인딩 기능이 필요한지 또는 선택적인지를 구별하기 위해 확장성 요소는 해당 요소에 부울 형식의 wsdl:required 특성을 배치할 수도 있습니다. 필요한 기본값은 false입니다. 필요한 특성은 이름 공간 "http://schemas.xmlsoap.org/wsdl/"에서 정의됩니다.

확장성 요소를 사용하면 기본 WSDL 규격을 개정하지 않고 네트워크와 메시지 프로토콜 영역을 새로 바꿀 수 있습니다. WSDL은 이러한 프로토콜을 정의하는 규격이 프로토콜이나 형식을 설명하는 데 사용하는 필수 WSDL 확장도 정의할 것을 권장합니다.

확장성 요소에 대한 예제를 보려면 3절, 4절 및 5절을 참조하십시오.

2.1.4 설명서

WSDL은 육안으로 읽을 수 있는 설명서의 컨테이너로서 선택적인 wsdl:document 요소를 사용합니다. 요소의 컨텐트는 임의의 텍스트 및 요소입니다(XSD의 "mixed"). 설명서 요소는 모든 WSDL 언어 요소 안에 허용됩니다.

2.2 형식

types 요소는 교환된 메시지와 관련된 데이터 형식 정의를 묶습니다. 상호 운영성 및 플랫폼 중립성을 최대화 하기 위해 WSDL은 정식의 형식 시스템으로서 XSD를 사용하고 이것을 기본적인 형식 시스템으로 처리합니다.

<definitions...>
    <types>
        <xsd:schema.../>*
    </types>
</definitions>

XSD 형식 시스템은 결과 네트워크 형식이 실제 XML인지 또는 결과 XSD 스키마가 특정 네트워크 형식을 확인하는지 여부에 관계 없이 메시지의 형식을 정의하는 데 사용될 수 있습니다. 특히, 동일한 메시지에 대해 다중 바인딩이 있거나 하나의 바인딩만 존재하지만 해당 바인딩 형식이 널리 사용되는 형식 시스템을 가지지 않는 경우에 유용합니다. 이러한 경우, XSD를 사용하여 추상 형식을 인코딩하기 위한 바람직한 접근 방법은 다음과 같습니다.

  • 특성이 아닌 요소 폼을 사용합니다.

  • 네트워크 인코딩에 고유한 특성이나 요소를 포함하지 않습니다(즉, 메시지의 추상 컨텐트와 무관함). soap:root, soap:encodingStyle, xmi:id, xmi:name 등이 이에 해당하는 예입니다.

  • 배열 형식을 모델링하려면 결과 폼이 SOAP v1.1 문서의 5절에 지정된 인코딩을 실제 사용하는지 여부와 관계 없이, Soap:Array 형식을 사용합니다. 배열 형식에 대해 ArrayOfXXX 이름을 사용합니다(여기서 XXX는 배열에서 항목의 형식임).

  • xsd:anyType 형식을 사용하여 모든 형식을 가질 수 있는 필드/매개 변수를 표현합니다.

하지만 현재의 추상적인 형식을 포함하여 앞으로 사용하게 될 모든 추상적인 형식을 설명하는 데 사용할 단일 형식의 시스템 문법을 기대하기 어렵기 때문에, WSDL은 형식 시스템이 확장성 요소를 통해 추가될 수 있도록 합니다. 확장성 요소는 types 요소 아래 나타나며 사용되는 형식 정의 시스템을 식별하고 형식 정의에 대해 XML 컨테이너 요소를 제공합니다. 이 요소의 역할은 XML 스키마 언어의 schema 요소의 역할과 비교할 수 있습니다.

<definitions...>
    <types>
        <-- type-system extensibility element --> *
    </types>
</definitions>

2.3 메시지

메시지는 하나 이상의 논리적인 parts로 구성되어 있습니다. 각 부분은 메시지 형식 특성을 사용하는 특정 형식 시스템의 형식과 연관되어 있습니다. 메시지 형식의 특성 집합은 확장할 수 있습니다. WSDL은 XSD에 사용되는 몇몇 메시지 형식의 특성을 정의합니다.

  • element. QName을 사용하는 XSD 요소를 참조합니다.

  • type. QName을 사용하는 XSD simpleType 또는 complexType을 참조합니다.

WSDL의 이름 공간과는 다른 이름 공간을 사용하면 다른 메시지 형식 특성이 정의될 수도 있습니다. 확장성 요소 바인딩에도 메시지 형식 특성이 사용됩니다.

메시지를 정의하는 구문은 다음과 같습니다. 사용된 형식 시스템에 따라 다양한 메시지 형식 특성은 굵게 표시되어 있습니다.

<definitions...>
    <message name="nmtoken"> *
        <part name="nmtoken" element="qname" type="qname"/> *
    </message>
</definitions>

메시지 name 특성은 해당 WSDL 문서에 정의된 모든 message 간에 고유한 이름을 제공합니다.

부분 name 특성은 해당 메시지의 모든 부분 간에 고유한 이름을 제공합니다.

2.3.1 메시지 부분

part는 message의 논리적인 추상 컨텐트를 설명하는 융통성 있는 메커니즘입니다. 바인딩은 부분에 관한 바인딩별 정보를 지정하기 위해 부분의 이름을 참조할 수 있습니다. 예를 들어, RPC를 사용하기 위해 메시지를 정의하는 경우, 부분은 메시지에 매개 변수를 나타낼 수도 있습니다. 하지만 부분의 실제 의미를 결정하기 위해서는 반드시 바인딩을 점검해야 합니다.

다중 부분 요소는 메시지에 여러 개의 논리적 단위가 있는 경우에 사용됩니다. 예를 들어, 다음 메시지는 주문서와 고객으로 구성되어 있습니다.

<definitions .... >
    <types>
        <schema .... >
           <element name="PO" type="tns:POType"/>
           <complexType name="POType">
               <all>
                   <element name="id" type="string/>
                   <element name="name" type="string"/>
                   <element name="items">
                       <complexType>
                           <all>
                               <element name="item" type="tns:Item" minOccurs="0"                 maxOccurs="unbounded"/>
                           </all>
                       </complexType>
                   </element>
              </all>
           </complexType>
 
           <complexType name="Item">
               <all>
                   <element name="quantity" type="int"/>
                   <element name="product" type="string"/>
               </all>
           </complexType>
           <element name="Invoice" type="tns:InvoiceType"/>
           <complexType name="InvoiceType">
               <all>
                   <element name="id" type="string"/>
               </all>
           </complexType>
        </schema>
    </types>
 
    <message name="PO">
        <part name="po" element="tns:PO"/>
        <part name="invoice" element="tns:Invoice"/>
    </message>
</definitions>

하지만, 메시지 컨텐트가 너무 복잡한 경우에는 대체 구문을 사용하여 형식 시스템을 직접 사용하는 복합 메시지 구조를 지정할 수 있습니다. 다음 예제에서, 본문은 주문서 또는 고객 집합이 될 수 있습니다.

<definitions .... >
    <types>
        <schema .... >
           <complexType name="POType">
               <all>
                   <element name="id" type="string/>
                   <element name="name" type="string"/>
                   <element name="items">
                       <complexType>
                           <all>
                               <element name="item" type="tns:Item" minOccurs="0"                 maxOccurs="unbounded"/>
                           </all>                  
                       </complexType>
                   </element>
               </all>
           </complexType>
 
           <complexType name="Item">
               <all>
                   <element name="quantity" type="int"/>
                   <element name="product" type="string"/>
               </all>
           </complexType>
           <complexType name="InvoiceType">
               <all>
                   <element name="id" type="string"/>
               </all>
           </complexType>
 
           <complexType name="Composite">
               <choice>
                   <element name="PO" minOccurs="1" maxOccurs="1" type="tns:POType"/>
                   <element name="Invoice" minOccurs="0" maxOccurs="unbounded"                         type="tns:InvoiceType"/>
               </choice>
           </complexType>
        </schema>
    </types>
 
    <message name="PO">
        <part name="composite" type="tns:Composite"/>
    </message>
</definitions>

2.3.2 추상적인 메시지와 구체적인 메시지

메시지 정의는 항상 메시지 컨텐트의 추상 정의로 간주합니다. 메시지 바인딩은 추상 컨텐트가 구체적인 서식에 매핑되는 방법을 설명합니다. 경우에 따라, 추상 정의가 구체적인 표현과 매우 흡사하거나 하나 이상의 바인딩과 정확히 일치하기도 합니다. 따라서 이러한 바인딩은 매핑 정보를 거의 제공하지 않습니다. 하지만 동일한 메시지 정의의 다른 바인딩은 확장 가능한 매핑 정보가 필요하기도 합니다. 따라서 바인딩을 점검해야 메시지가 실제로 "추상화되는 방법"을 결정할 수 있습니다.

2.4 포트 형식

포트 형식은 추상 작업 및 포함된 추상 메시지의 명명된 집합입니다.

<wsdl:definitions...>
    <wsdl:portType name="nmtoken">
        <wsdl:operation name="nmtoken".../> *
    </wsdl:portType>
</wsdl:definitions>

포트 형식의 name 특성은 해당 WSDL 문서에 정의된 모든 포트 형식에 고유한 이름을 제공합니다.

작업은 name 특성을 통해 명명됩니다.

WSDL에는 종점에서 지원할 수 있는 네 가지 전송 방식이 있습니다.

  • 단방향. 종점이 메시지를 받습니다.

  • 요청 - 응답. 종점이 메시지를 받고 관련된 메시지를 보냅니다.

  • 검색 - 응답. 종점이 메시지를 보내고 관련된 메시지를 받습니다.

  • 알림. 종점이 메시지를 보냅니다.

WSDL은 이러한 전송 방식을 operations로 참조합니다. 요청/응답 또는 검색/응답 작업이 두 개의 단방향 메시지를 사용하여 추상적으로 모델링되더라도, 이 작업을 근본적인 작업의 형식으로서 모델링하는 것이 유용하며 그 이유는 다음과 같습니다.

  • 요청/응답 또는 검색/응답 작업은 일반적입니다.

  • 더 복잡한 이동 정보를 도입하지 않고도 순서가 상호 연관될 수 있습니다.

  • 몇몇 종점은 메시지가 동기화된 요청/응답 작업의 결과일 경우에만 메시지를 받을 수 있습니다.

  • 단순한 흐름은 흐름 정의가 필요한 시점에 이러한 방식으로부터 알고리즘적으로 파생될 수 있습니다.

요청/응답 또는 검색/응답이 WSDL 문서에 논리적으로 상호 연관되어 있어도, 지정된 바인딩은 구체적인 상호 관련 정보를 설명합니다. 예를 들어, 요청 및 응답 메시지는 하나 또는 두 개의 실제 네트워크 통신의 부분으로 교환될 수도 있습니다.

기본 WSDL 구조가 4가지 전송 방식의 바인딩을 지원하지만 WSDL은 단방향과 요청-응답 방식의 바인딩만 정의합니다. 검색-응답 또는 알림용 프로토콜을 정의하는 규격이 이러한 방식을 사용할 수 있는 WSDL 바인딩 확장도 포함할 것으로 기대하고 있습니다.

작업은 QName 형식의 message 특성을 사용하여 포함된 메시지를 참조합니다. 이 특성은 WSDL에 의해 정의된 연결 규칙을 따릅니다(2.1.1절 참조).

2.4.1 단방향 작업

단방향 작업의 문법은 다음과 같습니다.

<wsdl:definitions...> <wsdl:portType...> *
        <wsdl:operation name="nmtoken">
           <wsdl:input name="nmtoken" message="qname"/>
        </wsdl:operation>
    </wsdl:portType >
</wsdl:definitions>

input 요소는 단방향 작업에 대한 추상적인 메시지 형식을 지정합니다.

2.4.2 요청-응답 작업

요청-응답 작업의 문법은 다음과 같습니다.

<wsdl:definitions...>
    <wsdl:portType...> *
        <wsdl:operation name="nmtoken" parameterOrder="nmtokens">
           <wsdl:input name="nmtoken" message="qname"/>
           <wsdl:output name="nmtoken" message="qname"/>
           <wsdl:fault name="nmtoken" message="qname"/>*
        </wsdl:operation>
    </wsdl:portType >
</wsdl:definitions>

input 및 output 요소는 각각 요청과 응답에 대한 추상적인 메시지 형식을 지정합니다. 선택적인 fault 요소는 프로토콜에 한정된 메시지뿐만 아니라 작업의 결과로서 출력되는 모든 오류 메시지에 대한 추상적인 메시지 형식을 지정합니다.

요청-응답 작업은 추상 개념입니다. 특정 바인딩은 HTTP 요청/응답과 같은 단일 통신 내에서 또는 HTTP 요청과 같은 두 개의 독립적인 통신으로서의 메시지를 실제로 보내는 방법을 결정하는 데 사용됩니다.

2.4.3 검색-응답 작업

검색-응답 작업의 문법은 다음과 같습니다.

<wsdl:definitions...>
    <wsdl:portType...> *
        <wsdl:operation name="nmtoken" parameterOrder="nmtokens">
           <wsdl:output name="nmtoken" message="qname"/>
           <wsdl:input name="nmtoken" message="qname"/>
           <wsdl:fault name="nmtoken" message="qname"/>*
        </wsdl:operation>
    </wsdl:portType >
</wsdl:definitions>

output 및 input 요소는 각각 검색된 요청 메시지와 응답 메시지에 대해 추상적인 메시지 형식을 지정합니다. 선택적인 fault 요소는 프로토콜에 한정된 메시지뿐만 아니라 작업의 결과로서 출력되는 모든 오류 메시지에 추상적인 메시지 형식을 지정합니다.

요청-응답 작업은 추상 개념입니다. 특정 바인딩은 HTTP 요청/응답과 같은 단일 통신 내에서 또는 HTTP 요청과 같은 두 개의 독립적인 통신으로서의 메시지를 실제로 보내는 방법을 결정하는 데 사용됩니다.

2.4.4 알림 작업

단방향 작업의 문법은 다음과 같습니다.

<wsdl:definitions...>
    <wsdl:portType...> *
        <wsdl:operation name="nmtoken">
           <wsdl:output name="nmtoken" message="qname"/>
        </wsdl:operation>
    </wsdl:portType >
</wsdl:definitions>

output 요소는 알림 작업에 추상적인 메시지 형식을 지정합니다.

2.4.5 작업 내 요소의 이름

input 및 output 요소의 name 특성은 이를 포함하는 포트 형식 내에서 모든 input 및 output 요소 간에 고유한 이름을 제공합니다.

작업 중 각 input 및 output 요소의 이름을 지정할 필요가 없도록 하기 위해 WSDL이 작업 이름을 기반으로 하는 몇몇 기본값을 제공합니다. name 특성이 단방향 또는 알림 메시지에 지정되지 않은 경우 기본값은 작업 이름이 됩니다. name 특성이 요청-응답 또는 검색-응답 작업의 input 또는 output 메시지에 지정되지 않은 경우, "Request"/"Solicit" 또는 "Response"가 각각 추가된 작업 이름이 기본값이 됩니다.

각 fault 요소는 바인딩이 fault 메시지의 구체적인 서식을 지정하도록 명명되어야 합니다. fault 요소의 이름은 작업에 대해 정의된 fault의 집합 내에서 고유합니다.

2.4.6 작업 내 매개 변수 순서

작업은 RPC 방식의 바인딩과 함께 사용될지의 여부를 지정하지 않습니다. 하지만 작업과 RPC 바인딩을 함께 사용하는 경우에는 원래 RPC 함수의 서명을 캡처하는 것이 유용합니다. 따라서 요청-응답 또는 검색-응답 작업은 nmtokens 형식의 parameterOrder 특성을 통해 매개 변수 이름 목록을 지정할 수도 있습니다. 특성 값은 단일 공백으로 분리된 메시지 부분의 이름 목록입니다. 명명된 부분은 다음 규칙을 반드시 준수해야 합니다.

  • 부분의 순서는 RPC 서명에서 매개 변수의 순서를 반영하도록 명명됩니다.

  • return 값 부분은 목록에 없습니다.

  • 부분 이름이 input 메시지와 output 메시지에 모두 나타나면 in/out 매개 변수입니다.

  • 부분 이름이 input 메시지에만 나타나면 in 매개 변수입니다.

  • 부분 이름이 output 메시지에만 나타나면 out 매개 변수입니다.

    이 정보는 "참고"로 제공되는 것이므로 RPC 서명과는 관계 없는 경우에는 무시해도 됩니다. 또한 작업이 RPC 방식 바인딩과 함께 사용되는 경우에도 이 정보 제공이 필요하지 않습니다.

2.5 바인딩

바인딩은 특정 portType에 의해 정의된 작업 및 메시지에 대해 메시지 형식 및 프로토콜 정보를 정의합니다. 지정된 portType에 대한 바인딩 수는 제한이 없습니다. 바인딩의 문법은 다음과 같습니다.

<wsdl:definitions...>
    <wsdl:binding name="nmtoken" type="qname"> *
        <-- extensibility element (1) --> *
        <wsdl:operation name="nmtoken"> *
           <-- extensibility element (2) --> *
           <wsdl:input name="nmtoken"> 
               <-- extensibility element (3) --> 
           </wsdl:input>
           <wsdl:output name="nmtoken"> 
               <-- extensibility element (4) --> *
           </wsdl:output>
           <wsdl:fault name="nmtoken"> *
               <-- extensibility element (5) --> *
           </wsdl:fault>
        </wsdl:operation>
    </wsdl:binding>
</wsdl:definitions>

name 특성은 이를 포함하는 WSDL 문서에 정의된 모든 바인딩에 고유한 이름을 제공합니다.

바인딩은 type 특성을 사용하여 바인드하는 portType을 참조합니다. 이 QName 값은 WSDL에 의해 정의된 연결 규칙을 따릅니다(2.1.1절 참조).

바인딩의 operation, input, output 및 fault 요소는 portType 내에서 정확히 작동하는 각 요소의 name 특성을 사용하는 해당 portType 요소와 상호 연관되어 있습니다(2.4.5절 참조).

바인딩 확장성 요소는 input (3), output (4) 및 fault 메시지 (5)에 대해 구체적인 문법을 지정하는 데 사용됩니다. 바인딩 단위 정보 (1)뿐만 아니라, 작업 단위 바인딩 정보 (2)도 지정됩니다.

바인딩 내의 작업은 바인딩의 portType 내에서 같은 이름을 가진 작업에 대한 바인딩 정보를 지정합니다. 예를 들어, 메서드 이름을 오버로드하는 경우처럼 작업 이름은 고유할 필요가 없기 때문에 작업 바인딩 요소의 이름 특성만으로 작업을 고유하게 식별하지 못할 수 있습니다. 이러한 경우에는 해당 wsdl:input과 wsdl:output 요소의 name 특성을 제공하여 올바른 작업을 식별하도록 해야 합니다.

한개의 바인딩은 한개의 프로토콜만 지정해야 합니다.

바인딩은 주소 정보를 지정하지 않을 수도 있습니다.

2.6 포트

포트는 바인딩에 대해 단일 주소를 지정하여 개별적인 종점을 정의합니다.

<wsdl:definitions...>
    <wsdl:service...> *
        <wsdl:port name="nmtoken" binding="qname"> *
           <-- extensibility element (1) -->
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

name 특성은 이를 포함하는 WSDL 문서에 정의된 모든 포트에 고유한 이름을 제공합니다.

QName 형식의 binding 특성은 WSDL에 의해 정의된 연결 규칙을 사용하는 바인딩을 참조합니다(2.1.1절 참조).

바인딩 확장성 요소 (1)는 포트에 대해 주소 정보를 지정하는 데 사용됩니다.

한개의 포트는 두개 이상의 주소를 지정하지 않을 수도 있습니다.

포트는 주소 정보가 아닌 바인딩 정보를 전혀 지정하지 않을 수도 있습니다.

2.7 서비스

서비스는 관련된 포트의 집합을 그룹화합니다.

<wsdl:definitions...>
    <wsdl:service name="nmtoken"> *
        <wsdl:port.../>*
    </wsdl:service>
</wsdl:definitions>

name 특성은 이를 포함하는 WSDL 문서에 정의된 모든 서비스에 고유 이름을 제공합니다.

서비스 내의 포트는 다음과 같은 관계를 가지고 있습니다.

  • 포트 간에 서로 통신하지 않습니다(예: 한 포트의 output은 다른 포트의 input이 될 수 없습니다).

  • 별도의 바인딩 및 주소를 사용하면서도 한개의 서비스에 포트 형식을 공유하는 여러 개의 포트가 있는 경우 포트는 대체 요소가 됩니다. 각 포트는 기능적으로 동등한 동작을 제공합니다(전송 및 메시지 형식은 각 바인딩에 의해 제한됨). 이 기능으로 WSDL 사용자는 프로토콜, 거리 등과 같은 몇몇 조건을 기반으로 하여 통신할 수 있는 특정 포트를 선택할 수 있습니다.

  • 이 포트를 확인하여 서비스의 포트의 형식을 결정할 수 있습니다. 이렇게 하면 WSDL 문서의 고객은 이 문서가 여러 개의 포트를 지원하는지의 여부에 따라 특정 서비스와 통신할지를 결정할 수 있습니다. 이 기능은 포트 형식의 작업간에 몇몇 묵시적인 관계가 있는 경우에 유용하며, 특정 작업을 수행하기 위해서는 포트 형식의 전체 집합이 반드시 표시되어야 합니다.

3. SOAP 바인딩

WSDL은 다음과 같은 프로토콜별 정보의 규격을 지원하는 SOAP 1.1 종점에 대한 바인딩을 포함합니다.

  • 바인딩과 SOAP 1.1 프로토콜과의 바운드 표시.

  • SOAP 종점에 대한 주소 지정 방법.

  • SOAP의 HTTP 바인딩에 대한 SOAPAction HTTP 헤더의 URI.

  • SOAP Envelope의 일부로서 전송되는 헤더의 정의 목록.

  • XSD의 SOAP 루트 지정 방법.

SOAP 바인딩의 집합은 진화하기 때문에 이 바인딩 문법은 아주 확실한 규격은 아닙니다. 추가 SOAP 바인딩은 반드시 이 문법의 개념에서 파생됩니다. 예를 들면 다음과 같습니다.

  • URI 주소 지정 스키마를 사용하지 않는 SOAP 바인딩은 3.8절에 정의된 soap:address 요소를 재배치하여 다른 주소 지정 스키마를 대체 사용합니다.

  • SOAPAction이 필요 없는 SOAP 바인딩은 3.4절에 정의된 soapAction 특성을 생략합니다.

3.1 SOAP 예제

다음 예제에서 SubscribeToQuotes SOAP 1.1 단방향 메시지는 SMTP 바인딩을 통해 StockQuote 서비스로 전달됩니다. 요청은 문자열 형식의 증권 시세 표시기 기호를 입력으로 받고, 대체 URI를 정의하는 헤더를 포함합니다.

예제 3. SOAP 헤더를   사용하는 SMTP 에서의   단방향   작업의 SOAP 바인딩

<?xml version="1.0"?>
<definitions name="StockQuote"
          targetNamespace="http://example.com/stockquote.wsdl"
          xmlns:tns="http://example.com/stockquote.wsdl"
          xmlns:xsd="http://www.w3.org/2000/10/XMLSchema"
          xmlns:xsd1="http://example.com/stockquote.xsd"
          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
          xmlns="http://schemas.xmlsoap.org/wsdl/">
 
    <message name="GetTradePriceInput">
        <part name="tickerSymbol" element="xsd:string"/>
        <part name="time" element="xsd:timeInstant"/>
    </message>
 
    <message name="GetTradePriceOutput">
        <part name="result" type="xsd:float"/>
    </message>
 
    <portType name="StockQuotePortType">
        <operation name="GetTradePrice">
           <input message="tns:GetTradePriceInput"/>
           <output message="tns:GetTradePriceOutput"/>
        </operation>
    </portType>
 
    <binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetTradePrice">
           <soap:operation soapAction="http://example.com/GetTradePrice"/>
           <input>
               <soap:body use="encoded" namespace="http://example.com/stockquote"
                          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
           </input>
           <output>
               <soap:body use="encoded" namespace="http://example.com/stockquote"
                          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
           </output>
        </operation>>
    </binding>
 
    <service name="StockQuoteService">
        <documentation>My first service</documentation>
        <port name="StockQuotePort" binding="tns:StockQuoteBinding">
           <soap:address location="http://example.com/stockquote"/>
        </port>
    </service>
</definitions>

이 예제는 GetLastTradePrice SOAP 1.1 요청이 SOAP 1.1 HTTP 바인딩을 통해 StockQuote 서비스로 전달될 수도 있음을 설명합니다. 이 요청은 문자열 형식의 주식 시세 표시기 기호 즉, timeInstant 형식의 time을 입력 받아 SOAP 응답에서 부동 소수점 형식으로 된 가격을 반환합니다.

예제 4. HTTP 에서의   요청 - 응답 RPC 작업의 SOAP 바인딩

<?xml version="1.0"?>
<definitions name="StockQuote" 
targetNamespace="http://example.com/stockquote.wsdl"
          xmlns:tns="http://example.com/stockquote.wsdl"
          xmlns:xsd1="http://example.com/stockquote.xsd"
          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
          xmlns="http://schemas.xmlsoap.org/wsdl/">
    <message name="GetLastTradePriceRequest">
        <part name="tickerSymbol" element="xsd:string"/>
        <part name="time" element="xsd:timeInstant"/>
    </message>
    <message name="GetLastTradePriceResponse">
        <part name="result" type="xsd:float"/>
    </message>
    <portType name="StockQuotePortType">
        <operation name="GetLastTradePrice">
           <input message="tns:GetLastTradePriceRequest"/>
           <output message="tns:GetLastTradePriceResponse"/>
        </operation>
    </portType>
    <binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetLastTradePrice">
           <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
           <input>
               <soap:body use="encoded" namespace="http://example.com/stockquote"
                          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
           </input>
           <output>
               <soap:body use="encoded" namespace="http://example.com/stockquote"
                          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
           </output>
        </operation>>
    </binding>
    <service name="StockQuoteService">
        <documentation>첫 번째 서비스</documentation> 
        <port name="StockQuotePort" binding="tns:StockQuoteBinding">
           <soap:address location="http://example.com/stockquote"/>
        </port>
    </service>
</definitions>

이 예제에서는 SOAP 1.1 HTTP 바인딩을 통해 StockQuote 서비스로 GetTradePrices SOAP 1.1 요청을 전송할 수 있음을 설명합니다. 요청은 시작과 종료 시간이 포함된 TimePeriod 구조를 정의한 응용 프로그램인 주식 시세 기호 문자열의 입력을 받아 해당 기간 내에 서비스가 기록한 주식 시세 배열뿐만 아니라 SOAP 응답으로 기록된 주기를 반환합니다. 이 서비스에 대응하는 RPC 서명은 매개 변수 tickerSymbol 및 timePeriod와 함께 출력 매개 변수 주기를 가지며 부동 소수점 배열을 반환합니다.

예제 5. HTTP 에서의   요청 - 응답 RPC 작업의 SOAP 바인딩

<?xml version="1.0"?>
<definitions name="StockQuote"
 
targetNamespace="http://example.com/stockquote.wsdl"
          xmlns:tns="http://example.com/stockquote.wsdl"
          xmlns:xsd="http://www.w3.org/2000/10/XMLSchema"
          xmlns:xsd1="http://example.com/stockquote/schema"
          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
          xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
          xmlns="http://schemas.xmlsoap.org/wsdl/">
 
    <types>
       <schema targetNamespace="http://example.com/stockquote/schema"
              xmlns="http://www.w3.org/2000/10/XMLSchema">
           <complexType name="TimePeriod">
              <all>
                  <element name="startTime" type="xsd:timeInstant"/>
                  <element name="endTime" type="xsd:timeInstant"/>
              </all>
           </complexType>
           <complexType name="ArrayOfFloat">
              <complexContent>
                  <restriction base="soapenc:Array">
                      <attribute ref="soapenc:arrayType" wsdl:arrayType="xsd:float[]"/>
                  </restriction>
              </complexContent>
           </complexType>
       </schema>
    </types>
 
    <message name="GetTradePricesInput">
        <part name="tickerSymbol" element="xsd:string"/>
        <part name="timePeriod" element="xsd1:TimePeriod"/>
    </message>
 
    <message name="GetTradePricesOutput">
        <part name="result" type="xsd1:ArrayOfFloat"/>
        <part name="frequency" type="xsd:float"/>
    </message>
 
    <portType name="StockQuotePortType">
        <operation name="GetLastTradePrice"             parameterOrder="tickerSymbol timePeriod frequency">
           <input message="tns:GetTradePricesInput"/>
           <output message="tns:GetTradePricesOutput"/>
        </operation>
    </portType>
 
    <binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetTradePrices">
           <soap:operation soapAction="http://example.com/GetTradePrices"/>
           <input>
               <soap:body use="encoded" namespace="http://example.com/stockquote"
                          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
           </input>
           <output>
               <soap:body use="encoded" namespace="http://example.com/stockquote"
                          encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
           </output>
        </operation>>
    </binding>
 
    <service name="StockQuoteService">
        <documentation>My first service</documentation>
        <port name="StockQuotePort" binding="tns:StockQuoteBinding">
           <soap:address location="http://example.com/stockquote"/>
        </port>
    </service>
</definitions>

3.2 SOAP 바인딩이 WSDL을 확장하는 방법

SOAP 바인딩은 다음과 같은 확장성 요소로 WSDL을 확장합니다.

<definitions .... >
    <binding .... >
        <soap:binding style="rpc|document" transport="uri">
        <operation .... >
           <soap:operation soapAction="uri" style="rpc|document">
           <input>
               <soap:body parts="nmtokens" use="literal|encoded"
                          encodingStyle="uri-list" namespace="uri">
               <soap:header message="qname" part="nmtoken" use="literal|encoded"
                            encodingStyle="uri-list" namespace="uri">*
                 <soap:headerfault message="qname" part="nmtoken" use="literal|encoded"
                                   encodingStyle="uri-list" namespace="uri"/>*
               <soap:header>
           </input>
           <output>
               <soap:body parts="nmtokens" use="literal|encoded"
                          encodingStyle="uri-list" namespace="uri">
               <soap:header message="qname" part="nmtoken" use="literal|encoded"
                            encodingStyle="uri-list" namespace="uri">*
                 <soap:headerfault message="qname" part="nmtoken" use="literal|encoded"
                                   encodingStyle="uri-list" namespace="uri"/>*
               <soap:header>
           </output>
           <fault>*
               <soap:fault name="nmtoken" use="literal|encoded"
                           encodingStyle="uri-list" namespace="uri">
            </fault>
        </operation>
    </binding>
 
    <port .... >
        <soap:address location="uri"/> 
    </port>
</definitions>

SOAP 바인딩의 각 확장 요소는 다음 절에 설명하였습니다.

3.3 soap:binding

Envelope, Header 및 Body와 같은 SOAP 바인딩 요소의 목적은 바인딩이 SOAP 프로토콜 서식에 바운드된다는 것을 나타내는 것입니다. 이 요소는 메시지의 인코딩 및 서식에 요청을 만들지 않습니다(예: SOAP 1.1 규격의 5절을 따르는 메시지).

soap:binding 요소는 SOAP 바인딩을 사용할 때 반드시 표시되어야 합니다.

<definitions...>
    <binding...>
        <soap:binding transport="uri" style="rpc|document">
    </binding>
</definitions>

style 특성의 값은 포함된 각 작업에 대한 style 특성의 기본값입니다. style 특성이 생략된 경우 "document"로 간주됩니다. style의 기능에 대한 정보는 3.4절을 참조하십시오.

요청된 transport 특성의 값은 이 바인딩이 SOAP의 어느 전송에 해당하는지를 나타냅니다. URI 값 http://schemas.xmlsoap.org/soap/http는 SOAP 규격에서 HTTP 바인딩에 해당합니다. SMTP, FTP 등과 같은 다른 전송을 나타내기 위해 여기에 다른 URI가 사용되기도 합니다.

3.4 soap:operation

soap:operation 요소는 작업에 대한 전반적인 정보를 제공합니다.

<definitions...>
    <binding...>
        <operation...>
           <soap:operation soapAction="uri" style="rpc|document">
        </operation>
    </binding>

</definitions>style 특성은 작업이 매개 변수 및 반환값을 포함하는 RPC 지향적인 메시지인지 문서를 포함하는 문서 지향적인 메시지인지를 나타냅니다. 이 정보는 적합한 프로그래밍 모델을 선택하는 데 사용됩니다. 특성이 지정되지 않은 경우 soap:binding 요소에 지정된 값이 기본값이 됩니다. soap:binding 요소가 스타일을 지정하지 않은 경우에는 "document"로 간주됩니다.

soapAction 특성은 이 작업에 대한 SOAPAction 헤더의 값을 지정합니다. 이 URI 값은 SOAPAction 헤더에 대한 값으로 직접 사용되어야 하며 요청 시 상대적인 URI 값을 절대값으로 만들 수 없습니다. SOAP의 HTTP 프로토콜 바인딩인 경우에는 이 값이 필요하며 기본값이 없습니다. 다른 SOAP 프로토콜 바인딩의 경우 이 값은 지정되지 않을 수도 있고 soap:operation 요소가 생략될 수도 있습니다.

3.5 soap:body

soap:body 요소는 SOAP Body 요소에서 메시지 부분이 나타나는 방법을 지정합니다.

메시지의 부분은 추상적인 형식 정의 또는 구체적인 스키마 정의가 될 수 있습니다. 추상 정의인 경우, 인코딩 스타일에 의해 정의된 일부 규칙 집합에 따라 형식에 순서가 정해집니다. 각 인코딩 스타일은 SOAP 규격에 정의된 바와 같이 URI 목록을 사용하여 식별됩니다. SOAP 인코딩(http://schemas.xmlsoap.org/soap/encoding/)과 같은 일부 인코딩 스타일은 지정된 추상적인 형식 집합에 대한 메시지 형식의 변형을 허용하기 때문에, 모든 변형된 형식을 이해하는 것은 메시지를 읽는 사람의 몫입니다. 즉, 읽는쪽이 올바르게 읽어야 합니다. 모든 변형을 지원할 필요가 없도록 하려면 메시지가 구체적으로 정의된 다음, 참고로서 원래 인코딩 스타일(있는 경우)임을 나타내야 합니다. 이 경우 메시지 작성자는 지정된 스키마를 정확하게 준수해야 합니다. 즉, 쓰는쪽이 올바르게 써야 합니다.

soap:body 바인딩 요소는 SOAP 메시지의 Body 요소 내에 있는 메시지 부분을 조합하는 방법에 대한 정보를 제공합니다. soap:body 요소는 RPC 지향적 메시지와 문서 지향적 메시지에 사용되지만 enclosing 작업의 스타일은 Body 구역을 구성하는 방법에 중요한 영향을 미칩니다.

  • 작업 스타일이 rpc인 경우 각 부분은 매개 변수 또는 반환 값이며 body에 있는 wrapper 요소에 나타납니다(SOAP 규격의 7.1절 참조). wrapper 요소는 작업 이름과 동일한 이름을 가지며 이름 공간은 이름 공간 속성의 값입니다. 각 메시지 부분(매개 변수)은 래퍼 아래 나타나며 호출에 대응하는 매개 변수와 동일한 이름의 accessor로 표현됩니다. 여기서 각 부분은 호출의 매개 변수와 같은 순서로 배열됩니다.

  • 작업 스타일이 문서인 경우 추가 래퍼는 없으며 메시지 부분은 SOAP Body 요소 바로 아래 나타납니다.

Body와 매개 변수 accessor 요소의 컨텐트를 정의하는 데 동일한 메커니즘을 사용합니다.

<definitions...>
    <binding...>
        <operation...>
           <input>
               <soap:body parts="nmtokens" use="literal|encoded"
                          encodingStyle="uri-list" namespace="uri">
           </input>
           <output>
               <soap:body parts="nmtokens" use="literal|encoded"
                          encodingStyle="uri-list" namespace="uri">
           </output>
        </operation>
    </binding>
</definitions>

nmtokens 형식의 선택적인 parts 특성은 어느 부분이 메시지의 SOAP Body 내에서 어디에 나타나는지를 표시합니다. 메시지의 다른 부분은 SOAP가 multipart/related MIME 바인딩과 함께 사용될 때와 같이 다른 메시지 영역에 나타날 수 있습니다. parts 특성이 생략되면 메시지에서 정의한 모든 부분이 SOAP Body 영역에 포함된 것으로 간주됩니다.

요청된 use 특성은 몇몇 인코딩 규칙을 사용하여 메시지 부분이 인코드되는지의 여부 또는 부분이 메시지의 구체적인 스키마를 정의했는지의 여부를 나타냅니다.

use가 encoded인 경우에는 각 메시지 부분은 type 특성을 사용하여 추상적인 형식을 참조합니다. 이러한 추상적인 형식은 encodingStyle특성으로 지정된 인코딩을 적용하여 구체적인 메시지를 작성하는 데 사용됩니다. namespace 특성이 추상적인 형식에 의해 명시적으로 정의되지 않은 컨텐트에만 적용되는 경우에도, namestypes 부분 및 namespace 특성의 값은 모두 인코딩의 입력이 됩니다. 참조된 인코딩 스타일이 SOAP 인코딩과 마찬가지로 인코딩 스타일의 변형을 허용하면 모든 변형이 지원되어야 합니다. 즉, 읽는쪽이 올바르게 읽어야 합니다.

use가 literal인 경우에 각 부분은 간단한 부분에 대해서는 element 특성을 사용하거나 복잡한 부분에 대해서는 type 특성을 사용해서 구체적인 스키마를 참조합니다(2.3.1절 참고). encodingStyle 특성의 값은 구체적인 서식이 SOAP 인코딩과 같은 특정 인코딩을 사용하여 파생된 것이지만 지정된 변형만 지원한다는 것을 나타냅니다. 즉, 쓰는쪽이 올바르게 써야 합니다.

encodingStyle 특성의 값은 각각이 단일 공백으로 분리된 URI 목록입니다. URI는 메시지 내에서 사용되는 인코딩을 제한 사항이 가장 많은 것에서 가장 적은 것의 순서로 표시합니다(SOAP 규격에 정의된 encodingStyle 특성과 동일함).

3.6 soap:fault

soap:fault 요소는 SOAP Fault Details 요소의 컨텐트를 지정합니다. 이 요소는 soap:body 요소 뒤에 패턴화됩니다(3.5절 참조).

<definitions...>
    <binding...>
        <operation...>
           <fault>*
               <soap:fault name="nmtoken" use="literal|encoded"
                                 encodingStyle="uri-list" namespace="uri">
           </fault>
        </operation>
    </binding>
</definitions>

name 특성은 soap:fault를 작업에 대해 정의된 wsdl:fault에 연결합니다.

fault 메시지는 단일 부분을 가져야 합니다. useencodingStyle 및 namespace 특성은 모두 soap:body와 동일한 방법으로 사용됩니다(3.5절 참조).

3.7 soap:header

soap:header 요소는 header가 SOAP Envelope의 Header 요소 내에서 전송되도록 정의할 수 있습니다. 이 구역에 모든 헤더를 나열할 필요는 없습니다. WSDL 문서의 다른 규격에 의해 헤더가 실제 페이로드에 추가되도록 하는 것이 일반적이기 때문에 여기에 모든 헤더를 나열할 필요가 없습니다.

<definitions .... >
    <binding .... >
        <operation .... >
           <input>
               <soap:body parts="nmtokens" use="literal|encoded"
                          encodingStyle="uri-list" namespace="uri">
           </input>
           <output>
               <soap:body parts="nmtokens" use="literal|encoded"
                          encodingStyle="uri-list" namespace="uri">
           </output>
        </operation>
    </binding>
</definitions>

useencodingStyle 및 namespace 특성은 모두 soap:body(3.5절 참조)와 같은 방식으로 사용되며 헤더에 매개 변수가 포함되지 않기 때문에 only style="document"인 것으로 가정합니다

Qname 형식의 message 특성과 nmtoken 형식의 part 특성은 헤더 형식을 정의하는 메시지 부분을 참조합니다. 부분 MAY가 참조하는 스키마는 use="literal"일 경우 soap:actor와 soap:mustUnderstand 특성의 정의를 포함하지만 use="encoded"일 경우에는 포함해서는 안됩니다. 참조된 메시지는 SOAP Body를 정의하는 메시지와 같지 않아도 됩니다.

soap:header 내에 나타나고 soap:header와 같은 구문을 갖는 선택적인 headerfault 요소를 사용하면 soap:header에서 정의하는 헤더에 속하는 오류 정보를 전송하는 데 사용된 헤더 형식 규격이 허용됩니다. SOAP 규격은 헤더에 속하는 오류가 헤더로 반환되어야 한다고 규정하며 이러한 메커니즘을 사용하면 이와 같은 헤더 형식 규격이 허용됩니다.

3.8 soap:address

SOAP 주소 바인딩은 포트에 주소(URI)를 지정하는 데 사용됩니다. SOAP 바인딩을 사용하는 포트는 반드시 하나의 주소만 지정해야 합니다.

<definitions...>
    <port...>
        <binding...>
           <soap:address location="uri"/> 
        </binding>
    </port>
</definitions>

4. HTTP GET 및 POST 바인딩

WSDL은 웹 브라우저와 웹 사이트 간의 상호 작용을 설명하기 위해 HTTP 1.1의 GET 및 POST 동사에 대한 바인딩을 포함합니다. 이 기능은 웹 브라우저가 아닌 응용 프로그램이 사이트와 상호 작용하도록 합니다. 다음과 같은 프로토콜별 정보를 지정할 수 있습니다.

  • HTTP GET 또는 POST를 사용하는 바인딩 표시

  • 포트에 대한 주소

  • 각 작업에 대한 상대 주소(포트에서 정의한 기본 주소에 상대적임)

4.1 HTTP GET/POST 예제

다음 예제는 지정된 포트 형식에 대해 다르게 바운드된 세 개의 포트를 보여 줍니다.

전달된 값이 part1=1, part2=2, part3=3인 경우, 각 포트에 대한 요청 형식은 다음과 같습니다.

port1: GET, URL="http://example.com/o1/A1B2/3"
port2: GET, URL="http://example.com/o1 p1=1&p2=2&p3=3
port3: POST, URL="http://example.com/o1", PAYLOAD="p1=1&p2=2&p3=3"

각 포트에 대한 응답은 GIF 또는 JPEG 이미지가 됩니다.

예제 5. GIF 또는 JPG    반환하는 GET  FORM POST

<definitions...>
    <message name="m1">
        <part name="part1" type="xsd:string"/>
        <part name="part2" type="xsd:int"/>
        <part name="part3" type="xsd:string"/>
    </message>
    <message name="m2">
        <part name="image" type="xsd:binary"/>
    </message>
    <portType name="pt1">
        <operation name="o1">
           <input message="tns:m1"/>
           <output message="tns:m2"/>
        </operation>
    </portType>
    <service name="service1">
        <port name="port1" binding="tns:b1">
           <http:address location="http://example.com/"/>
        </port>
        <port name="port2" binding="tns:b1">
           <http:address location="http://example.com/"/>
        </port>
        <port name="port3" binding="tns:b1">
             <http:address location="http://example.com/"/>
        </port>
    </service>
    <binding name="b1" type="pt1">
        <http:binding verb="GET"/>
        <operation name="o1">
           <http:operation location="o1/A(part1)B(part2)/(part3)"/>
           <input>
               <http:urlReplacement/>
           </input>
           <output>
               <mime:content type="image/gif"/>
               <mime:content type="image/jpeg"/>
           </output>
        </operation>
    </binding>
    <binding name="b2" type="pt1">
        <http:binding verb="GET"/>
        <operation name="o1">
           <http:operation location="o1"/>
           <input>
               <http:urlEncoded/>
           </input>
           <output>
               <mime:content type="image/gif"/>
               <mime:content type="image/jpeg"/>
           </output>
        </operation>
    </binding>
    <binding name="b3" type="pt1">
        <http:binding verb="POST"/>
        <operation name="o1">
           <http:operation location="o1"/>
           <input>
               <mime:content type="application/x-www-form-urlencoded"/>
           </input>
           <output>
               <mime:content type="image/gif"/>
               <mime:content type="image/jpeg"/>
           </output>
        </operation>
    </binding>
</definitions>

4.2 HTTP GET/POST 바인딩이 WSDL을 확장하는 방법

HTTP GET/POST 바인딩은 다음과 같은 확장 요소를 사용하여 WSDL을 확장합니다.

<definitions...>
    <binding...>
        <http:binding verb="nmtoken"/>
        <operation...>
           <http:operation location="uri"/>
           <input...>
               <-- mime elements -->
           </input>
           <output...>
               <-- mime elements -->
           </output>
        </operation>
    </binding>
    <port...>
        <http:address location="uri"/>
    </port>
</definitions>

이러한 요소는 다음 절에 설명되어 있습니다.

4.3 http:address

location 특성은 포트에 대해 기본 URI를 지정합니다. 이 특성의 값은 http:operation 바인딩 요소의 location 특성의 값과 결합됩니다. 자세한 내용은 4.5절을 참조하십시오.

4.4 http:binding

http:binding 요소는 이 바인딩이 HTTP 프로토콜을 사용한다는 것을 나타냅니다.

<definitions...>
    <binding...>
        <http:binding verb="nmtoken"/>
    </binding>
</definitions>

요청된 verb 특성의 값은 HTTP 동사를 나타냅니다. 일반적인 값은 GET 또는 POST이지만 다른 값이 사용될 수도 있습니다. HTTP 동사는 대소문자를 구별해야 합니다.

4.5 http:operation

location 특성은 작업에 대해 상대 URI를 지정합니다. 이 URI는 http:address 요소에 지정된 URI와 결합하여 HTTP 요청에 대한 전체 URI를 만듭니다. URI 값은 상대 URI여야 합니다.

<definitions...>
    <binding...>
        <operation...>
           <http:operation location="uri"/>
        </operation>
    </binding>
</definitions>

4.6 http:urlEncoded

urlEncoded 요소는 메시지의 모든 부분이 표준 URI 인코딩 규칙(name1=value&name2=value...)을 사용하는 HTTP 요청 URI에 인코드된다는 것을 나타냅니다. 매개 변수의 이름은 메시지 부분의 이름에 해당합니다. 부분에서 제공한 각 값은 이름=값 쌍을 사용하여 인코드됩니다. 이 값은 GET과 함께 사용되어 URL 인코딩을 지정하거나 POST와 함께 사용되어 FORM-POST를 지정하기도 합니다. GET과 함께 사용될 경우 "" 문자는 필요할 때 자동으로 추가됩니다.

<http:urlEncoded/>

URI 인코딩 매개 변수의 규칙에 대한 자세한 내용은 [5], [6] 및 [7]을 참조하십시오.

4.7 http:urlReplacement

http:urlReplacment 요소는 메시지의 모든 부분이 배치 알고리즘을 사용하는 HTTP 요청 URI로 인코드된다는 것을 나타냅니다.

  • http:operation의 상대 URI 값을 검색하여 검색 패턴 집합을 찾습니다.

  • http:operation의 값이 http:address의 location 특성 값과 결합되기 전에 검색이 수행됩니다.

  • 메시지 부분마다 하나의 검색 패턴이 있습니다. 검색 패턴 문자열은 괄호"(" 및 ")"로 둘러 쌓인 메시지 부분의 이름입니다.

  • 일치하는 경우 해당 메시지 부분의 값은 일치된 위치에 있는 내용으로 대치됩니다.

  • 일치 항목을 찾은 후에 값이 바뀌며 바뀐 값은 추가적인 검색 작업을 수행하지 않습니다.

메시지 부분에는 반복되는 값이 없을 수도 있습니다.

<http:urlReplacement/>

5. MIME 바인딩

WSDL은 추상적인 형식을 특정 MIME 형식으로 된 구체적인 메시지에 바인드하는 방법을 가지고 있으며 다음과 같은 MIME 형식에 대한 바인딩이 정의되어 있습니다.

  • multipart/related

  • text/xml

  • application/x-www-form-urlencoded (HTML로 된 폼을 전송하는 데 사용하는 형식)

  • 기타(MIME 형식 문자열 지정)

정의된 MIME 형식의 집합은 계속적으로 크기가 증가하기 때문에, 각 MIME 형식에 대해 XML 문법을 철저하게 정의하는 것이 WSDL의 목표는 아닙니다. 필요한 경우 언제든지 추가적인 MIME 형식에 대한 추가 문법을 정의할 수 있습니다. 컨텐트를 설명하는 데 MIME 형식 문자열을 사용할 수 있다면 아래 정의된 mime 요소를 사용할 수 있습니다.

5.1 MIME 바인딩 예제

예제 7. SOAP  multipart/related 사용

이 예제는 SOAP 1.1 HTTP 바인딩을 통해 GetCompanyInfo SOAP 1.1 요청을 StockQuote 서비스로 보내는 내용을 설명합니다. 요청은 문자열 형식의 증권 시세 표시기 기호를 입력으로 받습니다. 응답에는 MIME 형식 multipart/related로 인코드된 여러 개의 부분이 포함되어 있습니다. 즉, 부동 소수점으로 된 현재 증권 시세 가격이 있는 SOAP Envelope, HTML 형식으로 된 제로 또는 그 이상의 마케팅 자료 및 GIF 또는 JPFG 형식으로 된 선택적인 회사 로고 등이 여기에 해당합니다.

<definitions...>
    <types>
        <schema...> 
           <element name="GetCompanyInfo">
               <complexType>
                   <all>
                       <element name="tickerSymbol " type="string"/>
                   </all>
               </complexType>
           </element>
           <element name="GetCompanyInfoResult">
               <complexType>
                   <all>
                       <element name="result" type="float"/>
                   </all>
               </complexType>
           </element>
           <complexType name="ArrayOfBinary" base="soap:Array">
               <all>
                   <element name="value" type="xsd:binary"/>
               </all>
           </complexType>
        </schema>
    </types>
    <message name="m1">
        <part name="body" element="tns:GetCompanyInfo"/>
    </message>
    <message name="m2">
        <part name="body" element="tns:GetCompanyInfoResult"/>
        <part name="docs" type="xsd:string"/>
        <part name="logo" type="tns:ArrayOfBinary"/>
    </message>
    <portType name="pt1">
        <operation name="GetCompanyInfo">
           <input message="m1"/>
           <output message="m2"/>
        </operation>
    </portType> 
 
    <binding name="b1" type="tns:pt1">
        <operation name="GetCompanyInfo">
           <soap:operation soapAction="http://example.com/GetCompanyInfo"/>
           <input>
               <soap:body use="literal"/>
           </input>
           <output>
               <mime:multipartRelated>
                   <mime:part>
                       <soap:body parts="body" use="literal"/>
                   </mime:part>
                   <mime:part>
                       <mime:content part="docs" type="text/html"/>
                   </mime:part>
                   <mime:part>
                       <mime:content part="logo" type="image/gif"/>
                       <mime:content part="logo" type="image/jpeg"/>
                   </mime:part>
               </mime:multipartRelated>
           </output>
        </operation>
    </binding> 
 
    <service name="CompanyInfoService">
        <port name="CompanyInfoPort"binding="tns:b1">
           <soap:address location="http://example.com/companyinfo"/>
        </port>
    </service>        
</definitions>

5.2 MIME 바인딩이 WSDL을 확장하는 방법

MIME 바인딩은 다음과 같은 확장 요소를 사용하여 WSDL을 확장합니다.

<mime:content part="nmtoken" type="string"/>
<mime:multipartRelated>
    <mime:part> *
        <-- mime element -->
    </mime:part>
</mime:multipartRelated>
<mime:mimeXml part="nmtoken"/>
 
이러한 요소는 다음과 같은 WSDL의 위치에서 사용됩니다.
 
<definitions...>
    <binding...>
        <operation...>
           <input...>
               <-- mime elements -->
           </input>
           <output...>
               <-- mime elements -->
           </output>
        </operation>
    </binding>
</definitions>

MIME 요소는 input 및 output 아래 나타나 MIME 형식을 지정합니다. 여러 개의 MIME 요소가 나타나는 경우 이를 대체 요소로 간주합니다.

5.3 mime:content

모든 MIME 형식에 대해 새 요소를 정의할 필요가 없도록 하기 위해, MIME 형식 문자열 외에 전달할 추가 정보가 없는 경우 mime:content요소를 사용할 수 있습니다.

<mime:content part="nmtoken" type="string"/>

part 특성은 메시지 부분의 이름을 지정하는 데 사용됩니다. 메시지가 단일 부분을 가지고 있는 경우에는 부분의 특성은 선택적입니다. type 특성은 MIME 형식 문자열을 포함합니다. 형식의 값은 슬래시(/) 또는 와일드카드(*)로 구분된 두 영역이 있습니다. type 특성을 지정하지 않으면 모든 MIME 형식을 허용한다는 것을 나타냅니다.

반환 형식이 XML인데 스키마가 미리 알려지지 않은 경우, text/xml을 표시하는 일반적인 mime 요소를 사용할 수 있습니다.

<mime:content type="text/xml"/>

와일드카드(*)는 모든 텍스트 형식과 같이 mime 형식의 패밀리를 지정하는 데 사용됩니다.

<mime:content type="text/*"/>

다음 두 예제는 모든 mime 형식을 지정합니다.

<mime:content type="*/*"/>
<mime:content/>

5.4 mime:multipartRelated

multipart/related MIME 형식은 MIME 형식으로 된 부분의 임의 집합을 MIME 형식 "multipart/related"를 사용하는 하나의 메시지에 집계합니다. mime:multipartRelated 요소는 이런 메시지의 구체적인 형식을 나타냅니다.

<mime:multipartRelated>
    <mime:part> *
        <-- mime element -->
    </mime:part>
</mime:multipartRelated>

mime:part 요소는 multipart/related 메시지의 각 부분을 설명합니다. MIME 요소는 mime:part에 나타나며 해당 부분에 대한 구체적인 MIME 형식을 지정합니다. mime:part 내에 둘 이상의 MIME 요소가 나타나면 이들은 대체 요소입니다.

5.5 soap:body

SOAP 요청과 함께 MIME 바인딩을 사용할 때, MIME 요소로 soap:body 요소를 사용할 수 있습니다. 이것은 컨텐트 형식이 "text/xml"이고, 이를 포함하는 SOAP Envelope가 있음을 나타냅니다.

5.6 mime:mimeXml

SOAP 규격이 아니면서(SOAP Envelope가 없음) 특정 스키마를 가지고 있는 XML 페이로드를 지정하기 위해, mime:mimeXml 요소를 사용하여 구체적인 스키마를 지정할 수 있습니다. part 특성은 루트 XML 요소의 구체적인 스키마를 정의하는 메시지 부분을 참조합니다. part 특성은 메시지가 단일 부분을 가지고 있는 경우에는 생략될 수도 있습니다. 해당 부분은 간단한 부분에 대해서는 element 특성을 사용하고 복잡한 부분에 대해서는 type 특성을 사용하여 구체적인 스키마를 참조합니다(2.3.1절 참조).

<mime:mimeXml part="nmtoken"/>

6. 참조

[2] S. Bradner, "RFC에서 요청 수준을 표시하는 키워드 사용법", RFC 2119 tous.gif, Harvard University, 1997년 3월

[4] T. Berners-Lee, R. Fielding, L. Masinter, "Uniform Resource Identifiers (URI): 일반 구문", RFC 2396, MIT/LCS, U.C. Irvine, Xerox Corporation, 1998년 8월.

[5] http://www.w3.org/TR/html401/interact/forms.html - submit-format tous.gif

[6] http://www.w3.org/TR/html401/appendix/notes.html - ampersands-in-uristous.gif

[7] http://www.w3.org/TR/html401/interact/forms.html - h-17.13.4tous.gif

[8] Simple Object Access Protocol (SOAP) 1.1 "http://www.w3.org/TR/2000/NOTE-SOAP-20000508/"tous.gif

[10] W3C Working Draft "XML 스키마 1부: 구조tous.gif". 현재 처리 중입니다.

[11] W3C Working Draft "XML 스키마 2부: 데이터형식tous.gif". 현재 처리 중입니다.

A 1. URI에 대한 참고

이 절에서는 해당 규격에 직접적인 관련은 없지만 규격을 구현할 때 유용한 배경 정보를 제공합니다.

A 1.1 XML 이름 공간 및 스키마 위치

XML 스키마의 targetNamespace 또는 XML 인스턴스의 xmlns 특성을 해당 스키마의 위치로 잘못 인식하는 경우가 많습니다. 이름 공간은 사실 URI이고 이 URI는 위치를 나타낼 수 있으며 해당 위치에서 스키마를 검색할 수 있기는 하지만 이 스키마만 이름 공간과 연관된 것은 아닙니다. 특정 이름 공간과 연관된 여러 개의 스키마가 있을 수 있고, 특정 처리 컨텍스트에서 어떤 스키마를 사용할지는 XML의 프로세서가 결정합니다. 여기서 WSDL 규격은 유사한 개념의 XML 스키마를 기반으로 하는 <import> 메커니즘을 통해 처리 컨텍스트를 제공합니다.

A 1.2 상대 URI

이 문서를 통해 WSDL 및 XSD 문서에서 정식 URI를 볼 수 있습니다. 정식 URI의 사용은 단순히 참조하는 개념을 보여주는 것입니다. 상대 URI의 사용은 전적으로 허용되며 많은 경우에 그 사용이 보장됩니다. 상대 URI 처리에 관한 내용은 rfc2396를 참조하십시오.

A 1.3 URI 작성

WSDL로 작업할 때 항상 전역적으로 고유한 URI를 작성하는 대신 특정 엔티티에 대한 URI를 작성하여 해당 엔티티 버전(스키마, WSDL 문서 등)을 "의미"하도록 하는 것이 바람직한 경우가 있습니다. 이러한 유형의 동작에 사용하기 위해 예약된 특별한 기본 URI가 있습니다. 기본 URI인 http://tempuri.org/는 어떤 엔티티와도 고유한 연관성이 없는 URI를 구성하는 데 사용될 수 있습니다. 예를 들어, 두 사람 또는 두개의 프로그램에서 완전히 다른 두 스키마에 대해 URI인 http://tempuri.org/myschema를 동시에 사용할 수 있고 URI 사용의 범위가 교차하지 않으면 고유한 URI로 간주됩니다. 이러한 기능은 URI에 의해 참조된 엔티티가 처리 중인 컨텍스트 내에서 유효하기만 하다면 새로운 URI를 작성하지 않고도 설명될 수 있다는 장점이 있습니다. 안정적이고 고정된 엔티티에 대해, http://tempuri.org/를 기본 URI로 사용하는 것은 바람직하지 못합니다.

A 2. 네트워크 형식의 WSDL 예제

A 2.1. 예제 1

HTTP 요청에   포함된 SOAP 메시지

POST /StockQuote HTTP/1.1
Host: www.stockquoteserver.com
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "Some-URI"
 
<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   <SOAP-ENV:Body>
       <m:GetLastTradePrice xmlns:m="Some-URI">
           <symbol>DIS</symbol>
       </m:GetLastTradePrice>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

HTTP 응답에   포함된 SOAP 메시지

HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
 
<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
   <SOAP-ENV:Body>
       <m:GetLastTradePriceResponse xmlns:m="Some-URI">
           <Price>34.5</Price>
       </m:GetLastTradePriceResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

A 3. 확장성 요소의 위치

확장성 요소는 WSDL 문서의 다음과 같은 위치에서 나타날 수 있습니다.

위치

의미

사용법

definitions

확장성 요소는 WSDL 문서에 전반적으로 적용됩니다.

· WSDL 문서에 추가 정보 또는 정의를 전반적으로 소개합니다.

definitions/types

확장성 요소는 형식 시스템입니다.

· XSD가 아닌 형식 시스템에 메시지의 서식을 지정합니다.

definitions/service

확장성 요소는 service에 적용됩니다.

· service에 추가 정보 또는 정의를 소개합니다.

definitions/service/port

확장성 요소는 port에 적용됩니다.

· port에 address를 지정합니다.

definitions/binding

확장성 요소는 binding에 전반적으로 적용됩니다.

· 모든 작업에 적용할 프로토콜별 정보를 바운드 될 port 형식에 제공합니다.

definitions/binding/operation

확장성 요소는 operation에 전반적으로 적용됩니다.

· input 및 output 메시지에 적용할 프로토콜별 정보를 제공합니다.

definitions/binding/operation/input

확장성 요소는 operation에 대한 input 메시지를 적용합니다.

· 추상 메시지 부분이 바인딩 데이터 형식 및 구체적인 프로토콜에 매핑되는 방법에 대한 자세한 정보를 제공합니다.

· input 메시지에 추가적인 프로토콜별 정보를 제공합니다.

definitions/binding/operation/output

확장성 요소는 operation에 대한 output 메시지에 적용됩니다.

· 추상 메시지 부분이 바인딩 데이터 형식 및 구체적인 프로토콜에 매핑되는 방법에 대한 자세한 정보를 제공합니다.

· output 메시지에 추가 프로토콜별 정보를 제공합니다.

definitions/binding/operation/fault

확장성 요소는 operation의 fault 메시지에 적용됩니다.

· 추상 메시지 부분이 바인딩 데이터 형식 및 구체적인 프로토콜에 매핑되는 방법에 대한 자세한 정보를 제공합니다.

· fault 메시지에 추가적인 프로토콜별 정보를 제공합니다.

A 4. 스키마

A 4.1 WSDL 스키마

<schema xmlns="http://www.w3.org/2000/10/XMLSchema"
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
        targetNamespace="http://schemas.xmlsoap.org/wsdl/"
        elementFormDefault="qualified">
   <element name="documentation">
      <complexType mixed="true">
         <choice minOccurs="0" maxOccurs="unbounded">
            <any minOccurs="0" maxOccurs="unbounded"/>
         </choice>
         <anyAttribute/>
      </complexType>
   </element>
   <complexType name="documented" abstract="true">
      <sequence>
         <element ref="wsdl:documentation" minOccurs="0"/>
      </sequence>
   </complexType>
   <complexType name="openAtts" abstract="true">
      <annotation>
         <documentation>
         This type is extended by  component types
         to allow attributes from other namespaces to be added.
         </documentation>
      </annotation>
      <sequence>
         <element ref="wsdl:documentation" minOccurs="0"/>
      </sequence>
      <anyAttribute namespace="##other"/>
   </complexType>
   <element name="definitions" type="wsdl:definitionsType">
      <key name="message">
         <selector xpath="message"/>
         <field xpath="@name"/>
      </key>
      <key name="portType">
         <selector xpath="portType"/>
         <field xpath="@name"/>
      </key>
      <key name="binding">
         <selector xpath="binding"/>
         <field xpath="@name"/>
      </key>
      <key name="service">
         <selector xpath="service"/>
         <field xpath="@name"/>
      </key>
      <key name="import">
            <selector xpath="import"/>
            <field xpath="@namespace"/>
         </key>
      <key name="port">
         <selector xpath="service/port"/>
         <field xpath="@name"/>
      </key>
   </element>
   <complexType name="definitionsType">
      <complexContent>
         <extension base="wsdl:documented">
            <sequence>
               <element ref="wsdl:import" minOccurs="0" maxOccurs="unbounded"/>
               <element ref="wsdl:types" minOccurs="0"/>
               <element ref="wsdl:message" minOccurs="0" maxOccurs="unbounded"/>
               <element ref="wsdl:portType" minOccurs="0" maxOccurs="unbounded"/>
               <element ref="wsdl:binding" minOccurs="0" maxOccurs="unbounded"/>
               <element ref="wsdl:service" minOccurs="0" maxOccurs="unbounded"/>
               <any namespace="##other" minOccurs="0" maxOccurs="unbounded">
                  <annotation>
                     <documentation>to support extensibility elements </documentation>
                  </annotation>
               </any>
            </sequence>
            <attribute name="targetNamespace" type="uriReference" use="optional"/>
            <attribute name="name" type="NMTOKEN" use="optional"/>
         </extension>
      </complexContent>
  </complexType>
   <element name="import" type="wsdl:importType"/>
   <complexType name="importType">
      <complexContent>
   <extension base="wsdl:documented">
   <attribute name="namespace" type="uriReference" use="required"/>
      <attribute name="location" type="uriReference" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <element name="types" type="wsdl:typesType"/>
   <complexType name="typesType">
      <complexContent>
   <extension base="wsdl:documented">
   <sequence>
   <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
  </sequence>
   </extension>
  </complexContent>
  </complexType>
   <element name="message" type="wsdl:messageType">
      <unique name="part">
         <selector xpath="part"/>
         <field xpath="@name"/>
      </unique>
   </element>
   <complexType name="messageType">
      <complexContent>
   <extension base="wsdl:documented">
   <sequence>
   <element ref="wsdl:part" minOccurs="0" maxOccurs="unbounded"/>
  </sequence>
      <attribute name="name" type="NCName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <element name="part" type="wsdl:partType"/>
   <complexType name="partType">
      <complexContent>
   <extension base="wsdl:openAtts">
   <attribute name="name" type="NMTOKEN" use="optional"/>
      <attribute name="type" type="QName" use="optional"/>
      <attribute name="element" type="QName" use="optional"/>
   </extension>
  </complexContent>
  </complexType>
   <element name="portType" type="wsdl:portTypeType"/>
   <complexType name="portTypeType">
      <complexContent>
   <extension base="wsdl:documented">
   <sequence>
   <element ref="wsdl:operation" minOccurs="0" maxOccurs="unbounded"/>
  </sequence>
      <attribute name="name" type="NCName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <element name="operation" type="wsdl:operationType"/>
   <complexType name="operationType">
      <complexContent>
   <extension base="wsdl:documented">
      <choice>
         <group ref="wsdl:one-way-operation"/>
         <group ref="wsdl:request-response-operation"/>
         <group ref="wsdl:solicit-response-operation"/>
         <group ref="wsdl:notification-operation"/>
      </choice>
      <attribute name="name" type="NCName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <group name="one-way-operation">
      <sequence>
         <element ref="wsdl:input"/>
      </sequence>
   </group>
   <group name="request-response-operation">
      <sequence>
         <element ref="wsdl:input"/>
         <element ref="wsdl:output"/>
         <element ref="wsdl:fault" minOccurs="0" maxOccurs="unbounded"/>
      </sequence>
   </group>
   <group name="solicit-response-operation">
      <sequence>
         <element ref="wsdl:output"/>
         <element ref="wsdl:input"/>
         <element ref="wsdl:fault" minOccurs="0" maxOccurs="unbounded"/>
      </sequence>
   </group>
   <group name="notification-operation">
      <sequence>
         <element ref="wsdl:output"/>
      </sequence>
   </group>
   <element name="input" type="wsdl:paramType"/>
   <element name="output" type="wsdl:paramType"/>
   <element name="fault" type="wsdl:faultType"/>
   <complexType name="paramType">
      <complexContent>
   <extension base="wsdl:documented">
   <attribute name="name" type="NMTOKEN" use="optional"/>
      <attribute name="message" type="QName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <complexType name="faultType">
      <complexContent>
   <extension base="wsdl:documented">
   <attribute name="name" type="NMTOKEN" use="required"/>
      <attribute name="message" type="QName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <complexType name="startWithExtensionsType" abstract="true">
      <complexContent>
   <extension base="wsdl:documented">
   <sequence>
   <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
  </sequence>
   </extension>
  </complexContent>
  </complexType>
   <element name="binding" type="wsdl:bindingType"/>
   <complexType name="bindingType">
      <complexContent>
   <extension base="wsdl:startWithExtensionsType">
   <sequence>
   <element name="operation" type="wsdl:binding_operationType" minOccurs="0"     maxOccurs="unbounded"/>
  </sequence>
      <attribute name="name" type="NCName" use="required"/>
      <attribute name="type" type="QName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <complexType name="binding_operationType">
      <complexContent>
   <extension base="wsdl:startWithExtensionsType">
   <sequence>
   <element name="input" type="wsdl:startWithExtensionsType" minOccurs="0"/>
      <element name="output" type="wsdl:startWithExtensionsType" minOccurs="0"/>
      <element name="fault" minOccurs="0" maxOccurs="unbounded">
         <complexType>
            <complexContent>
   <extension base="wsdl:startWithExtensionsType">
   <attribute name="name" type="NMTOKEN" use="required"/>
         </extension>
  </complexContent>
  </complexType>
      </element>
  </sequence>
      <attribute name="name" type="NCName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <element name="service" type="wsdl:serviceType"/>
   <complexType name="serviceType">
      <complexContent>
   <extension base="wsdl:documented">
   <sequence>
   <element ref="wsdl:port" minOccurs="0" maxOccurs="unbounded"/>
      <any namespace="##other" minOccurs="0"/>
  </sequence>
      <attribute name="name" type="NCName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
   <element name="port" type="wsdl:portType"/>
   <complexType name="portType">
      <complexContent>
   <extension base="wsdl:documented">
   <sequence>
   <any namespace="##other" minOccurs="0"/>
  </sequence>
      <attribute name="name" type="NCName" use="required"/>
      <attribute name="binding" type="QName" use="required"/>
   </extension>
  </complexContent>
  </complexType>
  <attribute name="arrayType" type="string"/>
</schema>

A 4.2 SOAP 바인딩 스키마

<schema xmlns="http://www.w3.org/2000/10/XMLSchema"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        targetNamespace="http://schemas.xmlsoap.org/wsdl/soap/">
   <element name="binding" type="soap:bindingType"/>
   <complexType name="bindingType">
      <attribute name="transport" type="uriReference" use="optional"/>
      <attribute name="style" type="soap:styleChoice" use="optional"/>
   </complexType>
   <simpleType name="styleChoice">
      <restriction base="string">
   <enumeration value="rpc"/>
      <enumeration value="document"/>
  </restriction>
   </simpleType>
   <element name="operation" type="soap:operationType"/>
   <complexType name="operationType">
      <attribute name="soapAction" type="uriReference" use="optional"/>
      <attribute name="style" type="soap:styleChoice" use="optional"/>
   </complexType>
   <element name="body" type="soap:bodyType"/>
   <complexType name="bodyType">
      <attribute name="encodingStyle" type="uriReference" use="optional"/>
      <attribute name="parts" type="NMTOKENS" use="optional"/>
      <attribute name="use" type="soap:useChoice" use="optional"/>
      <attribute name="namespace" type="uriReference" use="optional"/>
   </complexType>
   <simpleType name="useChoice">
      <restriction base="string">
   <enumeration value="literal"/>
      <enumeration value="encoded"/>
  </restriction>
   </simpleType>
   <element name="fault" type="soap:faultType"/>
   <complexType name="faultType">
      <complexContent>
   <restriction base="soap:bodyType">
   <attribute name="parts" type="NMTOKENS" use="prohibited"/>
   </restriction>
  </complexContent>
  </complexType>
   <element name="header" type="soap:headerType"/>
   <complexType name="headerType">            
      <all>
          <element ref="soap:headerfault">
      </all>
      <attribute name="message" type="QName" use="required"/>
      <attribute name="parts" type="NMTOKENS" use="required"/>
      <attribute name="use" type="soap:useChoice" use="required"/>
      <attribute name="encodingStyle" type="uriReference" use="optional"/>
      <attribute name="namespace" type="uriReference" use="optional"/>      
   </complexType>
   <element name="headerfault" type="soap:headerfaultType"/>
   <complexType name="headerfaultType">            
      <attribute name="message" type="QName" use="required"/>
      <attribute name="parts" type="NMTOKENS" use="required"/>
      <attribute name="use" type="soap:useChoice" use="required"/>
      <attribute name="encodingStyle" type="uriReference" use="optional"/>
      <attribute name="namespace" type="uriReference" use="optional"/>      
   </complexType>
   <element name="address" type="soap:addressType"/>
   <complexType name="addressType">
      <attribute name="location" type="uriReference" use="required"/>
   </complexType>
</schema>

A 4.3 HTTP 바인딩 스키마

<schema xmlns="http://www.w3.org/2000/10/XMLSchema"
        xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
        targetNamespace="http://schemas.xmlsoap.org/wsdl/http/">
   <element name="address" type="http:addressType"/>
   <complexType name="addressType">
      <attribute name="location" type="uriReference" use="required"/>
   </complexType>
   <element name="binding" type="http:bindingType"/>
   <complexType name="bindingType">
      <attribute name="verb" type="NMTOKEN" use="required"/>
   </complexType>
   <element name="operation" type="http:operationType"/>
   <complexType name="operationType">
      <attribute name="location" type="uriReference" use="required"/>
   </complexType>
   <element name="urlEncoded">
      <complexType>
  </complexType>
   </element>
   <element name="urlReplacement">
      <complexType>
  </complexType>
   </element>
</schema>

A 4.4 MIME 바인딩 스키마

<schema  targetNamespace="http://schemas.xmlsoap.org/wsdl/mime/"
         xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
         xmlns="http://www.w3.org/2000/10/XMLSchema">
   <element name="content" type="mime:contentType"/>
   <complexType name="contentType" content="empty">
      <attribute name="type" type="string" use="optional"/>
      <attribute name="part" type="NMTOKEN" use="optional"/>
   </complexType>
   <element name="multipartRelated" type="mime:multipartRelatedType"/>
   <complexType name="multipartRelatedType" content="elementOnly">
      <element ref="mime:part" minOccurs="0" maxOccurs="unbounded"/>
   </complexType>
   <element name="part" type="mime:partType"/>
   <complexType name="partType" content="elementOnly">
      <any namespace="targetNamespace" minOccurs="0" maxOccurs="unbounded"/>
      <attribute name="name" type="NMTOKEN" use="required"/>
   </complexType>
   <element name="mimeXml" type="mime:mimeXmlType"/>
   <complexType name="mimeXmlType" content="empty">
      <attribute name="part" type="NMTOKEN" use="optional"/>
   </complexType>
</schema>

최종 수정일: 2002년 7월 20일



출처 - https://msdn.microsoft.com/ko-kr/library/cc671589.aspx

'Language > XML' 카테고리의 다른 글

참조, 예제 사이트  (0) 2016.12.13
XML, SOAP, WSDL, UDDI  (0) 2016.12.07
XML 스키마  (0) 2013.07.03
:

XML, SOAP, WSDL, UDDI

Language/XML 2016. 12. 7. 08:44

3. XML, SOAP, WSDL, UDDI

 

3.1. XML(eXtensible Markup Language) 기초

 

3.1.1. XML 개요

 

XML(eXtensible Markup Language)는 데이터를 전송하고 저장하는데 사용되는 마크업 언어(markup language). XML HTML(Hyper Text Markup Language)와는 다른 목적으로 사용된다. HTML이 데이터를 표시하는데 사용된다면, XML은 데이터를 전송하고 저장하는데 사용된다.

 

사실 XML은 어떤 일을 하지 않는다. XML은 정보를 구조화하고 저장하고 전송하기 위해 사용된다다음은 갑돌이가 갑순이에게 보내는 쪽지를 XML 형식으로 저장한 예이다.

 

<note>

<from>갑돌이</from>

<to>갑순이</to>

<heading>알림</heading>

<body>이번 주말 데이트 잊지 마!</body>

</note>

 

위의 쪽지(note)에는 보내는 사람(from)과 받는 사람(to)의 정보가 포함되어 있으며제목(heading)과 메시지 몸체(body)를 갖고 있다그러나 이 XML 다큐먼트는 아무 것도 하지 않는다그저 태그(tag) 안에 둘러싸여 있는 순수한 정보일 뿐이다이 다큐먼트를 보내고 받고표시하기 위해서는 누군가 소프트웨어를 작성해야 한다.

 

XML에는 특별한 것이 없다그저 평범한 텍스트일 뿐이다텍스트를 처리할 수 있는 소프트웨어는 XML도 처리할 수 있다그러나 XML 애플리케이션은 XML 태그를 특별한 방식으로 처리한다태그의 기능적인 의미는 애플리케이션에 달려있다.

 

위의 예에서 <from> <to>와 같은 태그는 XML 표준에 정의되어 있지 않다이들 태그는 XML 다큐먼트의 저작자가 만들어 낸” 것이다그것은 XML 언어가 어떤 태그도 미리 정의하고 있지 않기 때문이다반면에 HTML에서 사용되는 태그와 구조는 미리 정의되어 있다. HTML 다큐먼트는 <p> <h1>과 같이 HTML 표준에서 정의된 태그 만 사용해야 한다그러나 XML은 저작자가 자기 자신의 태그와 다큐먼트 구조를 정의할 수 있게 한다.

 

XML HTML을 대체하는 것이 아니라 보완한다대부분의 웹 애플리케이션에서 XML은 데이터를 전송하는데 사용되지만, HTML은 데이터를 형식화하고 표시하는데 사용된다.

 

3.1.2. XML의 이점

 

XML은 데이터 저장과 공유를 단순화하기 위해 웹 개발의 여러 분야에서 사용된다.

 

1)     XML HTML로부터 데이터를 분리한다
HTML 
다큐먼트에 동적인 데이터를 표시해야 하는 경우에 데이터가 변경될 때마다 HTML를 수정해야 하는 번거러운 작업을 해야 한다이런 경우라면 데이터를 별도의 XML 파일에 저장하게 하고 HTML에는 데이터의 레이아웃을 결정하고 표시하는데 집중하게 할 수 있다그리고 몇 행의 자바 스크립트로 XML 파일을 읽어서 HTML에 데이터의 내용을 표시하게 함으로써 해당 데이터가 변경될 때 HTML을 변경하지 않아도 되게 할 수 있다.

2)     XML은 데이터 공유를 단순하게 한다
실세계에서 컴퓨터 시스템과 데이터베이스는 호환되지 않는 형식으로 데이터를 저장한다그러나 XML 데이터는 평범한 텍스트 형식으로 저장된다이것은 소프트웨어와 하드웨어 독립적인 형식으로 데이터를 저장할 수 있는 기능을 제공하므로,서로 다른 애플리케이션이 공유할 수 있는 데이터를 손쉽게 생성할 수 있게 된다.

3)     XML은 데이터 전송을 단순하게 한다
XML
을 사용하면 호환되지 않는 시스템 사이에 데이터를 손쉽게 교환할 수 있다개발자들이 가장 많이 시간을 소비하는 문제 중의 하나가 인터넷을 통해 호환되지 않은 시스템 사이에 데이터를 교환하는 일이다. XML로 데이터를 교환하면 이런 복잡성을 크게 감소시킬 수 있다.

4)     XML은 플랫폼 변화를 단순하게 한다
새로운 시스템(하드웨어나 소프트웨어 플랫폼)으로 업그레이드하는 것은 항상 많은 시간을 소비하게 한다많은 양의 데이터가 변환되어야 하며 호환되지 않는 데이터를 손실되는 경우가 많다. XML 데이터는 텍스트 형식으로 저장되므로데이터를 잃어버리지 않고도 새로운 운영체제나 애플리케이션으로 확장하거나 업그레이드하기 쉽다.

5)     XML은 새로운 인터넷 언어를 만드는데 사용된다

 

많은 새로운 인터넷 언어들이 XML을 기반으로 생성된다그 중 몇가지 예를 들면 다음과 같다.

 

o  XHTML : HTML의 최신 버전

o  WSDL : 웹 서비스 디스크립션

o  WAP  WML : 핸드헬드 디바이스의 마크업 언어

o  RSS : 뉴스 제공 언어

o  RDF OWL : 리소스와 온톨로지 디스크립션

o  SMIL : 웹 용 멀티미디어 디스크립션

 

3.1.3. XML 트리

 

XML 다큐먼트는 루트(root)에서 시작하여 리프(leaf)로 분기하는 트리(tree) 구조를 형성한다. XML 다큐먼트는 자기 소개 형식의 단순한 구문을 사용한다.

 

<?xml version="1.0" encoding="UTF-8"?>

<note>

<from>갑돌이</from>

<to>갑순이</to>

<heading>메시지</heading>

<body>이번 주말 데이트 잊지 마!</body>

</note>

 

위의 예에서 첫번째 행에서는 XML을 선언한다이 문서가 XML 다큐먼트임을 말해준다여기에서는 XML 1.0 버전을 사용하며, UTF-8 인코딩 방식 즉유니코드 인코딩 방식을 사용함을 정의하고 있다두번째 행에서는 XML 다큐먼트의 루트 요소를 기술한다이 문서는 note라고 말하는 것과 같다다음 4행은 4개의 리프 요소 즉, from to, heading, body를 기술한다마지막 행은 루트 요소의 끝임을 정의한다.

 

위의 예에서 처럼 XML 다큐먼트는 하나의 루트 요소를 포함해야 하며루트 요소는 다른 모든 요소의 부모로서의 역할을 하게 된다. XML 다큐먼트에서 요소들은 트리 구조를 형성한다트리는 루트에서 시작하여 최하위 레벨까지 분기한다모든 요소는 서브 요소 또는 자식 요소를 가질 수 있다.

 

<root>

        <child>

                   <subchild>….</subchild>

        </child>

</root>

 

요소 사이의 관계를 기술하는데 부모(parent), 자식(child), 이웃(sibling)이란 용어가 사용된다부모 요소는 자식들을 가질 수 있으며같은 레벨에 있는 자식들을 이웃이라고 한다모든 요소는 텍스트 컨텐츠(content)와 애트리뷰트(attribute)를 가질 수 있다.

 

 



위의 그림은 하나의 책을 다음과 같이
 XML로 표현한다.

 

<bookstore>

<book category="COOKING">

  <title lang="en">Everyday Italian</title>

  <author>Giada De Laurentiis</author>

  <year>2005</year>

  <price>30.00</price>

</book>

<book category="CHILDREN">

  <title lang="en">Harry Potter</title>

  <author>J K. Rowling</author>

  <year>2005</year>

  <price>29.99</price>

</book>

<book category="WEB">

  <title lang="en">Learning XML</title>

  <author>Erik T. Ray</author>

  <year>2003</year>

  <price>39.95</price>

</book>

</bookstore>

 

위의 예에서 루트 요소는 <bookstore>이며모든 <book> 요소는 <bookstore> 안에 포함된다. <book> 요소는 4개의 자식 요소를 가지며여기에는 <title>, <author>, <year>, <price>가 포함된다.

 

3.1.4. XML 구문 규칙

 

XML 구문 규칙은 아주 단순하며 논리적이므로배우기 쉽고 사용하기 쉽다.

 

1)     모든 XML 문서는 끝 태크(end tag)를 가져야 한다

 

HTML에서는 다음과 같이 끝 태그를 갖지 않는 요소를 볼 수 있다.

<p>이것은 문단입니다

<p>이것은 다른 문단입니다

 

그러나 XML에서는 끝 태그를 생략할 수 없으며모든 요소는 끝 태그를 가져야만 한다.

 

<p>이것은 문단입니다</p>

<p>이것은 다른 문단입니다</p>

 

2)     XML 태그는 대소문자를 구별한다

 

XML 요소는 XML 태그를 사용하여 정의되며, XML 태그는 대소문자를 구별한다따라서 <Letter> 태그와 <letter> 태그는 서로 다르다시작 태그와 끝 태그는 같은 대소문자로 작성되어야 한다.

 

<Message>이것은 틀렸습니다</message>

<message>이것은 맞습니다</message>

 

3)     XML 요소는 적절하게 네스팅되어야 한다

 

HTML에서는 다음과 같이 부적절하게 네스팅된 요소를 종종 볼 수 있다.

 

<b><i>이 텍스트는 굵은체와 기울임체입니다</b></i>

 

그러나 XML에서는 모든 요소가 서로 안에서 적절하게 네스팅되어야 한다여기에서 적절하게 네스팅된다라고 하는 말은 다음 예와 같이 <i> 요소가 <b> 요소 안에서 열렸을 때 <b> 요소 안에서 닫혀야 한다는 것을 의미한다.

 

<b><i>이 텍스트는 굵은체와 기울임체입니다</i></b>

 

4)     XML 다큐먼트는 하나의 루트 요소를 가져야 한다

 

XML 다큐먼트는 모든 요소의 부모가 되는 하나의 요소를 포함해야 하며이 요소를 루트 요소라고 한다.

 

<root>

     <child>

                <subchild>….</subchild>

     </child>

</root>

 

5)     XML 애트리뷰 값은 쌍따옴표로 둘러싸야 한다

 

HTML과 같이 XML 요소도 이름과 값의 쌍으로 된 애트리뷰트를 가질 수 있다. XML에서 애트리뷰트 값은 항상 쌍따옴표로 둘러싸야 한다다음 두개의 XML 다큐먼트에서 첫번째 것은 틀린 것이고 두번째 것이 맞는 것이다.

 

<note date=2008/6/22>

<from>갑돌이</from>

<to>갑순이</to>

</note>

 

<note date=”2008/6/22”>

<from>갑돌이</from>

<to>갑순이</to>

</note>

 

6)     엔터티 참조(entity reference)

 

어떤 문자는 XML에서 특별한 의미를 갖는다예를 들어 XML 요소 안에 ‘<’ 문자를 사용한다면 XML 파서는 새로운 요소가 시작한다고 해석하여 에러를 발생시키게 될 것이다다음 구문은 에러를 발생시킨다.

 

<message>if salary < 1000000 then</message>

 

이러한 에러를 피하려면 ‘<’ 문자를 엔터티 참조로 대체해야 한다.

 

<message>if salary &lt; 1000000 then</message>

 

XML에서는 5개의 엔터티 참조가 미리 정의되어 있다.

 

&lt;

< 

작다

&gt;

> 

크다

&amp;

&

엠퍼샌드

&apos;

홑따옴표

&quot;

쌍따옴표

 

7)     주석

 

XML에서 주석 구문은 HTML과 유사하다.

 

<!-- 이것은 주석문입니다 -->

 

8)     공백 문자

 

HTML에서 다음과 같이 여러 개의 공백 문자(white space character)는 하나의 공백 문자로 축소된다.

 

HTML

안녕하세요?             내 이름은 전병선입니다.

결과

안녕하세요내 이름은 전병선입니다.

 

그러나 XML에서는 여러 개의 공백 문자는 그대로 유지된다.

 

HTML

안녕하세요?             내 이름은 전병선입니다.

결과

안녕하세요?             내 이름은 전병선입니다.

 

9)     개행 문자

 

윈도우 애플리케이션에서 개행 문자는 CR(carriage return) LF(line feed)의 결합으로 저장된다반면에 유닉스 애플리케이션에서는 LF 문자로만 저장되며매킨토시 애플리케이션에서는 CR 문자로만 저장된다. XML에서 개행(new line) 문자는 LF 문자로 저장된다.

 

3.1.5. XML 요소

 

XML 요소는 요소의 시작 태그에서 시작하여 끝 태그까지의 모든 것으로하나의 요소는 다른 요소나 텍스트 또는 요소와 텍스트의 혼합을 포함할 수 있다또한 요소는 애트리뷰트를 가질 수 있다.

 

<bookstore>

<book category="CHILDREN">

  <title lang="en">Harry Potter</title>

  <author>J K. Rowling</author>

  <year>2005</year>

  <price>29.99</price>

</book>

<book category="WEB">

  <title lang="en">Learning XML</title>

  <author>Erik T. Ray</author>

  <year>2003</year>

  <price>39.95</price>

</book>

</bookstore>

 

위의 예에서 <bookstore> <book>은 다른 요소를 포함하고 있기 때문에 요소 컨텐츠(element contents)를 갖는다. <author>는 텍스트를 포함하므로 텍스트 컨텐츠(text contents)를 갖는다위의 예에서 <book> 요소만 값이 각각 “CHILDREN”“WEB” category란 애트리뷰트를 갖는다.

 

XML 요소는 다음과 같은 명명 규칙을 따라야 한다.

 

o     이름은 문자숫자기타 문자를 포함할 수 있다

o     이름은 숫자나 구두점 문자로 시작할 수 없다

o     이름은 xml 이나 XML, Xml 등으로 시작할 수 없다

o     이름에는 공백 문자를 포함시킬 수 없다

 

어떤 이름이라도 사용할 수 있으며 예약어는 없다.

 

이름을 이해하기 쉽게 만드는 것이 좋다. <first_name>, <last_name>와 같이 단어는 밑줄 문자로 연결시킨다이름은 가능한 한 짧고 단순한 것이 좋다따라서 <the_title_of_the_book> 보다는 <book_title>이 더 낫다. ‘-‘ 문자는 사용하지 않는다. <first-name>이란 이름을 사용한다면 first에서 name을 빼는 것으로 오해할 소지가 있다마찬가지로 ‘.’ 문자는 사용하지 않는다. <first.name> 이란 이름을 사용한다면 first 란 객체의 name 이란 속성을 의미하는 것으로 오해할 수 있다또한 ‘:’ 문자도 피한다콜론 문자는 네임스페이스(namespace)에 사용된다.

 

XML 요소는 확장 가능하다, XML 요소는 더 많은 정보를 포함할 수 있도록 확장시킬 수 있다다음 XML 예를 생각해보기로 한다.

 

<note>

<from>갑돌이</from>

<to>갑순이</to>

<body>이번 주말 데이트 잊지 마!</body>

</note>

 

우리가 위의 XML 다큐먼트로부터 <from>, <to>, 그리고 <body> 요소를 추출하여 다음과 같은 결과를 보여주는 애플케이션을 작성해야 한다고 생각해보자.

 

메시지

 

수신 : 갑순이

발신 갑돌이

 

내용 이번 주말 데이트 잊지 마!

 

그런데 XML 다큐먼트의 저작자가 이 문서에 다음과 같이 여분의 정보를 추가했다고 하자.

 

<note>

<date>2008-06-22</date>

<from>갑돌이</from>

<to>갑순이</to>

<heading>메시지</heading>

<body>이번 주말 데이트 잊지 마!</body>

</note>

 

이 경우 기존의 애플리케이션에는 에러가 발생할까아니다애플리케이션은 아직도 XML 다큐먼트로부터 <from>, <to>, 그리고 <body> 요소를 추출하여 같은 결과를 보여줄 수 있다. XML의 하나의 이점은 애플리케이션을 변경시키지 않고 확장시킬 수 있다는 것이다.

 

3.1.6. XML 애트리뷰트

 

HTML에서는 시작 태그에 애트리뷰트를 지정할 수 있다애트리뷰트는 요소에 대한 추가적인 정보를 제공한다. <img src=”computer.gif”> 에서 “src” 애트리뷰트는 <img> 요소에 대한 추가적인 정보를 제공한다. XML 요소도 마찬가지로 시작 태그에 애트리뷰트를 지정할 수 있다애트리뷰트는 데이터가 아닌 정보를 제공하는데 사용된다다음 예에서 파일 타입은 데이터로서는 적당하지 않지만소프트웨에가 이 요소를 처리하는데 필요한 정보를 제공한다.

 

<file type="gif">computer.gif</file>

 

XML 애트리뷰트는 따옴표로 묶어야 한다이때 홑따옴표나 쌍따옴표 어느 것을 사용할 수도 있다예를 들어 사람의 성별에 대하여 person 태그는 다음과 같이 작성될 수 있다.

 

<person sex=”female”>

 

또는,

 

<person sex=’female’>

 

애트리뷰트 값에 쌍따옴표가 포함되어 있다면 다음 예와 같이 홑따옴표로 묶을 수 있다.

 

<gangster name=’George “Shotgun” Ziegler’>

 

또는엔터티 참조를 사용하여 다음과 같이 표현할 수 있다.

 

<gangster name=”George &quot;Shotgun&quot; Ziegler”>

 

애트리뷰트를 사용할 때와 요소를 사용할 때를 판단할 수 있는 규칙은 없다다음 두가지 예를 살펴보기로 하자.

 

첫번째 예:

<person sex=”남자”>

     <firstname>병선</firstname>

     <lastname></lastname>

</person>

 

두번째 예:

<person>

     <sex>남자</sex>

     <firstname>병선</firstname>

     <lastname></lastname>

</person>

          

첫번째 예에서는 sex는 애트리뷰트이고두번째 예에서는 요소이다두 코드의 예는 같은 정보를 제공하기 때문에 어느 방법을 사용해도 좋다일반적으로 HTML에서는 애트리뷰트를 많이 사용하지만, XML에서는 요소를 사용하는 것이 유리할 때가 많다.

 

애트리뷰트를 사용할 때 몇가지 문제가 있다.

 

o     애트리뷰트는 여러 값을 포함할 수 없다

o     애트리뷰트는 트리 구조를 포함할 수 없다

o     애트리뷰트는 쉽게 확장할 수 없다

 

또한 애트리뷰트는 읽고 유지 보수하기 어렵게 한다따라서 데이터에 대해서는 요소를 사용하고데이터로서 적당하지 않은 정보에 대해서만 애트리뷰트를 사용하는 것이 좋다.

 

따라서 다음과 같이 애트리뷰트를 사용하는 것 보다는,

 

<note year=”2008”, month=”06”, day=”22”>

<date>2008-06-22</date>

<from>갑돌이</from>

<to>갑순이</to>

<heading>메시지</heading>

<body>이번 주말 데이트 잊지 마!</body>

</note>

 

다음과 같이 요소를 사용하는 것이 바람직하다.

 

<note>

<date>

<year>2008</year>

<month>06</month>

<day>22</day>

</date>

<from>갑돌이</from>

<to>갑순이</to>

<heading>메시지</heading>

<body>이번 주말 데이트 잊지 마!</body>

</note>

 

3.1.7. XML 스키마

 

XML 다큐먼트의 구조를 정의할 때 DTD(document type definition)을 사용할 수 있다. DTD는 유효한 요소와 애트리뷰트 목록으로 XML 다큐먼트의 구조를 정의한다다음은 note.dtd 파일명을 갖는 DTD의 예를 보여준다.

 

<!DOCTYPE note [

     <!ELEMENT note (from, to, heading, body)>

     <!ELEMENT from           (#PCDATA)>

     <!ELEMENT to              (#PCDATA)>

     <!ELEMENT heading       (#PCDATA)>

     <!ELEMENT body           (#PCDATA)>

]>

 

W3C에서는 DTD 대신에 XML 기반의 스키마(schema) , XML 스키마를 지원한다다음은 note.xsd 파일명을 갖는 XML 스키마의 예를 보여준다.

 

<?xml version="1.0"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

           targetNamespace="http://www.w3schools.com"

           xmlns="http://www.w3schools.com"

                   elementFormDefault="qualified">

<xs:element name=”note”>

<xs:complexType>

     <xs:sequence>

                <xs:element name=”from”           type=”xs:string”/>

                <xs:element name=”to”              type=”xs:string”/>

                <xs:element name=”heading”       type=”xs:string”/>

                <xs:element name=”body”           type=”xs:string”/>

     </xs:sequence>

</xs:complexType>

</xs:element>

 

XML 스키마는 XML 다큐먼트의 구조를 기술하며, XML 스키마 언어를 XSD(XML schema definition)이라고 한다위의 XML스키마 예에서 note 요소는 다른 요소를 포함하기 때문에 복합 타입(complex type)이 된다. from이나 to, heading, body 등의 요소는 다른 요소를 포함하지 않으므로 단순 타입(simple type)이 된다.

 

XML 다큐먼트는 XML 스키마를 참조할 수 있다다음은 note.xsd 라는 파일명을 갖는 XML 스키마를 참조하는 XML 다큐먼트의 예를 보여준다.

 

<?xml version="1.0"?>

<note

     xmlns="http://www.w3schools.com"

     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

     xsi:schemaLocation="http://www.ensoa.co.kr note.xsd">

<from>갑돌이</from>

<to>갑순이</to>

<heading>메시지</heading>

<body>이번 주말 데이트 잊지 마!</body>

</note>

 

3.1.8. XML DOM

 

XML DOM(document object model) XML 다큐먼트에 접근하고 조작하는 표준 방법을 정의한다. DOM XML 다큐먼트를 트리 구조로 간주한다모든 요소는 DOM 트리를 통해서 접근할 수 있다요소의 컨텐츠(텍스트와 애트리뷰트)는 수정될 수도 있고 삭제될 수 있으며새로운 요소가 생성될 수도 있다요소와 텍스트애트리뷰트는 모두 노드(note)로 간주된다다음 예제 코드는 <to> 요소로부터 텍스트를 구하는 예이다.

xmlDoc.getElementsByTagName("to")[0].childNodes[0].nodeValue

위의 예에서

 

o  xmlDoc은 파서(parser)가 생성하는 XML 다큐먼트이고,

o  getElementsByTagName(“to”)[0] 은 첫번째 <to> 요소에 접근하며,

o  childNodes[0] <to> 요소의 첫번째 자식 노드를 가르키며,

o  nodeValue는 노드의 값 즉텍스트를 가르킨다

 

3.1.9. XML 네임스페이스

 

XML에서 요소명은 개발자가 정의한다따라서 다른 XML 애플리케이션에서 생성한 XML 다큐먼트가 혼합된다면 이름이 충돌할 수도 있다예를 들어 다음 XML 코드는 HTML의 테이블 정보를 표현한다.

 

<table>

     <tr>

     <td>사과</td>

     <td>바나나</td>

     </tr>

</table>

 

그러나 다음 XML 코드는 가구인 탁자에 대한 정보를 표현한다.

 

<table>

     <name>밥상</name>

     <width>300</width>

     <length>800</length>

</table>

 

이들 두 XML 코드가 함께 사용된다면 <table> 이란 요소명이 충돌하게 된다모두 <table>이란 요소를 포함하고 있지만 서로 다른 컨텐츠와 의미를 갖는다이 경우 XML 파서는 이들 사이의 차이점을 처리하는 방법을 알지 못하게 된다.

 

XML에서 이름 충돌을 피할 수 있는 가장 손쉬운 방법은 전치사(prefix)를 사용하는 것이다다음은 전치사를 사용한 예이다.

 

<h:table>

     <h:tr>

     <h:td>사과</h:td>

     <h:td>바나나</h:td>

     </h:tr>

</h:table>

<f:table>

     <f:name>밥상</f:name>

     <f:width>300</f:width>

     <f:length>800</f:length>

</f:table>

 

XML에 전치사를 사요알 때 전치사에 대한 네임스페이스(namespace)가 정의되어야 한다네임스페이스는 요소의 시작 태그에xmlns 애트리뷰트로 정의한다네임스페이스는 다음과 같은 구문을 갖는다.

 

xmlns:prefix=”URI”

 

위의 구문에서 URI(Uniform Resource Identifier)는 인터넷 리소스를 식별하는 문자열로서가장 일반적인 URI는 인터넷 도메인 주소를 나타내는 URL(Uniform Resource Locator)이며, URN(Universal Resource Name) URI의 일종이다.

 

다음은 네임스페이스를 정의한 예이다.

 

<root>

<h:table xmlns:h="http://www.w3.org/TR/html4/">

     <h:tr>

     <h:td>사과</h:td>

     <h:td>바나나</h:td>

     </h:tr>

</h:table>

<f:table xmlns:f=”http://www.ensoa.co.kr/furniture”>

     <f:name>밥상</f:name>

     <f:width>300</f:width>

     <f:length>800</f:length>

</f:table>

</root>

 

네임스페이스는 다음과 같이 XML 루트 요소에 정의할 수도 있다.

 

<root

     xmlns:h="http://www.w3.org/TR/html4/"

     xmlns:f=”http://www.ensoa.co.kr/furniture”>

<h:table>

     <h:tr>

     <h:td>사과</h:td>

     <h:td>바나나</h:td>

     </h:tr>

</h:table>

<f:table>

     <f:name>밥상</f:name>

     <f:width>300</f:width>

     <f:length>800</f:length>

</f:table>

</root>

 

다음과 같은 구문으로 요소에 디폴트 네임스페이스를 정의하면 모든 자식 요소에 전치사를 붙여줘야 하는 부담에서 벗어날 수 있다.

 

xmlns=”namespaceURI”

 

다음은 디폴트 네임스페이스를 사용한 예이다.

 

<table xmlns="http://www.w3.org/TR/html4/">

     <tr>

     <td>사과</td>

     <td>바나나</td>

     </tr>

</table>

<table xmlns=”http://www.ensoa.co.kr/furniture”>

     <name>밥상</name>

     <width>300</width>

     <length>800</length>

</table>

 

3.1.10. XML 인코딩

 

XML 다큐먼트는 한글과 같은 비 아스키 문자를 포함할 수 있다따라서 XML 다큐먼트를 읽을 때 다음과 같은 인코딩 문제가 발생할 수 있다.

 

o  텍스트 컨텐츠에 유효하지 않은 문자가 있다
XML 
에 비 아스키 문자가 포함되어 있으며인코딩이 지정되지 않은 채 파일이 아스키 코드로 저장될 때 발생한다

o  현재 인코딩 방식을 지정된 인코딩 방식으로 전환이 지원되지 않는다
1
바이트 인코딩(UTF-8) 방식이 지정된 채 2바이트 유니코드(UTF-16) XML 파일이 저장되거나, 2바이트 인코딩(UTF-16)방식이 지정된 채 1바이트인 아스키 코드로 저장될 때 발생한다

 

이러한 XML 인코딩 문제를 피하기 위해서는 다음 사항에 유의한다.

 

o  항상 인코딩 애트리뷰트를 사용한다

o  인코딩을 지원하는 에디터를 사용한다

o  에디터가 사용하는 인코딩 방식을 확인한다

o  인코딩 애트리뷰트와 동일한 인코딩을 사용한다

 

XML 인코딩 애트리뷰트는 다음과 같이 지정한다.

 

<?xml version="1.0" encoding="UTF-8"?>

 

인코딩이란 특정한 문자 집합 안의 문자들을 컴퓨터 시스템에서 사용할 목적으로 일정한 범위 안의 정수(코드값)들로 변환하는 방법이다여기에는 유니코드 코드 포인트를 8비트 숫자의 집합으로 나타내는 UTF-8이나, 16비트 숫자의 집합으로 나타내는 UTF-16, 그리고 대부분의 일반적인 문자 인코딩들이 포함된다.

 

UTF-8(8-bit Unicode Transformation Format)은 유니코드를 위한 가변 길이 문자 인코딩 방식 중 하나로, UTF-8 인코딩은 유니코드 한 문자를 나타내기 위해 1바이트에서 4바이트까지를 사용한다. UTF-16(16-bit Unicode Transformation Format)도 유니코드 문자 인코딩 방식의 하나로서주로 사용되는 기본 다국어 평면 (BMP, Basic multilingual plane)에 속하는 문자들은 그대로16비트 값으로 인코딩이 되고 그 이상의 문자는 특별히 정해진 방식으로 32비트로 인코딩이 된다.

 

EUC-KR 인코딩 방식은 유니코드 이전의 완성형 한글을 표현한다. EUC(Extended Unix Code, 확장 유닉스 코드)란 한국어중국어일본어 문자 전산화에 주로 사용되는 8비트 문자 인코딩 방식이며, EUC-KR은 이 인코딩 방식을 사용하여 한글 등 한국어에서 사용되는 문자를 표현한 것이다코드 페이지 949(CP949)는 마이크로소프트 한글 윈도우에서 사용되는 코드 페이지이다.본래는 KSC 5601의 완성형 한글을 표현한 코드 페이지였으나확장 완성형 혹은 통합형 한글 코드(Unified Hangul Code)이라는 명칭으로 확장되어 모든 현대 한글을 수용하게 되었다마이크로소프트에서는 이 인코딩을 기반 문자 집합 이름인 "ks_c_5601-1987"로 사용하고 있다.

 

 


 

 

3.2. SOAP(simple object access protocol) 기초

 

3.2.1. SOAP 개요

 

SOAP(simple object access protocol)은 분산 환경에서 구조적인 정보를 교환하기 위한 XML 기반의 프로토콜이다원칙적으로 SOAP은 트랜스포트 독립적이며따라서 HTTP JMS, SMTP 또는 FTP 등과 같은 다양한 프로토콜과 조합해서 사용될 수 있다그러나 현재로서 가장 일반적인 SOAP 메시지 교환 방식은 HTTP를 통한 것이며, HTTP 프로코콜은 WS-I 기본 프로파일(WS-I Basic Profile) 1.0에 의해서 지원되는 유일한 프로토콜이다.

 

SOAP는 다음과 같은 특징을 갖는다.

 

o     SOAP은 커뮤니케이션 프로토콜이다

o     SOAP은 애플리케이션 사이의 커뮤니케이션을 목적으로 한다

o     SOAP은 메시지 전송 형식이다

o     SOAP은 인터넷을 통한 커뮤니케이션을 위해 설계되었다

o     SOAP은 언어 독립적이다

o     SOAP XML을 기반으로 한다

o     SOAP은 단순하며 확장 가능하다

o     SOAP은 방화벽을 피할 수 있게 한다

o     SOAP W3C 표준이다

 

SOAP 2000 4월에 1.1 버전이 확정되었고, 2003 5월에 1.2 버전이 발표되었다여기에서는 특별한 언급이 없는 한 SOAP 1.1에 대해서 설명한다그것은 현재로서는 SOAP 1.2 보다 SOAP 1.1이 훨씬 광범위하게 사용되고 있고, JAX-WS를 사용하여 구현된 웹 서비스 엔드포인트가 디폴트 바인딩으로 SOAP 1.1/HTTP를 사용하고 있기 때문이다.

 

3.2.2. SOAP 구문

 

SOAP 메시지는 다음 요소를 포함하는 일반적인 XML 다큐먼트이다.

 

o     Envelope 요소 : XML 다큐먼트를 SOAP 메시지로 식별하게 하는 필수 요소

o     Header 요소 : 헤더 정보를 포함하는 0개 이상의 선택 요소

o     Body 요소 : 호출 및 응답 정보를 포함하는 하나의 필수 요소

o     Fault 요소 : 메시지를 처리하는 동안 발생한 에러에 대한 정보를 제공하는 선택 요소

 

SOAP 구문 규칙은 다음과 같다.

 

o     SOAP 메시지는 XML을 사용하여 인코딩되어야 한다

o     SOAP 메시지는 SOAP Envelope 네임스페이스를 사용해야 한다

o     SOAP 메시지는 SOAP Encoding 네임스페이스를 사용해야 한다

o     SOAP 메시지는 DTD 참조를 포함하지 않아야 한다

o     SOAP 메시지는 XML 처리 명령을 포함하지 않아야 한다

 

SOAP 메시지의 골격 구조는 다음과 같다.

 

<?xml version="1.0"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

  <soap:Header>

                ...

                ...

  </soap:Header>

  <soap:Body>

               ...

               ...

    <soap:Fault>

                 ...

                 ...

    </soap:Fault>

  </soap:Body>

</soap:Envelope>

 

3.2.3. SOAP Envelope 요소

 

SOAP Envelope 요소는 SOAP 메시지의 루트 요소이며, XML 다큐먼트를 SOAP 메시지로 정의한다. Envelope 요소의 xmlns:soap 네임스페이스 애트리뷰트는 다음과 같이 정의된다.

 

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope">

 

Envelope 요소의 encodingStyle 애트리뷰트는 다큐먼트에서 사용되는 데이터 타입을 정의하기 위해 사용된다이 애트리뷰트는 어떤 SOAP 요소에서도 나타날 수 있으며요소의 컨텐츠와 모든 자식 요소에 적용된다. SOAP 메시지는 디폴트 인코딩이 없다.

 

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

 

3.2.4. SOAP Header 요소

 

SOAP Header 요소는 트랜잭션이나 인증과 같이 SOAP 메시지에 관한 애플리케이션에 고유한 정보를 포함한다. Header 요소가 있다면 Envelope 요소의 첫번째 자식 요소가 되어야 한다.

 

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

     <soap:Header>

<m:Trans xmlns:m="http://www.ensoa.co.kr/transaction/">234</m:Trans>

</soap:Header>

</soap:Envelope>

 

위의 예에서는 Header 요소는 Trans 자식 요소를 포함하고 있으며, Trans 요소는 값이 1mustUnderstand 애트리뷰트를 포함하며, Trans 요소의 값은 234이다.

 

SOAP은 디폴트 네임스페이스("http://schemas.xmlsoap.org/soap/envelope") 3개의 애트리뷰트를 정의한다이들 애트리뷰트에는 actor mustUnderstand, 그리고 encodingStyle 특성이 포함된다. SOAP Header에 정의된 애트리뷰트는 수신자가 SOAP 메시지를 처리하는 방법을 정의한다.

 

SOAP 메시지는 메시지 경로에 따라 서로 다른 엔드포인트(endpoint)를 넘겨줌으로써 송신자로부터 수신자로 이동한다. SOAP메시지의 모든 부분이 SOAP 메시지의 최종 엔드포인트에 전달되어야 하는 것은 아니다그보다는 메시지 경로에 있는 하나 이상의 엔드포인트에 전달될 수도 있다.

 

SOAP actor 애트리뷰트는 Header 요소가 특정한 엔드포인트에 전달되도록 하는데 사용된다. actor 애트리뷰트에는 다음 구문과 같이 URI 값을 지정한다.

 

soap:actor=”URI”

 

다음은 actor 애트리뷰트의 사용 예를 보여준다.

 

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

    <soap:Header>

        <m:Trans xmlns:m="http://www.ensoa.co.kr/transaction/"

soap:actor=” http://www.ensoa.co.kr/appml/">234</m:Trans>

    </soap:Header>

</soap:Envelope>

 

SOAP mustUnderstand 애트리뷰트는 헤더 요소를 수신자가 필수적으로 처리해야 하는 지 선택적으로 처리해야 하는 지를 지정하는데 사용된다. Header 요소의 자신 요소에 1이 지정되면 Header를 처리하는 수신자가 요소를 인식해야만 한다는 것을 나타낸다수신자가 요소를 인식하지 못한다면 Header 요소를 처리할 때 실패해야 한다. mustUnderstand 애트리뷰트에는 0 또는 1을 지정한다.

 

soap:mustUnderstand=”0 | 1”

 

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

     <soap:Header>

         <m:Trans xmlns:m="http://www.ensoa.co.kr/transaction/"

soap:mustUnderstand=”1”>234</m:Trans>

     </soap:Header>

</soap:Envelope>

 

SOAP encodingStyle 특성은 Envelope 요소의 encodingStyle 특성과 동일하다.

 

3.2.5. SOAP Body 요소

 

SOAP Body 요소는 메시지의 최종 엔드포인트를 목적지에 전달되어야 하는 실제 SOAP 메시지를 포함한다. SOAP Body 요소의 자신 요소는 네임스페이스가 명시될 수 있다. SOAP은 디폴트 네임스페이스("http://schemas.xmlsoap.org/soap/envelope")에 Body 요소 안에 에러 메시지를 표시하는데 사용되는 SOAP Fault 요소를 정의한다.

 

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

     <soap:Body>

       <m:getPrice xmlns:m="http://www.ensoa.co.kr/prices">

                         <m:Item>Apples</m:Item>

       </m:getPrice>

     </soap:Body>

</soap:Envelope>

 

위의 예에서는 사과의 값을 요청한다. m:getPrice m:Item 요소는 애플리케이션에 고유한 요소이며 SOAP 표준의 일부는 아니다이 요청에 대하여 SOAP 응답은 다음과 같은 형태를 취한다.

 

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

     <soap:Body>

        <m:getPriceResponse xmlns:m="http://www.ensoa.co.kr/prices">

           <m:Price>500</m:Price>

        </m:getPriceResponse>

     </soap:Body>

</soap:Envelope>

 

3.2.6. SOAP Fault 요소

 

SOAP Fault 요소는 SOAP 메시지에 대한 에러 및 상태 정보를 저장하는데 사용된다. SOAP 메시지로부터의 에러 메시지는 Fault 요소에 저장되어 전송된다. Fault 요소는 Body 요소의 자식 노드이며, SOAP 메시지 안에 단 한번 만 나타날 수 있다.

 

SOAP Fault 요소는 다음과 같은 하위 요소를 갖는다.

 

하위 요소

설명

<faultcode>

에러 코드

<faultstring>

에러 설명

<faultactor>

에러를 발생시킨 액터에 대한 정보

<detail>

Body 요소에 관련된 애플리케이션 특정한 에러 정보

 

<faultcode> 요소에 저장되는 에러 코드는 다음과 같이 정의되어 있다.

 

에러

설명

VersionMismatch

SOAP Envelope 요소에 잘못된 네임스페이스가 지정됨

MustUnderstand

mustUnderstand 특성에 “1”이 지정되어 있는 Header 요소의 자식 요소를 알 수 없음

Client

메시지가 잘못되었거나 부정확한 정보를 포함하고 있음

Server

서버에 문제가 발생하여 메시지를 처리할 수 없음

 

3.2.7. SOAP HTTP 바인딩

 

HTTP 프로토콜을 사용할 때 HTTP TCP/IP 상에 커뮤니케이션한다. HTTP 클라이언트가 TCP를 사용하여 HTTP 서버에 연결되며클라이언트 서버에 HTTP 요청 메시지를 보낼 수 있다.

 

POST /item HTTP/1.1

Host: 192.168.1.1

Content-Type: text/plain

Content-Length: 200

 

이때 서버는 요청을 처리하고 클라이언트에게 HTTP 응답을 되돌려보낸다응답에는 요청 상태를 표시하는 상태 코드가 포함된다.

 

200 OK

Content-Type: text/plain

Content-Length: 200

 

위의 예에서 서비스는 성공을 나타내는 상태 코드 200을 반환하고 있다만약 서버가 요청을 해독할 수 없다면 다음과 같은 응답을 반환하게 된다.

 

400 Bad Request

Content-Length: 0

 

SOAP은 트랜스포트 레이어에 독립적이므로 반드시 HTTP 프로토콜을 사용해야 하는 것은 아니지만현재로서 가장 일반적인 SOAP 메시지 교환 방식이 HTTP 프로토콜을 사용하는 하는 것이다그러므로 우리는 SOAP 메서드는 SOAP 인코딩 규칙을 준수하는 HTTP 요청/응답으로 생각할 수 있다따라서 다음과 같은 공식을 세울 수도 있다.

 

HTTP + XML = SOAP

 

이때 SOAP 요청은 HTTP POST이거나 HTTP GET 요청이며, HTTP POST 요청은 Content-Type Content-Length 등 적어도2개의 HTTP 헤더를 지정한다.

 

SOAP 요청/응답의 Content-Type 헤더는 XML 몸체에서 사용하게 될 메시지의 MIME 타입과 문자 인코딩을 정의한다.

 

Content-Type: MIMETYPE; charset=문자인코딩

 

Content-Type 헤더의 예는 다음과 같다.

 

POST /item HTTP/1.1

Content-Type: application/soap+xml; charset=utf-8

 

SOAP 요청/응답의 Content-Length 헤더에는 요청 또는 응답 몸체의 바이트 수를 지정한다.

 

Content-Length: 바이트수

 

POST /item HTTP/1.1

Content-Type: application/soap+xml; charset=utf-8

Content-Length: 250

 

다음 예는 getStockPrice 요청을 서버에 전송한다요청에는 StockName 매개변수를 가지며, Price 매개변수가 응답에 반환된다이 기능의 네임스페이스는 “http://localhost/stock” 주소에 저장된다.

 

POST /stock HTTP/1.1

Host: localhost

Content-Type: application/soap+xml; charset=utf-8

Content-Length: 250

 

<?xml version=”1.0”?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

     <soap:Body xmlns:m="http://localhost/stock">

        <m:getStockPrice>

           <m:StockName>PC</m:StockName>

        </m:getStockPrice>

     </soap:Body>

</soap:Envelope>

 

위의 요청에 대한 SOAP 응답은 다음과 같다.

 

HTTP/1.1 200 OK

Content-Type: application/soap+xml; charset=utf-8

Content-Length: 250

 

<?xml version=”1.0”?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"

                   soap:encodingStyle=" http://schemas.xmlsoap.org/soap/encoding">

     <soap:Body xmlns:m="http://localhost/stock">

          <m:getStockPriceResponse>

             <m:Price>2000000</m:Price>

          </m:getStockPriceResponse>

     </soap:Body>

</soap:Envelope>

 

 

 

 

 

3.3. WSDL UDDI 기초

 

3.3.1. WSDL 개요

 

WSDL(Web Services Description Language)는 웹 서비스와 접근 방법을 기술하는 XML 기반의 언어이다.

 

o     WSDL XML로 작성된다

o     WSDL XML 다큐먼트이다

o     WSDL은 웹 서비스를 기술하는데 사용된다

o     WSDL은 웹 서비스의 위치와 웹 서비스가 제공하는 오퍼레이션을 명시한다

o     WSDL은 아직 W3C 표준은 아니다

 

WSDL 1.1 2001 5월에 W3C Note로 출판되었으며, 2002 7월에 WSDL 1.2가 Working Draft로 출판되었지만 Recommendation 되지는 못하였다가장 최신 버전인 WSDL 2.0은 Candidate Recommendation 상태이다여기에서는 특별한 언급이 없는 한WSDL 1.0에 대해서 설명한다그것은 JAX-WS 2.0의 핵심 컴포넌트가 WSDL 1.1 Java 사이를 맵핑하고 있으며, WSDL 2.0에 대해서는 맵핑이 정의되어 있지 않기 때문이다.

 

3.3.2. WSDL 다큐먼트 구조

 

WSDL 다큐먼트는 단순한 XML 다큐먼트로서 웹 서비스를 기술하는 정의 집합(set of definition to describe a web service)을 포함한다.

 

WSDL 다큐먼트는 다음과 같은 요소를 사용하여 웹 서비스를 기술한다.

 

요소

정의

<portType>

웹 서비스가 수행하는 오퍼레이션

<message>

웹 서비스가 사용하는 메시지

<types>

웹 서비스가 사용하는 데이터 타입

<binding>

웹 서비스가 사용하는 커뮤니케이션 프로토콜

 

<portType> 요소는 가장 중요한 WSDL 요소로서웹 서비스와 웹 서비스가 수행하는 오퍼레이션그리고 포함되는 메시지를 정의한다. <portType> 요소는 전통적인 프로그래밍 언어에서 함수 라이브러리나 모듈클래스와 유사하다. <message> 요소는 오퍼레이션의 데이터 요소를 정의한다각 메시지는 하나 이상의 파트(part)로 구성될 수 있다파트는 전통적인 프로그래밍 언어에서 함수의 매개변수와 유사하다. <types> 요소는 웹 서비스가 사용하는 데이터 타입을 정의한다플랫폼 중립성을 극대화하기 위해 XML 스키마 구문을 사용하여 데이터 타입을 정의한다. <binding> 요소는 각 포트의 메시지 형식과 프로토콜을 상세히 정의한다.

 

WSDL 다큐먼트의 주요 구조는 다음과 같다.

 

<definitions>

<types>

           타입 정의

</types>

<message>

           메시지 정의...

</message>

<portType>

            포트 정의...

</portType>

<binding>

            바인딩 정의

</binding>

</definitions>

 

WSDL 다큐먼트는 확장 요소(extension element)나 서비스 요소(service element)와 같은 다른 요소를 포함하여 하나의 WSDL다큐먼트에 여러 개의 웹 서비스 정의를 함께 묶을 수 있다.

 

다음은 WSDL 다큐먼트의 간단한 예이다.

 

<message name="getTermRequest">

   <part name="term" type="xs:string"/>

</message>

<message name="getTermResponse">

   <part name="value" type="xs:string"/>

</message>

<portType name="glossaryTerms">

  <operation name="getTerm">

      <input message="getTermRequest"/>

      <output message="getTermResponse"/>

  </operation>

</portType>

 

위의 예에서 <portType> 요소는 포트 이름으로 glossaryTerms그리고 오퍼레이션 이름으로 getTerm을 정의한다. getTerm오퍼레이션은 getTermRequest라는 입력 메시지와 getTermResponse라는 출력 메시지를 갖는다. <message> 요소는 각 메시지의 파트 및 관련된 데이터 타입을 정의한다전통적인 프로그래밍에 비교한다면 glossaryTerms 는 함수 라이브러리가 되고, getTerm은 입력 매개변수로 getTermRequest와 반환 타입으로 getTermResponse를 갖는 함수가 된다.

 

3.3.3. WSDL 포트

 

WSDL 포트(port)는 웹 서비스가 노출하는 인터페이스를 기술한다앞에서 언급한 바와 같이 <portType> 요소는 가장 중요한 WSDL 요소로서웹 서비스와 웹 서비스가 수행하는 오퍼레이션그리고 포함되는 메시지를 정의한다포트는 웹 서비스와의 연결 점(connection point)를 정의한다.

 

WSDL에서 가장 일반적인 오퍼레이션 타입(operation type)은 요청-응답(request-response)이지만, WSDL은 다음과 같은 4개의 타입을 정의한다.

 

포트 타입

정의

단방향(one-way)

오퍼레이션은 메시지를 받을 수는 있지만 응답을 반환하지는 않는다

요청-응답(reques-response)

오퍼레이션은 메시지를 받으며 응답을 반환한다

응답 청구(solicit-response)

오퍼레이션은 요청을 보낼 수 잇으며 응답을 기다린다

알림(notfication)

오퍼레이션이 메시지를 보낼 수 있지만 응답을 기다리지는 않는다

 

단방향(one-way) 오퍼레이션의 예는 다음과 같다.

 

<message name="newTermValues">

   <part name="term" type="xs:string"/>

   <part name="value" type="xs:string"/>

</message>

<portType name="glossaryTerms">

   <operation name="setTerm">

      <input name="newTerm" message="newTermValues"/>

   </operation>

</portType >

 

위의 예에서 glossaryTerms 포트는 setTerm이라는 단방향 오퍼레이션을 정의한다. setTerm 오퍼레이션은 term value 입력 매개변수를 갖는 newTermsValues 메시지를 사용하여 새로운 용어집 메시지의 입력을 허용한다그러나 오퍼레이션에 응답을 정의하지는 않고 있다.

 

요청-응답(reques-response) 오퍼레이션의 예는 다음과 같다.

 

<message name="getTermRequest">

   <part name="term" type="xs:string"/>

</message>

<message name="getTermResponse">

   <part name="value" type="xs:string"/>

</message>

<portType name="glossaryTerms">

  <operation name="getTerm">

      <input message="getTermRequest"/>

      <output message="getTermResponse"/>

  </operation>

</portType>

 

위의 예에서 glossaryTerms 포트는 getTerm이라는 요청-응답 오퍼레이션을 정의한다. getTerm 오퍼레이션은 term 매개변수를 갖는 getTermRequest 요청 메시지를 요구하며, value라는 매개변수를 갖는 getTermResponse 응답 메시지를 반환하고 있다.

 

3.3.4. WSDL 바인딩

 

WSDL 바인딩(bindings)는 웹 서비스의 메시지 형식과 프로토콜을 상세히 정의한다

 

다음 요청-응답 오퍼레이션은 SOAP에 바인딩하는 예를 보여준다.

 

<message name="getTermRequest">

   <part name="term" type="xs:string"/>

</message>

<message name="getTermResponse">

   <part name="value" type="xs:string"/>

</message>

<portType name="glossaryTerms">

  <operation name="getTerm">

      <input message="getTermRequest"/>

      <output message="getTermResponse"/>

  </operation>

</portType>

<binding type="glossaryTerms" name="b1">

<soap:binding style="document"

transport="http://schemas.xmlsoap.org/soap/http" />

  <operation>

    <soap:operation soapAction="http://localhost/getTerm"/>

    <input>

      <soap:body use="literal"/>

    </input>

    <output>

      <soap:body use="literal"/>

    </output>

  </operation>

</binding>

 

<binding> 요소는 두개의 애트리뷰트를 갖는다. name 애트리뷰트는 바인딩 이름을 정의하며, type 애트리뷰트는 바인딩 포트를 가르킨다위의 예에서는 바인딩 이름은 b1이고바인딩 포트는 glossaryTerms 포트가 된다.

 

<soap:binding> 요소도 두개의 애트리뷰트를 갖는다. style 애트리뷰트는 “rpc”이거나 “document” 일 수 있다위의 예에서는document가 사용되고 있다. transport 애트리뷰트는 사용할 SOAP 프로코콜을 정의한다위의 예에서는 HTTP가 사용된다.

 

<operation> 요소는 포트가 노출하는 각 오퍼레이션을 정의한다각 오퍼레이션에 대하여 대응되는 SOAP 액션(action)을 정의해야 한다또한 입출력이 인코딩되는 방법도 지정해야 한다위의 예에서는 literal이 사용되고 있다.

 

3.3.5. UDDI 기초

 

UDDI(Universal Description, Discovery and Integration)는 비즈니스가 웹 서비스를 등록하고 검색할 수 있는 플랫폼 독립적인 디렉터리 서비스 프레임워크다.

 

o     UDDI는 웹 서비스에 대한 정보를 저장하고 있는 디렉터리다

o     UDDI WSDL로 기술된 웹 서비스 인터페이스의 디렉터리다

o     UDDI SOAP을 통해 커뮤니케이션한다

 

UDDI XML, HTTP, DNS 프로토콜과 같은 W3C IETF 인터넷 표준을 사용하며, WSDL을 사용하여 웹 서비스 인터페이스를 기술한다이와함께 SOAP를 채택하여 이기종 플랫폼 프로그래밍 기능을 제공한다.



[출처] 3. XML, SOAP, WSDL, UDDI|작성자 엔소아


'Language > XML' 카테고리의 다른 글

참조, 예제 사이트  (0) 2016.12.13
WSDL(Web Services Description Language)  (0) 2016.12.12
XML 스키마  (0) 2013.07.03
:

Domain-Specific Language

Language/Groovy 2016. 9. 9. 15:35

Domain-Specific Language(DSL)은 특정 도메인에 특화된 비교적 작고 간단한 프로그래밍 언어이다. 도메인 문제를 해결 할 때, 문제 해결 아이디어와 최대한 유사한 형태의 문법으로 프로그래밍 할 수 있도록 하는 것이 주요 컨셉트이다.

DSL의 반대 개념은 General Purpose Language(GPL)이며 Java나 C++같은 보편적인 언어를 지칭한다. GPL로는 컴퓨터로 해결 가능한 모든 종류의 문제를 풀 수 있지만 그것이 항상 최선의 방법은 아니며 DSL 바로 그 특정한 종류(도메인 영역)의 문제를 해결하기에 더 적합한 언어가 되는 것을 그 목적으로 한다.

이클립스에서는 Xtext를 이용하여 DSL을 정의하고 에디터 및 DSL 기반 인터프리터 또는 제네레이터를 공급할 수 있다. GPL 중에서도 Java Script등과 같은 다이니믹 타이핑 언어등을 개발할 때에는 DLTK를 이용할 수 있다.


출처 - http://eclipse.or.kr/wiki/Domain-Specific_Language

'Language > Groovy' 카테고리의 다른 글

DSL(Domain Specific Language) 이해하기  (0) 2016.09.09
Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
:

DSL(Domain Specific Language) 이해하기

Language/Groovy 2016. 9. 9. 15:34

DSL(Domain Specific Language) 이해하기

1. DSL이란
특정 도메인(산업, 분야등 특정 영역)에 특화된 언어를 말한다. 
"문제 영역의 해결에는 그 영역의 언어를 전제로 둬야하며, 거기에서 프로그래밍 솔루션을 꺼내는 것이 중요하다." 라고 Dave Thomas가 한 말을 생각하면 이해하기 쉽다.

특정 영역의 문제 해결에는 그 영역에 맞는 특화된 도구를 사용하자라는 것이다. 어찌보면 과도로 끝내도 될 일을 맥가이버칼을 들이대는 격이다. 그리고 표현 방식은 해당 도메인의 전문가가 이해할 수 있는 형태(고급 언어)여야 한다.

실제로 Ruby처럼 그 코드가 일반 자연어를 읽는 것과 같이 쉽게 이해되기 때문에 도메인 전문가와 프로그래머가 아이디어를 공유하기 좋고 거기에 Lisp 계열이어서 DSL에 많이 활용된다.

그리고 DSL은 개발 생산성과 도메인 전문가와 커뮤니케이션을 원할하게 하기 위해서 도입되는 경향이 많지만, No Silver Bullet.

참고로 UNIX에서도 특정한 응용 영역의 문제를 해결하기 위해 그 영역에만 적용할 수 있는 특수한 언어를 만들어 문제를 해결하는 오랜 전통이 존재한다. 이런 언어를 "작은 언어(little language)", 또는 "미니 언어(mini language)"라고 부르는데 DSL도 이와 유사하다고 볼 수 있다.

2. 내부 DSL과 외부 DSL
2.1 내부 DSL
- 호스트 언어 구문을 이용하여 자체적으로 의존하는 무언가를 만드는 경우에 해당된다.
- 내부 DSL에서는 API와 DSL의 경계가 모호해 비슷하게 생각하는 경향이 있다. 
    . 좀 더 일반 사용자가 알아보기 쉬운 API가 내부 DSL로 생각해도 될 듯 하다.
- 호스트 언어 능력과 지금까지 사용하던 도구를 그대로 사용할 수 있다는 점, 처리 결과를 쉽게 예측할 수 있어서 해당 언어를 잘 알면 친근할 수 있다.
- 형태
    . 메타 프로그래밍의 형태로 언어에 미니 언어를 만들 수 있다. 
    . 원래 언어로 새로운 구문으로 도입 된다. 그래서 언어 확장을 일으켜 다른 언어가 된다.
    . 인라인 코드 형태로 표현될 수도 있다.
- 적합한 언어 : Lisp, Ruby, Smalltalk

2.2 외부 DSL
- 호스트 언어와 다른 언어 (XML, Makefile과 같은 고유 형식)에서 생성된 DSL.
- GUI 도구를 제공해 주는 것이 특징.
- 외부 DSL에서는 DSL과 범용 언어(GPL : General Purpose Language)과의 경계가 모호해지는 경향이 있다.
    . 그 차이는 언어 작성자와 언어 사용자의 목적에 있다. 특정 영역에서 언어의 작성자가 아닌 사용자의 목적에 부합하는, 이해를 할 수 있으면 외부 DSL이다.
- 외부 DSL 개발자가 자유롭게 DSL의 형식을 결정할 수 있다.
- 형태
    . 실행 파일에서 DSL 을 동적 로딩할 수 있다.
    . DSL 컴파일러를 만들어서 표현할 수 있다.
    . DSL을 범용 언어로 코드로 변환한다.
- 적합한 언어 : Java, C#, C++

3. DSL의 장점과 단점
3.1 장점
- 반복이 제거되고 비슷한 처리 코드는 자동 생성(템플릿) 된다.
- 프로그래밍 코드의 양이 적고 가독성이 높다.
- 특정 프로그래머(lay programer - martin fowler)들과 커뮤니케이션이 쉽다.
    . XML, CSS, SQL 등

3.2 단점
- 설계가 어렵다.
- 잘 설계가 되지 않는다면 읽기 어려운 코드가 될 수 있다.
- 하위 호환성을 유지해야 한다.

4. 우리 주변에 있는 DSL
- java 
    . ANT, Maven, struts-config.xml, Seasar2 S2DAO, HQL(Hibernate Query Language), JMock
- Ruby
    . Rails Validations, Rails ActiveRecord, Rake, RSpec, Capistrano, Cucumber
- 기타
    . SQL, CSS, Regular Expression(정규식), Make, graphviz


[참조 사이트]
출처 - http://www.mimul.com/pebble/default/2013/06/21/1371806174467.html


'Language > Groovy' 카테고리의 다른 글

Domain-Specific Language  (0) 2016.09.09
Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
:

자바 람다식(Lambda Expressions in Java)

Language/JAVA 2016. 9. 9. 10:24

자바 람다식(Lambda Expressions in Java)

Java2015-07-23 

자바 8에서 람다식(Lambda Expressions)이 추가 되었습니다. 이번 포스팅은 간단하게 람다식에 대해 알아보고자 합니다.

1. Lambda Expressions

람다식을 간단히 정의하면 다음과 같습니다.

식별자 없이 실행 가능한 함수 표현식

요즘은 정의만 보면 잘 모르겠어요. 부연 설명을 조금 해봅시다. 람다식은 자바 8의 가장 특징적인 기능입니다. 또한 기존의 불필요한 코드를 줄이고 가독성을 향상시키는것에 목적을 두고 있습니다.

대표적으로 반복문이나 비교문이 있겠습니다. 어떤식으로 코드를 줄이는지 한번 알아봅시다.

2. Lambda Expressions Example

자바에서 람다식을 사용하려면 다음과 같은 방법으로 사용이 가능합니다.

( parameters ) -> expression body
( parameters ) -> { expression body }
() -> { expression body }
() -> expression body
...

너무 단순한가요? 실제 코드로 확인을 해봅시다.

2.1. Basic

"Hello World."라는 단어를 출력하고 종료하는 쓰레드를 만들어봅시다. 처음으로는 기존에 존재하던 방식으로 작성해보고 다음은 람다식으로 작성해봅니다.

2.1.1. Traditional Code

기존에 있던 방식으로 만든 코드는 다음과 같습니다.

// Thread - traditional
new Thread(new Runnable() {
	@Override
	public void run() {
		System.out.println("Hello World.");
	}
}).start();

쓰레드를 돌리기 위해 Runnable 인터페이스를 새롭게 작성해서 매개변수로 넣었습니다. 이 코드를 람다식으로 바꾸면 어떻게 될까요?

2.1.2. Lambda Expression Code

아래에 있는 코드는 위에 있던 코드를 람다식으로 다시 작성한 것입니다.

// Thread - Lambda Expression
new Thread(()->{
	System.out.println("Hello World.");
}).start();

기존에 있던 코드량이 줄어든 것을 볼 수 있습니다. 인자가 없기 때문에 ()로 작성하고 실제로 동작할 코드를 ->{ ... }의 내부에 작성했습니다. 처음에 적어두었던 () -> { expression body } 구조입니다. 하지만 이것은 꽤나 단순한 예제이고 이것으로 람다식이 끝은 아닙니다.

2.2. Using @FunctionalInterface

객체지향 언어인 자바에서 값이나 객체가 아닌 하나의 함수(Funtion)을 변수에 담아둔다는 것은 이해가 되지 않을 것입니다. 하지만 자바 8에서 람다식이 추가 되고 나서는 하나의 변수에 하나의 함수를 매핑할 수 있습니다.

실제로 다음과 같은 구문을 실행시키고자 한다면 어떻게 해야할까요?

Func add = (int a, int b) -> a + b;

분명히 int형 매개 변수 a,b를 받아 그것을 합치는 것을 람다식으로 표현한것입니다. 그러면 Func는 무엇이어야 할까요?

답은 interface입니다. 위와 같은 람다식을 구현하려면 Func 인터페이스를 아래처럼 작성합니다.

interface Func {
	public int calc(int a, int b);
}

이 인터페이스에서는 하나의 추상 메소드를 가지고 있습니다. 바로 calc라는 메소드입니다. 이 메소드는 int형 매개 변수 2개를 받아 하나의 int형 변수를 반환합니다. 아직 내부 구현은 어떻게 할지 정해지지 않았죠.

이 내부 구현을 람다식으로 만든것이 처음에 보셨던 코드입니다. 아래의 코드죠.

Func add = (int a, int b) -> a + b;

여기까지는 진행에 무리가 없어보입니다. 그러면 혹시 Func 인터페이스에 메소드를 추가하게 되면 어떻게 될까요?

람다식으로 구현했던 add 함수 코드에서 오류가 납니다. 기본적으로 람다식을 위한 인터페이스에서 추상 메소드는 단 하나여야 합니다. 하지만 이러한 사실을 알고 있다 하더라도 람다식으로 사용하는 인터페이스나 그냥 메소드가 하나뿐인 인터페이스나 구별을 하기 힘들뿐더러 혹시라도 누군가 람다식으로 사용하는 인터페이스에 메소드를 추가하더라도 해당 인터페이스에서는 오류가 나지 않습니다.

따라서 이 인터페이스는 람다식을 위한 것이다라는 표현을 위해 어노테이션 @FunctionalInterface을 사용합니다. 실제로 저 어노테이션을 선언하면 해당 인터페이스에 메소드를 두 개 이상 선언하면 유효하지 않다는 오류를 냅니다. 즉, 컴파일러 수준에서 오류를 확인 할 수 있습니다.

다음처럼 Func 인터페이스의 코드가 변경됩니다.

@FunctionalInterface
interface Func {
	public int calc(int a, int b);
}

2.2.1. Various uses

물론 add 함수처럼 내부 구현을 할 수 있지만 조금 더 다양하게 구현을 해봅시다. 다음의 코드는 조금씩 내부 구현을 바꿔본 예제 코드입니다.

Func sub = (int a, int b) -> a - b;
Func add2 = (int a, int b) -> { return a + b; };

뺄셈을 위한 sub 함수도 만들었고 add 함수와 비슷하지만 expression body에 return 키워드를 붙인 add2 함수도 있습니다.

2.2.2. Using Function

그러면 실제로 람다식을 통해 내부를 구현한 함수 add는 어떻게 사용할까요? 다음의 코드를 봅시다.

int result = add.calc(1,2) + add2.calc(3, 4); // 10

위에서 만들어뒀던 add 함수와 add2 함수를 사용했습니다. 따라서 result 변수의 결과값으로 10이라는 것을 알 수 있습니다.

3. Stream API

람다식을 소개하면서 Stream API를 소개 하지 않을 수 없겠지요. 람다식을 추가하면서 같이 추가된 Stream API를 살펴봅시다. 이번 포스팅은 람다식이 주요 내용이었기 때문에 간단하게 사용법만 알아봅시다.

3.1. Get Stream

먼저 Stream API를 사용하려면 stream을 얻어와야 합니다. 얻는 방법은 다음과 같습니다.

Arrays.asList(1,2,3).stream(); // (1)
Arrays.asList(1,2,3).parallelStream(); // (2)

콜렉션 관련 객체라면 stream을 얻어올 수 있습니다. (1)번 방법은 일반적인 stream을 가져오는 것이고 (2)번 방법은 병렬로 stream을 가져옵니다.

3.2. Working Stream

실제로 얻어온 stream에 연산을 해봅시다. 주요하게 쓰이는 몇가지 API만 살펴봅시다.

3.2.1. forEach

stream의 요소를 순회해야 한다면 forEach를 활용할 수 있습니다.

Arrays.asList(1,2,3).stream()
					.forEach(System.out::println); // 1,2,3

3.2.2. map

stream의 개별 요소마다 연산을 할 수 있습니다. 아래의 코드는 리스트에 있는 요소의 제곱 연산을 합니다.

Arrays.asList(1,2,3).stream()
					.map(i -> i*i)
					.forEach(System.out::println); // 1,4,9

3.2.3. limit

stream의 최초 요소부터 선언한 인덱스까지의 요소를 추출해 새로운 stream을 만듭니다.

Arrays.asList(1,2,3).stream()
					.limit(1)
					.forEach(System.out::println); // 1

3.2.4. skip

stream의 최초 요소로부터 선언한 인덱스까지의 요소를 제외하고 새로운 stream을 만듭니다.

Arrays.asList(1,2,3).stream()
					.skip(1)
					.forEach(System.out::println); // 2,3

3.2.5. filter

stream의 요소마다 비교를 하고 비교문을 만족하는 요소로만 구성된 stream을 반환합니다.

Arrays.asList(1,2,3).stream()
					.filter(i-> 2>=i)
					.forEach(System.out::println); // 1,2

3.2.6. flatMap

stream의 내부에 있는 객체들을 연결한 stream을 반환합니다.

Arrays.asList(Arrays.asList(1,2),Arrays.asList(3,4,5),Arrays.asList(6,7,8,9)).stream()
					.flatMap(i -> i.stream())
					.forEach(System.out::println); // 1,2,3,4,5,6,7,8,9

3.2.7. reduce

stream을 단일 요소로 반환합니다.

Arrays.asList(1,2,3).stream()
					.reduce((a,b)-> a-b)
					.get(); // -4

이 코드는 조금 설명이 필요할 것 같습니다. 우선, 첫번째 연산으로 1과 2가 선택되고 계산식은 앞의 값에서 뒤의 값을 빼는 것이기 때문에 결과는 -1이 됩니다. 그리고 이상태에서 -1과 3이 선택되고 계산식에 의해 -1-3이 되기 때문에 결과로 -4가 나옵니다. 뒤로 추가 요소가 있다면 차근차근 앞에서부터 차례대로 계산식에 맞춰 계산하면 됩니다.

3.2.8. collection

아래의 코드들은 각각의 메소드로 콜렉션 객체를 만들어서 반환합니다.

Arrays.asList(1,2,3).stream()
					.collect(Collectors.toList());
Arrays.asList(1,2,3).stream()
					.iterator();


출처 - http://jdm.kr/blog/181

'Language > JAVA' 카테고리의 다른 글

왜 자바에서 static의 사용을 지양해야 하는가?  (6) 2018.02.22
Generic 메서드  (0) 2017.10.20
자바 JVM 정리(Java Virtual Machine)  (0) 2016.08.05
GC  (0) 2016.08.05
Do You Really Get Classloaders?  (0) 2016.08.04
:

Metaprogramming

Language/Groovy 2016. 9. 9. 10:23

http://docs.groovy-lang.org/docs/latest/html/documentation/core-metaprogramming.html#_delegating_metaclass

'Language > Groovy' 카테고리의 다른 글

Domain-Specific Language  (0) 2016.09.09
DSL(Domain Specific Language) 이해하기  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
:

그루비의 동적 객체지향 - 1

Language/Groovy 2016. 9. 9. 10:16

그루비에서 필드 변수의 기본 접근 영역은 특별하게 다루어진다.


1. 필드를 정의할 때 접근 제한자를 지정하지 않으면 이름에 맞는 ‘프로퍼티’가 만들어진다.(geter,seter)



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class SomeClass {
     
    public    public_var='old_public_var_val'  
    String    string_var='old_string_var_val'
    def       def_var   ='old_def_var_val'//필드를 정의할 때 접근 제한자를 지정하지 않으면 이름에 맞는 ‘프로퍼티’가 만들어진다.(geter,seter)
    static    static_var='old_static_var_val'
    protected protected_var1, protected_var2, protected_var3
    private   assignedField = new Date()
    public static final String CONSTA = 'a', CONSTB = 'b'
     
 
}
    SomeClass somclass = new SomeClass();
    println 'b_somclass.public_var : '+somclass.public_var;
    println 'b_somclass.string_var : '+somclass.string_var;
    println 'b_somclass.def_var : '+somclass.def_var;
     
    println '';
    somclass.public_var = 'new_public_var_val';
    somclass.string_var = 'new_string_var_val';
    somclass.def_var = 'new_def_var_val';
    println '';
     
     
    println 'a_somclass.public_var : '+somclass.public_var;
    println 'a_somclass.string_var : '+somclass.string_var;
    println 'a_somclass.def_var : '+somclass.def_var;
     
     
    println '';
    println SomeClass.class.methods.name.grep(~/[get].*/);//get으로 시작하는 메서드이름가져와보자
    println SomeClass.class.methods.name.grep(~/[set].*/);//set으로 시작하는 메서드이름가져와보자
        
    /* 결과
        b_somclass.public_var : old_public_var_val
        b_somclass.string_var : old_string_var_val
        b_somclass.def_var : old_def_var_val
         
         
        a_somclass.public_var : new_public_var_val
        a_somclass.string_var : new_string_var_val
        a_somclass.def_var : new_def_var_val
         
        [getMetaClass, this$dist$invoke$2, this$dist$set$2, this$dist$get$2, getString_var, getDef_var, getStatic_var, getProperty, equals, toString, getClass]
        [setMetaClass, this$dist$invoke$2, this$dist$set$2, this$dist$get$2, super$1$wait, super$1$wait, super$1$wait, super$1$toString, super$1$notify, super$1$notifyAll, super$1$getClass, super$1$equals, super$1$clone, super$1$hashCode, super$1$finalize, setString_var, setDef_var, setStatic_var, setProperty, equals, toString]
    */


위결과를 보면 자동으로 생성된 프로퍼티는 getString_var, getDef_var, getStatic_var setString_var, setDef_var, setStatic_var 가생성된걸 볼수있다. 




2. 그루비는 자바의 접근제한자를 쓸수있다?


1
2
3
4
5
6
7
8
package _2.sub
 
class GClass {
    private private_var='private_var';
    public public_var='public_var';
    protected protected_var='protected_var';
    def def_var ='def_var';
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package _2
 
import _2.sub.GClass;
GClass g = new GClass();
println "${g.private_var.class}    :    ${g.private_var}";
println "${g.public_var.class}    :    ${g.public_var}"; //자바에서는 이것만 허용이 될것이다.
println "${g.protected_var.class}    :    ${g.protected_var}";
println "${g.def_var.class}    :    ${g.def_var}";
/*
결과
class java.lang.String    :    private_var
class java.lang.String    :    public_var
class java.lang.String    :    protected_var
class java.lang.String    :    def_var
*/


접근제한자를 썼지만 자유롭게 접근이 가능하다. 


3. 배열 첨자 연사자로 필드 변수 사용하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package _3
 
class SomeClass {
     
    public    public_var='old_public_var_val'  
    String    string_var='old_string_var_val'
    def       def_var   ='old_def_var_val'//필드를 정의할 때 접근 제한자를 지정하지 않으면 이름에 맞는 ‘프로퍼티’가 만들어진다.(geter,seter)
    static    static_var='old_static_var_val'
    protected protected_var1, protected_var2, protected_var3
    private   assignedField = new Date()
    public static final String CONSTA = 'a', CONSTB = 'b'
     
 
}
    SomeClass somclass = new SomeClass();
    println 'b_somclass.public_var : '+somclass['public_var'];
    println 'b_somclass.string_var : '+somclass['string_var'];
    println 'b_somclass.def_var : '+somclass['def_var'];
     
    println '';
    somclass['public_var'] = 'new_public_var_val';
    somclass['string_var'] = 'new_string_var_val';
    somclass['def_var'] = 'new_def_var_val';
    println '';
     
     
    println 'a_somclass.public_var : '+somclass['public_var'];
    println 'a_somclass.string_var : '+somclass['string_var'];
    println 'a_somclass.def_var : '+somclass['def_var'];
     
     
    println '';
 
    /* 결과
    b_somclass.public_var : old_public_var_val
    b_somclass.string_var : old_string_var_val
    b_somclass.def_var : old_def_var_val
     
     
    a_somclass.public_var : new_public_var_val
    a_somclass.string_var : new_string_var_val
    a_somclass.def_var : new_def_var_val
    */




4. 필드 접근 메커니즘 확장하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package _4
 
class SomeClass {
     
    public    public_var='old_public_var_val'  
    String    string_var='old_string_var_val'
    def       def_var   ='old_def_var_val'//필드를 정의할 때 접근 제한자를 지정하지 않으면 이름에 맞는 ‘프로퍼티’가 만들어진다.(geter,seter)
    static    static_var='old_static_var_val'
    protected protected_var1, protected_var2, protected_var3
    private   assignedField = new Date()
    public static final String CONSTA = 'a', CONSTB = 'b'
     
    def notVar_GetCallCount=0;
    def notVar_SetCallCount=0;
     
    Object get (String name) {
        notVar_GetCallCount++;
        println "get : ${name}";
        return 'pretend value'
    }
    void set (String name, Object value) {
        notVar_SetCallCount++
        println "set  name : ${name},   value : ${value}";
    }
 
}
    SomeClass somclass = new SomeClass();
    println 'b_somclass.public_var : '+somclass.public_var;
    println 'b_somclass.string_var : '+somclass.string_var;
    println 'b_somclass.def_var : '+somclass.def_var;
    println 'b_somclass.not_var: '+somclass.not_var;
     
    println '';
    somclass.public_var = 'new_public_var_val';
    somclass.string_var = 'new_string_var_val';
    somclass.def_var = 'new_def_var_val';
    somclass.not_var = 'new_not_var_val';
    println '';
     
     
    println 'a_somclass.public_var : '+somclass.public_var;
    println 'a_somclass.string_var : '+somclass.string_var;
    println 'a_somclass.def_var : '+somclass.def_var;
    println 'a_somclass.not_var : '+somclass.not_var;
     
     
    println '';
    println 'somclass.notVar_GetCallCount   :   ' + somclass.notVar_GetCallCount;
    println 'somclass.notVar_SetCallCount   :   ' + somclass.notVar_SetCallCount;
        
    /* 결과
        b_somclass.public_var : old_public_var_val
        b_somclass.string_var : old_string_var_val
        b_somclass.def_var : old_def_var_val
        get : not_var
        b_somclass.not_var: pretend value
         
        set  name : not_var,   value : new_not_var_val
         
        a_somclass.public_var : new_public_var_val
        a_somclass.string_var : new_string_var_val
        a_somclass.def_var : new_def_var_val
        get : not_var
        a_somclass.not_var : pretend value
         
        somclass.notVar_GetCallCount   :   2
        somclass.notVar_SetCallCount   :   1
 
    */


여기서 중요하게 볼것이 존재하지 않는 프로퍼티를 접근할 때 get 이라는 메서드를 통하여 접근한다 
존재하지 않는 프로퍼티에 변수값을 지정할 때 set(‘name’,Object)로 접근한다는것도 잊지말어라. 


메서드와인자


자바의 접근제한자를 쓸수 있으며 리턴형은 생략가능하다. 접근제한자나 리턴형을 지정하지 않을 때는 키워드 def를 사용한다. 
def를 쓰면 리턴값의 자료형이 지정되지 않았다고 생각할수 있다 (물론, 리턴이 없는 void메서드일수도 있다) 
내부적으로 java.lang.Object가 리턴된다. 접근제한자는 def로 선언시 public선언된다. 

5.메서드정의하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package _5
 
class SomeClass {
    static void main (args){                        //#1 자동으로 public 접근제한자 없으므로  자바 메인메서드가 된다.
    println "call main"
        def some = new SomeClass()
        some.publicVoidMethod()
        assert 'hi' == some.publicUntypedMethod()//true
        assert 'ho' == some.publicTypedMethod()//true
          combinedMethod()                            //#2 현재 클래스의 정적 메서드 호출
    }
    static void main2 (args){
        args+'before';
        args+'after';;
    }
    static main3 (args){
        args+'before';
        args+'after';;
    }
    void publicVoidMethod(){
    }
    def publicUntypedMethod(){
        return 'hi'   
    }
    String publicTypedMethod() {       
        return 'ho'      
    }
    protected static final void combinedMethod(){
    }   
}
//println '-----';  //이부분 주석을 풀게되면  이클래시는 Script를 상속받게된다 따라서 run메서드를 실행시키게된다.
/* 결과
call main
*/



main 메서드에 흥므로운 것이 있다 
첫째는 : public디폴트이기 때문에 생략 
둘째는 : 실행할 수 있는 클래스의 메인메서드는 인자가 String[] 이어야 한다. 여기서 arg가 암묵적으로Object 가 되는데도 그루비의 메서드 디스패치 덕분에 메인 메서드로 동작한다. 
여기서 void도 생략가능하다 메인 메서드로 동작한다.static main(args) 





6.인자리스트 정의 인자에 따른 메서드 호출.다르게


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package _6
 
class SomeClass {   
    static void main (args){ 
        assert 'untyped' == method(1)                  
        assert 'typed'   == method('whatever')       
        assert 'two args'== method(1,2)
        println  method(1)                
        println  method('whatever')       
        println  method(1,2)              
        println  method(1,2,3)              
    }   
    static method(arg) {       
        return 'untyped'      
    }
    static method(String arg){    
        return 'typed' 
    }
    static method(arg1, Number arg2){    
        return 'two args' 
    }
     def method(arg1,arg2,arg3){    
        return 'boolean args' 
    }
}
/*
결과
untyped
typed
two args
Caught: groovy.lang.MissingMethodException: No signature of method: static _6.SomeClass.method() is applicable for argument types: (java.lang.Integer, java.lang.Integer, java.lang.Integer) values: [1, 2, 3]
Possible solutions: method(java.lang.Object), method(java.lang.String), method(java.lang.Object, java.lang.Number), method(java.lang.Object, java.lang.Object, java.lang.Object)
    at _6.SomeClass.main(_6.groovy:11)
 
*/


오버로딩 같은개념 
여기서 맨마지막 method(1,2,3) 호출이 오류난건 자바에서처럼 메인메서드에서 호출할려는 메서드는 static이여야한다 그걸 위반했다. 

7.고급인자 사용 기술


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package _7
 
import java.util.List;
import java.util.Map;
 
class Summer {
    def sumWithDefaults(a, b, c=0){                         //#1
        return a + b + c
    }
    def sumWithList(List args){                             //#2
        return args.inject(0){sum,i -> sum += i}//0초기값으로주고(누적값sum) list인자만큼 돈다.(인자값:i)
    }   
    def sumWithOptionals(a, b, Object[] optionals){         //#3
        return a + b + sumWithList(optionals.toList())
    }
    def sumNamed(Map args){                                 //#4
        ['a','b','c'].each{args.get(it,0)}      //초기값부여
        return args.a + args.b + args.c
    }
    def sumNamed(Map args,Map args1,Map args2){                                 //#4
        return 3;
    }
}
 
def summer = new Summer()
 
//#1호출
assert 2 == summer.sumWithDefaults(1,1)    
assert 3 == summer.sumWithDefaults(1,1,1)  
println  'summer.sumWithDefaults(1,1)       :'+summer.sumWithDefaults(1,1)     
println  'summer.sumWithDefaults(1,1,1)     :'+summer.sumWithDefaults(1,1,1)
 
//#2호출
assert 2 == summer.sumWithList([1,1])      
assert 3 == summer.sumWithList([1,1,1])    
println 'summer.sumWithList([1,1])      : '+summer.sumWithList([1,1])  
println 'summer.sumWithList([1,1,1])    : '+summer.sumWithList([1,1,1])
 
 
//#3호출
assert 2 == summer.sumWithOptionals(1,1)
assert 3 == summer.sumWithOptionals(1,1,1)
assert 6 == summer.sumWithOptionals(1,1,1,1,1,1)
println    'summer.sumWithOptionals(1,1)          :'+summer.sumWithOptionals(1,1)         
println    'summer.sumWithOptionals(1,1,1)        :'+summer.sumWithOptionals(1,1,1)       
println    'summer.sumWithOptionals(1,1,1,1,1,1)  :'+summer.sumWithOptionals(1,1,1,1,1,1) 
 
 
//map호출
assert 2 == summer.sumNamed(a:1, b:1)      
assert 3 == summer.sumNamed(a:1, b:1, c:1)
assert 1 == summer.sumNamed(c:1)
assert 3 == summer.sumNamed([a:1, b:1, c:1])
assert 3 == summer.sumNamed([a:1, b:1, c:1],[a:1, b:1, c:1],[a:1, b:1, c:1])
println 'summer.sumNamed(a:1, b:1)                                       :'+summer.sumNamed(a:1, b:1)                                       
println 'summer.sumNamed(a:1, b:1, c:1)                                  :'+summer.sumNamed(a:1, b:1, c:1)                                   
println 'summer.sumNamed(c:1)                                            :'+summer.sumNamed(c:1)                                             
println 'summer.sumNamed([a:1, b:1, c:1])                                :'+summer.sumNamed([a:1, b:1, c:1])                                 
println 'summer.sumNamed([a:1, b:1, c:1],[a:1, b:1, c:1],[a:1, b:1, c:1]):'+summer.sumNamed([a:1, b:1, c:1],[a:1, b:1, c:1],[a:1, b:1, c:1]) 
 
/*
결과
summer.sumWithDefaults(1,1)     :2
summer.sumWithDefaults(1,1,1)     :3
summer.sumWithList([1,1])       : 2
summer.sumWithList([1,1,1]) : 3
summer.sumWithOptionals(1,1)          :2
summer.sumWithOptionals(1,1,1)        :3
summer.sumWithOptionals(1,1,1,1,1,1)  :6
summer.sumNamed(a:1, b:1)                                        :2
summer.sumNamed(a:1, b:1, c:1)                                  :3
summer.sumNamed(c:1)                                            :1
summer.sumNamed([a:1, b:1, c:1])                                :3
summer.sumNamed([a:1, b:1, c:1],[a:1, b:1, c:1],[a:1, b:1, c:1]):3
 
*/



여기서 눈여겨볼것이… 3가지있는데. 
1. 디폴트값 
def sumWithDefaults(a, b, c=0){} 
2. 파라미터 집합형자료형 ,로 바로넣기(동적 메시지 디스패처가 넘치는 인자들을 배열에 담아서 전달) 
def sumWithOptionals(a, b, Object[] optional){} 
assert 6 == summer.sumWithOptionals(1,1,1,1,1,1) 
3. 집합자료형 , 바로넣기 2 (동적 메시지 디스패처가 넘치는 인자들을 배열에 담아서 전달) 
def sumNamed(Map args){} 
sumNamed(Map args,Map args1,Map args2){} 
assert 3 == summer.sumNamed(a:1, b:1, c:1) 
assert 3 == summer.sumNamed([a:1, b:1, c:1]) 
assert 3 == summer.sumNamed([a:1, b:1, c:1], [a:1, b:1, c:1], [a:1, b:1, c:1]) 

고급명명기법
1
2
3
4
5
6
class g{
    def g=55;
}
Map args = [ a:1,b:2,c:3,d:4]
println args.'size'();
println new g().'g';

역시 여기서도 스크립트의 파워풀한기능을 볼수있다. 




8.안전한 참조연산자


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package _8
 
def map = [a:[b:[c:1]]]          
 
assert map.a.b.c == 1       //일반적인접근
 
if (map && map.a && map.a.x){           //#1        평가단축기법   
    assert false;
    assert map.a.x.c == null
}   
             
//널포인트 발생에따른 예외처리를 해줘야한다.. 일반적인처리..  a안에는x가 없으니..
try {
    assert map.a.x.c == null
} catch (NullPointerException npe){     //#2
    println "NullPointerException : ${npe}"
}
  
//안전하게 참조하는  ? 연산자를 제공한다 이연산자는 앞에 있는 참조 변수가 null이면 현재 해석중인 표현식을 중지하고 null리턴한다.
assert map?.a?.x?.c == null             //#3
println 'map?.a?.x?.c '+map?.a?.x?.c
/*
결과
NullPointerException : java.lang.NullPointerException: Cannot get property 'c' on null object
map?.a?.x?.c null
*/


괜찮은 기능이기긴 하지만 저 안전한 참조연산자를 쓸정도로 애매모호한 구조를 설계하지말아야 될것같은 느낌이다. 


생성자들


생성자는 세가지 방법으로 호출할수 있다 
1. 자바에서 하던방식 
2. 키워드 as, asType를 이용한 강제형변환 
3. 암묵적 형변환 방식 

9.위치기반 인자를 이용한 생성자


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package _9
 
import java.util.Map;
 
class VendorWithCtor {                     
    String name, product
    VendorWithCtor(){
        println "디폴트 생성자"
    }
     
    VendorWithCtor(Map map){
        println "Map 파라미터1개 생성자호출 : ${map},  ${map.class.name}"
        this.name = name;
    }
    VendorWithCtor(name){
        println "파라미터1개 생성자호출 : ${name},  ${name.class.name}"
        this.name = name;
    }
    VendorWithCtor(name, product) {             //#1
        println "파라미터2개 생성자호출  : ${name}, ${product}"
        this.name    = name
        this.product = product
    }
}
 
def first = new VendorWithCtor('Canoo','ULC_일반적인 생성')   //#2 일반적은 생성자호출
def first2 = new VendorWithCtor()   //#2 일반적은 생성자호출
 
def second = ['Canoo','ULC_강제 형변환'] as VendorWithCtor  //#3 강제 형변환
//def second = ['Canoo','ULC'].asType(VendorWithCtor)  //#3 강제 형변환
def second2 = [] as VendorWithCtor  //#3 강제 형변환
Map map = [a:1,b:2,c:3];
def second3 = map as VendorWithCtor  //#3 강제 형변환 이건안된다..디폴트가탄다.. Map이있지만도..
 
VendorWithCtor third = ['Canoo','ULC_암묵적 형변환']          //#4 암묵적 형변환
VendorWithCtor third2 = ['Canoo 암묵적 형변환']          //#4 암묵적 형변환
 
 
/*
결과
파라미터2개 생성자호출  : Canoo, ULC_일반적인 생성
디폴트 생성자
파라미터1개 생성자호출 : [Canoo, ULC_강제 형변환],  java.util.ArrayList
파라미터1개 생성자호출 : [],  java.util.ArrayList
디폴트 생성자
파라미터2개 생성자호출  : Canoo, ULC_암묵적 형변환
파라미터1개 생성자호출 : Canoo 암묵적 형변환,  java.lang.String
*/


역시 자바와다르게 형변환할때도 생성자 호출이된다… 하지만 저 해쉬맵은 왜안될까… 
역시 자바와다르게 스크립트기반언어기 때문에 개발시 부분부분 테스트가 더욱더 세밀하게 이루워져야할것같다. 




이름기반 이자로 생성자 호출하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package _10
 
class Vendor {                     
    //String name, product
    def name, product
    //private String name, product  //private 해도 적용됨
}
 
new Vendor()                           
new Vendor(name:   'Canoo')             
new Vendor(product:'ULC')               
new Vendor(name:   'Canoo', product:'ULC')            
new Vendor(name:   'Canoo', product:1)         //자동케스팅   
//new Vendor(name:   'Canoo', product2:1) //오류남            
 
def vendor = new Vendor(name: 'Canoo')
assert 'Canoo' == vendor.name
println vendor.name;
 
 
//암묵적 생성
java.awt.Dimension area;
area = [20,100];
assert area.width ==20;
assert area.height == 100;
println    'area.width :    '+area.width ;  
println    'area.height : '+area.height ;
 
/*
결과
Canoo
area.width :    20.0
area.height : 100.0
 
*/


위처럼 사용할수도있다.. 하지만 위 같은 상황을 쓸일이 많이 생길지 의문이다.. 

클래스와 스크립트 구성하기



그루비 클래스 생성을 할수 있다. 

1. 그루비 파일에 클래스 정의가 ‘하나도 없다면’ 그파일은 스크립트로 동작한다. 
다시말해 자동으로 Script 클래스를 상속받는다. 
자동으로 생성된 클래스의 이름은 확장자를 뺀 소스 파일의 이름과 같다. 
파일의 코드는run메서드에 들어가고, 실행할 수 있겠끔 main 메서드도 만들어진다. 
2. 그루비 파일에 클래스가 ‘하나’ 만 있고, 그 클래스 이름이 확장자를 제외한 소스 파일의 이름과 같다면 자바와 같은 일대일 대응 관계가 된다. 
3. 그루비는 그루비 파일에 클래스가 ‘몇개’ 정의되어있든 그것들이 public이든 private이든 소스파일의 이름과 같은 클래스가 있던 없던 다수용할수 있다. 그루비 컴파일러인 groovyc는 파일에 정의된 클래스 각각에 대해 *.class 파일을 생성한다. 이파일을 프롬프트에서 goovy를 통해서 실행하거나 IDE에서 호출하는 식으로 스크립트로서 사용할 때는 파일에서 첫번째로 정의된 클래스에 main 메서드가 있어야한다. 
4. 그루비 파일에 클래스 정의와 스크립트 코드를 섞어서 쓸수도 있다 이렇게 하면 실행시에 주 클래스는 스크립트 코드가 된다 따로 소스 파일이 이름과 같은 이름으로 클래스를 만들 필요는 없다. 

명시적으로 컴파일 하지 않았다면 그루비에서 클래스를 찾을 때는 클래스 이름 과 같은 *.groovy소스 파일을 검색한다. 이때 이름이 중요하다 그루비는 찾고 있는 클래스와 이름이 같은 파일만 검색한다 , 파일을 발견하면 그파일 안에 모든 클래스를 분석해서 로딩한다. 



패키지로 구조화하기


*.groovy소스 파일들은 *.class파일로 컴파일하지 않아도 쓸수있기때문에 클래스를 찾을때 *.groovy 파일도 함께 찾는다. 이때도 같은 방식으로 검색한다 즉 그루비는 business/Vendor.groovy파일에서 business 패키지에 속한 Vendor 클래스를 검색할것이다. 

클래스패스


그루비가 *.groovy 파일을 찾을때 클래스 패스를 사용한다. 
주어진 클래스를 찾다가 *.class 와 *.groovy 모두 발견됐다면 둘중 최근에 변경된 파일을 사용한다 즉 *.groovy 가 *.class보다 최근에 변경됐다면 *.groovy 컴파일한후 *.class를 사용한다. 

패키지예제 

1
2
3
4
5
6
7
8
9
10
11
12
package business
 
class Vendor {
    public String  name
    public String  product
    public Address address = new Address()
}
 
class Address  {
    public String  street, town, state
    public int     zip
}


임포트예제 

1
2
3
4
5
6
7
import business.*
 
def canoo = new Vendor()
canoo.name          = 'Canoo Engineering AG'
canoo.product       = 'UltraLightClient (ULC)'
 
assert canoo.dump() =~ /ULC/


몇몇 스크립트 언어와 달리 임포트를 해도 클래스나 파일을 실제로 포함하는것이 아니다 다만 클래스 이름을 해석할 때 참고할 정보를 주는것뿐이다. 

그루비는 패키지 6개와 클래스2개를 자동 임포트한다. 
java.lang.* 
java.util.* 
java.io.* 
java.net.* 
groovy.lang.* 
groovy.util.* 
java.math.BigInteger 
java.mat.BigDecimal 


14.클래스 별칭


as 를통하여 클래스 별칭(type aliasing)을 만들수 있다. 
이것은 클래스 이름 충돌을 해결하거나 지역적인 수정 혹은 써드파트 라이브러리 버그를 수정하는데 사용한다. 

1
2
3
4
5
6
7
package _14.oldpack
 
public class _14_A {
    public def calc(def a, def b){
        return a+b;
    }
}


1
2
3
4
5
6
7
8
9
10
11
package _14
 
import _14.oldpack._14_A as GO;
 
class A_14_A extends GO {
    public def calc(def a, def b){
        return a*b;
    }
}
def  f =  new A_14_A();
println f.calc(4,4);




클래스 패스에 관련된 추가 사항


그루비가 *.class 파일과 *.groovy 파일에서 클래스를 찾아낸다는 점은 그루비를 다를때 이해하고 있어야 하는 중요한 부분이다. 안타깝게도 여기서 문제가 종종 발생하는데... 

그루비의 클래스패스는 %GROOVY_HOME%\conf 디렉터리의 특별한 설정파일이있다. 
groovy-starter.conf 

맨마지막줄 #지워서 활성화시키면 좋은 기능이 살아난다. 
user.home으로 상징되는 사용자의 홈 디렉터리에서 서브디렉터리로 .groovy/lib를 만들고 *.class나 *.jar 파일 넣어두면 그루비가 쓸때마다 로딩되도록한다. 
user.home찾기힘들다면 
groovy –e “println System.properties.’user.home’” 

셀제목셀제목셀제목
구분정의목적과 사용법
JDK/JRE%JAVA_HOME%/lib,%JAVA_HOME%/lib/extJRE부트 클래스패스와 확장 라이브러리들
OS설정CLASSPATH변수일반적인 기본설정
커맨드라인CLASSPATH 변수특수설정
java-cp,--classpath,option실행시 설정
Groovy%GROOVY_HOME%/lib그루비 실행환경
Groovy-cp그루비 실행시 설정
Groovy.현재 디렉터리 클래스패스로 하는 디폴트 클래스 패스


고급 객체지향 기능


1. 상속하기 
그루비에서는 그루비와 자바의 클래스나 인터페이스를 상속받아서 확장할 수 있다. 자바 쪽에서도 그루비 클래스나 인터페이스를 상속받을수 있다. 
2. 인터페이스 
자바의 인터페이스를 완벽하게 지원한다. 
자바의 추상 메서드도 지원한다. 
그루비는 더 동적으로 인터페이스를 사용할 수 있는 기능을 제공한다. 메서드가 하나만 있는 인터페이스인 MyInterface와 클로저 myClosure가 있다면 이 myClosure를 키워드 as 이용하여 MyInterface로 강제 형변환할수도있다 

16.멀티메서드


자바에 메서드를 호출하면 명시한 자료형을 참조해서 메서드를 찾는다 그에 반해 그루비에서는 인자의 동적 자료형을 고려해서 적절한 메서드를 찾아낸다 그루비의 이런 기능을 멀티메서드(multimethod)라고한다 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package _16
 
def oracle(Object o) { return 'object' }
def oracle(String o) { return 'string' }
 
Object x = 1    
Object y = 'foo'
 
assert 'object' == oracle(x)
assert 'string' == oracle(y) //#1 자바라면 ‘object’를 호출할것이다.
println oracle(x);
println oracle(y);
println oracle(y as Object);
/* 결과
object
string
object
*/


인자x는 Object로 표시됐지만 동적으로는 Integer이다. 
인자y는 Object로 표시됐지만 동적으로는 String이다. 
명시적으로 가도록 선언하고 싶다면 명시적으로 형변환을 해주면된다. 
그루비에서는 인자의 자료형을 동적으로 검사한다. 

17.equals를 선택적으로 재정의하는 메서드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package _17
 
class Equalizer {
    boolean equals(Equalizer e){ //이건 equals의 오버라이딩 된것이 아니라.  오버로딩된것이다.
        println 'equals Equalizer : '+e.class
        return true
    }
//  boolean equals(e){ //이건 equals의 오버라이딩 된것이 아니라.  오버로딩된것이다.
//      println 'equals Object : '+e.class
//      return true
//  }
}
 
Object same  = new Equalizer()         
Object other = new Object()
//여기 객체를 Object로 받았다.
 
assert   new Equalizer().equals( same  ) //Equalizer에서 정의한 equals로 간다. //그루비는 자동으로 클레스형대로 찾아서간다
assert ! new Equalizer().equals( other ) // Object의 equals 메서드를 호출한다. //그루비는 자동으로 클레스형대로 찾아서간다
/*결과
equals Equalizer : class _17.Equalizer
*/



뭐가 다른지 자바를 보자 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import GG;
import java.util.Date;
 
 
class GG{
     
    public boolean equals(String obj) {
        System.out.println("equals String");
        return true;
    }
}
 
public class TestJava {
    public static void main(String[] args) {
        GG g = new GG();
         
        Object s = new String("---");
        Object d = new Date(0);
         
        System.out.println( g.equals(s) );
        System.out.println( g.equals(d) );
         
         
        System.out.println( g.equals((String)s) );
    }
}
/*결과
false
false
equals String
true
 */





18.그루비빈 사용하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package _18
 
import java.io.Serializable
 
class MyBean implements Serializable {
    def untyped
    String typed
    def item1, item2
    def assigned = 'default value'
    //접근제한자가 붙은건 자동프로퍼티가 생기지 않는다.
}
 
def bean = new MyBean()
assert 'default value' == bean.getAssigned()
println bean.getAssigned();
 
bean.setUntyped('some value')
assert 'some value' == bean.getUntyped()
println bean.getUntyped();
 
bean = new MyBean(typed:'another value',untyped:'untyped---',item1:'item1---')
assert 'another value' == bean.getTyped()
assert 'untyped---' == bean.getUntyped()
assert 'item1---' == bean.getItem1()
println bean.getTyped();
println bean.getUntyped();
println bean.getItem1();
 
/*
결과
default value
some value
another value
untyped---
item1---
*/


여기서 중요하게 볼것이 생성할때 프로퍼티를 통하여 초기값을 줄수 있다는것이다. 


읽기전용 프로퍼티
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package _18
import java.io.Serializable
 
class MyBean implements Serializable {
    final def untyped='untyped'
}
 
def bean = new MyBean()
assert 'untyped' == bean.getUntyped()
println bean['untyped']
bean['untyped']='tttttttttt'; //error
bean.setUntyped('----------untyped');//error
 
//프로퍼티 변수에 final을 붙이면 읽기전용이 된다




프로퍼티 name에 접근하는 방법 비교 

자바그루비
getName()name
setName('a')name='a'

19.getter, setter 메서드 만들기만해도 그루비 프로퍼티 접근형식으로 접근가능


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package _19
 
class MrBean {
    String firstname, lastname              //#1
     
    String getName(){                       //#2 가상 프로퍼티의 생성자 가상의name이생긴거다/
        return "$firstname $lastname"
    }
}
 
def bean = new MrBean(firstname: 'Rowan')   //#3
bean.lastname = 'Atkinson'                  //#4
 
assert 'Rowan Atkinson' == bean.name        //#5 자동으로 생긴name 읽고 쓰고한다.
println bean.name ;
/*결과
Rowan Atkinson
*/


위에서 보는것처럼 프로퍼티접근하는거에대한 일관성이 유지된다. 오우.~ 




20.프로퍼티 함수를 통하여 접근하지 않고! @기호로 필드 변수에 바로 접근하기.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package _20
 
class DoublerBean {
    public value                           //#1
     
    void setValue(value){
        this.value = value                 //#2
    }
     
    def getValue(){                       
        value * 2                          //#3
    }
}
 
def bean = new DoublerBean(value: 100)    
 
assert 200 == bean.value                   //#4
assert 100 == bean.@value                  //#5
 
println 'bean.value;    :  '+bean.value;
println 'bean.@value;   :  '+bean.@value;
/* 결과
bean.value;    :  200
bean.@value;   :  100
*/


필드 변수와 같은 영역에서는 fieldname 이나this.fieldname을 필드 변수에 대한 접근으로 해석하며 프로퍼티에 대한 접근으로 보지 안흔ㄴ다 영역 밖에서는 referenc.@fieldname문법을 이용해야 동일한 효과를 얻는다. 
주의할것이 하나있는데 정적인 영역 (static context)에서 @를 사용하거나 def x = this;x.@fieldname 같은식으로 사용하면 이상한 현상이 발생한다. 권장하지 않는다. 


자바빈 스타일 이벤트처리


그루비는 빈의 내부를 검사(bean introspection) 해서 할당문의 필드 변수가 리스너 등록 메서드 인지 확인한다 등록 메서드로 판단되면 ClosureListener 클래스가 자동으로 붙고 이벤트가 발생하면 클로저를 호출해준다. 

그루비 

1
2
3
4
def button = new JButton('push');
button.actionPerformed ={event->
    println button.text;
}



자바 

1
2
3
4
5
6
final JButton button = new JButton("push");
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        System.out.println(button.getText());
    }
});





필드, 접근자, 맵, Expando


그루비 코드에는 object.name같은 표현이 자주 눈에 띈다 그루비가 이런 표현식을 만났을 때 하는 일을 나열해 보면 다음과같다. 

1. object가맵이면 object.name은 맵의 키에서 name을 찾아 대응하는 값을 가르킨다. 

1
2
def map = [a:1,b:2];
println map.a;



2. name이 object의 프로퍼티일 때는 프로퍼티를 찾아서 가리킨다 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class gogo{
    def oneProperty='oneProperty_value'
    def towProperty='towProperty_value'
    public def getOneProperty() {
        println 'call getOneProperty'
        return oneProperty;
    }
}
def g = new gogo();
println g.oneProperty;
/*결과
call getOneProperty
oneProperty_value
*/



3. 모든 그루비 객체는 자신만의 getProperty(name) 과 setProperty(name,value)메서드를 정의할수 있다. 맵(map)객체도 이방법으로 키를 프로퍼티처럼 제공한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
class gogo{
    def oneProperty='oneProperty_value'
    def towProperty='towProperty_value'
    public def getOneProperty() {
        println 'call getOneProperty'
        return oneProperty;
    }
}
def g = new gogo();
println g.getProperty('towProperty');
/*결과
towProperty_value
*/




4. object.get(name)메서드를 만들면 필드 변수 접근을 가로챌수 있다 이부분은 그루비 실행환경의 최전방에 속한다 자바빈에 적당한 프로퍼티도 없고 getProperty메서드도 구현되지 않았을때 사용된다. 
그리고 당연히 name에 특수 문자가 있어서 식별자(identifier)로 사용할수 없을 때도 문자열로 만들면 사용할수 있다 즉 object.’my-name’과같이 만들면된다 또 GString 이용할수도있다. def name = ‘my-name’;Object.”$name” 처럼 써도된다 Object에는 getAt이라는 메서드가 있어서 object[name] 형식으로 프로퍼티에 접근하도록 위임할수 있다. 

21.프러퍼티 접근 및 get,set메서드 활용 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package _21
 
class Go{
    def g;
    public void setG(def g) {
        println 'setG '+g;
        this.g = g;
    }
     
     
    public void set(def name,def value) {
        println 'No such property Name -> Set Name:'+name+', value:'+value;
    }
    public def get(String name) {
        return 'No such property Name -> '+name;
    }
     
}
  
def g = new Go([g:'1']);
println 'g.g;  : ' + g.g; 
println 'g.gg; : ' + g.gg;
 
println '';
g.g='g_value'
g.gg='gg_value';
println '';
 
println 'g.g;  : ' + g.getAt("g"); 
println 'g.gg; : ' + g.getAt("gg");
 
 
 
/*결과
setG 1
g.g;  : 1
g.gg; : No such property Name -> gg
 
setG g_value
No such property Name -> Set Name:gg, value:gg_value
 
g.g;  : g_value
g.gg; : No such property Name -> gg
*/



여기서 주의할것은 get,set 메서드는 프로퍼티가 없을경우에만 탄다는것이다. 
프로퍼티가 있는것은 자신의 접근 메서드로 바로 호출된다.
 



그루비 특장점 활용하자


그루비의 강력한 세가지 기능을 설명한다 
1. GPath 
2. 확산연산자(spread operator 
3. use 


22.GPath는 객체들의 구조를 탐색하는 강력한 도구이다. XPath이름을 따서 왔다나..?


GPath예제 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package _22
 
//현재 객체를 문자열로 표현한것이다
println 'this : '+this;
 
//이객체의 클래스가 무엇인지 알려면 this.getClass라고하면되지만 그루비에서는 다음처럼 가능하다.
println 'this.class   : '+this.class;
 
//클래스 객체는 getMethods메서드를 통해서 메서드들의 리스트를 제공한다
println 'this.class.methods   :  '+this.class.methods;
 
//모든 메서드에는 getName메서드가있으니 이렇게 입력해보자
println 'this.class.methods.name   : '+this.class.methods.name;
 
//get으로 시작하는 메서드이름가져와보자
println 'this.class.methods.name.grep(~/get.*/) : '+this.class.methods.name.grep(~/get.*/);
 
/*결과
this : _22._22@1766806
this.class   : class _22._22
this.class.methods   :  [public void _22._22.super$1$wait(), public void _22._22.super$1$wait(long,int), public void _22._22.super$1$wait(long), public java.lang.String _22._22.super$1$toString(), public void _22._22.super$1$notify(), public void _22._22.super$1$notifyAll(), public java.lang.Class _22._22.super$1$getClass(), public boolean _22._22.super$1$equals(java.lang.Object), public java.lang.Object _22._22.super$1$clone(), public int _22._22.super$1$hashCode(), public void _22._22.super$1$finalize(), public java.lang.Object _22._22.this$dist$invoke$4(java.lang.String,java.lang.Object), public void _22._22.this$dist$set$4(java.lang.String,java.lang.Object), public java.lang.Object _22._22.this$dist$get$4(java.lang.String), public java.lang.Object _22._22.super$3$getProperty(java.lang.String), public void _22._22.super$3$setProperty(java.lang.String,java.lang.Object), public void _22._22.super$3$println(), public void _22._22.super$3$println(java.lang.Object), public void _22._22.super$3$print(java.lang.Object), public void _22._22.super$3$printf(java.lang.String,java.lang.Object), public void _22._22.super$3$printf(java.lang.String,java.lang.Object[]), public java.lang.Object _22._22.super$3$evaluate(java.lang.String), public java.lang.Object _22._22.super$3$evaluate(java.io.File), public groovy.lang.MetaClass _22._22.super$2$getMetaClass(), public void _22._22.super$2$setMetaClass(groovy.lang.MetaClass), public groovy.lang.Binding _22._22.super$3$getBinding(), public void _22._22.super$3$setBinding(groovy.lang.Binding), public void _22._22.super$3$run(java.io.File,java.lang.String[]), public java.lang.Object _22._22.super$3$invokeMethod(java.lang.String,java.lang.Object), public static void _22._22.main(java.lang.String[]), public java.lang.Object _22._22.run(), public void groovy.lang.Script.setBinding(groovy.lang.Binding), public java.lang.Object groovy.lang.Script.invokeMethod(java.lang.String,java.lang.Object), public groovy.lang.Binding groovy.lang.Script.getBinding(), public void groovy.lang.Script.println(), public void groovy.lang.Script.println(java.lang.Object), public void groovy.lang.Script.run(java.io.File,java.lang.String[]) throws org.codehaus.groovy.control.CompilationFailedException,java.io.IOException, public void groovy.lang.Script.setProperty(java.lang.String,java.lang.Object), public java.lang.Object groovy.lang.Script.getProperty(java.lang.String), public void groovy.lang.Script.print(java.lang.Object), public void groovy.lang.Script.printf(java.lang.String,java.lang.Object), public void groovy.lang.Script.printf(java.lang.String,java.lang.Object[]), public java.lang.Object groovy.lang.Script.evaluate(java.io.File) throws org.codehaus.groovy.control.CompilationFailedException,java.io.IOException, public java.lang.Object groovy.lang.Script.evaluate(java.lang.String) throws org.codehaus.groovy.control.CompilationFailedException, public groovy.lang.MetaClass groovy.lang.GroovyObjectSupport.getMetaClass(), public void groovy.lang.GroovyObjectSupport.setMetaClass(groovy.lang.MetaClass), public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException, public final void java.lang.Object.wait() throws java.lang.InterruptedException, public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public boolean java.lang.Object.equals(java.lang.Object), public java.lang.String java.lang.Object.toString(), public native int java.lang.Object.hashCode(), public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
this.class.methods.name   : [super$1$wait, super$1$wait, super$1$wait, super$1$toString, super$1$notify, super$1$notifyAll, super$1$getClass, super$1$equals, super$1$clone, super$1$hashCode, super$1$finalize, this$dist$invoke$4, this$dist$set$4, this$dist$get$4, super$3$getProperty, super$3$setProperty, super$3$println, super$3$println, super$3$print, super$3$printf, super$3$printf, super$3$evaluate, super$3$evaluate, super$2$getMetaClass, super$2$setMetaClass, super$3$getBinding, super$3$setBinding, super$3$run, super$3$invokeMethod, main, run, setBinding, invokeMethod, getBinding, println, println, run, setProperty, getProperty, print, printf, printf, evaluate, evaluate, getMetaClass, setMetaClass, wait, wait, wait, equals, toString, hashCode, getClass, notify, notifyAll]
this.class.methods.name.grep(~/get./) : [getBinding, getProperty, getMetaClass, getClass]
*/




23.확산 연산자 사용하기


이연산자는 확산-도트 연산자에서 리스트의 각 요소로 대응해 주는 역활을 한다. 
배열기호 연산자와는 반대라고 할수 있다 배열기호 연산자는 쉼표로 구분된 객체들을 모아서 리스트로 만들어준다. 확산 연산자는 리스트의 요소들을 수신자에게 배포해준다. 수신자는 여러 인자를 받을수 있는 메서드이거나 리스트를 생성할수있다. 
어떤 메서드가 결과 값을 리스트에 담아서 리턴 하는데 호출한 측에서 이결과 값들을 다른 메서드에 전달해야 하는 상황을 생각해보자 확산 연산자를 이용하면 결과 값을 그대로 두번재 메서드에 인자로 전달할수있다. 
아래와 같이 하면 여러개 리턴하는 메서드와 이를 수신하는 메서드들을 섞어 쓰기쉽고 수신하는 메서드에서는 인자로 따로 선언할수있다. 

23.확산연산자 예제.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package _23
 
def getList(){
    return [1,2,3,4]
}
 
def sum(a,b,c,d){
    return a+b+c+d;
}
println 'this.list;           : ' + this.list;
println 'getProperty ('list') : ' + getProperty ('list');
 
println 'sum(*list)  : '+  sum(*list)
println 'sum(list)   : '+  sum(list)
println '-------------'
println '[*list]       : ' + [*list];   //여기서 확산연산자의 기능을 확인해볼수있다. 오리지널 값이 그대로 .값이들어간다.
println '[]+list       : '+([]+list);   //뒤쪽에+연산이 들어가는건  list를 처리한 값을 앞쪽 리스트에 넣겠다는소리다. 즉 확산연산자랑 같은 일을한다.
println '[list]       : ' + [list];     //그자체가들어간다/
assert 10==sum(*list);
assert 10==sum(list);
assert 11==sum([2,2,3,4]);
assert 12==sum(3,2,3,4);
 
 
println '-------------'
def range=(1..3);
assert [0,1,2,3] ==[0,*range];
assert [0,1,2,3] ==[0]+range;
assert [0,1,2,3] ==[0,range].flatten();
println '[0,*range];            :  '+([0,*range]);        //확산연산자의 연산된값 즉 오리지널
println '[0,range]              :  '+[0,range];
println '[0]+range;             :  '+([0]+range);            
println '[0,range].flatten();   :  '+([0,range].flatten());  
 
 
 
println '-------------'
//맵에서도 쓸수있다.
def map =[a:1,b:2];
assert [a:1,b:2,c:3] == [c:3,*:map];
assert [a:1,b:2,c:3] == [c:3,*:map];
assert [a:1,b:2,c:3] == [c:3]+[*:map];
 
println '[c:3,*:map];   : '+([c:3,*:map]);  
println '[c:3,*:map];   : '+([c:3,*:map]);  
println '[c:3]+[*:map]; : '+([c:3]+[*:map]);
 
/*결과
this.list;           : [1, 2, 3, 4]
getProperty ('list') : [1, 2, 3, 4]
sum(*list)  : 10
sum(list)   : 10
-------------
[*list]       : [1, 2, 3, 4]
[]+list       : [1, 2, 3, 4]
[list]       : [[1, 2, 3, 4]]
-------------
[0,*range];            :  [0, 1, 2, 3]
[0,range]              :  [0, 1..3]
[0]+range;             :  [0, 1, 2, 3]
[0,range].flatten();   :  [0, 1, 2, 3]
-------------
[c:3,*:map];   : [c:3, a:1, b:2]
[c:3,*:map];   : [c:3, a:1, b:2]
[c:3]+[*:map]; : [c:3, a:1, b:2]
 
*/


여기를 보면 확산연산자의 진짜능력을 알수있다. 
[*list] : [1, 2, 3, 4] 
[]+list : [1, 2, 3, 4] 
[list] : [[1, 2, 3, 4]] 


24.키워드 use를 이용한 카테고리 섞기



'숫자형 문자열' + '숫자형 문자열' = 산술계산값 
처럼 결과값을 얻고 싶다고 한다면 ‘1’+’1’ 하면 안될것이다. 
이문제를 해결하기위해 그루비는 use키워드를 제공한다 이키워드는 카테고리를 이용해 
클래스에 인스턴스 메서드를 추가할 수 있다. 

1
2
3
use (StringCalculationCategory) {
    write(read()+read())
}


여기서 카테고리는 정적 메서드 (카테고리 메서드) 가 포함된 클래스다 
키워드 use는이 메서드의 첫째 인자로 문자열의 인스턴스를 전달해서, 인스턴스 메서드처럼 동작하게 해준다. 

1
2
3
4
5
class StringCalculationCategory {   
    static def plus(String self, String operand) {
        //구현
    }        
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package _24
 
class StringCalculationCategory {   
    static def plus(String self, String operand) {
        println 'plus String'
        try {   
            return self.toInteger() + operand.toInteger()
        }
        catch (NumberFormatException fallback){
            return (self << operand).toString()
        }
    }
     
    static def plus(def self, def operand) {
        println 'plus Def'
            return (self + operand)
    }
     
    static def plus(ArrayList self, ArrayList operand) {
        println 'plus ArrayList'
        //(self + operand) 이렇게하게되면 재귀호출이된다.  오류남
        //self.plus(operand) 이렇게하게되면 재귀호출이된다.  오류남
            int cnt = self.size;
            for (int i = 0; i < operand.size; i++) {
                self[cnt++] = operand[i]
            }
            return self;
    }
     
              
    static def minus(String self, String operand) {
        println 'minus String'
        try {   
            return self.toInteger() - operand.toInteger()
        }
        catch (NumberFormatException fallback){
            return (self << operand).toString()
        }
    }       
     
 
//여기서부터 StringCalculationCategory 클래스의 정의를 따르겠다.
use (StringCalculationCategory.class) {
    assert 1    == '1' + '0'
    assert 2    == '1' + '1'
    assert 'x1' == 'x' + '1'
    assert 0    == '1' - '1'
    println '------------------';
    println "'1' + '0'   :  ${('1' + '0') } "
    println "'1' + '1'   :  ${('1' + '1') } "
    println "'x' + '1'   :  ${('x' + '1') } "
    println "'1' - '1'   :  ${('1' - '1') } "
     
    println ([1,2,3,4]+[5,6,7,8]);
     
}
 
/*결과
plus String
plus String
plus String
minus String
------------------
plus String
'1' + '0'   :  1
plus String
'1' + '1'   :  2
plus String
'x' + '1'   :  x1
minus String
'1' - '1'   :  0
plus ArrayList
[1, 2, 3, 4, 5, 6, 7, 8]
 
*/



카테고리는 주어진 클로저와 현재 스레드에서만 동작한다 이런식의 변경이 전역적으로 반영되면 부작용이 발생되기때문이다. 
1. 특수목적의 메서드를 제공한다 StringCalculation Category에서 본것처럼 연산 메서드의 대상이 동일한 클래스이고 기존 동작을 재정의해야 할 때 사용한다 예제처럼 연산자를 재정의하는 특수한 경우에도 쓸수있다. 
2. 라이브러리 클래스에 메서드를 추가한다 ‘불안전 라이브러리 클래스’가 의심될때 사용하면 효과적이다. 
3. 함께 동작하는 서로 다른 수신자 클래스를 위한 메서드 모음을 제공한다. 예를들어 java.io.OutputStream을 위한 encryptedWrite 메서드와 java.io.InputStream을 위한 encryptedWrite 메서드와 java.io.InputStream을 위한 decryptedRead메서드를 제공할수 있다. 
3. 자바에서 Decorator 패턴을 써야 하는 겨웅에는 사용한다 하지만 메서드들을 많이 만들어야 하는 불편이 더는 겪지 않아도 된다. 
4. 클래스가 너무 커졌을 때 이를 한개의 핵심 클래스와 상황에 맞는 여러개의 카테고리로 나눌수 있다 그리고 use에는 카테고리 클래스를 여러개줄수 있으므로 이카테고리들을 핵심 클래스와 함께 사용한다. 

25.use 메서드 사용하기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package _25
 
class PCategory{
    static void gogoSave(Object self){
        //self 지정하는함수
        println 'gogoSave Call '+self.gogo;;
    }
}
class GG{
 
    def gogo=[1,3]
    GG(){  
        use (PCategory){
            gogoSave();
        }
        gogoSave();
    }
    public void gogoSave(){
        println 'GG Class gogoSave()';
    }
     
     
}
 
def gg = new GG();
 
/*결과
gogoSave Call [1, 3]
GG Class gogoSave()
*/


카테고리 메서드를 Object에 할당하면 그 메서드는 모든 객체에서 사용할수 있다. 
Object보다 더작은 영역에 적용하는 법에도 관심이 있을것이다 모든Collection 클래스나 여러분이 작성한 클래스들 중 특정 인터페이스를 공유하는 비즈니스 객체들 전부에 대해 적용할수도있다. 
use에는 카테고리 클래스 여러개를 줄수 있다 카테고리들은 쉼표로 불리하거나 리스트로줘도된다. 
use(a,b,c,d) use([a,b,c,d]).. 


그루비 메타 프로그래밍




그루비에는 가로챌수 있는 지점이 셀수 없이 많다 
그중 어떤것을 고르는지에 따라 그루비가 내부적으로 제공하는 기능을 이용할수도 있고 
재정의할 코드의 양이 달라지기도 한다. 
특징들이 모여서 그루비의 메타오브젝트 프로토콜(MOP,Meta-Object Protocol)를 이룬다 

메타 프로그래밍 이해



그루비의 모든것은 GroovyObject 인터페이스에서 시작된다. 

1
2
3
4
5
6
7
8
9
package groovy.lang;
 
public interface GroovyObject {
Object invokeMethod(String name, Object args);
Object getProperty(String propertyName);
void setProperty(String propertyName, Object newValue);
MetaClass getMetaClass();
void setMetaClass(MetaClass metaClass);
}


그루비에서 당신이 작성한 모든 클래스는 GroovyClassGenerrator에 의해서 생성된다 덕분에 
그클래스들은 GroovyObject 인터페이스를 구현하여 각메서드의 디폴트 구현을 포함하게된다. 
(재정의하지 않았다면말이다.) 

보통의 자바 클래스를 그루비 클래스로 인식시키려면 GroovyObject인터페이스를 구현하면된다 더간편하게는 디폴트 메서드를 구현되어있는 GroovyObjectSupport추상클래스를 상속해도된다. 




MetaClass
GroovyObject는 메타 오브젝트 프로토콜의 핵심인 MetaClass와 밀접한 관련이 있다. 이클래스가 Groovy 클래스에 대한 모든 메터 정보를 제공한다 여기서 제공되는 메타정보들은 사용가능한 메서드,필드, 변수, 프로퍼티등이다. 그리고 다음 메서들들도 구현되어있다. 
1
2
3
4
Object invokeMethod(Object obj,String methodName,Object args)
Object invokeMethod(Object obj,String methodName,Object[] args)
Object invokeStaticMethod(Object obj,String methodName,Object args)
Object invokeStaticMethod(Object obj,String methodName,Object[] args)

위메서드들이 실제로 메서드 호출을 담당하는 메서드들이디. 이메서드들은 자바의 리플렉션 API나 자동으로 생성된 리플렉터(reflector) 클래스를 통해서 메서드를 호출한다 성능을 생각한다면 기본적으로는 리플렉터 메서드를 이용한다. 




Groovy-Object.invokeMethod의 디폴트 구현은 모든 호출을 자신의 MetaClass에게 전달한다. MetaClass는 MetaClassRegistry라는 중앙 저장소에 저장되고 꺼내진다. 





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package groovy.lang;
//디폴트 메서드를 구현되어있는 GroovyObjectSupport
import org.codehaus.groovy.runtime.InvokerHelper;
public abstract class GroovyObjectSupport implements GroovyObject {
 
    // never persist the MetaClass
    private transient MetaClass metaClass;
 
    public GroovyObjectSupport() {
        this.metaClass = InvokerHelper.getMetaClass(this.getClass());
    }
 
    public Object getProperty(String property) {
        return getMetaClass().getProperty(this, property);
    }
 
    public void setProperty(String property, Object newValue) {
        getMetaClass().setProperty(this, property, newValue);
    }
 
    public Object invokeMethod(String name, Object args) {
        return getMetaClass().invokeMethod(this, name, args);
    }
 
    public MetaClass getMetaClass() {
        if (metaClass == null) {
            metaClass = InvokerHelper.getMetaClass(getClass());
        }
        return metaClass;
    }
    public void setMetaClass(MetaClass metaClass) {
        this.metaClass = metaClass;
    }
}


MetaClassRegistry는 싱글톤 이어야하지만 아직은 아니다. 어쨌든 실질적으로 한개뿐인 이객체의 인스턴스를 얻을떄는 InvokerHelper의 팩토리 메서드를 사용한다. 



위그림 Groovy in Action책 발췌하였습다. 


출처 - http://www.gliderwiki.org/wiki/215

'Language > Groovy' 카테고리의 다른 글

DSL(Domain Specific Language) 이해하기  (0) 2016.09.09
Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 2  (0) 2016.09.08
What does .delegate mean in groovy?  (0) 2016.09.08
ExpandoMetaClass  (0) 2016.09.08
:

그루비의 동적 객체지향 - 2

Language/Groovy 2016. 9. 8. 16:44

그루비가 메서드를 호출하는 자바 바이트 코드를 생성할 때는 (몇차례 전달 과정을 거친후에) 다음중 한가지 방식으로 작성한다 
1. 클래스 자체적인 invokeMethod 구현 호출(내부적으로 다른 MetaClass로 연결될수있다) 
2. 자신의 MetaClass이용 getMetaClass().invokeMethod()호출 
3. MetaClassRegisty에 저장된 객체의 자료형에 대한 MetaClass를 이용 



위그림은 Groovy in Action 책에서 발췌하였습니다 

1. 모든 메서드 호출을 중간에 가로챌(intercept) 수있다. 즉 모든 로깅(logging)이나 추적(tracing)을 가로채서 보안 관련 사항을 적용하고 트랜잭션 처리를 할수있다. 
2. 다른객체로 호출을 전달할 (relay) 수있다 예를들어 감싸는 객체(wapper)가 자신이 처리하지 못하는 메서드를 감싸진(wrapped) 객체에게 전달하는 일이 가능하다 
* 이게 클로저가 동작하는 방식이다 클로저는 메서드 호출을 자신의 대리인(delegate)에게 전달한다. 
3. 사실 다른 일을 하면서 겉으로 메서드인척 가장할(pretend)수있다 예를들어 Html클래스는 body라는 메서드가 있는것처럼 보이면서 내부적으로는 print(‘body’) 를수행할수도있다. 
* 이것이 바로 빌더(builder)의 동작방식이다 빌더들은 중첩된 구조를 만들기 위해 메서드가 있는 척한다. 


가로채기(intecept) 전달하기(relay) 가장하기(pretend)등 몇가지 구현방법
1. GroovyObject의 invokeMethod를 구현 혹은 재정의해서 메서드를 가장하거나 전달할수있다 (정의해놓은 다른 메서드들은 평사시처럼 동작함) 
2. GroovyObject의 invokeMethod를 구현 혹은 재정의하기 GroovyInterceptable인터페이스를 구현해서 메서드의 호출을 가로챈다. 
3. MetaClass를 구현하고 GroovyObject의 setMetaClss를 호출한다.(넣는다) 
4. MetaClass를 구현하고 MetaClassRegistry에 대상 클래스들을 등록한다(그루비와 자바 클래스가 모두 가능하다) 이방식은 ProxyMetaClass를 통해 지원된다. 
일반적으로 invokeMethod를 재정의 또는 구현하는것은 도트메서드 “.”메서드 이름 연산자를 재정의하는것과같다. 



26.GroovyInterceptable인터페이스를 구현해서 메서드의 호출



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package _26
 
 
import org.codehaus.groovy.runtime.StringBufferWriter
import org.codehaus.groovy.runtime.InvokerHelper
 
class Traceable implements GroovyInterceptable{                //#1
     
    private int indent = 0
     
    Object invokeMethod(String name, Object args){              //#3
        System.out.println ("n" + '  '*indent + "before methodName : '$name'")
        indent++
        def metaClass = InvokerHelper.getMetaClass(this)        //|#4
        def result = metaClass.invokeMethod(this, name, args)   //|#4
        indent--
        System.out.println ("n" + '  '*indent + "after  methodName : '$name',  reuslt : $result ")
        return result
    }     
class Whatever extends Traceable {                              //#5
    int outer(){
        return inner()       
    }
    int inner(){
        return 1
    }
}
 
def traceMe = new Whatever() //#6
 
println 'traceMe.outer() :  '+traceMe.outer();
 
/*결과
 
before methodName : 'outer'
 
  before methodName : 'inner'
 
  after  methodName : 'inner',  reuslt : 1
 
after  methodName : 'outer',  reuslt : 1
traceMe.outer() :  1
*/


아쉬운점이있다면 
1. GroovyObject 대해서 만 동작한다는점 따라서 다른 일반적인 자바클래스에서 쓸수없음 
2. 대상클래스가 이미 상속을 받고있다면 적용할수없음 


27.MetaClassRegistry에서 우리의 MetaClass를 바꿔치자


MetaClassRegistry에서 우리의 MetaClass를 바꿔치는 방법도 있을것이다. 
이를위해 ProxyMetaClass라는 클래스가 있다. 
이클래스는 기존 MetaClass의 기능과 더불어 Interceptor 를 이용한 가로채기 기능을 제공한다
 

interface Interceptor 

1
2
3
4
5
6
package groovy.lang;
public interface Interceptor {
Object beforeInvoke(Object object, String methodName, Object[] arguments);
Object afterInvoke(Object object, String methodName, Object[] arguments, Object result);
boolean doInvoke();
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package _27
 
class CustomInterceptor implements Interceptor{
    def beforeInvoke(Object object, String methodName, Object[] arguments) {        //메서드호출전
        println "beforeInvoke   object : ${object}, methodName : ${methodName}, arguments : ${arguments}"
        return null;
    }
    def afterInvoke( Object object, String methodName, Object[] arguments, Object result) {//메서드호출후
        println "afterInvoke   object : ${object}, methodName : ${methodName},  arguments : ${arguments}, result : ${result}"
        return result;
    }
    public boolean doInvoke() {
        return true;
    }
}
 
class Whatever1 {                           
    int outer(){
        return inner()       
    }
    int inner(){
        return 1
    }
}
 
def log = new StringBuffer("n")
def tracer = new CustomInterceptor()                  //# implements Interceptor 구현한다.
 
def proxy = ProxyMetaClass.getInstance(Whatever1.class) //적용할 클래스를 선택한다.
proxy.interceptor = tracer                              //proxy에 인텁셉터를 등록한다. (내가만든거)
proxy.use {                                           
    assert 1 == new Whatever1().outer()                
}  
/*결과
beforeInvoke   object : class _27.Whatever1, methodName : ctor, arguments : []
afterInvoke   object : class _27.Whatever1, methodName : ctor,  arguments : [], result : _27.Whatever1@e79839
beforeInvoke   object : _27.Whatever1@e79839, methodName : outer, arguments : []
beforeInvoke   object : _27.Whatever1@e79839, methodName : inner, arguments : []
afterInvoke   object : _27.Whatever1@e79839, methodName : inner,  arguments : [], result : 1
afterInvoke   object : _27.Whatever1@e79839, methodName : outer,  arguments : [], result : 1
*/


출처 - http://www.gliderwiki.org/wiki/219

'Language > Groovy' 카테고리의 다른 글

Metaprogramming  (0) 2016.09.09
그루비의 동적 객체지향 - 1  (0) 2016.09.09
What does .delegate mean in groovy?  (0) 2016.09.08
ExpandoMetaClass  (0) 2016.09.08
Groovy - 문법 (Java 와 비교해서 다른 점 위주로)  (0) 2016.09.05
: