공간정보/표준2018. 6. 20. 16:20

현재 우리나라의 공간데이터 교환은 많은 문제가 있습니다. 생산자가 데이터 공개를 기피하는 것은 논외로 치더라도, 기껏 공개한 데이터도 공개 주체마다 제 각각이라서 사용하기 힘듧니다. 그것은 단순히 교환 포맷의 문제가 아닙니다. 데이터 교환용 공통 스키마가 있어야만 해결될 수 있습니다.

데이터 생산자는 자신의 데이터를 이 공통 스키마에 따라 변환한 임시 데이터셋을 만들고, 이를 지정한 공통 포맷으로 변환한 후, 이렇게 만들어진 파일을 공유해야 합니다. 이렇게 해야 각각의 생산자별로 다른 형태의 데이터가 만들어지는 상황을 피할 수 있습니다.

공간데이터 교환을 위해 가장 좋은 포맷은 GML(지리 마크업 언어 : Geography Markup Language)입니다. ISO TC211 표준에서도 교환표준으로 GML을 추천하고 있습니다. GML은 XML의 일종입니다. XML은 IT 분야에서 정보 전달의 수단으로 널리 사용되고 있습니다. 특히 해당 XML의 구조를 xsd(xml schema definition)이라는 구조로 저장하여 이를 함께 배포함으로써 데이터의 일관성을 유지하고 사용성을 높이고 있습니다. 마찬가지로 GML도 그 구조를 xsd로 만들어 함께 배포하면 데이터 교환에 관한 문제가 해결될 수 있습니다.

이상이 제가 공간정보 유통과 표준에 대하여라는 글에서 주장한 내용입니다. 

공간정보를 GML로 변환하는 것은 어렵지 않습니다. 대부분의 공간정보 처리 소프트웨어가 모두 GML을 지원하고 있기 때문입니다. 그런데 그 GML의 구조를 나타내는 XML Schema는 어떻게 만들어야 할까요? 이 글이 바로 공통스키마를 만들고, 이 공통스키마를 xsd로 변환하는 방법에 관한 글입니다.

공통 응용스키마 작성

공통 응용스키마는 다른 응용스키마와 마찬가지로, ISO 19103에서 정의한 개념스키마 언어인 UML(통합 모델링 언어 : Unified Modelling Language)를 사용하여, ISO 19109에서 정의한 응용 스키마 규칙에 따라 만듧니다. UML은 그래픽 언어이기 때문에 파워포인트나 포토샵 아니면 그냥 워드프로세서로 만들 수도 있지만, 대부분은 UML 도구를 사용하여 만들게 됩니다. 오픈소스인 Modellio도 정말 좋은 대안입니다. 하지만, ISO TC211에서는 상용 프로그램인 Enterprise Architect를 사용하여 작업하므로, 저도 이 도구를 사용하고 있습니다.

또한 응용스키마는 클래스 다이어그램 뿐만 아니라, 각각의 패키지/클래스/속성/연관 등에 대한 상세한 설명을 담은 문서가 필요한데, 이것도 UML 도구의 문서화도구를 사용해야만 편리합니다. 클래스 다이어그램과 문서가 일관성을 유지할 수 있기 때문입니다. 이와 같은 장점때문에 ISO 19109 응용스키마 법칙에서는 UML도구의 문서화 기능을 사용하여 문서화하라고 권고하고 있습니다. 따라서 UML 도구는 공간정보 응용스키마를 작업하려면 필수사항이라고 할 수 있습니다.

그런데 Enterprise Architect 를 사용하여 만든 응용스키마는 xsd로 직접 변환이 되지 않습니다. 기본적으로 UML 은 프로그래밍 언어가 아니기 때문이기도 하고, UML과 XML이 만들어진 개념이 많이 차이가 있기 때문입니다. 그렇다고하여 UML로 만들어진 응용스키마를 보면서 xsd를 따로 만드는 것은 에너지 낭비일 뿐만 아니라 불일치가 발생할 가능성이 높으므로 반드시 피해야 합니다.

ShapeChange를 이용한 UML ->XML Schema 변환

ISO TC211에서는 이 문제를 ShapeChange 등의 공개 소프트웨어를 사용하여 해결하였습니다. 상세한 내용은 ShapeChange를 이용하여 UML 모델에서 XML 스키마 생성하기를 읽어보시면 됩니다. 이 절차의 핵심은 ShapeChange입니다. 이 소프트웨어가 UML의 교환포맷인 XMI를 읽어들여서 모델의 적합성을 체크한 후, xsd를 만들어주는 소프트웨어 입니다. 

ShapeChange를 설치하고 테스트하는 방법은 ShapeChange 소개글에 나와 있습니다. 간단히 정리하면 Java Runtime Enviroment 32비트버전을 설치하고, ShapeChange 압축파일을 적당한 위치에 압축해제 해주고 약간의 설정을 해주면 됩니다.

ShapeChange 소개글에서 사용한 테스트용 응용스키마는 아래와 같이 생겼습니다. 복잡한 연관 관계는 없이 기본적인 데이터 유형 몇개와, 이들을 사용하는 지형지물 유형 2개가 정의되어 있습니다.

이 응용스키마에 대한 xmi 파일은 아래와 같습니다. 참고로 xmi 는 OMG에서 개발한 XML 교환용 포맷입니다.

test.xml

아래는 이 파일의 시작부분입니다. 매우 복잡합니다. ㅎㅎ

아래는 ShapeChange를 실행시켰을 때 나오는 XML Schema 파일 (.xsd)입니다. 

test.xsd

아래는 이 파일의 시작부분만 일부 캡처한 것입니다. 물론 많이 복잡합니다만, xml 스키마를 일부 알아볼 수 있겠네요.

예를 들어, 위에 있는 클래스 다이어그램에서는 DataType2를 아래와 같이 정의했습니다.

xsd에서는 아래와 같이 정의되어 있습니다. 제가 아직 XML/GML을 공부하는 중이라 잘은 모르지만, 예쁘게 정리된 것 같습니다. ㅎㅎㅎ

  <element name="DataType2" substitutionGroup="gml:AbstractObject" type="test:DataType2Type"/>
  <complexType name="DataType2Type">
    <sequence>
      <element maxOccurs="unbounded" name="string" type="string"/>
      <element minOccurs="0" name="integer" type="integer"/>
    </sequence>
  </complexType>
  <complexType name="DataType2PropertyType">
    <sequence>
      <element ref="test:DataType2"/>
    </sequence>
  </complexType>

보너스 - 지형지물 카탈로그 제작

게다가... ShapeChange를 사용하면 xml 스키마 뿐만 아니라, ISO 19110에서 규정한 지형지물 카탈로그(Feature Catalogue)도 만들 수 있습니다.  아래가 그 html 파일입니다.

test.html

그리고 아래는 이 지형지물 카탈로그의 첫부분만 캡쳐한 것입니다.

지형지물 카탈로그는 ISO 19110 표준에 정의되어 있습니다만, 우리나라에서 아직까지 지형지물 카탈로그를 제작해서 공개한 경우는 못본 것 같습니다. 그런데 이렇게 쉽게 제작할 수 있다면 지형지물 카탈로그도 더 많이 공개될 수 있지 않을까 기대해볼 수 있을 것 같네요.

====

아직까지 자세히 들여다보지는 못해서 사용법이나 자세한 설정방법 등은 잘 알지 못하지만, 응용스키마 작업을 하려면 ShapeChange를 반드시 활용해야 한다는 것은 알게되었네요. 오래전부터 고민해왔었는데, 이걸 발견하니 속이 후련해지는 느낌입니다. ㅎㅎㅎ

민, 푸른하늘


Posted by 푸른하늘 푸른하늘이

댓글을 달아 주세요

공간정보/표준2018. 3. 30. 22:21

Aaron Skonnard
DevelopMentor 

 March 2003 

적용분야 :
    Type systems
    XML Schema definition language (XSD)
    Web Services development 

요약 : XML Schema는 XML 프로세싱의 미래에서 핵심적 역할을 담당할 것이다. 특히 웹서비스의 경우, 고수준의 추상화의 바탕이 되는 중요한 기둥중 하나가 될 것이다. 이 글은 XML Schema 정의 언어의 사용법을 좀더 자세하게 설명한다. (인쇄시 22쪽)

개요(Introduction)

1+2 = ?

소프트웨어에서 이러한 문제의 답을 내는 데에는 유형(type) 시스템이 필요하다. 프로그래밍 언어는 품질이 높은 코드를 생산하기 위한 임무를 간단히 하기 위하여 유형 시스템을 사용한다. 유형 시스템은 여러가지 유형과 연산을 정의하며, 개발자들은 이들을 선택하여 자신의 프로그램에 적용한다. 유형은 값 영역, 다른 말로하면 가능한 값의 집합을 정의한다. 예를 들어 위의 연산이 숫자형(numeric) 유형이라면 답은 3이 될것이다. 하지만 문자열(string)의 경우 + 연산자의 정의에 따라 다르겠지만, "12"가 될 수 있다.

유형시스템의 중요한 잇점중 하나는 컴파일러가 코드에 에러가 있는지 미리 알 수 있어, 많은 오류를 미리 피할 수 있다는 것이다. 컴파일러는 또한 유형 시스템 정보를 사용하여, 주어진 유형에 따른 연산 코드를 생성할 수 있다. 또한 컴파일러와 실행프로그램은 특정한 유형에 대한 메모리 할당 방법을 결정하는데 유형시스템에 의존하며, 그 결과 개발자들은 지겹고 까다로운 문제를 잊어버릴 수 있다.

많은 언어와 실행프로그램은 프로그램이 실행될 때 유형 정보를 검사하는 것도 가능하다. 프로그래머는 어떠한 사건에서든 유형의 특성을 질문하고, 그 답에 따라 결정을 내릴 수 있다. 이와 같이 실행시 유형정보를 검사하는 기법을 일반적으로 reflection이라고 한다. Reflection은 Microsoft®.NET Framework 와 Java와 같은 주류 프로그램밍 환경에서 중요한 역할을 담당한다. 즉, 가상머신(CLR(common language runtime)이나 JVM)은 보안, 가비지 청소, 직렬화, 원격 메소드 실행, 웹서비스 통합 등 대부분의 프로그램이 필요한 추가 서비스를 제공함으로써, 프로그래머의 수많은 고민을 효과적으로 줄여준다.


<그림 1> 유형 정보의 장점

또한 잘 정의된 유형 시스템과 reflection을 사용하면 언어와 잘 맞는 더 나은 도구를 제작할 수 있다. Developers have quickly grown used to things like Microsoft® Intellisense®, code completion, and those handy red squiggles that greatly speed up the development process. 전체적인 좋은 유형 시스템은 많은 재미있는 장점(그림 1 참고)을 제공하며, 거의 모두 당연히 받아들이기는 쉽지만 없을 경우 엄청나게 필요할 것이다.

XML 1.0은 합리적인 유형 시스템이 존재하지 않는 언어의 좋은 예이다. 유형 시스템이 없으면 XML 1.0 문서에 포함된 정보는 오직 문자로만 취급될 수 있다. 이렇게 되면 개발자들은 미리 "진짜 유형"이 무엇인지 파악해야 하며, 강제로 코드에 넣어 구현해야 할 것이다.

XML Schema 정의 언어(XSD)는 XML 처리 환경을 위한 유형 시스템을 제공한다. 간단히 말해 XML Schema가 있으면 사용하고자 하는 유형을 서술하는 것이 가능하다. XML Schema 유형에 적합한 XML 문서를 instance 문서라고 하는 경우가 많은데, 이는 전통적인 객체지향체계에서 클래스(class)와 객체(object)의 관계와 상당히 유사하다. (그림 2 참고). 이는 DTD(Document Type Definition)의 작동 방법론과 개념적으로 상당한 차이가 있다. XML Schema는 전통적인 프로그래밍 언어나 데이터베이스 시스템 등에 매핑할 때 훨씬 더 유연성이 많다. 이러한 환경에서 XML Schema는 DTD의 사용을 대부분 대체하였다.


<그림 2> 객체지향과 XML 스키마 개념

XML Schema는 그림1에서 나타낸 모든 장점을 제공해줄 수 있다. (단 XML에 대해서만) XML Schema 유형 정보를 포함한 논리적 XML 문서는 PSVI(post schema-validation Infoset : 스키마 인증 Infoset?)이라고 한다. PSVI를 사용하면, 다른 프로그래밍 환경과 마찬가지로 프로그램 실행중에 XML Schema 기반의 reflection을 수행할 수 있다. 전체적으로 XML Schema는 XML 프로세싱의 미래에서 핵심적 역할을 담당할 것이다. 특히 웹서비스의 경우, 고수준의 추상화의 바탕이 되는 중요한 기둥 중 하나가 될 것이다. 이 글은 XML Schema 정의언어의 사용법에 대해 좀더 자세하게 설명하는 글이다.

Datatypes: Value and Lexical Spaces

XML Schema는 개발자들이 사용할 수 있는 내장 데이터유형이 제공된다. (W3C XML Schema Part 2: Datatypes 페이지에 유용한 그림이 있음) 이들 모든 유형은 http://www.w3.org/2001/XMLSchema 네임스페이스에서 찾을 수 있다. 각각의 유형은 정의된 값 공간이 있다. 유형의 값 공간은 간단히 해당 유형의 인스턴스에 사용할 수 있는 값의 집합이다.


<그림 3> byte 유형의 값공간

예를 들어 XML Schema는 byte라는 이름의 내장 유형이 있다. byte의 값 공간의 -128 부터 127까지 이다. 또다른 예로 XML Schema boolean 유형은 값 공간이 훨씬 간단하다. 즉 true 와 false 두가지 값만으로 구성된다. 전체적으로 44개의 내장유형이 있으며, 각각 값 공간이 달라서 다양한 데이터 모델링 수요를 충족시킬 수 있다. 

그림 4는 많은 내장 유형이 다른 유형의 값 영역의 부분집합으로 정의됨을 표시한다. 이를 제한에 의한 유도(derivation by restriction)이라고 한다. 예를 들어 byte의 값 공간은 short의 값 공간의 부분집합이며, short의 값 공간은 int 값 공간의 부분집합, int의 값 공간은 long의 값 공간의 부분집합... 등이다. 따라서 기본적인 집합 이론에 따라 유도된 유형의 인스턴스는 또한 상위 유형의 인스턴스이기도 하다. (엄밀하게 말해 이들은 모두 anySimpleType 자체의 부분집합이다.)

Although programming languages use value space information to figure out how much memory will be needed to represent values, developers seldom need to worry about representing them as text. 프로그래밍 언어가 값공간 정보를 사용하여 값을 표현하는데 필요한 메모리 양을 파악하지만, 개발자들은 값들을 그냥 문자로 표현해도 크게 걱정할 필요가 없다. 하지만 XML을 사용하면 해당 인스턴스가 XML 1.0 파일로 직렬화될 가능성이 높다는 사실을 무시할 수 없으며, 이때 값에 대한 사전적 표현이 필요하게 된다. 모든 XML Schema 프로세서가 이를 독립적으로 결정한다면 상호운영성이 보장되지 못하게 될 것이다. 따라서 각 유형의 값공간을 정의함과 함께 XML Schema는 허용되는 사전적 표현도 정의하고 있다.


<그림 4> 유형 부분집합

예를 들어, 불리언 true 값은 "ture" 혹은 "1"로 표현할 수 있으며, 불리언 false 값은 "false" 또는 "0"으로 표현할 수 있다. double 형 10 은 "10", "10.0", "10.0000", 심지어는 "0.01E3"로도 표현할 수 있다. 또한 date 형의 값인 2003년 1월1일은 "2003-01-01"로 사전적으로 표현할 수 있다. 각각의 유형에 대해 사전적 형태(및 모든 변화된 형태)를 표준화하면 개발자들이 직렬화방법의 복잡성을 무시하면서 값들을 자유롭게 처리할 수 있다.

네임스페이스에서 유형 정의하기

대부분의 프로그래밍 언어는 내장 유형도 제공하지만, 개발자가 자체적으로 유형을 정의할 수 있다. 이를UDT(user-defined types : 사용자 정의 유형)라고 한다. UDT를 정의할 때 대부분의 언어는 네임스페이스도 함께 정의함으로써 동일한 이름을 사용하는 다른 UDT와 우연히 충돌하는 것을 방지할 수 있도록 하고 있다. XML 네임스페이스의 작동방법에 관한 내용은 XML 네임스페이스의 이해를 참고하라. 그림 5는 C# 네임스페이스 정의와 XML Schema 정의를 비교한 것이다. 보시는 바와 같이 XML 스키마도 네임스페이스 내에서 유형을 정의할 수 있다.


<그림 5> 네임스페이스에서의 데이터 유형 정의

xsd:schema 요소는 네임스페이스에 무엇이 포함되는지 범위를 정하고, targetNamespace 속성은 네임스페이스의 이름을 지정한다. 예를 들어, 다음의 XML Schema 템플릿은 http://example.org/publishing이라는 새로운 네임스페이스를 정의한다.

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

   <xsd:simpleType name="AuthorId">
      <!-- define value space details here -->
          ...
   </xsd:simpleType>

   <xsd:complexType name="AuthorType">
      <!-- define structural details here -->
          ...
   </xsd:complexType>

   <!-- global element/attribute declarations -->
   <xsd:element name="author" type="tns:AuthorType"/>
   <xsd:attribute name="authorId" type="tns:AuthorId"/>
       ...
</xsd:schema> 

xsd:schema 요소 내에 (직계 자손 immediate child로) 정의된 모든 것은 글로벌로 간주되므로, 자동적으로 target 네임스페이스에 연결된다. 위의 예에서는 http://example.org/publishing 네임스페이스에 AuthorId, AuthorType, author, authorId 등 4가지가 정의되어 있다. 그 결과 누군가 이러한 것을 하나라도 참조한다면, 네임스페이스 정규화 이름(namespace-qualified name)을 사용해야 한다.

네임스페이스 정규화 이름을 사용하려면 스키마의 targetNamespace값에 사상되는 또다른 네임스페이스 선언이 필요하다. 위에서 'tns' 접두사 선언이 이러한 목적이다. 따라서 나의 스키마에서 정의던 무엇인가를 참조하려면, 위의 예와 같이 'tns' 접두사를 사용하면 된다.

xsd:schema 요소내에서는 두가지 유형을 정의할 수 있다. simple 유형(xsd:simpleType)과 complex 유형(xsd:complexType)이다. Simple 유형은 텍스트만 들어가는 요소나 속성만 넣을 수 있다. Simple 유형은 구조를 정의할 수 없고 값 공간만 정의할 수 있다. 여러개의 속성이 있거나 하위 요소가 있는 등 추가적인 구조가 필요한 경우에는 complex 유형을 정의해야 한다.

유형정의와 함께 (xsd:element를 사용하여) global 요소와 (xsd:attribute를 사용하여) global 속성을 정의하고 유형으로 할당할 수 있다. 위의 예에서는 author라는 global 요소와 authorId라는 global 속성을 정의하였다. 이들 constructs도 global이기 때문에 인스턴스 문서에서 사용하려면 target 네임스페이스를 지정해야 한다.  다음의 XML 문서에는 위에서 정의한 author 요소의 인스턴스가 들어있다.

<x:author xmlns:x="http://example.org/publishing">
  <!-- structure determined by complexType definition -->
      ...
</x:author>

또한 다음 XML 문서는 global 속성 authorId를 포함하고 있다.

<!-- authorId value constrained by simpleType definition -->
<publication xmlns:x="http://example.org/publishing" 
   x:authorId="333-33-3333"/>  

또한, http://www.w3.org/2001/XMLSchema-instance 네임스페이스의 type 속성을 사용하면, 인스턴스 문서에서 요소에 유형을 명시적으로 할당할 수도 있다. 이 네임스페이스에는 인스턴스 문서에서만 사용할 수 있는 약간의 속성만 포함되어 있다. 이 type 속성을 사용하는 것은 일부 프로그래밍 언어에서의 타입 강제변형(type casting)과 유사하다. 다음의 예는 AutorId 유형에 genericId 요소(스키마에서는 정의되지 않음)를 명시적으로 할당한 것이다.

<genericId 
  xmlns:x="http://example.org/publishing"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:type="tns:AuthorId"
>333-33-3333</genericId>

Notice that AuthorId is the same type that we assigned to the global authorId attribute shown above. This illustrates that you can assign simple types to either attributes or text-only elements to constrain their values respectively. Also, it's important to note that the xsi:type technique for assigning type only applies to elements and not attributes.

여기에서 주목할 것은 AuthorId가 위에서 global 속성 authorID에 할당한 것과 동일한 유형이라는 것이다. 이와같이 simple 유형을 속성이나 문자만 가능한 요소에 지정하여, 그들의 값을 제한할 수 있다. 단, 유형을 지정할 때 xsi:type 기법을 사용하는 것은 요소에만 가능하고 속성에는 불가능하다는 점을 기억해야 한다.

Simple 유형 정의하기

대부분의 프로그래밍 언어는 여러가지 내장유형을 구조형으로 재배치하는 것만 허용할 뿐, 값 공간을 사용자가 정의할 수 있는 새로운 simple 유형을 정의하는 것은 안된다. 이러한 점에서 XML Schema는 차이가 있다. XML Schema의 경우 사용자가 맞춤형 simple 유형을 정의할 수 있다. 이때 사용자 simple 유형의 값 공간은 기반이 되는 내장 유형의 값공간의 부분집합이어야 한다.

새로운 simple 유형은 xsd:simpleType 요소를 사용하여 정의할 수 있다. xsd:simpleType 요소내에서 (xsd:restriction 요소를 사용하여) 값 공간을 제한하고자 하는 기본 유형을 정의한다.  xsd:restriction 요소에서는 기본 유형으로부터 제한할 내용을 지정한다. 예를 들어 다음의 simple 유형은 xsd:double 과 xsd:date 값공간을 xsd:minInclusive와 xsdmaxInclusive 요소를 사용하여) 좀더 구체적인 범위로 줄이고 있다.

...
   <xsd:simpleType name="RoyaltyRate">
      <xsd:restriction base="xsd:double">
         <xsd:minInclusive value="0"/>
         <xsd:maxInclusive value="100"/>
      </xsd:restriction>
   </xsd:simpleType>

   <xsd:simpleType name="Pubs2003">
      <xsd:restriction base="xsd:date">
         <xsd:minInclusive value="2003-01-01"/>
         <xsd:maxInclusive value="2003-12-31"/>
      </xsd:restriction>
   </xsd:simpleType>

   <xsd:element name="rate" type="tns:RoyaltyRate"/>
   <xsd:element name="publicationDate" type="tns:Pubs2003"/>
...

다음 문서에는 위에서 정의한 요소의 유효 인스턴스가 포함되어 있다.

<x:rate xmlns:x="http://example.org/publishing">17.5</x:rate>
<x:publicationDate xmlns:x="http://example.org/publishing">2003-06-01</x:publicationDate>

XML Schema는 이러한 유형에 사용할 수 있는 facet을 정의하고 있다.(그림 1) 대부분의 facet은 모든 유형에 적용되지 않는다. (특정 유형에만 의미를 갖는다.)  대부분의 facet은 유형의 값 공간을 제한하지만, patter facet은 해당 유형의 사전공간(lexical space)을 제한한다. 값공간이나 사전공간을 제한하면 간접적으로 상대방도 제한하게 된다. (값공간을 제한하면 사전공간에 영향을 미치게 된다는 뜻) 위의 예에서는 기본 유형의 값 공간을 제한하고 있는데, 아래의 예에서는 regular expression을 사용하여 문자열의 사전공간을 제한한다.

...
   <xsd:simpleType name="SSN">
      <xsd:restriction base="xsd:string">
         <xsd:pattern value="\d{3}-\d{2}-\d{4}"/>
      </xsd:restriction>
   </xsd:simpleType>
   <xsd:simpleType name="PublisherAssignedId">
      <xsd:restriction base="xsd:string">
         <xsd:pattern value="\d{2}-\d{8}"/>
      </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:element name="authorId" type="tns:SSN"/>
   <xsd:element name="pubsAuId" type="tns:PublisherAssignedId"/>
   <xsd:element name="phone" type="tns:Phone"/>
...

다음 문서는 이렇게 정의된 요소들에 대한 적법한 인스턴스가 포함되어 있다.

<x:authorId xmlns:x="http://example.org/publishing">123-45-6789</x:authorId>
<x:pubsAuId xmlns:x="http://example.org/publishing">01-23456789</x:pubsAuId>
<x:phone xmlns:x="http://example.org/publishing">(801)390-4552</x:phone>

regular expression(pattern facet으로 지정된)에 맞는 문자열만 해당 유형의 유효 인스턴스로 간주된다.

<표 1> Facets

Facet 요소 

  설명

 xsd:enumeration

 해당 유형이 값은 나열된 값중 하나이어야 함

 xsd:fractionDigits

 소숫점 아랫자리의 수

 xsd:length

 문자열 기반 유형의 경우 문자의 수, binary 기반 유형의 경우 8진수(octet)의 수, 목록 기반 유형의 경우 아이템의 수

 xsd:maxExclusive

 유형의 값 공간중 제외할 상위 한도

 xsd:maxInclusive

 유형의 값 공간중 포함될 상위 한도

 xsd:maxLength

 문자열 기반 유형의 경우 문자의 최대수, binary 기반 유형의 경우 8진수(octet)의 최대수, 목록 기분 유형의 경우 아이템의 최대수

 xsd:minExclusive

 유형의 값 공간중 제외할 하위 한도

 xsd:minInclusive

 유형의 값 공간중 포함될 하위 한도

 xsd:minLength

 문자열 기반 유형의 경우 문자의 최소수, binary 기반 유형의 경우 8진수(octet)의 최소수, 목록 기분 유형의 경우 아이템의 최소수

 xsd:pattern

 유형이 매칭되어야 할 regular expression 기반의 패턴

 xsd:totalDigits

 숫자 기반의 유형에서 소숫점 이하 수의 최대 갯수

 xsd:whiteSpace

 공백문자 정규화 법칙

또다른 재미있는 facet으로는 xsd:enumeration이 있다. 이는 값공간을 나열된 값(enumerated values)로 제한하는 것이다. 다음은 xsd:NMTOKEN의 값공간을 4개의 지정된 나열값으로 제한하는 예이다.

...
   <xsd:simpleType name="PublicationType">
      <xsd:restriction base="xsd:NMTOKEN">
         <xsd:enumeration value="Book"/>
         <xsd:enumeration value="Magazine"/>
         <xsd:enumeration value="Journal"/>
         <xsd:enumeration value="Online"/>
      </xsd:restriction>
   </xsd:simpleType>
   <xsd:element name="pubType" type="tns:PublicationType"/>
...

아래 문서는 위에서 정의한 요소에 대한 유효 인스턴스를 담고 있다.

<x:pubtype xmlns:x="http://example.org/publishing">Online</x:pubtype>

<표 2> Simple 유형 구축 기법

 유도 유형 

 설명

 xsd:restriction

 새로운 유형은 기존 유형의 제한이다. 즉, 유효 값의 범위가 줄어든다.

 xsd:list

 새로운 유형은 다른 simple 유형의 공백문자로 구분된 목록이다.

 xsd:union

 새로운 유형은 2개 이상의 simple 유형의 union 이다.

유형의 값 공간을 제한하는 외에, 다른 유형의 list 또는 union인 새로운 simple 유형을 만들 수도 있다. xsd:restriction 대신 xsd:list 또는 xsd:union 요소를 사용하면 된다. (표 2 참조) xsd:list를 사용할 때 지정된 값공간으로부터 공백문자로 구분된 값 목록을 정의한다. 명심할 것은 xsd:list 또는 xsd:union을 사용할 때에는 xsd:restriction 과 같은 derivation hierarchy가 없기 때문에, 이 경우에는 유형 호환성이 성립되지 않는다는 것이다. 아래는 SSN 값의 목록으로서 AuthorList라는 새로운 유형을 정의하는 예이다.

...
   <xsd:simpleType name="AuthorList">
      <xsd:list itemType="tns:SSN"/>
   </xsd:simpleType>
   <xsd:element name="authors" type="tns:AuthorList"/>
...

다음 문서는 authors의 유효한 인스턴스를 담고 있다.

<x:authors xmlns:x="http://example.org/publishing">111-11-1111 222-22-2222 333-33-3333 444-44-4444</x:authors>

xsd:union의 경우, 여러개의 값 공간을 합한 새로운 값 공간의 새로운 유형을 생성할 수 있다. union 유형의 인스턴스는 지정된 값공간 중 어떤 하나로부터의 값을 가질 수 있다. 예를 들어, 다음의 AuthorId 유형은 SSN 값공간과 PublisherAssignedId 값공간을 결합하였다.

...
   <xsd:simpleType name="AuthorId">
      <xsd:union memberTypes="tns:SSN tns:PublisherAssignedId"/>
   </xsd:simpleType>
   <xsd:element name="authorId" type="tns:AuthorId"/>
...

다음 두가지는 모두 authorId 요소에 대한 유효한 인스턴스이다.

<x:authorId xmlns:x="http://example.org/publishing">111-11-1111</x:authorId>
<x:authorId xmlns:x="http://example.org/publishing">22-22222222</x:authorId>

XML Schema는 사용자정의 유형을 지원한다. 좀더 정확하게 말하자면 맞춤형 값/사전 공간을 지원한다. 이것이 XML의 강력한 기능중 하나이다. 대부분의 프로그래밍 언어는 이러한 기능을 허용하지 않으므로, 개발자들은 이러한 문제를 응용프로그램 코드 (일반적으로 property setter를 사용하여) 처리해야한다. 정확한 필요에 따라 맞춤형 값/사전 공간을 정의하는 능력이 있음으로써 오류 처리와 검증코드를 레이어 수준에서 처리가 가능하다.

Complex 유형 정의

XML Schema는 여러가지 simple 유형(이나 값 공간)을 배열하여 complex 유형이라고 하는 구조로 만들 수 있다. 새로운 complex 유형을 정의하려면 아래의 예와 같이 스키마의 target 네임스페이스내에서 xsd:complexType 요소를 사용하면 된다.

...
   <xsd:complexType name="AuthorType">
      <!-- compositor goes here -->
   </xsd:complexType>
...

xsd:complexType 요소는 compositor라는 것이 포함되어 있는데, 그것은 그 유형의 내용의 구성, 즉 콘텐트 모델을 서술한다. XML Schema는 3개의 compositor를 정의한다. 이들은 xsd:sequence, xsd:choice, xsd:all 을 포함한 complex 유형 정의에 사용될 수 있다.

Compositor 는 particle를 포함한다. particle은 다른 compositor, 요소 선언, wildcard, 모델 그룹 등을 포함한다. 속성 선언은 particle로 간주되지 않는다. 반복이 없기 때문이다. 따라서 속성 선언은 compositor 내에 위치하지 않고 complex type 선언 끝에 있는 compositor 이후에 위치한다.

<표 3> Complex 유형 Compositor

 Compositor

 설명

 xsd:sequence

 포함된 particle의 순서있는 sequence

 xsd:choice

 포함된 particle의 선택

 xsd:all

 모든 포함된 partile. 순서는 관계없음

An element declaration (xsd:element) is probably the most commonly used particle. The following complexType named AuthorType defines an ordered sequence of two element children and an attribute, each of a different simple type:

요소선언(xsd:element)는 가장 널리 사용되는 particle이다. 아래의 AuthrType 이라는 compelxType은 2개의 요소의 순서있는 sequence와 하나의 속성으로 구성된다. 각각은 simple 유형이다.

...
   <xsd:complexType name="AuthorType">
      <!-- compositor goes here -->
      <xsd:sequence>
         <xsd:element name="name" type="xsd:string"/>
         <xsd:element name="phone" type="tns:Phone"/>
      </xsd:sequence>
      <xsd:attribute name="id" type="tns:AuthorId"/>
   </xsd:complexType>

   <xsd:element name="author" type="tns:AuthorType"/>
...

xsd:complexType 요소내에 선언된 요소와 속성은 로컬로 간주된다. 로컬 요소와 속성은 정의된 해당 문맥 내에서만 사용될 수 있다. 이는 로컬 요소/속성이 인스턴스 문서에서 네임스페이스 정규화(qualified)가 필요한지에 관한 흥미로운 질문을 불러온다. 로컬 요소와 속성은 항상 target 네임스페이스로 정규화된 부모 요소(대부분 글로벌)내에 포함되므로, 정규화가 필요하지 않다고 주장할 수 있다. 이는 대부분의 프로그래밍 언어의 작동방식과 유사하다. 클래스를 네임스페이스 내에 선언하면, 클래스 이름만이 네임스페이스에 의해 정규화(qualified)되고, 로컬 멤버는 정규화되지 않는다. 

이러한 이유로 XML Schema에서 로컬 요소와 속성은 정규화하지 않는 것이 기본이다. 따라서, autho 요소의 적합한 인스턴스는 다음과 같은 형태를 갖는다.

<x:author xmlns:x="http://example.org/publishing"
   id="333-33-3333">
   <name>Aaron Skonnard</name>
   <phone>(801)390-4552</phone>
</x:author>

하지만 XML Schema에서는 로컬 요소/속성을 정규화할지 말지를 제어하는 방법이 있다. 아래 예와 같이 xsd:element/xsd:attribute 에 form 속성을 사용하거나, xsd:schema 에elementFormDefault/attributeFormDefault 를 사용하면 된다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   targetNamespace="http://example.org/publishing"
   xmlns:tns="http://example.org/publishing"
   elementFormDefault="qualified" 
   attributeFormDefault="qualified"
>
   ...
</xsd:schema>

이 스키마에 대해, 다음의 인스턴스는 유효한 인스턴스가 된다. (위에 있는 인스턴스는 유효하지 않다)

<x:author xmlns:x="http://example.org/publishing"
   x:id="333-33-3333">
   <x:name>Aaron Skonnard</x:name>
   <x:phone>(801)390-4552</x:phone>
</x:author>

대부분의 경우 스키마에 부합하기만 하면, 어떠한 네임스페이스 스타일을 사용하던 무방하다.

아울러 아래와 같이,  글로벌로 선언된 요소/속성을 complex 유형내에서 ref 속성을 사용하여 참조하는 방법도 가능하다.

...
   <!-- global definitions -->
   <xsd:attribute name="id" type="tns:AuthorId"/>
   <xsd:element name="name" type="xsd:string"/>
   <xsd:element name="author" type="tns:AuthorType"/>

   <xsd:complexType name="AuthorType">
      <!-- compositor goes here -->
      <xsd:sequence>
         <!-- reference to global element -->
         <xsd:element ref="tns:name"/>
         <xsd:element name="phone" type="tns:Phone"/>
      </xsd:sequence>
      <!-- reference to global attribute -->
      <xsd:attribute ref="tns:id"/>
   </xsd:complexType>
...

id와 name이 글로벌 요소이므로, 인스턴스 문서에서 정규화하여 사용해야 한다. "ref"를 사용하면 AuthorType 문맥 내에서 글로벌 요소를 사용할 수 있지만, 정규화 필요성은 변하지 않는다. phone 요소는 로컬로 정이되어 있으므로, form 을 어떻게 사용하느냐에 따라 정규화시켜야 할수도, 아닐 수도 있다. xsd:schema에서 elementFormDefault="unqualified"를 사용했다고 가정하면, 유효한 인스턴스는 아래와 같은 형태를 취한다.

<x:author xmlns:x="http://example.org/publishing"
   x:id="333-33-3333">
   <x:name>Aaron Skonnard</x:name>
   <phone>(801)390-4552</phone>
</x:author>

아래는 중첩된 complex 유형, 다른 compositer, 반복 particle을 사용하는 좀더 복잡한 예이다.

...
   <xsd:complexType name="AddressType">
      <xsd:all>
         <xsd:element name="street" type="xsd:string"/>
         <xsd:element name="city" type="xsd:string" minOccurs="0"/>
         <xsd:element name="state" type="tns:State" minOccurs="0"/>
         <xsd:element name="zip" type="tns:Zip"/>
      </xsd:all>
   </xsd:complexType>

   <xsd:complexType name="PublicationsListType">
      <xsd:choice maxOccurs="unbounded">
         <xsd:element name="book" type="xsd:string"/>
         <xsd:element name="article" type="xsd:string"/>
         <xsd:element name="whitepaper" type="xsd:string"/>
      </xsd:choice>
   </xsd:complexType>

   <xsd:complexType name="AuthorType">
      <xsd:sequence>
         <xsd:choice>
            <xsd:element name="name" type="xsd:string"/>
            <xsd:element name="fullName" type="xsd:string"/>
         </xsd:choice>
         <xsd:element name="address" type="tns:AddressType"/>
         <xsd:element name="phone" type="tns:Phone" 
            minOccurs="0" maxOccurs="unbounded"/>
         <xsd:element name="recentPublications"
            type="tns:PublicationsListType"/>     
      </xsd:sequence>
      <xsd:attribute name="id" type="tns:AuthorId"/>
   </xsd:complexType>

   <xsd:element name="author" type="tns:AuthorType"/>

이 예에서 AuthorType는 sequence와 속성으로 구성되는데, sequence 내에는 또다른 compositor인 choice와 3개의 요소가 포함된다. 이들 요소중 일부는 다른 사용자 정의 complex 유형인 AddressType 과 PublicationListType) 이기 때문에, 효과적으로 중첩된 구조를 정의하고 있다. choice란 "name" 또는 "fullName"요소 중 하나만 허용된다는 뜻이다. 마지막으로 AddressType에 있는 all 은 순서는 관계없다는 의미이다.

또한, phone 요소 선언은 minOccurs/maxOccurs 속성을 사용하여 횟수를 제한하고 있다. 횟수 제한은 complex 유형에 들어 있는 모든 particle에 적용할 수 있다. 기본 값은 1로서, 해당 particle이 지정된 위치에 정확하게 한번 등장해야 함을 의미한다. minOccurs="0"을 지정하면 해당 particle이 옵션이라는 뜻이며, maxOccurs="unbounded"는 particle이 얼마든지 반복될 수 있음을 의미한다. 아울러 minOccurs="3" 이나 maxOccurs="77"과 같이 임의의 한계를 설정할 수도 있다. occurence 제한을 compositor에 사용하면 해당 그룹 전체에 적용된다. (PublicationListType의 경우, choice에 횟수 제한을 적용하였다.) 아래는 새로운 AuthorType에 대한 유효한 인스턴스의 예이다.

<x:author xmlns:x="http://example.org/publishing"
   id="333-33-3333">
   <name>Aaron Skonnard</name>
   <address>
      <street>123 Main</street>
      <zip>84043</zip>
   </address>
   <phone>801-729-0924</phone>
   <phone>801-390-4555</phone>
   <phone>801-825-3925</phone>
   <recentPublications>
     <whitepaper>Web Service Abstractions</whitepaper>
     <book>Essential XML Quick Reference</book>
     <article>Web Services and DataSets</article>
     <article>Understanding SOAP</article>
     <book>Essential XML</book>
   </recentPublications>
</x:author>

기본으로 complex 유형은 폐쇄된(closed) 콘텐츠 모델이다. 이는 지정된 particle만이 인스턴스내에 나타나야 함을 의미한다. 하지만, XML Schema는 wildcard라는 것을 사용하여 개방형(open) 콘텐츠모델을 정의할 수 있다. complex 유형 내에 xsd:any를 사용하면, 해당 위치에 어떠한 요소도 들어갈 수 있음을 의미하여, 미리 예상할 수 없는 것에 대한 placeholder로 만들수 있다. 아울러 xsd:anyAttribute 를 사용하면 속성에 대한 placeholder도 정의할 수 있다.

...
   <xsd:complexType name="AuthorType">
      <!-- compositor goes here -->
      <xsd:sequence>
         <xsd:element name="name" type="xsd:string"/>
         <xsd:element name="phone" type="tns:Phone"/>
         <xsd:any minOccurs="0" maxOccurs="unbounded"/>
      </xsd:sequence>
      <xsd:anyAttribute/>
   </xsd:complexType>
   <xsd:element name="author" type="tns:AuthorType"/>
...

아래는 위에서 정의한 author 요소에 대한 유효한 인스턴스의 예이다.

<x:author xmlns:x="http://example.org/publishing"
   xmlns:aw="http://www.aw.com/legal/contracts"
   aw:auId="01-3424383"
>
   <!-- explicitly defined by the complexType -->
   <name>Aaron Skonnard</name>
   <phone>801-825-3925</phone>

   <!-- extra elements that replace wildcard -->
   <aw:contract xmlns:aw="http://www.aw.com/legal/contracts">
      <title>Essential Web Services Quick Reference</title>
      <deadline>2003-06-01</deadline>
   </aw:contract>
   ...
</x:author>

When using wildcards it's also possible to constrain the namespace the content actually comes from. Both xsd:any and xsd:anyAttribute come with an optional namespace attribute that may contain any of the values shown in Table 4. This makes it possible to be very specific about where the wildcard replacement content comes from. 

wildcard를 사용할 때, 내용이 올 네임스페이스를 한정하는 것도 가능하다. xsd:any와 xsd:anyAttribute 모두 옵션으로 네임스페이스 속성을 넣을 수 있는데, 표 4에 있는 값중 하나를 넣을 수 있다. 이렇게 하면, wildcard 내용을 한정할 수 있다.

<표 4> Wildcard 네임스페이스 속성

 Attribute 값 

 허용되는 요소

 ##any

 모든 네임스페이스의 요소

 ##other

 targetNamespace를 제외한 모든 네임스페이스의 요소

 ##targetNamespace

 targetNamespace에 있는 모든 요소

 ##local

 정규화되지 않은 요소(네임스페이스 없음)

 list of ns string

 네임스페이스 목록내에 있는 요소

wildcard를 사용할 경우, 스키마 프로세서가 검증과정에서 그 wildcard 내용을 어떻게 처리해야 하는지를 지정할 수 있다. xsd:any와 xsd:anyAttribute는 모두 processContents 속성이 있는데, lax/strict/skip 등 세가지 중 하나의 값을 가질 수 있다. 이값은 wildcard 위치에 들어오는 내용에 스키마 검증을 어떻게 수행해야 하는지를 나타낸다. Strict란 내용에 대해 반드시 검증해야 한다는 뜻이다. Lax는 schema 정보가 있을 경우에만 검증을 해야 함을 의미하고, skip은 schema 검증을 수행하지 말라는 의미이다.

 Let's look at an example that uses these attributes. The schema for SOAP 1.1 actually leverages wildcards and both of these attributes to define the structure of the soap:Header and soap:Body elements:

이들 속성을 사용하는 예를 살펴보자. SOAP 1.1 스키마는 실제로 wildcard를 활용하고 있으며, 이들 속성들을 soap:Header와 soap:Body 요소의 구조를 정의하고 있다.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"       
  xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/"         
  targetNamespace="http://schemas.xmlsoap.org/soap/envelope/" >
  ...
  <xs:element name="Header" type="tns:Header" />

  <xs:complexType name="Header" >
    <xs:sequence>
      <xs:any namespace="##other" minOccurs="0" 
       maxOccurs="unbounded" processContents="lax" />
    </xs:sequence>
    <xs:anyAttribute namespace="##other" 
     processContents="lax" />
  </xs:complexType>

  <xs:element name="Body" type="tns:Body" />

  <xs:complexType name="Body" >
    <xs:sequence>
      <xs:any namespace="##any" minOccurs="0" 
       maxOccurs="unbounded" processContents="lax" />
    </xs:sequence>
    <xs:anyAttribute namespace="##any" 
     processContents="lax" />
  </xs:complexType>
  ...
</xs:schema>

스키마에 따르면, soap:Header는 0개 이상의 요소를 포함할 수 있고, 속성도 갯수제한없이 포함시킬 수 있다. 요소나 속성 모두 targetNamespace가 아닌 네임스페이스에 속해야 한다. 반면 soap:Body의 경우 0개 이상의 요소와 갯수 제한없는 속성을 포함시킬 수 있는데, 네임스페이스는 어디라도 관계없다. 이 두가지 경우에서 ㄱ검증은 스키마 정보가 런타임으로 제공될 경우에만 수행된다. (lax) soap:Header 또는 soap:Body에 어떠한 내용이 들어올 지 예측할 수 있는 방법이 전혀 없기 때문에, wildcard는 유연하고도 개행된 프레임워크를 정의하는 방법을 제공한다.

스키마 찾기 및 관리

이 쯤에서 발생할 질문중의 하나는 XML Schema 프로세서가 주어진 인스턴스 문서에 대한 스키마 정의를 어떻게 실행중에 알아내는 방법이다. XML Schema 프로세서는 인스턴스 문서의 네임스페이스를 키로 해당하는 스키마를 찾아내지만, XML Schema 사양에는 정확히 어떻게 처리할지에 대해서는 정의되어 있지 않다. 대부분의 프로세서는 필요한 모든 스키마를 미리 스키마 캐시에 불러 올 수 있도록 허용한다. 그런 뒤 실행중에는 프로세서에게 스키마 캐시만 지정하면 특정 인스턴스에 필요한 스키마를 효과적으로 참조할 수 있다.

XML 스키마는 인스턴스문서에서 스키마 위치를 제공하는 방법도 정의하고 있다. 이는 아래와 같이 xsi:schemaLocation을 사용하면 된다.

<x:author xmlns:x="http://example.org/publishing"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://example.org/publishing pubs.xsd"
>
...

xsi:schemaLocation 속성은 네임스페이스명 목록과 URI 위치 쌍을 공백문제로 분리하여 제공할 수 있다. 이를 사용해 특정 스키마 파일을 찾아낼 수 있다. 하지만, 이것은 힌트에 불과할 뿐, 프로세서가 더 효과적인 메커니즘이 있을 경우, 다른 곳을 찾을 수도 있다.

결론

XML 스키마는 XML을 위한 표현력 짱 유형시스템을 제공하여, 아주 강력한 서비스를 제공할 수 있다. 이 글에서는 simple유형 및 complex 유형 정의를 포함한 기본적인 XML 스키마 정의에 대해 다루었다. Simple 유형 정의를 사용하면, 문자만이 가능한 유형 및 속성에 맞춤형 값공간을 정의할 수 있다. Complex 유형 정의를 사용하면 Simple 유형을 배열하여 구조체를 만들 수 있다.

XML Schema는 실제로 여기에서 논의한 것보다 훨씬 더 많은 기능이 있다. 예를 들어 complex 유형 정의는 기존의 유형을 확장하거나 제한하는 등의 유도 유형을 만들 수 있어, 객체지향의 클래스 상속성과 비슷한 방법으로 complex 유형의 계층화를 정의할 수 있다. Complex 유형 계층화를 사용하면, 인스턴스 문서에서 대체 기법도 가능하다. XML Schema는 또한 XML Schema 정의를 여러개의 파일과 네임스페이스로 분리한 후, 나중에 포함(include) 또는 수입(import)하는 방법을 통해 재사용성, 편의성 및 유지관리성이 증가시킬 수 있다. 이러한 고급 주제는 다음 글을 위해 남겨둔다.

XML Schema에 관한 좀더 자세한 내용은 Essential XML Quick Reference를 참고하라. XML Schema 챕터에는 각각의 구조와 데이터유형에 관한 간단한 설명과 예제가 포함되어 있다.

.===

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


Posted by 푸른하늘 푸른하늘이

댓글을 달아 주세요

공간정보/표준2018. 3. 22. 16:32

모든 XML 기술중에서 XML Schema는 소프트웨어 개발자에게 가장 중요하다. XML 문서에 유형(type) 정보를 넣을 수 있게 되었기 때문이다.

먼저, XML Schema 이전 상황부터 살펴보자. XML 1.0 사양은 XML 어휘를 서술하는 내장 문법인 DTD(Document Type Definitions) 와 함께 출현했다. XML 1.0 이 그 전신인 SGML (Standard Generalized Markup Language)의 문법을 물려받은 것을 고려할 때, DTD는 사실 상당한 기간을 살아남았다고 할 수 있다.

DTD를 사용하면 XML 문서의 구조를 서술할 수 있다. 예를 들어, 직원 정보를 서술하기 위해 다음과 같은 XML 어휘를 사용한다고 해보자.

<employee id="555-12-3434">
  <name>Monica</name>
  <hiredate>1997-12-02</hiredate>
  <salary>42000.00</salary>
</employee>

다음은 이 문서의 구조를 서술하는 DTD 이다.

<!-- employee.dtd -->
<!ELEMENT employee (name, hiredate, salary)>
<!ATTLIST employee
          id CDATA #REQUIRED>
<!ELEMENT name (#PCDATA)>
<!ELEMENT hiredate (#PCDATA)>
<!ELEMENT salary (#PCDATA)>

이 DTD는 DOCTYPE 선언을 통해 원래의 문서에 연결할 수 있다. 아래는 그 예이다.

<!DOCTYPE employee SYSTEM "employee.dtd">
<employee id="555-12-3434">
  <name>Monica</name>
  <hiredate>1997-12-02</hiredate>
  <salary>42000.00</salary>
</employee>

DTD를 사용하는 가장 큰 장점은 검증이다. 검증을 할 때, XML 1.0 파서는 이 XML 1.0 파일을 읽어들이면서 연계된 DTD도 함께 읽어들여, 정의에 합당한지 검증한다. DTD를 사용하여 검증을 하게 되면, 어플리케이션에서 다루어야할 많은 상당한 양의 오류 처리를 감소시킬 수 있다.

DTD가 많은 SGML 기반의 전자출판 응용에는 잘 어울리지만, 현재의 Web 환경과 같은 소프트웨어 개발 분야에 적용하면서 DTD의 한계는 금방 드러나게 되었다. DTD의 주요한 한계로는 DTD 문법이 XML 기반이 아니며, 네임스페이스를 지원하지 않고, 전형적인 프로그래밍 언어의 데이터타입을 지원하지 않으며, 맞춤형 유형을 정의할 수 없다는 것 등이다.

DTD 문법 자체가 XML이 아니기 때문에, 프로그램적으로 정의를 처리할 때, 표준 XML 도구들을 사용할 수 없다. 대부분의 XML 1.0 프로세서는 DTD 검증을 지원하기는 하나, DTD 문법의 복잡성으로 인해 DTD에 있는 정보에 대한 프로그램적 접근을 지원하지 않는다.

DTD는 XML 네임스페이스가 존재하기 전에 만들어졌으므로, 두가지가 함께 잘 작동하지 않는다는 건 놀라운 일이 아니다. 사실 DTD를 사용하여 네임스페이스를 지원하는 문서를 만든다는 것은 동그란 구멍에 네모난 통을 끼우려는 것과 비슷하다. 이 작업의 끔찍한 일면을 알고싶다면, 내가 네임스페이스 지원 DTD를 제공하는 XML Files 컬럼을 쓴 2001년 5월 작업을 확인하기 바란다. 결론적으로, 대부분의 개발자들은 DTD나 네임스페이스 둘중의 하나만 사용하였고, 둘 다 사용하는 경우는 거의 없었다.

DTD는 프로그램 데이터 유형이 존재하지 않는 문서 중심 시스템을 위해 개발되었다. 그 결과 속성을 기술하는 단 몇가지의 유형 식별자만이 존재한다. (그림 1 참고) 이러한 유형식별자도 일반적인 프로그램 언어에서 사용하는 유형과 전혀 다르다. 이 식별자들은 텍스트(CDATA)의 특별한 케이스에 불과하다. 아울러 이러한 유형은 텍스트만 있는 요소에는 적용할 수 없고 속성에만 적용할 수 있다.

<그림 1> DTD 유형의 종류

마지막으로 DTD 유형 시스템은 확장이 불가능하다. 즉, 그림 1에 있는 유형만 쓸수 있다는 것이다. 자신의 문제 영역에서 의미있는 맞춤형 유형을 생성하는 것은 DTD로는 불가능하다. 이러한 한계만으로도 XML Schema가 새롭고도 흥미로운 미래를 제안하자 XML 개발자는 DTD로부터 탈출할 충분한 이유가 되었다.

XML Schema 기본

XML Schema는 그 자체가 XML 어휘를 사용하여 XML 인스턴스 문서를 기술한다. "인스턴스(instance)"라는 용어는 스키마가 여러 문서의 클래스를 서술하고, 여러 개의 다른 인스턴스가 존재할 수 있기 때문이다. (그림 2) 이것은 오늘날 객체지형 시스템에서 클래스와 객체와의 관계와 비슷하다. 클래스는 스키마에 해당하고, 객체는 XML 문서에 해당한다. 따라서 XML Schema를 사용하는 동안 하나 이상의 문서와 작업을 하게 된다.


<그림 2> 네임스페이스 식별자 링크

스키마 정의에 사용되는 요소는 http://www.w3.org/2001/XMLSchema 네임스페이스에 들어 있다. 나는 이 문서에서 이 네임스페이스를 xsd로 결합할 것이다. 다음은 기본 스키마 템플릿이다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://example.org/employee/">
  <!-- definitions go here -->
</xsd:schema>

스키마 정의에는 반드시 최상위 요소인 xsd:schema가 있어야 한다. xsd:schema 내에는 다양한 요소들이 중첩될 수 있는데, xsd:element, xsd:attribute, xsd:complexType 등이 그 예이다. 

스키마 정의 자체가 XML 문서이므로 DTD의 한계 중에서 첫번째 한계가 해결된다. 스키마 정의도 표준적인 XML 1.0 도구 및 서비스(예: DOM, SAX, XPath, XSLT 등)를 사용하여 처리할 수 있다. 이와 같은 간편성으로 인해 스키마 도구가 홍수처럼 쏟아져 나오게 되었다.

XML Schema 와 네임스페이스

xsd:schema 요소 내에 위치한 정의는 targetNamespace 속성에 정의된 네임스페이스로 자동적으로 연결된다. 위의 예의 경우, 스키마 정의는 http://example.org/employee 네임스페이스에 연계된다.

네임스페이스 식별자는 XML 문서와 해당 Schema 정의를 연결하는 키이다. (그림 2 참고) 예를 들어, 다음의 XML 인스턴스 문서는 http://example.org/employee/ 네임스페이스에서 온 employee 요소를 포함한다.

<tns:employee xmlns:tns="http://example.org/employee/" />

employee 요소의 네임스페이스는 스키마 정의의 targetNamespace와 동일하다.

employee 요소를 처리하면서 스키마의 장점을 활용하려면, 처리기가 올바른 스키마 정의를 찾을 필요가 있다. 스키마 처리기가 특정 네임스페이스를 위한 스키마 정의 파일을 찾는 방법은 사양에 정의되어 있지 않다. 그러나 대부분의 처리기는 스키마를 메모리 캐시에 불러들인 후, 문서 처리에 사용하는 것을 허용한다. 예를 들어 아래의 JScript® 기반 코드은 MSXML 4.0을 사용한 간단한 방법을 나타낸 것이다.

var sc = new ActiveXObject("MSXML2.XMLSchemaCache.4.0);
sc.add("http://example.org/employee/", "employee.xsd");
var dom = new ActiveXObject("MSXML2.DOMDocument.4.0");
dom.schemas = sc;

if (dom.load("employee.xml")) 
  WScript.echo("success: document conforms to Schema");   
else
  WScript.echo("error: invalid instance");

Microsoft® .NET 이나 기타 대부분의 XML Schema 처리기도 이와 비슷한 방법으로 작동한다.

아래(역자 주 : XML0204.exe 인데 링크가 깨져서 파일은 없음)는 명령어 방식의 검증 유틸리티로서, 이 글에 논의된 원칙들을 시험해 볼 수 있다. 이 검증 유틸리티에서는 검증하고자 하는 인스턴스 문서와 함께, 필요에 따라 많은 스키마 정의 파일을 지정할 수 있다. 명렁어 사용법은 다음과 같다.

c:>validate instance.xml -s schema1.xsd -s schema2.xsd ...

XML Schema는 아울러 schemaLocation 속성을 사용하여, 인스턴스 문서내에서 필요한 스키마 정의문서의 소재를 제공할 수도 있다. schemaLocation속성은 http://www.w3.org/2001/XMLSchema-instance 네임스페이스에 있으며, 이 네임스페이스는 인스턴스 문서에서만 사용되는 속성만 특별히 담고 있다. 나는 지금부터 이 네임스페이스를 xsi 접두사와 결합시켜 사용하겠다. xsi:schemaLocation 속성은 아래의 예와 같이 공백문자로 구분된 네임스페이스 식별자와 URL의 쌍으로 이루어진다.

<tns:employee xmlns:tns="http://example.org/employee/"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://example.org/employee/ 
                      http://develop.com/aarons/employee.xsd"
/>

이 경우, 처리기가 http://example.org/employee/ 네임스페이스를 위한 적절한 스키마 정의에 이미 접근하지 못했을 경우, http://develop.com/aarons/employee.xsd 로부터 다운로드 받을 수 있다.

요소와 속성(Elements and Attributes)

요소와 속성은 targetNamespace의 일부로서 정의될 수 있다. 각각 xsd:element와 xsd:attribute 요소를 사용한다. 예를 들어 아래와 같은 인스턴스 문서를 기술하려고 한다고 하자.

<tns:employee xmlns:tns="http://example.org/employee/"
  tns:id="555-12-3434">
  <tns:name>Monica</tns:name>
  <tns:hiredate>1997-12-02</tns:hiredate>
  <tns:salary>42000.00</tns:salary>
</tns:employee>

가장 간단한 방법은 다음과 같은 스키마 정의를 사용하면 된다.  

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://example.org/employee/">
  <xsd:element name="employee"/>
  <xsd:element name="name"/>
  <xsd:element name="hiredate"/>
  <xsd:element name="salary"/>
  <xsd:attribute name="id"/>
</xsd:schema>

주목할 점은 xsd:element와 xsd:attribute 선언을 xsd:schema 요소에 넣기만 함으로써, 자동적으로 이들이 http://example.org/employee/ 네임스페이스에 연결되었다는 것이다. 이러한 선언은 스키마에서 global 로 간주된다. 이들이 최상위 xsd:schema 요소의 자식요소(child)이기 때문이다.

이 스키마에서는 이들 요소/속성이 http://example.org/employee/ 네임스페이스의 일부라고 지정하였기 때문에, (위에서 보인 원래의 인스턴스처럼) 인스턴스 문서에서 해당 네임스페이스에 연결시켜야 한다. 인스턴스에 네임스페이스를 미세하게 변경해도 인스턴스가 무효가 된다. 예를 들어, 다음의 문서는 무자격(unqualified) name, hiredate, salary 요소 그리고 무자격 id 속성이 포함되어 있다.

<tns:employee xmlns:tns="http://example.org/employee/"
  id="555-12-3434">
  <name>Monica</name>
  <hiredate>1997-12-02</hiredate>
  <salary>42000.00</salary>
</tns:employee>

위의 스키마 정의에서는 이들 요소/속성이 http://example.org/employee/ 네임스페이스에 속한다고 했지만, 여기에서는 이들 요소/속성이 네임스페이스에 연결되어 있지 않아 이 인스턴스가 무효가 된 것이다.

이번엔 훨씬 더 미세한 변화로서, 네임스페이스 접두사 대신 기본 네임스페이스 선언을 사용하는 경우를 살펴보자.

<employee xmlns="http://example.org/employee/"
  id="555-12-3434">
  <name>Monica</name>
  <hiredate>1997-12-02</hiredate>
  <salary>42000.00</salary>
</employee>

이 경우 모든 요소들은 기본 네임스페이스(http://example.org/employee/)에 연결되었지만, id 속성만은 여전히 무자격이다. 기본 네임스페이스는 속성에는 적용되지 않기 때문이다. 그 결과 이 문서 인스턴스도 무효로 간주된다.

보시는 바와 같이, XML 네임스페이스는 XML Schema의 가장 핵심에 있다. XML Schema를 사용하려면 네임스페이스의 작동방법에 대해 완전히 이해해야 한다. 인스턴스 문서가 스키마에 지정된 것과 일치하지 못하면 무효화되기 때문이다.

아울러 이 간단한 예제에는 요소의 내용 및 네임스페이스 내의 요소간의 구조적 관계등에 아무런 제한도 없다는 것도 알 수 있다. 이 스키마는 아래의 DTD와 동등하다. (단 속성 선언은 생략)

<!ELEMENT employee ANY>
<!ELEMENT name ANY>
<!ELEMENT hiredate ANY>
<!ELEMENT salary ANY>

따라서 다음의 XML 인스턴스 문서가는 전혀 이치에 닿지 않지만, 스키마에는 적합하다.

<tns:name xmlns:tns="http://example.org/employee/">
  <tns:employee>
    <tns:hiredate>42.000</hiredate>
    <tns:salary tns:id="555-12-3434">Monica</tns:salary>
  </tns:employee>
</tns:name>

XML Schema는 Complex 유형 선언을 통해 요소의 구조를 기술할 수 있다.

Complex 유형 선언 

DTD를 사용할 경우, 요소의 내용 모델은 다음과 같이 ELEMENT 선언에서 정의할 수 있다.

<!ELEMENT employee (name, hiredate, salary)>

이 ELEMENT 선언은 employee 요소에 name 요소, hiredate 요소, salary 요소 순으로 들어 있음을 말하고 있다.

XML Schema에서는 xsd:complexType 요소와 xsd:element 선언을 중첩시킴으로써 요소의 내용 모델을 정의할 수 있다. 다음은 그 예이다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://example.org/employee/">
  <xsd:element name="employee">
    <xsd:complexType>
      <!-- employee's content model goes here -->
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

XML Schema 모델은 변수에 유형선언을 결합한다는 의미에서 좀더 프로그래밍 언어와 비슷하다. xsd:complexType을 사용하면 어떤 구조를 가진 요소의 유형을 선언할 수 있다.  요소 선언 내에 xsd:complexType 을 중첩시키면 요소(변수와 같이)에 효과적으로 결합시킬 수 있다. 유형선언이 DTD로부터 XML Schema로의 중요한 패러다임 이동이다.

xsd:complexType 요소의 내부에 넣는 것은 DTD ELEMENT 선언 다음 괄호 안에 무엇을 넣는 것과 유사하다. 위에 있는 employee ELEMENT 선언은 name, hiredate, salary 요소의 순서있는 나열을 정의한다. 콤마 대신 pipe(|) 를 사용하면 하나의 요소를 선택한다는 의미이다.

<!ELEMENT employee (name | hiredate | salary)>

XML Schema에서는 compositor 요소를 통하여 내용모델의 특성을 정의할 수 있다. compositor는 xsd:complexType 요소의 자식요소로 중첩시켜 사용한다. XML Schema는 xsd:sequence, xsd:choice, xsd:all 등 세가지 compositor가 있다.

<그림 3> Complex 유형의 Compositor

Compositor 

 동등한 DTD 표현

 정의

 xsd:sequence

 콤마로 분리한 그룹

 아이템들의 순서있는 나열

 xsd:choice

 Pipe(|)로 분리한 그룹

 포함된 아이템중 하나의 선택

 xsd:all

 -

 순서에 관계없이 모든 아이템 포함


xsd:sequence와 xsd:choice 요소는 위에서 설명한 DTD 예제들과 동등하다. 하지만, xsd:all은 새로운 개념으로서, 순서에 관계없이 모든 아이템으로 구성되는 내용모델을 지정한다. 이는 DTD 문법에 정의되어 있지않다. 구지 DTD를 이용하여 이와 같은 의미론을 정의하려면 아래와 같이 모든 요소를 명시적으로 나열하면 된다.

<!ELEMENT employee ( (name, hiredate, salary) |
                               (name, salary, hiredate) |
                               (hiredate, name, salary) |
                               (hiredate, salary, name) |
                               (salary, name, hiredate) | 
                               (salary, hiredate, name) ) >

이와 같이, 순열과 조합으로 인해 짜증이 나기 시작할 것이다. XML Schema는 "all" compositor도 sequcen와 choice처럼 1급 compositor이기 때문에 훨씬 깔끔하다. 

Compositor 요소는 글로벌 요소선언, 로컬 요소 선언, 다른 compositor, 기타 와일드카드나 group 참조와 같은 기타 구조요소에 대한 참조를 포함할 수 있다. 그림 4에 표시한 예제 스키마는 xsd:complexType에서 다른 부분에 정의된 글로벌 요소를 참조하는 예이다.

<그림 4> 글로벌 요소 참조의 예

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

  <xsd:element name="employee">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="tns:name"/>
        <xsd:element ref="tns:hiredate"/>
        <xsd:element ref="tns:salary"/>
      </xsd:sequence> 
    </xsd:complexType>
  </xsd:element>

  <xsd:element name="name"/>
  <xsd:element name="hiredate"/>
  <xsd:element name="salary"/>
</xsd:schema>

이상에서 알 수 있는 것과 같이 ref 속성은 접두사가 있는 요소명을 취한다. 스키마에서 글로벌 요소를 선언하면, 이는 자동적으로 targetNamespace로 연계된다는 것을 기억하라. 글로벌요소를 이름으로 참조하면, 이들은 정규화 이름으로 취급된다. 만약 ref="tns:name" 대신 ref="name"을 사용한다면, 스키마 처리기는 아무런 네임스페이스에도 속하지 않는 name 요소 혹은 기본 네임스페이스에 있는 name 요소를 찾게 된다. 하지만 이 경우, name요소는 http://example.org/employee 네임스페이스에 속해 있으므로, 찾을 수 없게되고, 유효하지 못한 XML 문서가 된다.

http://example.org/employee/ 네임스페이스를 어떤 문서의 기본 네임스페이스로 지정할 경우, 그림 5와 같이 접두사를 사용하지 않고도 글로벌 요소를 참조할 수 있다. (즉, ref="name"이 적법)

<그림 5> 네임스페이스 접두사를 사용하지 않는 방법

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://example.org/employee/"
  targetNamespace="http://example.org/employee/">

  <xsd:element name="employee">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element ref="name"/>
        <xsd:element ref="hiredate"/>
        <xsd:element ref="salary"/>
      </xsd:sequence> 
    </xsd:complexType>
  </xsd:element>

  <xsd:element name="name"/>
  <xsd:element name="hiredate"/>
  <xsd:element name="salary"/>
</xsd:schema>

그림 4와 그림 5의 스키마 샘플은 논리적으로 동등하다. 직렬화 형태가 다를 뿐이다. 두가지 모두 employee요소의 내용을 제한하고 있다. employee 요소는 name, hiredate, salary요소를 반드시 포함해야 하고, 이들 모두 http://example.org/employee/ 네임스페이와 연결되어 있어야 한다.

로컬요소 선언

인스턴스 문서에서 사용할 유일한 top-level 요소는 employee이므로 name, hiredate, salary를 글로벌 요소로 선언할 이유는 전혀 없다. 따라서 name, hiredate, salary요소를 employee 요소의 내용 모델 내에서 로컬로 선언할 수 있다.

예를 들어 그림 6에 있는 스키마에는 로컬 요소의 시퀀스를 포함하는 employee 요소 선언이 포함되어 있다. 이 예에서 name, hiredate, salary요소는 실제 employee요소의 일부로 선언되어 있어 이 인스턴스의 다른 곳에서는 사용할 수 없다. employee 요소 선언은 최상위 xsd:schema 요소의 자손으로 글로벌하게 나타나는 오직 하나의 요소이다. 이것은 재미있는 질문을 유도한다. 로컬요소는 targetNamespace에 연결되어 있는 것인가?

<그림 6> 로컬 요소 선언

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://example.org/employee/">
  <!-- global element declarations -->
  <xsd:element name="employee">
    <xsd:complexType>
      <xsd:sequence>
        <!-- local element declarations -->
        <xsd:element name="name"/>
        <xsd:element name="hiredate"/>
        <xsd:element name="salary"/>
      </xsd:sequence> 
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

로컬 스콥과 네임스페이스

이 질문에 대한 해답을 이해하기 쉽도록 C#과 같이 네임스페이스를 지원하는 프로그래밍 언어의 간단한 예를 생각해 보자. 아래의 C# 클래스 선언은 "example" 네임스페이스 내에 정의되어 있다.  

namespace example {
  public class employee {
    public string name;
    public string hiredate;
    public double salary;
  }
}

이 네임스페이스에서 어떤 식별자가 실제로 보일 것인가? 단하나! employee 뿐이다. name, hiredate, salary 식별자는 employee 클래스 내에서만 보인다. 따라서, 아래에서 보는 바와 같이 employee는 네임스페이스 식별자를 붙여 정규화(qualified)해야 하지만, name 등의 로컬 멤버는 불가능하다.

// employee is namespace-qualified
example.employee c = new example.employee();
// local members are unqualified

c.name = "Monica";
c.hiredate = "1997-12-02";
c.salary = 42000.00;

// this does not work, nor make sense
// c.example.name = "Monica";

XML 스키마 설계자는 로컬 스콥의 적용에 대해 고려한 것처럼 보인다. 기본적으로 이와 동일하게 작동되기 때문이다. XML Schema에서도 인스턴스 문서에서 글로벌 요소만 정규화(qualified) 시켜야 하며, 로컬 요소는 unqualified하게 남겨두어야 한다. 아래는 그림 6에서 보인 스키마의 유효한 인스턴스 문서의 예이다.

<!-- global element qualified -->
<tns:employee xmlns:tns="http://example.org/employee/">
  <!-- local elements unqualified -->
  <name>Monica</name>
  <hiredate>1997-12-02</hiredate>
  <salary>42000.00</salary>
</tns:employee>

이 인스턴스를 변경하여 name, hiredate, salary요소를 http://example.org/employee 네임스페이스에 정규화시킬 경우, 스키마에 맞지 않게 된다. 기본 네임스페이스 선언을 아주 미묘하게만 변화시켜도 이런 일이 발생함을 기억해야 한다.

이러한 방식을 모두다 좋아하는 건 아니기 때문에 XML Schema 설계자는 로컬 요소를 인스턴스에서 정규화(qualified)시킬 것인지 말것인지를 제어할 수 있도록 만들었다. 아래와 같이 form 속성을 사용하여 요소별로 제어하면 된다.

<xsd:element name="employee">
  <xsd:complexType>
    <xsd:sequence>
      <!-- local element declarations -->
      <xsd:element name="name" form="qualified"/>
      <xsd:element name="hiredate"/>
      <xsd:element name="salary" form="qualified"/>       
    </xsd:sequence> 
  </xsd:complexType>
</xsd:element>

이 employee 요소에 대한 적합한 인스턴스는 아래와 같이 자식요소로서 정규화 name 요소, 비정규화 hiredate 요소, 정규화 salary 요소가 순서대로 나와야 한다.

<tns:employee xmlns:tns="http://example.org/employee/">
  <tns:name>Monica</tns:name>
  <hiredate>1997-12-02</hiredate>
  <tns:salary>42000.00</tns:salary>
</tns:employee>

또, 아래와 같이 elementFormDefault 속성을 사용하면 스키마에 있는 모든 로컬요소 선언의 기본 설정을 뒤집을(toggle) 수 있다.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://example.org/employee/"
  elementFormDefault="qualified">
     •••
</xsd:schema>

이렇게 하면 기본으로 인스턴스에서 모든 로컬 요소를 정규화(qualified)시켜야 한다. (특정한 요소에 대해 form 속성으로 설정을 엎어쓰지 않았다는 가정하에), 다음은 그 예이다.

<tns:employee xmlns:tns="http://example.org/employee/">
  <tns:name>Monica</tns:name>
  <tns:hiredate>1997-12-02</tns:hiredate>
  <tns:salary>42000.00</tns:salary>
</tns:employee>

이 경우 모든 요소가 정규화요소이므로, 기본 네임스페이스 선언을 사용할 수 있고, 다음 인스턴스가 유효하게 된다.

<employee xmlns="http://example.org/employee/">
  <name>Monica</name>
  <hiredate>1997-12-02</hiredate>
  <salary>42000.00</salary>
</employee>

횟수 제한(Occurrence Constraints)

DTD에서는 *. +. ? 등의 변경자(modifier)를 사용하여 내용모델에서 요소의 등장횟수를 제어할 수 있다. XML Schema는 이러한 변경자를 사용하지 않고 단순히 minOccrs와 maxOccurs 등 두가지 속성을 정의한다. 이 속성들은 요소 선언, compositor, 기타 몇가지 스키마 구조에서 사용될 수 있다.

아이템이 등장할 최소횟수와 최대횟수는 각각 minOccurs와 maxOccurs를 사용하여 지정한다. 이 속성의 기본값은 모두 1이다. maxOccurs의 값으로 "unbounded"를 사용하면 등장회수를 제한하지 않는다는 뜻이다.

다음의 DTD ELEMENT 선언을 살펴보자:

<!ELEMENT employee ( (fname, (middle | mi)?, lname, lname?), (project, role)* )>

이 ELEMENT 선언은 그림 7과 같이 minOccurs/maxOccurs과 여러개의 중첩된 compositor를 사용하여 XML Schema로 다시 작성할 수 있다.

<그림 7> minOccurs/maxOccurs 사용 예

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://example.org/employee/">
  <xsd:element name="employee">
    <xsd:complexType>
      <xsd:sequence>

        <xsd:sequence>
          <xsd:element name="fname/>
          <xsd:choice minOccurs="0">
            <xsd:element name="middle"/>
            <xsd:element name="mi"/>
          </xsd:choice>
          <xsd:element name="lname" maxOccurs="2"/>
        </xsd:sequence>

        <xsd:sequence minOccurs="0" maxOccurs="unbounded">
          <xsd:element name="project"/>
          <xsd:element name="role"/>
        </xsd:sequence>        

      </xsd:sequence> 
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

Complex 유형과 속성

DTD의 경우, 속성은 특정 요소를 위해 정의된다. 다음의 ATTLIST 선언은 employee 요소의 id 속성에 연결되어 있다.

<!ELEMENT employee (name, hiredate, salary)>
<!ATTLIST employee id CDATA #REQUIRED>

DTD에서는 글로벌 속성을 정의하는 것이 불가능하다. DTD 속성은 위와같이 반드시 특정 요소에 연결시켜야 한다.

XML Schema는 요소와 마찬가지로 속성도 글로벌 또는 로컬로 선언할 수 있다. 글로벌 속성은 최상위 xsd:schema 요소 내에 xsd:attribute 를 사용하여 정의한다. 최초의 스키마 예제는 id라는 글로벌 속성을 정의하였다. 글로벌 속성은 어디에서 사용될지 모르는 경우를 위한 의도이다.

속성을 xsd:complexType 정의내에 포함시킬 수 있다. 이 경우에는 해당 유형의 로컬 속성이 된다. xsd:complex 요소 내부에 사용하면, xsd:attribute 요소는 반드시 child compositor뒤에 나와야 한다. 다음은 그 예이다.

  <xsd:element name="employee">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="name"/>
      </xsd:sequence>
      <xsd:attribute name="id"/>
    </xsd:complexType>
  </xsd:element>

글로벌 속성은 요소와 마찬가지로 인스턴스문서에서 정규화(qualify)해야 하며, 로컬 속성은 정규화해서는 안된다. 아래는 위에서 정의한 employee요소에 대한 유효한 인스턴스이다.

<tns:employee xmlns:tns="http://example.org/employee/"
       id='555-12-3434'>
    <name>Monica</name>
</tns:employee>

그러나 로컬 속성을 정규화시키고 싶을 경우, 요소와 마찬가지로 form 속성이나 attributeFormDefault 속성을 사용하여 이러한 방식을 변경시킬 수 있다. 

Named 유형과 재사용

이제까지 나는 요소의 유형(혹은 구조)를 xsd:complexType 정의를 사용하여 정의하였다. 그러나 이러한 유형정의는 named가 아니다. 새롭게 선언된 요소에 연결되기 때문이다. 이 접근법은 C++에서 anonymous 유형을 사용하는 것과 비슷하다. 예를 들어, 아래의 C++ 코드는 pt 변수를 anonymous 구조에 지정한 것이다.

struct {
  double x;
  double y;
} pt;

이러한 anonymous 유형은 명백한 단점이 있다. 재사용할 수 없다는 것이다. 따라서 대부분의 C++ 개발자들은 named 유형을 사용한다.

struct Point {
  double x;
  double y;
};
Point pt1;

XML 스키마는 named 유형도 지원한다. 대부분의 개발자들은 재사용 가능성때문에 이러한 접근법을 좋아한다. 하나의 스키마에서 named 유형을 재사용하는 것 뿐만 아니라, xsd:include와 xsd:import를 사용하면 여러 스키마에서 named 유형을 재사용할 수 있다.

이름은 xsd:complexType에서 name 속성을 통해 부여할 수 있다. 요소선언을 type 속성을 사용하면 named 유형에 결합할 수 있다. 아래의 스키마는 이러한 요소 바인딩의 예를 보인 것이다.

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

  <xsd:complexType name="EmployeeType">
    <xsd:sequence>
      <xsd:element name="name"/>
      <xsd:element name="hiredate"/>
      <xsd:element name="salary"/>       
    </xsd:sequence> 
  </xsd:complexType> 

  <xsd:element name="employee" type="tns:EmployeeType"/>

</xsd:schema>

여기에서 xsd:complexType 정의는 이 스키마에서 글로벌이기 때문에, targetNamespace에 자동으로 연결된다. 즉, type 속성에서 EmployeeType을 참조하려면 반드시 정규화(qualified) 이름을 사용해야 한다는 뜻이다. 이 글 위쪽에서 정의한 anonymous xsd:complexType 을 named 유형으로 변경하는 것은 어렵지 않다. 이러한 두 기법간의 변경은 매우 쉽다.

named 유형을 사용하는 또다른 장점으로, 원하지 않는다면 스키마에서 글로벌 요소를 사용할 필요가 없다는 것이다. 그대신 인스턴스 문서에서 xsi:type 속성을 통해 요소의 type을 명시적으로 지정할 수 있다. xsi:sype 은 http://www.w3org/2001/XMLSchema-instance 네임스페이스에 있다. 예를들어, 이러한 임무를 수행하기 위해 아래와 같은 인스턴스를 고려해 보자.

<foo xsi:type="tns:EmployeeType"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:tns="http://example.org/employee/">

  <name>Monica</name>
  <hiredate>1997-12-02</hiredate>
  <salary>42000.00</salary>
</foo>

이 foo 요소는 스키마 어디에서도 정의되어 있지 않으나, 나는 이 유형을 명시적으로 지정하였다. 이것만으로도 XML 프로세서가 이 내용을 어떻게 처리해야 할 지 파악한다. 이 기법은 대부분의 프로그래밍 언어에서의 cast 기법과 유사하다.

데이터 유형 소개(Introducing Data Types)

이제까지 xsd:attribute, xsd:element, xsd:complexType 정의를 통해 문서의 자세한 구조를 서술하는 방법을 다뤘다. 하지만, 하지만, 지금까지 본 스키마에서는 name, hiredate, salary 요소 및 id 속성의 특성에 대해서는 설명하지 않았다. 지금 시점에는 이 요소들과 속성에는 아무것이나 들어가도 적법하다. name, hiredate, salary 요소와 id 속성은 특정한 형식에 따른 문자만 포함되어야 한다.

DTD에서는 #PCDATA 토큰을 통하여 어떤 요소에 오직 문자만이 포함되도록 지정할 수 있다.

<!ELEMENT name (#PCDATA)>

불행히도, 그 요소내에 포함된 문자열 형식은 아무 것도 지정할 수 없다.


<그림 8> XML Schema 내장 데이터 유형

이것이 XML Schema가 과거의 DTD에 비해 크게 약진한 것이다. (특히 소프트웨어 개발자에게) XML Schema는 텍트트 요소 및 속성의 내용을 제한하는데 사용할 수 있는 여러가지 데이터 유형을 정의한다. (그림 8) 각각의 데이터 유형은 명시적으로 정의된 값영역과, 명시적으로 정의된 사전 공간(lexical space : 다른 말로 XML 문서에 사용 가능한 문자열 포맷)이 있다. 예를 들어 double 값 4,200은 그림 9와 같이 다양한 방법으로 사전적으로 표현할 수 있다. 주어진 데이터 유형에 대한 값공간 및 사전 공간에 대한 자세한 내용은 XML Schema 사양 Part 2에 명시되어 있다. ("Recommended Reading" 사이드바에 있는 관련 URL 참고)


<그림 9> 사전적 표현

따라서, 어떤 요소나 속성에 사용되는 문자열을 제한하기 위해서는 적절한 값/사전적 공간을 선택하고, 아래와 같이 type 요소에 적용하면 된다.

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

  <xsd:complexType name="EmployeeType">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
      <xsd:element name="hiredate" type="xsd:date"/>
      <xsd:element name="salary" type="xsd:double"/>       
    </xsd:sequence> 
    <xsd:attribute name="id" type="xsd:string"/>
  </xsd:complexType> 
  <xsd:element name="employee" type="tns:EmployeeType"/>

</xsd:schema>

스키마 프로세서가 이전 스키마의 인스턴스를 검증할 때 각각의 요소/속성에 포함된 문자가 정의된 유형의 사전적 표현에 적합한지 확인하게 된다.

그림 8에서 볼 수 있는 바와 같이 모든 상황에 맞는 데이터 유형이 있다. 그럼에도 불구하고, 내장 데이터 유형이 자신의 필요에 정확하게 맞지 않을 경우가 발생할 수 있다. 예를 들어 이전 스키마 정의에서 id 속성은 string 유형이지만, 실제 원하는 건 사회보장번호(Social Security Numer)일 수 있다. XML Schema 는 이러한 상황에 맞는 맞춤형 simple 유형을 정의할 방법이 있지만, 이건 이 글 후편을 기대하시라.

뒷 마무리글

XML Schema는 DTD의 한계와 약점을 모두 극복했다. XML Schema 문법은 XML 1.0 을 따른다. XML Schema는 네임스페이스를 완전히 지원하도록 설계되었다. 또한 가장 중요한 것은 XML Schema를 사용하면 전형적인 프로그래밍 언어의 데이터 유형 및 맞춤형 simple/complex 유형을 지원한다는 것이다.

비록 W3C 가 최근에 최종 XML Schema 권고사항을 발행했지만, (2001년 5월), 다양한 XML 및 Web 서비스 관련 인프라를 통해 이미 이 사양이 널리 지원받고 있다. 이 인프라에는 XML Schema를 기반으로 XML 프로세싱 코드를 자동적으로 생성하고, 실시간으로 동적 proxy/stubs를 build하며, 에디터와 기타 도구에 IntelliSense® 를 제공하며, 스키마 검증 기법을 통해 에러 처리를 간략히 하는 등이 포함된다. MSXML 4.0, SOAP 툴킷 2.0, .NET 등이 모두 XML Schema를 사용했을 때 성취할 수 있는 훌륭한 예들이다.

이번 달의 글은 기본 XML Schema만을 다루고 있다. 나는 DTD에서 제공한 모든 기능 및 몇몇 추가 기능을 설명하였다. 2부에서는 XML Schema의 고급 기능을 다루고자 한다.

===

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

Posted by 푸른하늘 푸른하늘이

댓글을 달아 주세요