공간정보/표준

XML 스키마 라이브러리 설계(Designing XML Schema Libraries)

하늘이푸른오늘 2018. 3. 30. 23:07

Aaron Skonnard

DevelopMentor

May 2003

Summary : XML Schema 정의 언어(XSD)는 include와 import와 같은 포함 메커니즘을 통해 코어 라이브러리의 재사용 및 장기 관리를 가능하게 한다. 아울러 스키마 정의를 여러개의 파일 및 네임스페이스로 분할하여 관리할 수 있다. 오늘날 사용되는 프로그래밍 언어 방식의 유형 계층을 모델링할 수 있는 스키마 라이브러리의 설계방법에 대해 배워보자.

개요

XML Schema 정의 언어(XSD)는 XML 문서를 설명하는 언어중 대세가 되고 있다. XML Schema는 simple 및 complex 유형을 정의할 수 있다. Simple 유형은 문자만의 요소/속성에 맞춤식 값공간을 정의할 수 있으며, Complex 유형은 이러한 값 공간을 배열하여 구조체를 만들수 있다. XML 스키마는 매우 표현이 뛰어나고 XML 기반 응용에 강력한 서비스를 제공할 수 있다. 특히 웹 서비스 도메인에 유용하다. 이러한 서비스로는 검증, reflection, 자동 직렬화(type mapping) 원격 메소드 발동 등이 지원되며, XML을 위한 IntelliSense와 같은 기능을 잘 지원해줄 수 있다.

나의 첫번째 글 XML Schema의 이해에서는 핵심적인 XSD 언어 구성과 사용법에 대해 다루었다. XSD는 사실 이 글에서 다루었던 것 보다 훨씬 더 강력한 기능을 제공한다. Complex 유형 정의에서는 확장이나 제한을 사용한 유도 유형을 지원함으로써, complex 유형 계층을 정의할 수 있다. complex 유형 계층이 있으면, 인스턴스 문서에서 대체 기법을 사용할 수 있게 된다. XSD는 또한 schema 정의를 여러개의 파일과 네임스페이스로 분할 한 뒤, include 혹은 import 하는 방법을 사용함으로써, 재사용과 간편한 유지보수를 가능하게 한다. 이러한 좀더 고급 설계중심의 주제가 이 글의 주제이다.

유형 계층 (Type Hierarchies)

XML Schema의 이해에서 XSD에 내장된 simple 데이터 유형의 종류를 보였다. (그림 1) 이들 데이터유형은 유형 계층으로 정의된다. 그림 1에서 화살표는 제한에 의한 유도(derivation by restriction)를 나타낸다. 즉, byte는 short을 제한함으로서 유도되는데, 이는 byte의 값공간이 short의 값공간의 부분집합이라는 의미이다. 이러한 계층성을 유형간의 관계로 생각할 수 있다. 예를 들어, 제한에 의한 유도를 사용할 때, 유도된 유형의 인스턴스는 기반 유형의 인스턴스이기도 하다는 뜻이다. (즉, byte는 short의 일종이며, short은 int의, int는 long의 일종 등등이다.)


<그림 1> XSD 내장 데이터유형

XSD를 사용하면 이 계층을 확장하여 맞춤형 simple 유형을 정의할 수 있다. 새로운 simple 유형을 정의할 때, 기본적으로 내장 데이터유형으로부터 유도된 새로운 유형을 정의하고, 하나 이상의 facet을 제한하게 된다. 다음의 예에서 tns:PersonAge 는 xsd:byte로부터 유도하여, 값공간을 0에서 100까지로 제한한다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
   ...
   <xsd:simpleType name="PersonAge">
      <xsd:restriction base="xsd:byte">
         <xsd:minInclusive value="0"/>
         <xsd:maxInclusive value="100"/>
      </xsd:restriction>
   </xsd:simpleType>
   ...
</xsd:schema>

이제 tns:PersonAge 와 xsd:byte 사이에는 유형 관계가 있다. tns-PersonAge는 xsd:byte의 유효한 인스턴스이기도 하며, xsd:byte는 xsd:short 의 유효한 인스턴스이고, xsd:short는 xsd:int의 유효한 인스턴스인다. 이러한 관계는 제한을 통한 유도 유형에서는 항상 성립한다. Simple 유형의 제한에 관한 자세한 내용은 XML Schema의 이해를 참고하라.

Complex 유형의 경우, 위의 예에서 보인 것처럼 유형 계층을 정의할 때 제한 혹은 확장에 의하여 유형을 유도할 수 있다. 하지만, 제한과 확장을 동시에 적용할 수 없다. 하나의 유형정의에서는 제한과 확장 두가지 중 하나만 사용하여 유도할 수 있다. 따라서 기본 유형을 제한하고 확장하고 싶다면, 두번을 별도로 유형 정의해야 한다.

제한에 의한 Complex 유형 유도

Simple 유형과 마찬가지로, 제한에 의하여 Complex 유형을 유도할 수 있다. 이는 기존의 Complex 유형으로부터 그 구조체를 형성하는 요소나 속성을 제한함으로써 새로운 Complex 유형을 유도할 수 있다는 것이다. 예를 들어, 사람을 표현하는 아래의 Complex 유형을 살펴보자.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
   ...
   <xsd:complexType name="Person">
      <xsd:sequence>
         <xsd:element name="name" type="xsd:string"/>
         <xsd:element name="age" type="xsd:byte " minOccurs="0"/>
         <xsd:element name="phone" type="xsd:string" minOccurs="1" maxOccurs="5"/>
      </xsd:sequence>
      <xsd:attribute name="id" type="xsd:string "/>
   </xsd:complexType>
   <xsd:element name="person" type="tns:Person"/>
   ...
</xsd:schema>

다음의 XML 문서는 tns:Person 유형의 유효한 인스턴스를 담고 있다. 참고로 로컬 요소 및 속성은 정규화되어 있지 않다. (로컬 요소와 속성은 기본이 비정규화(unqualified)임) 또한 age 요소는 생략되어 있으며, phone 요소는 세번 들어가 있다. 이는 모두 유형 정의에서 허용된 것이다.

<tns:person xmlns:tns="http://example.org/person"
   id="333-22-4444">
   <name>Bob Smith</name>
   <phone>801-444-5555</phone>
   <phone>801-555-6666</phone>
   <phone>801-666-7777</phone>   
</tns:person>

만약 tns:PersonType을 구성하는 일부 요소 및 속성을 제한하려고 한다면, xsd:complexContent와 xsd:restriction 요소를 사용하여, 제한(restriction)에 의한 새로운 complex 유형을 유도할 수 있다.

...
<xsd:complexType name="RestrictedPerson">
   <xsd:complexContent>
      <xsd:restriction base="tns:Person">
        <!-- redefine base type's particles here -->
        ...
      </xsd:restriction>
   </xsd:complexContent>
</xsd:complexType>
...

xsd:complexContent 요소는 해당 complex 유형이 다른 complex 유형으로 유도된 것을 나타낸다. xsd:restriction 요소는 원래 제한하기 전의 기본 유형과, 모든 기본 유형(base type)의 particle(compositor, 요소 정의, wildcard 등)에 대한 새로운 정의를 포함한다. 이렇게 할 때에는 반드시 기본유형의 모든 particle을 나열해야 하며, 기본유형의 부모가 있을 경우, 해당 내용도 포함시켜야 한다.

기본유형은 두가지 방법으로 제한할 수 있다. 한가지는 값공간을 더 좁게 제한하는 것. 또다른 하나는 particle의 출현빈도(occurrence constraint)를 강하게하는 것이다. "값공간을 좁계"하는 것은 기본 유형에서 사용된 simple 유형으로부터 유도된 다른 유형으로 유형을 변경하면 된다. 예를 들어, tns:PersonType에서 age 요소는 xsd:byte 유형이다. age 요소의 유형을 tns:PersonAge으로 변경하면 age의 값 공간을 더 제한할 수 있다.

base 유형의 출현빈도 제한을 더 강하게 지정하는 방식으로 제한할 수도 있다. "강한 출현빈도 제한"이란, 새로운 출현빈도 제한이 기본유형의 출현빈도 제한 범위이내에 한다는 것을 의미한다. 예를 들어, 위의 phone 요소는 최소 1번에서 최대 5번 등장할 수 있다고 정의되어 있다. 유도 유형에서 이 요소를 제한 할 때, phone 요소의 minOccurs를 1보다 작게하거나, maxOccurs를 5보다 크게 바꿀 수는 없다. 하지만, 아래처럼 두면 해당요소가 반드시 2번 나타나도록 횟수제한을 강하게 하고 있다. (여전히 기본 유형의 횟수제한에 유효하다)

...
   <xsd:element name="phone" type="xsd:string"
                minOccurs="2" maxOccurs="2"/>
...

이러한 방식으로 작동되므로, 어떤 요소가 기본 유형에서 필수로 정의되어 있다면(name, phone 등), 유도 유형에서는 이를 삭제시킬 수 없다. (참고 : minOccus 와 maxOccurs의 기본값은 1이다) 하지만, 기본 유형에서 선택적 요소인 경우(age), maxOccurs=0으로 두어 출현하지 못하도록 막거나, minOccurs=1로 두어 필수 요소로 만들 수 있다. 여기에서도, 유도 유형의 횟수제한은 기본유형에서 정의된 범위 내에 있을 때만 적법하다.

속성에서도 이와 동일하다. 속성이 기본유형에서 선택적일 경우(기본값임), 필수로 만들거나, 금지시킬 수 있다.('prohibited'는 제한에서만 적용되며, 그렇지 않은 경우 무시됨) 예를 들어 다음의 속성 정의는 유도 유형에서 id 속성을 금지시키는 방법을 나타낸다.

...
<xsd:attribute name="id" type="xsd:string" use="prohibited" />
...

이제 제한된 complex 유형의 완전한 예를 살펴보자, 아래의 예제는 xsd:string을 pattern facet으로 제한한 새로운 simple 유형을 정의하고 있다. 이들 유형은 base 유형에 사용되는 일부 요소및 속성 정의를 제한하는데 사용된다. 아울러 여러 요소 및 속성 선언의 횟수제한을 제한하고 있다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
   <xsd:simpleType name="PersonAge">
      <xsd:restriction base="xsd:byte">
         <xsd:minInclusive value="0"/>
         <xsd:maxInclusive value="100"/>
      </xsd:restriction>
   </xsd:simpleType>

   <xsd:simpleType name="Phone">
      <xsd:restriction base="xsd:string">
         <xsd:pattern value="\d{3}-\d{3}-\d{4}"/>
      </xsd:restriction>
   </xsd:simpleType>

   <xsd:complexType name="Person">
      <xsd:sequence>
         <xsd:element name="name" type="xsd:string"/>
         <xsd:element name="age" type="xsd:byte " minOccurs="0"/>
         <xsd:element name="phone" type="xsd:string"
                      minOccurs="1" maxOccurs="5"/>
      </xsd:sequence>
      <xsd:attribute name="id" type="xsd:string "/>
   </xsd:complexType>

  <xsd:complexType name="RestrictedPerson">
      <xsd:complexContent>
         <xsd:restriction base="tns:Person">
            <!-- redefine base particles here -->
            <xsd:sequence>
               <xsd:element name="name" type="xsd:string"/>
               <xsd:element name="age" type="tns:PersonAge" 
                            minOccurs="1"/>
               <xsd:element name="phone" type="tns:Phone"
                            minOccurs="1" maxOccurs="1"/>
            </xsd:sequence>
           <xsd:attribute name="id" use="prohibited" />
         </xsd:restriction>
      </xsd:complexContent>
   </xsd:complexType>

   <xsd:element name="person" type="tns:Person"/>
   <xsd:element name="resPerson" type="tns:RestrictedPerson"/>
...
</xsd:schema>

이 예에서 우리는 새롭게 정의한 simple 유형(tns:PersonAge와 tns:Phone)을 사용하여 age와 phone 요소의 값공간을 제한하였다. 아울러 age 요소에 대해 횟수제한을 강화하였다. (이제 필수요소가 됨) phone 요소는 이제 정확하게 한번만 출현해야 하고, id 속성의 경우 선택적이 아니라 금지되었다. 아래의 문서는 tns:RestrictedPerson의 유효한 인스턴스를 담고 있다.

<tns:resPerson xmlns:tns="http://example.org/person">
   <name>Bob Smith</name>
   <age>33</age>
   <phone>333-333-4444</phone>
</tns:resPerson>

이 새로운 유형의 모든 측면은 기본 유형을 제한한 것이므로, tns:RestrictedPerson의 유효한 인스턴스는 tns:Person의 유효한 인스턴스이기도 하다. 그러나 그 반대는 참이 아니다. tns:Person의 유효한 인스턴스라고 하여 반드시 tns:RestrictedPerson의 유효한 인스턴스는 아니다.

확장에 의한 Complex 유형 정의

제한에 의한 유도가 중요하고, XML 스키마의 핵심요소이기는 하지만, 대부분의 프로그래밍 언어에서는 지원되지 않는다. 따라서, 스키마 정의를 프로그램 유형으로 매핑할 때, 마지막 예제에 있는 대부분은 프로그램의 클래스로 자연스럽게 변환되지 않는다. 하지만 확장에 의한 유도는 대부분의 객체지형 프로그래밍 언어에서 상당히 널리 사용되며, XSD에서도 지원된다.

Complex 유형만이 확장에 의한 유도가 지원된다. (즉, 기존의 simple 유형을 확장하여 새로운 simple 유형을 만드는 것은 불가능하다.) 하지만, 기존의 simple 유형 또는 complex 유형을 확장하여 새로운 complex 유형을 유도할 수 있다. 어느 유형으로부터 유도하는지는 xsd:simpleContent 또는 xsd:complexContent 요소를 사용하여 지정하며, xsd:extension을 중첩사용하여, 확장에 의한 유도를 표시한다.

simple 유형으로부터 확장에 의한 유도를 할 때에는, 새로운 유형에 속성 만 추가할 수 있다. (요소를 추가하면 더이상 simple 유형이 아니다.) 다음의 예는 기존의 simple 유형인 tns:Phone 으로부터 속성을 추가하여 새로운 complex 유형인 tns:PhoneWithLabel을 정의하는 방법을 나타낸다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
...
   <xsd:simpleType name="Phone">
      <xsd:restriction base="xsd:string">
         <xsd:pattern value="\d{3}-\d{3}-\d{4}"/>
      </xsd:restriction>
   </xsd:simpleType>
   <xsd:complexType name="PhoneWithLabel">
      <xsd:simpleContent>
         <xsd:extension base="tns:Phone">
           <!-- add attributes here -->
           <xsd:attribute name="label" type="xsd:string" use="required" />
         </xsd:extension>
      </xsd:simpleContent>
   </xsd:complexType>
   <xsd:element name="phone" type="tns:PhoneWithLabel"/>
...
</xsd:schema>

xsd:simpleContent는 xsd:complexType의 자식으로서 나타난다. 이는 새로운 유형의 내용이 character 데이터만 있고 다른 child 요소가 없음을 표시한다. 이 경우, 유형에 속성을 추가만 했으며, 이로 인해 새로운 유형은 complex 유형이 되었다. (simple 유형은 어떠한 구조도 가질 수 없다. 속성조차). 아래는 tns:PhoneWithLabel 의 유효한 인스턴스이다.

<tns:phone xmlns:tns="http://example.org/person"
   label="mobile">333-333-4444</tns:phone>

이미 존재하는 complex 유형으로부터 확장하는 것도 동일한 방법으로 가능하다. 다만, xsd:complexContent를 사용하여, 새로운 유형의 내용에 character 데이터 외에도 다른 내용(예를 들어 추가적인 child 요소)이 존재함을 표시한다. 아래의 예는 tns:Person으로부터 새로운 complex 유형을 유도하는 방법을 나타낸다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
...
   <xsd:complexType name="Person">
      <xsd:sequence>
         <xsd:element name="name" type="xsd:string"/>
         <xsd:element name="age" type="xsd:byte "  minOccurs="0"/>
         <xsd:element name="phone" type="xsd:string"  minOccurs="1" maxOccurs="5"/>
      </xsd:sequence>
      <xsd:attribute name="id" type="xsd:string "/>
   </xsd:complexType>

   <xsd:element name="person" type="tns:Person"/>

   <xsd:complexType name="ExtendedPerson">
      <xsd:complexContent>
         <xsd:extension base="tns:Person">
            <xsd:sequence>
               <xsd:element name="sex" type="xsd:string"/>
               <xsd:element name="weight" type="xsd:double"/>
               <xsd:element name="height" type="xsd:double"/>
            </xsd:sequence>
         </xsd:extension>
      </xsd:complexContent>
   </xsd:complexType>
...
</xsd:schema>

확장에 의하여 유도할 때에는 base 유형에 어떠한 변화도 만들 수 없다. base 유형에 새로운 particle을 추가하는 것만 가능하다. base 유형을 변경시킬 수 없으므로, 제한에 의한 유도에서처럼 particle을 재정의 할 필요가 없다. 그 대신, 그냥 새로운 내용을 정의하면 base 유형의 내용의 뒤에 추가된다. 새로운 내용은 논리적으로 base 유형 내용의 뒤에 추가되어, 아래와 같이 마치 두개의 내용 모델이 하나의 xsd:sequence 요소 내에 둘러싸여진 것처럼 작동된다. 

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
...
   <!-- the logical content model of the derived type -->
   <xsd:complexType name="ExtendedPerson">
      <xsd:sequence>
         <!-- base type's content model -->
         <xsd:sequence>
            <xsd:element name="name" type="xsd:string"/>
            <xsd:element name="age" type="xsd:byte " minOccurs="0"/>
            <xsd:element name="phone" type="xsd:string" minOccurs="1" maxOccurs="5"/>
         </xsd:sequence>
         <!-- derived type's content model -->
         <xsd:sequence>
            <xsd:element name="sex" type="xsd:string"/>
            <xsd:element name="weight" type="xsd:double"/>
            <xsd:element name="height" type="xsd:double"/>
         </xsd:sequence>
         <xsd:attribute name="id" type="xsd:string "/>
      </xsd:sequence>
   </xsd:complexType>
...
</xsd:schema>

다음은 tns:ExtentedPerson의 유효한 인스턴스의 예이다.

<tns:extPerson xmlns:tns="http://example.org/person"  id="333-22-4444">
   <name>Bob Smith</name>
   <phone>801-444-5555</phone>
   <phone>801-555-6666</phone>
   <phone>801-666-7777</phone>   
   <sex>M</sex>
   <weight>175</weight>
   <height>72</height>
</tns:extPerson>

제한에 의한 유도와는 달리, 확장된 유형의 인스턴스는 base 유형의 유효한 인스턴스가 아닌 경우가 많다. 여기에서 보는 것처럼 tns:extPerson은 절대 tns:Person의 유효한 인스턴스가 아니다.

 There are a few restrictions to derivation by extension when it comes to xsd:all compositors. If a complex type uses xsd:all as its top-level compositor, you can't extend it by adding new particles—you can only add attributes in this case. Also, you can't extend another complex type with an xsd:all unless it has an empty content model. 확장에 의한 유도에서 xsd:all compositor를 사용할 때에는 몇가지 제한이 있다. complex 유형이 최상위 compositor로서 xsd:all을 사용하면, 새로운 particle을 추가하는 방식으로 확장할 수 없다. - 이 경우에는 속성 추가만 가능하다. 또한, xsd:all이 있는 complex 유형은 내용모델이 비어있지(empty) 않는한 확장할 수 없다.

유도 방지(Blocking Derivation)

'봉인(sealed)' 클래스 정의와 비슷하게, 특정한 유형으로부터 새로운 유도를 방지하고자 할 경우가 있다. XSD에서는 xsd:complexType에 final 속성을 통하여 가능하다. (참고로, substitution 구릅을 사용할 때 요소선언에서 final 속성을 사용할 수도 있다.) final을 사용하면, 특정유형으로부터 유도 메커니즘이 금지된다. final 속성의 값은, extension, restriction, #all 등 세가지이다. 예를 들어, 다음의 complex 유형 정의는 tns:Person 기본유형으로부터 제한에 의한 유도 및 확장에 의한 유도 모두를 금지한다.

...
   <xsd:complexType name="Person" final="#all">
      ... <!-- omitted for brevity -->
   </xsd:complexType>
...

아래의 예와 같이 xsd:schema에 finalDefault 속성을 사용하면, 스키마 전체에서 final 속성을 기본값으로 지정할 수 있다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person"
   finalDefault="restriction">
...


이렇게 하면 기본값으로, complex 유형간에서 제한에 의한 유도가 허용되지 않는다. 위에서 보인 final 속성을 사용하여 이 설정을 덮어씌워야만 변경이 가능하다.

추상 유형(Abstract Types)

대부분의 객체지향 언어에서는 추상 유형을 정의할 수 있다. 이 문맥에서 추상유형이란 인스턴스화 할 수 없는 것을 말한다. 추상 base 클래스와 추상 base 인터페이스가 이러한 유형의 예이다. One purpose of abstract types is to define a contract that other derived types will implement. 추상유형의 목적은 다른 유도 유형이 수행된다는 계약을 정의하는 것이다. This makes it possible to build applications around abstract interfaces, supporting numerous possible implementations. The assumption is that some derived type will be substituted for the abstract type at runtime. 

아울러 XSD는 추상 complex 유형을 정의할 수 있다. 아래와 같이 xsd:complexType 요소에 abstract 속성(boolean)을 사용하면 된다.

...
   <xsd:complexType name="Person" abstract="true">
      ... <!-- omitted for brevity -->
   </xsd:complexType>
...

이 속성의 기본값은 false로서, complex 유형은 구체적이라는 것이다. 추상 스키마 유형은 XML 인스턴스 문서에서 유형으로 나타날 수 없다는 것을 의미한다. 그대신 추상유형이 있어야 할 곳에 반드시 유도된 유형이 대체되어 나타나야 한다.

xsi:type을 사용한 대체(substitution)

유형계층을 정의하면 좋은 장점은 유도된 유형의 인스턴스를 대체할 수 있는 능력이다. 객체지향 기술에서 폴리모피즘과 같이, base 유형이 와야할 곳에 유도 유형을 대체할 수 있다. XML 문서에서 이를 실행할 수 있는 방법중 하나가 xsi:type을 사용하는 방법이다.

xsi:type을 사용하면 요소의 유형을 명시적으로 지정할 수 있다. 이렇게 함으로써 스키마에서 요소선언이 없을 경우에 조차, 어떤 요소가 특정 유형이라고 주장하는 것이 가능해진다. 이 속성은 유도된 유형의 인스턴스를 기반유형이 와야 할 곳에 사용할 때 가장 널리 사용된다. 이 경우, 스키마 프로세스는 xsi:type 속성에서 지정한 유형이 기대된 기반유형으로부터 유도되는 것을 보증한다. 예를 들어, 아래의 tns:Family라는 complex 유형 정의를 고려해 보자. 이 유형은 tns:Person 유형의 여러개의 person 요소 목록을 포함하고 있다. 

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
...
   <xsd:complexType name="Family">
      <xsd:sequence>
         <xsd:element name="person" type="tns:Person"
                      maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   <xsd:element name="family" type="tns:Family"/>
...
</xsd:schema>

아래의 문서는 tns:Family의 유효한 인스턴스를 담고 있다. 주목할 점은 tns:RestrictedPerson 과 tns:ExtendedPerson 의 인스턴스가 tns:Person의 인스턴스가 와야할 곳에 대체되었다는 점이다.

<tns:family xmlns:tns="http://example.org/person"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema">
   <person id="333-22-4444">
      <name>Bob Smith</name>
      <phone>801-444-5555</phone>
      <phone>801-444-6666</phone>
   </person>
   <person xsi:type="tns:RestrictedPerson">
      <name>Mary Smith</name>
      <age>33</age>
      <phone>333-333-4444</phone>
   </person>
   <person xsi:type="tns:ExtendedPerson" id="333-22-5555">
      <name>Jenny Smith</name>
      <phone>801-444-5555</phone>
      <phone>801-444-6666</phone>
      <sex>F</sex>
      <weight>105</weight>
      <height>65</height>
   </person>
</tns:family>

이상과 같이 이런 대체 스타일은 유형이름에 기반하며, 요소 이름에 기반하는 것이 아니다. 즉, 유형을 결정할 때 요소 이름만 봐서는 안된다는 것이다. 하지만 XSD에는 요소 이름에만 완전히 기반하는 대체유형도 존재한다.

대체 그룹(Substitution Groups)

대체그룹은 요소기반의 대체를 허용한다. 대체그룹을 형성하려면, 전역적 요소선언이 그룹의 'head'로서 작용한다. (어떤 전역 요소이든 특별한 무언가가 필요없이 'head'로서 작동한다.) 그래서 다른 전역요소 선언은 substitutionGroup 속성을 사용하고 아래와 같이 head 요소의 이름을 지정함으로써, 대체 그룹에 참여할 수 있다. 

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
...
   <xsd:element name="person" type="tns:Person"/>
   <xsd:element name="resPerson" type="tns:RestrictedPerson"
                substitutionGroup="tns:person" />
   <xsd:element name="extPerson" type="tns:ExtendedPerson"
                substitutionGroup="tns:person" />
...
</xsd:schema>

대체그룹의 멤버는 반드시 head 요소의 유형에 관련되어야 한다. 즉, 멤버요소는 head 요소와 동일한 유형일 수도 있고, 제한/확장 유형일 수도 있다. 이 법칙은 대체된 요소가 head 요소의 위치에 사용될 때 의미가 있다는 것을 보장한다.

For example, with the above substitution group in place, we can change the complex type definition for tns:Family to reference the global tns:person element: 예를 들어, 위의 대체그룹에서 우리는 tns:Family에 대한 complex 유형 정의를 전역적 tns:person 요소를 참조하도록 변경시킬 수 있다.?

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">
...
   <xsd:complexType name="Family">
      <xsd:sequence>
         <xsd:element ref="tns:person" maxOccurs="unbounded"/>
      </xsd:sequence>
   </xsd:complexType>
   <xsd:element name="family" type="tns:Family"/>
...
</xsd:schema>

그러면 다음과 같이 이 위치에 tns:person 대체그룹의 어떤 멤버도 대체할 수 있다.

<tns:family xmlns:tns="http://example.org/person">
   <tns:person id="333-22-4444">
      <name>Bob Smith</name>
      <phone>801-444-5555</phone>
      <phone>801-444-6666</phone>
   </tns:person>
   <tns:resPerson>
      <name>Mary Smith</name>
      <age>33</age>
      <phone>333-333-4444</phone>
   </tns:resPerson>
   <tns:extPerson id="333-22-5555">
      <name>Jenny Smith</name>
      <phone>801-444-5555</phone>
      <phone>801-444-6666</phone>
      <sex>F</sex>
      <weight>105</weight>
      <height>65</height>
   </tns:extPerson>
</tns:family>

주목할 점은 tns:person, tns:resPerson, tns:extPerson은 모두 전역 요소로서 스키마의 target 네임스페이스에 의해 정규화(qualified)되어야 한다는 점이다. 아울러, 요소기반의 대체를 사용하기 때문에, 더이상 유형정보를 지정하기 위해 xsi:type 을 사용할 필요가 없다는 점도 주목해야 한다. 이제 요소의 유형은 이름만으로부터 끌어낼 수 있다.

대체 방지(Blocking Substitution)

유도와 마찬가지로, 특정 유형에서 대체가 사용되는 것을 막고 싶거나 대체그룹에서 요소가 사용되는 것을 막고 싶을 경우가 있을 수 있다. xsd:complexType 또는 xsd:element에 block 속성을 사용하면 특정 유형/요소에 대한 대체를 막을 수 있다. The block attribute allows you to specify what derived types/elements may be used in place of the type/element at hand. block 속성을 사용하면, 유도된 유형/요소가 직접적으로 그 유형/요소에 사용될 것인지 지정할 수 있다. block 속성에 허용되는 값은 extension, restriction, substitution, #all 등이다. 예를 들어 아래의 complex 유형 정의는 확장된 유형을 tns:Person이 필요한 자리에 대체될 수 없도록 방지한다.

...
   <xsd:complexType name="Person" block="extension">
      ... <!-- omitted for brevity -->
   </xsd:complexType>
...

또한 xsd:schema 요소에 blockDefault 속성을 사용함으로써, block 속성에 대한 기본값을 지정할 수 있다. 아래의 예는 이 스키마에서 기본값으로 모든 종류의 대체가 금지된다. (특별한 complex 유형이 block 속성을 사용하여 이 설정을 바꾸지 않는한)

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person"
   blockDefault="#all">
...

xsd:complexType과 마찬가지로, xsd:element의 abstract 속성과 final 속성을 사용하여, 대체그룹에서 요소가 어떻게 사용될지를 제어하는 것도 가능하다.

재사용(Reuse)

스키마 라이브러리를 설계할 때, 아마도 다양한 스키마에서 공유되어야 할 필요가 있는 글로벌 유형이 있을 것이다. 이러한 경우, 이러한 유형을 별도의 스키마 파일에 정의를 한 후, 다른 스키마 파일에 include 혹은 import 시키고 싶을 수 있다. 여러개의 스키마 파일로 부터 스키마를 만드는 것은 매우 흔한 일이다.

XSD에서는 xsd:include와 xsd:import 요소를 통해 이러한 재사용이 가능하다. xsd:include는 별도의 파일에 있는 스키마를 포함시킬 수 있다. 이 경우, 두개의 스키마 파일의 target 네임스페이스가 동일해야 한다. 하지만, target 네임스페이스가 없는 스키마 파일을 include 시키는 것도 가능하다. 이 경우, 포함된 construct들은 새로운 target 네임스페이스의 일부가 된다. 아래는 xsd:include의 작동방법 예시이다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person"
>
   <xsd:include schemaLocation="globals.xsd"/>
   ...

xsd:include가 어떤 경우에는 편리할 수 있지만, 실재로는 xsd:import가 더 널리 사용된다. xsd:import는 다른 네임스페이스에서 새로운 스키마로 유형을 'import'시킬 수 있다. 이경우, import된 유형은 여전히 원래의 네임스페이스에 존재하지만, 새로운 네임스페이스에서 새로운 유형을 구축하는데 사용될 수 있다. 예를 들어, 다음의 스키마 정의에는 tns:Person 유형이 포함되어 있다.

<!-- personBase.xsd -->
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:tns="http://example.org/person"
   targetNamespace="http://example.org/person">


   <xsd:complexType name="Person">
      <xsd:sequence>
         <xsd:element name="name" type="xsd:string"/>
         <xsd:element name="age" type="xsd:byte " 
                      minOccurs="0"/>
         <xsd:element name="phone" type="xsd:string"
                      minOccurs="1" maxOccurs="5"/>
      </xsd:sequence>
      <xsd:attribute name="id" type="xsd:string "/>
   </xsd:complexType>
</xsd:schema>

우리는 이 스키마를 새로운 스키마로 import하며, 이 유형을 기본유형으로 하여 새로운 complex 유형을 정의하는데 사용할 수 있다.

<!-- person.xsd -->
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:basetns="http://example.org/person"
   xmlns:tns="http://example.org/person/derived"
   targetNamespace="http://example.org/person/derived">

   <xsd:import namespace="http://example.org/person"
               schemaLocation="personBase.xsd"/>

   <xsd:complexType name="ExtendedPerson">
      <xsd:complexContent>
         <xsd:extension base="basetns:Person">
            <xsd:sequence>
               <xsd:element name="sex" type="xsd:string"/>
               <xsd:element name="weight" type="xsd:double"/>
               <xsd:element name="height" type="xsd:double"/>
            </xsd:sequence>
         </xsd:extension>
      </xsd:complexContent>
   </xsd:complexType>
</xsd:schema>

다른 네임스페이스에 있는 유형을 import 할 때에는 제한에 의한 유도(derivation by restriction)을 주의깊게 사용해야 한다. import된 Complex 유형에 정규화된(qualified) 지역요소가 있고(즉, elementFormDefault="qualified"), 새로이 제한된 complex 유형을 정의할 경우, 지역요소의 네임스페이스를 바꾸게 됨으로써, 제한이 invalid하게 된다. import 된 유형의 지역 요소가 비정규화 요소일 경우, 새롭게 제한된 유형이 네임스페이스를 변경하지 않으므로, 이런 일이 발생하지 않는다.

결론

As you can see, XSD is a powerful and flexible language capable of representing many of the type hierarchies commonly used in today's object-oriented systems. Even better, XSD makes it possible for applications to take advantage of type relationships through XML substitution techniques. This makes it possible to model not only the type hierarchies themselves, but also how they're used in practice. 이상과 같이 XSD는 현재 객체지향시스템에서 널리 사용되는 유형 계층의 상당 부분을 표현할 수 있는 강력하고도 유연한 언어이다. 아울러, XSD를 사용하면 응용에서 XML 대체기법을 통해 유형 관계를 이용할 수 있다. 따라서, 유형 계층화 그자체 뿐만아니라, 실재로 어떻게 사용되는지를 모델링할 수 있다.

XSD의 포함 메카니즘(즉, include 와 import)를 사용하면 핵심 라이브러리를 재사용하고 장기간 유지관리가 쉽게 된다. XSD가 완벽한 건 아니지만, 현재 사용되는 다양한 프로그래밍 언어 고유의 유형을 모델링할수 있는 스키마 라이브러리를 설계하기 위한 확고한 기반을 제공한다. XML 스키마에 관한 좀 더 많은 정보는 Essential XML Quick Reference 등 아래에 있는 기타 문헌을 참고하라.

참고문헌

XML Schema Part 0: Primer 

XML Schema Part 1: Structures 

XML Schema Part 2: Datatypes 

Essential XML Quick Reference

===

원문 : https://msdn.microsoft.com/en-us/library/aa468549.aspx