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: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