사전 XML
포맷

XML

gabury1

XML 은 데이터를 글자로 적어 두는 규칙입니다. 값마다 꺾쇠 괄호로 감싼 이름표를 붙여 적고, 그 글자 덩이를 그대로 파일에 담거나 그대로 전송합니다. 사람이 열어 봐도 읽히도록 만들어졌습니다. 대신 담을 수 있는 것은 정해진 범위의 문자뿐입니다.

쉽고 빠른 이해

무슨 일을 하는 물건인가 — 값마다 이름표를 붙여 글자로 적어 두는 규칙입니다. 안드로이드 앱마다 하나씩 들어가는 AndroidManifest.xml 이 그렇게 적힌 파일입니다.

왜 이렇게 하나 — 만든 사람들이 사람이 읽을 수 있는 문서를 앞자리에 두고, 적는 양이 짧아지는 것은 목록의 맨 끝에 뒀습니다. 여는 이름표와 닫는 이름표에 같은 이름을 두 번 적어 길어지는 것은 처음부터 감수한 값입니다.

어떻게 도나

  1. 먼저 문법을 봅니다. 이름표 짝이 맞고 허용된 글자만 썼는가입니다.
  2. 문서에 딸린 별도의 선언이 있으면, 그 선언이 건 제약까지 지켰는가를 그다음에 봅니다. 선언이 없으면 그 자리 자체가 없습니다.
  3. 읽는 쪽은 적힌 값을 그대로 받지 않습니다. 줄바꿈을 한 가지로 맞추고, 이름표에 딸린 값은 탭과 줄바꿈을 스페이스로 접어서 넘깁니다.

대가 — 적을 수 있는 글자가 정해져 있어 아무 바이트나 그대로 담지 못합니다. 적은 값이 적은 모양 그대로 돌아오지도 않습니다. 원문에 캐리지 리턴을 썼는지 줄바꿈을 썼는지는 읽는 쪽에 남지 않습니다.

상세

XML(Extensible Markup Language, 확장 가능 마크업 언어)은 W3C(World Wide Web Consortium, 월드 와이드 웹 컨소시엄)가 정하는 텍스트 포맷입니다. 명세는 자신이 하는 일을 두 가지로 적습니다. XML 문서라고 부르는 데이터 객체의 갈래를 기술하고, 그것을 처리하는 컴퓨터 프로그램의 동작을 일부 기술한다는 것입니다. "일부" 라는 한정이 붙어 있습니다. 문서의 생김새는 명세가 정하지만 그 문서로 무엇을 할지는 정하지 않습니다.

XML 문서는 엔티티라고 부르는 저장 단위들로 이뤄집니다. 엔티티는 파싱되는 데이터나 파싱되지 않는 데이터를 담습니다. 파싱되는 데이터는 문자의 나열이고, 그중 일부는 문자 데이터가 되고 일부는 마크업이 됩니다. 마크업은 문서의 저장 배치와 논리 구조에 대한 기술을 담습니다. 그리고 XML 은 그 저장 배치와 논리 구조에 제약을 거는 장치를 제공합니다.

flowchart TD
    D["XML 문서"] --> E["엔티티 · 저장 단위"]
    E --> P["파싱되는 데이터"]
    E --> U["파싱되지 않는 데이터"]
    P --> C["문자 데이터"]
    P --> M["마크업"]

갈림이 두 자리에서 일어납니다. 엔티티 아래에서 파싱되는 쪽과 파싱되지 않는 쪽이 갈립니다. 그중 파싱되는 쪽만 아래로 한 층 더 갈립니다.

적격과 유효

지켜야 하는 선이 두 층입니다.

텍스트 객체가 적격(well-formed) XML 문서이려면 셋이 다 맞아야 합니다. 문서 하나가 통째로 document 라는 프로덕션에 들어맞아야 합니다. 명세가 적어 둔 적격성 제약을 전부 만족해야 합니다. 그 문서 안에서 직접이든 간접이든 참조되는 파싱 대상 엔티티가 각각 적격이어야 합니다. 적격은 문법의 층입니다. 태그가 짝이 맞고 허용된 문자만 쓰였는가입니다.

유효(valid)는 그 위의 층입니다. 문서에 딸린 문서 타입 선언이 있고 문서가 그 선언이 표현하는 제약을 지키면 그 XML 문서는 유효합니다. 문서 타입 선언이 없으면 유효를 따질 자리 자체가 없습니다. 적격이기만 한 문서가 됩니다.

flowchart TD
    T["텍스트 객체"] --> G1{"document 프로덕션에 통째로 들어맞나"}
    G1 -->|예| G2{"적격성 제약을 전부 만족하나"}
    G2 -->|예| G3{"참조되는 파싱 대상 엔티티가 각각 적격인가"}
    G3 -->|예| W["적격 XML 문서"]
    G1 -->|아니오| X["적격 XML 문서가 아니다"]
    G2 -->|아니오| X
    G3 -->|아니오| X
    W --> DT{"문서 타입 선언이 딸려 있나"}
    DT -->|없다| W2["적격이기만 한 문서"]
    DT -->|있고 그 제약을 지킨다| V["유효 XML 문서"]

세 관문을 다 지나야 적격이고, 적격이 된 뒤에야 유효를 따질 자리가 생깁니다. 문서 타입 선언이 없는 문서는 그 자리로 넘어가지 못하고 적격이기만 한 문서로 끝납니다.

맨 앞의 선언

XML 문서는 쓰이는 XML 의 판을 밝히는 XML 선언으로 시작해야 합니다(SHOULD). MUST 가 아니라 SHOULD 입니다. 명세는 선언이 붙은 문서와 붙지 않은 문서를 나란히 예시로 실어서 둘 다 적격임을 보입니다.

인코딩도 같은 모양입니다. 엔티티가 바이트 순서 표시로도 인코딩 선언으로도 시작하지 않으면 XML 처리기는 그것이 UTF-8(Unicode Transformation Format 8-bit, 유니코드 변환 형식 8비트)로 인코딩됐다고 가정해야 합니다(MUST). 적는 쪽에는 선택이지만 읽는 쪽에는 강제인 자리입니다. 그리고 모든 XML 처리기는 UTF-8 과 UTF-16(Unicode Transformation Format 16-bit, 유니코드 변환 형식 16비트) 두 인코딩의 엔티티를 읽을 수 있어야 합니다(MUST).

표현 한계

XML 이 무엇을 못 담는지가 이 포맷의 본체입니다. 못 담는 것 가운데 상당수는 오류로 드러나지 않고, 명세가 처리기에게 값을 바꿔 넘기라고 시키는 쪽으로 나타납니다.

문서에 들어갈 수 없는 문자

문서에 쓸 수 있는 문자의 범위를 명세가 프로덕션 하나로 못 박습니다.

Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
/* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */

탭과 줄바꿈과 캐리지 리턴 셋을 뺀 C0 제어문자가 이 범위 밖입니다. 널 문자도 밖입니다. 서러게이트 블록과 U+FFFE, U+FFFF 도 빠져 있습니다. 모든 XML 처리기는 이 범위에 있는 문자는 전부 받아들여야 합니다(MUST). 뒤집으면 범위 밖의 문자는 문서에 적을 방법이 없습니다.

block-beta
columns 6
  h1["구멍 · C0 제어문자 · 탭 줄바꿈 캐리지 리턴만 예외"]
  o1["허용 · #x20-#xD7FF"]
  h2["구멍 · 서러게이트 블록"]
  o2["허용 · #xE000-#xFFFD"]
  h3["구멍 · #xFFFE #xFFFF"]
  o3["허용 · #x10000-#x10FFFF"]
  note["왼쪽이 낮은 코드포인트 · 칸 너비는 실제 크기에 비례하지 않는 개요도"]:6

허용 범위가 통짜 한 덩이가 아닙니다. 낮은 쪽 끝과 가운데와 높은 쪽 끝 세 자리에 구멍이 뚫려 있고, 그 사이에 남은 세 구간만 쓸 수 있습니다.

문자 참조로 우회할 수도 없습니다. XML 1.1 명세는 임의의 유니코드 문자를 XML 문서에 표준적으로 표현하려는 요구가 상당했다고 적고, 그래서 XML 1.1 이 #x1 부터 #x1F 까지의 제어문자에 대한 문자 참조를 허용한다고 적습니다. 그 문자들 대부분이 XML 1.0 에서는 금지돼 있다는 문장이 바로 이어집니다. 널 문자는 XML 1.1 에서도 직접 적는 것과 문자 참조 둘 다 막혀 있습니다.

반대 방향의 변화도 있습니다. U+007F 부터 U+009F 까지의 제어문자는 XML 1.0 에서는 그대로 쓸 수 있었으나 XML 1.1 에서는 문자 참조로만 쓸 수 있습니다. 명세는 이 대목에서 하위 호환을 조금 내주는 것이 중요하지 않다고 판단했다고 적습니다.

허용 문자 집합이 이렇게 정해져 있으므로 임의의 바이트열을 그대로 담는 자리가 없습니다. 이진 데이터를 실으려면 문자로 바꿔 적어야 하고, 그것은 포맷이 그 바이트열을 담게 된 것이 아닙니다.

원문 줄바꿈이 되돌아오지 않는 자리

응용의 일을 덜어 주기 위해, XML 처리기는 파싱 전에 입력 시점에 외부 파싱 엔티티의 모든 줄바꿈을 정규화한 것처럼 동작해야 합니다(MUST). 문서 엔티티도 여기 포함됩니다. 두 글자 나열 #xD #xA 와 #xA 가 뒤따르지 않는 #xD 를 모두 #xA 하나로 바꿉니다.

캐리지 리턴과 줄바꿈을 이어 적은 문서와 줄바꿈만 적은 문서가 파서를 지나면 같아집니다. 원문이 어느 쪽이었는지를 XML 문서 안에 담을 자리가 없습니다.

속성값에 담기지 않는 것

속성값에도 정규화가 걸립니다. 줄바꿈은 앞의 규칙대로 이미 #xA 로 정규화된 뒤이고, 그 위에서 절차가 돕니다. 문자 참조는 참조된 문자를 정규화 값에 덧붙이고, 엔티티 참조는 그 엔티티의 대체 텍스트에 같은 단계를 재귀로 적용합니다. 공백 문자 곧 #x20 · #xD · #xA · #x9 는 스페이스 하나(#x20)로 바뀌어 덧붙습니다. 나머지 문자는 그대로 덧붙습니다.

속성 타입이 CDATA 가 아니면 처리기는 정규화한 속성값을 한 번 더 손봐야 합니다(MUST). 앞뒤의 스페이스를 버리고, 이어진 스페이스들을 스페이스 하나로 줄입니다.

flowchart TD
    S["속성에 적은 글자"] --> L["개행 정규화 · 파싱 전에 이미 끝나 있다"]
    L --> N["속성값 정규화 · 문자를 하나씩 훑어 정규화 값을 만든다"]
    N --> T{"속성 타입이 CDATA 인가"}
    T -->|CDATA| A["읽는 쪽에 도착"]
    T -->|CDATA 가 아니다| E["앞뒤 스페이스를 버리고 이어진 스페이스를 하나로 줄인다"]
    E --> A

얹히는 순서가 정해져 있습니다. 개행 정규화가 먼저 끝나 있고, 속성값 정규화가 그 결과 위에서 돕니다. 마지막 한 단계는 속성 타입이 CDATA 냐로 갈립니다. CDATA 면 거기서 끝이고, 아니면 스페이스를 접는 손질이 한 번 더 붙습니다.

속성값 안에 탭이나 줄바꿈을 적어도 읽는 쪽에는 스페이스로 도착합니다. 정규화의 결과가 문자 나열 하나이므로, 속성 하나에 중첩된 구조를 넣을 자리도 없습니다.

같은 이름을 두 번 쓸 자리도 없습니다. 속성 이름은 같은 시작 태그나 빈 요소 태그 안에서 두 번 넘게 나타나서는 안 됩니다(MUST NOT). 명세가 적격성 제약으로 못 박은 자리입니다.

그대로 적을 수 없는 글자

앰퍼샌드(&)와 왼쪽 꺾쇠(<)는 문자 그대로의 모양으로 나타나서는 안 됩니다(MUST NOT). 마크업 구분자로 쓰일 때, 또는 주석·처리 명령·CDATA 섹션 안에 있을 때가 예외입니다. 다른 자리에서 이 글자가 필요하면 숫자 문자 참조나 &amp; · &lt; 문자열로 이스케이프해야 합니다(MUST).

오른쪽 꺾쇠(>)는 &gt; 로 표현할 수 있습니다. 그리고 내용 안에서 ]]> 라는 문자열로 나타나는데 그것이 CDATA 섹션의 끝을 표시하는 것이 아닐 때는, 호환을 위해 &gt; 나 문자 참조로 이스케이프해야 합니다(MUST).

CDATA 섹션은 <![CDATA[ 와 ]]> 로 구분되고 그 안에서는 왼쪽 꺾쇠와 앰퍼샌드를 이스케이프 없이 적을 수 있습니다. 이것은 값이 그대로 담기게 된 것이 아니라 구분자를 다른 문자열로 옮긴 것입니다. 그 구분자와 겹치는 값은 다시 이스케이프해야 합니다.

값의 타입을 정하는 자리

XML 문법은 값이 숫자인지 날짜인지 문자열인지 가리지 않습니다. 타입을 얹으려면 다른 명세가 필요합니다. XML Schema Definition Language 명세는 자신을 XML 1.0 문서의 구조를 기술하고 내용을 제약하는 기능을 제공하는 언어라고 소개합니다. 데이터 타입과 요소와 그 내용, 속성과 그 값을 제약하고 문서화하는 일을 그 언어가 맡고, XML DTD(Document Type Definition, 문서 타입 정의)가 제공하던 것을 실질적으로 재구성하고 상당히 확장한다고 적습니다. 제약을 다른 명세가 얹는다는 것 자체가 XML 문법에는 그 자리가 없다는 뜻입니다.

그 다른 명세도 전부를 담지는 않습니다. XML Schema 명세는 자신이 정의하는 언어가 응용이 필요로 할 수 있는 모든 기능을 제공하려 들지는 않는다고 적습니다.

예시

명세가 싣는 최소 문서

XML
<?xml version="1.0"?>
<greeting>Hello, world!</greeting>

XML 1.0 명세가 적격 문서의 예로 싣는 덩이입니다. 첫 줄이 XML 선언이고 둘째 줄이 문서 요소 하나입니다. 명세는 바로 뒤에 선언 없이 <greeting>Hello, world!</greeting> 만 적은 것도 마찬가지로 적격이라고 덧붙입니다.

AndroidManifest.xml

XML
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="1"
    android:versionName="1.0">

    <!-- Beware that these values are overridden by the build.gradle file -->
    <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="26" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <!-- This name is resolved to com.example.myapp.MainActivity
             based on the namespace property in the build.gradle file -->
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".DisplayMessageActivity"
            android:parentActivityName=".MainActivity" />
    </application>
</manifest>

Android 공식 문서가 앱 매니페스트 설명에 싣는 AndroidManifest.xml 한 벌입니다. 앱마다 이 이름의 파일이 들어갑니다. xmlns:android 속성이 android 라는 접두어를 URI(Uniform Resource Identifier, 통합 자원 식별자) 하나에 묶고, 그 뒤의 android:versionCode 나 android:minSdkVersion 은 그 접두어를 단 속성 이름입니다. android:minSdkVersion="15" 의 15 는 숫자가 아니라 따옴표 안의 문자 나열입니다. 그것을 숫자로 읽는 것은 이 파일을 읽는 쪽의 몫입니다. <!-- 로 여는 두 덩이는 주석이고, <uses-sdk … /> 처럼 슬래시로 닫은 것은 내용이 없는 빈 요소 태그입니다.

MDN 이 싣는 문서

XML
<?xml version="1.0" encoding="UTF-8"?>
<message>
    <warning>
         Hello World
    </warning>
</message>

MDN(Mozilla Developer Network)의 XML 소개 문서가 싣는 덩이입니다. XML 선언에 encoding="UTF-8" 이 붙어 있습니다. warning 요소 안의 들여쓰기 스페이스는 문자 데이터로 들어갑니다. 줄바꿈은 다릅니다. 앞의 표현 한계가 적은 개행 정규화는 문서 엔티티 전체에 걸립니다. 이 덩이를 캐리지 리턴으로 적어 두었어도 읽는 쪽에는 #xA 하나로 도착합니다. 탭과 줄바꿈을 스페이스 하나로 접는 절차는 그 위에 따로 얹히는 속성값 정규화 쪽입니다. 이 덩이에는 속성이 없습니다.

배경

문서에 마크업을 달아 구조를 적는 일은 XML 이 처음이 아닙니다. XML 은 SGML(Standard Generalized Markup Language, 표준 일반화 마크업 언어) 곧 ISO 8879 의 응용 프로파일이자 제한된 형태입니다. 만들어진 방식상 XML 문서는 적합한 SGML 문서이기도 합니다. 새 마크업을 만든 것이 아니라 이미 있던 것을 깎아 낸 것이 출발점입니다.

무엇을 위해 깎았는지가 명세에 목록으로 남아 있습니다. XML 은 W3C 후원 아래 1996년에 꾸려진 XML 워킹그룹이 만들었습니다. 원래 이름은 SGML 편집 검토 위원회였고, Sun Microsystems 의 Jon Bosak 이 의장을 맡았습니다. 명세가 적어 둔 설계 목표는 열 개입니다. 인터넷에서 곧바로 쓸 수 있을 것, 다양한 응용을 지원할 것, SGML 과 호환될 것, XML 문서를 처리하는 프로그램을 쉽게 짤 수 있을 것, 선택 기능의 수를 절대 최소로 이상적으로는 0으로 둘 것. 그리고 문서가 사람이 읽을 수 있고 알맞게 명료할 것, 설계를 빨리 준비할 것, 설계가 형식적이고 간결할 것, 문서를 만들기 쉬울 것.

열 번째가 이 포맷의 모양을 설명합니다. XML 마크업에서 간결함은 중요도가 최소라고 명세가 적습니다. 사람이 읽는 것을 여섯 번째에 두고 마크업의 간결함을 맨 끝에 둔 목록입니다. 같은 이름을 여는 태그와 닫는 태그에 두 번 적어 문서가 길어지는 것은 이 목록이 처음부터 감수한 값입니다.

관련 항목

이것이 기대는 개념과 표준

직렬화 · SGML · ISO 8879 · Unicode · ISO/IEC 10646 · UTF-8 · UTF-16 · 바이트 순서 표시 · BCP 47 · RFC 3986 · RFC 2119 · URI

XML 명세가 정의하는 용어

엔티티 · 문자 참조 · CDATA · 문서 타입 선언 · DTD · 마크업 · 속성 · 프로덕션 · 태그 · 이스케이프 · 파서

이것을 바탕으로 쓰는 명세·제품

Namespaces in XML · XML Schema · XSLT · XPath · EXSLT · XHTML · MathML · SVG · RSS · RDF · OpenSearch description format · Android

이것을 실어 나르는 미디어 타입

미디어 타입 · application/xml · text/xml · application/xml-dtd · application/xml-external-parsed-entity · +xml 접미사

이것을 만든 조직

W3C · 워킹그룹 · Sun Microsystems

이것과 역할을 나누는 이웃 언어

HTML · CSS

다른 이름: Extensible Markup Language · 확장 가능 마크업 언어