인쇄용 버전       전송     
평가 및 의견을 보내려면 클릭하십시오.
Related Articles

Stephen Toub로부터 처리 프로세스를 보다 세부적으로 제어하기 위해 ThreadPool에 라운드 로빈 예약 지원 기능을 추가하는 방법을 알아봅니다.

Stephen Toub

MSDN Magazine January 2009

...

Read more!

If you want to develop high-performance and high-quality commercial applications, you�ll still look to C++ and native code. Direct2D will help you deliver the graphics power you need.

Kenny Kerr

MSDN Magazine June 2009

...

Read more!

VRTA(Visual Round-trip Analyzer)를 다운로드하여 여러분의 웹 페이지 로딩 문제의 원인을 파악하고 12가지 일반적인 해결책을 알아보십시오.

Jim Pierson

MSDN Magazine November 2008

...

Read more!

이번 달에는 Windows 7 베타의 가상 디스크 API 및 Microsoft VHD(가상 하드 디스크) 형식에 대해 살펴봅니다.

Kenny Kerr

MSDN Magazine 4월 2009

...

Read more!

이달에는 디버깅에 사용되는 풍부한 정보를 제공하는 의사 변수와 형식 지정자에 대해 살펴보겠습니다.

Kenny Kerr

MSDN Magazine December 2008

...

Read more!

Also by this Author

새로운 Visual C++ 2008 Feature Pack은 최신 사용자 인터페이스를 만들기 위한 새로운 대형 MFC 클래스 집합을 포함하여 편리한 최신 기능을 Visual C++에 추가합니다.

Kenny Kerr

MSDN Magazine April 2008

...

Read more!

Windows Vista의 새로운 작업 스케줄러를 사용하여 이전 버전보다 훨씬 다양한 작업을 할 수 있습니다. 몇 가지 기본적인 개념과 함께 여러분의 예약 작업 프로젝트에 사용할 수 있는 구성 요소를 소개하겠습니다.

Kenny Kerr

MSDN Magazine October 2007

...

Read more!

Whether you're storing database connection strings, user credentials, or logon info, you'll need to practice good defensive programming techniques to avoid those surprise situations in which your data is exposed. In this article, author Kenny Kerry shows you how.

Kenny Kerr

MSDN Magazine November 2004

...

Read more!

Windows Vista의 출시로 인해 C++ 개발자가 수행할 수 있는 작업의 범위는 더욱 늘어났습니다. 새로 시작하는 이 칼럼을 통해 이러한 작업을 위해 알아야 할 내용에 대해 살펴보도록 하겠습니다.

Kenny Kerr

MSDN Magazine August 2007

...

Read more!

병렬 패턴 라이브러리를 사용하면 보다 쉽게 병렬 처리를 활용할 수 있습니다. 이 기능을 비롯한 여러 Visual C++ 2010 기능을 살펴보십시오.

Kenny Kerr

MSDN Magazine February 2009

...

Read more!

Popular Articles

이제 SQL Server 2005에서 정규식을 사용하여 효과적으로 정교한 텍스트 분석을 수행할 수 있습니다.

David Banister

MSDN Magazine February 2007

...

Read more!

여기에서는 Windows Presentation Foundation을 사용하여 프로그래밍 방식으로, 그리고 선언적으로 데이터를 바인딩하고 표시하는 기술을 소개합니다.

Josh Smith

MSDN Magazine July 2008

...

Read more!

이번 칼럼에서는 Jeff Prosise가 UpdatePanel을 사용하기에 적합한 경우와 웹 메서드나 페이지 메서드에 대한 비동기 호출을 대신 사용하는 것이 적절한 경우에 대해 설명합니다.

Jeff Prosise

MSDN Magazine June 2007

...

Read more!

WPF는 .NET Framework 3.0에 새로 추가된 가장 중요한 기술 중 하나입니다. 이번 달 칼럼에서 John Papa는 WPF의 데이터 바인딩 기능을 소개합니다.

John Papa

MSDN Magazine December 2007

...

Read more!

Ray Djajadinata

MSDN Magazine May 2007

...

Read more!

XmlLite
네이티브 C++를 위한 작고 빠른 XML 파서
Kenny Kerr

이 기사에서 다루는 내용:
  • XmlLite와 다른 XML 파서의 비교
  • XmlLite의 장점과 제한 사항
  • XML 읽고 쓰기
  • XML 보안 고려 사항
이 기사에서 사용하는 기술:
XML, C++
.NET Framework는 성공을 거두고 있지만 Microsoft는 네이티브 C++ 개발도 여전히 중요하게 생각하고 있습니다. 이러한 사실은 네이티브 C++로 작성된 응용 프로그램에 적합한, 오버헤드는 낮지만 성능은 뛰어난 XML 판독기 및 작성기를 제공하는 XmlLite를 내놓은 것을 보면 알 수 있습니다.
관리 코드는 System.Xml 네임스페이스를 통해 XML을 폭넓게 지원하며, COM에 의존하는 기존 Visual Basic® 및 C++ 응용 프로그램은 MSXML(Microsoft® XML Core Services)을 통해 이와 비슷한 기능을 이용할 수 있습니다. 그러나 이러한 방법은 작고 빠른 XML 파서를 원하는 네이티브 C++ 개발자에게는 그다지 훌륭한 옵션이 아니므로 XmlLite가 좋은 대안이 될 것입니다.
이 기사에서는 XmlLite로 어떤 일을 할 수 있는지 살펴보겠습니다. 기대 수준을 맞추기 위해 먼저 XmlLite의 첫 릴리스에서 제공되지 않는 기능부터 간단히 알아보겠습니다. 첫째, XmlLite는 DOM(문서 개체 모델) 구현을 제공하지 않으며 XML 스키마 또는 DTD(문서 종류 정의) 유효성 검사도 제공하지 않습니다. 또한 커서 기반 탐색(예: XPath), 스타일시트, serialization과 같은 높은 수준의 기능도 지원하지 않습니다. 그러나 Microsoft .NET Framework의 거의 모든 XML 기능이 XmlReader와 XmlWriter 클래스를 기반으로 구축되는 것과 마찬가지로, 이와 같은 기능상의 공백은 XmlLite를 기반으로 구축되는 기능을 사용하여 얼마든지 보충할 수 있습니다.
이제 XmlLite가 제공하는 기능에 대해 알아보겠습니다. 간단히 말하면 XmlLite는 풀(pull) 프로그래밍 모델을 구현하는 캐시되지 않는 전진 전용 파서, 그리고 캐시되지 않는 전진 전용 XML 생성기를 제공합니다. 두 가지 모두 매우 유용한 것으로 입증되었습니다.

새로운 XML 파서가 필요한 이유
매일 사용하는 라이브러리에 익숙해져 있고 XML을 광범위하게 사용하고 있는 개발자로서는 새로운 XML 파서가 필요한 이유에 당연히 강한 의구심을 가질 것입니다. 이 새로운 파서의 가치를 평가하기 위해서는 먼저 현재 XML 파서 분야의 상황을 전체적으로 살펴보아야 합니다.
응용 프로그램이 이미 .NET Framework를 이용하고 있다면 결정은 간단합니다. System.Xml을 사용하면 됩니다. 어차피 XmlLite 역시 .NET Framework의 XmlReader 및 XmlWriter 클래스의 설계를 기반으로 만들어졌기 때문입니다. 일반적으로 C++로 작성된 관리되는 응용 프로그램에서도 XmlLite를 사용하여 얻을 수 있는 장점은 없습니다. 결국 중요한 것은 XmlLite의 기능이 XmlReader와 XmlWriter 클래스가 제공하는 기능에 비해 훨씬 더 간단하다는 점입니다. XmlLite와 NET Framework의 기본 유형을 비교한 그림 1을 보면 이를 알 수 있습니다. 반면 응용 프로그램에 전적으로 네이티브 코드만 사용하는 경우 Microsoft 기술 관련에서 일반적으로 사용되는 솔루션은 MSXML입니다.

XmlLite .NET Framework
IXmlReader 인터페이스 XmlReader 클래스
IXmlWriter 인터페이스 XmlWriter 클래스
XmlReaderProperty 열거형 XmlReaderSettings 클래스
XmlWriterProperty 열거형 XmlWriterSettings 클래스
CreateXmlReaderInputWithEncodingCodePage
CreateXmlReaderInputWithEncodingName
CreateXmlWriterOutputWithEncodingCodePage
CreateXmlWriterOutputWithEncodingName
Encoding 클래스
XmlNodeType 열거형 XmlNodeType 열거형
MSXML은 전혀 다른 두 가지 XML 파서를 제공합니다. 첫 번째 파서는 다양한 형태로 사용 가능한 DOM 구현입니다. 작업 중인 XML 문서의 크기가 비교적 작고 메모리 내에서 읽고 쓰기 위해 XML 문서에 대한 임의 액세스가 필요하다면 DOM 구현이 적절합니다. 이후에 나온 MSXML 버전에는 SAX2(Simple API for XML) 구현이 도입되었지만 이 구현이 이름처럼 정말 "간단한지"에 대해서는 논란의 여지가 있습니다. SAX2를 사용하는 경우에는 작업을 시작하기도 전에 최소한 두 개의 COM 인터페이스를 구현해야 합니다. 하나는 XML 문서의 여러 노드로부터 알림을 수신하는 인터페이스이고, 다른 하나는 구문 분석 오류 알림을 받는 인터페이스입니다.
MSXML에 SAX2 구현이 추가된 이유는 SAX2 파서가 DOM 구현과 달리 XML 문서를 스트림으로 읽고, 다양한 노드에 도달한 시점을 사용자에게 알려 주기 때문입니다. 이는 응용 프로그램의 메모리 소비량이 구문 분석하는 문서의 크기에 따라 커지지 않음을 의미합니다.
SAX2의 문제점은 이 모델의 근본적인 복잡성에 있으며, .NET Framework에서 SAX2 구현을 제공하지 않는 이유도 바로 이러한 복잡성 때문입니다. SAX2에서는 인터페이스나 이벤트를 구현해야 하며, 개발자가 더 우회적인 프로그래밍 모델을 사용해야 하고 부가적인 상태 관리도 해야 하므로 응용 프로그램이 복잡해질 수밖에 없습니다. 반면 .NET Framework의 XmlReader 및 XmlWriter 클래스, 그리고 XmlLite의 IXmlReader 및 IXmlWriter 인터페이스는 외부 상태나 알림을 전혀 관리할 필요가 없는, 함수 내에서 직접 사용 가능한 간소한 파서를 제공합니다.
XmlLite는 설계의 간소함 덕분에 MSXML SAX2 구현보다 훨씬 더 높은 성능을 제공할 수 있습니다. SAX2 파서가 큰 문서를 처리하는 데는 DOM 구현보다 낫다고 해도 XmlLite와 비교하면 성능이 떨어집니다.
간단히 말해 XmlLite는 MSXML보다 성능도 뛰어나고 네이티브 C++에서 사용하기도 훨씬 더 쉽습니다. 물론 Visual Basic 및 COM 기반 스크립팅 언어 부문에서는 MSXML이 여전히 가장 적절한 솔루션이겠지만 네이티브 Visual C++®는 마침내 전용으로 설계된 XML 파서를 갖게 되는 것입니다. XmlLite는 Windows Vista™ 및 그 이후 버전에 포함되지만 32비트와 64비트 버전의 Windows® XP와 Windows Server® 2003에 대한 업데이트로도 제공됩니다. 이 업데이트 패키지는 COM 등록을 포함하지 않으므로 MSXML에 있었던 설치 및 버전 관리 문제가 발생하지 않습니다.

COM을 "가볍게"
XmlLite는 이름뿐만 아니라 실제로도 가벼운 XML 파서입니다. XmlLite는 COM의 가장 뛰어난 부분, 즉 프로그래밍 규칙만 취하고 COM 등록, 런타임 서비스, 프록시, 스레딩 모델, 마샬링과 같이 복잡하고 자주 필요하지 않은 부분은 버렸습니다.
XML 판독기 및 작성기는 XmlLite.dll에서 내보낸 함수를 통해 생성됩니다. XmlLite.lib에 연결하고 Windows SDK의 XmlLite.h 헤더 파일을 포함하면 이러한 함수에 액세스할 수 있습니다. 여기서 생성되는 COM 방식의 인터페이스는 친숙한 IUnknown 인터페이스 방식을 사용하여 수명을 관리합니다. COM IStream 인터페이스도 적절한 역할을 수행하며 저장소를 나타냅니다. 이외에는 COM에 의존하는 부분이 없습니다. COM 클래스를 등록할 필요가 없고 필수 사항인 CoInitialize 함수를 호출할 필요도 없습니다. 남아 있는 몇몇 COM 요소는 ATL(Active Template Library) CComPtr 클래스가 관리합니다. 그러나 XmlLite는 단일 스레드 시나리오에서의 성능을 위해 스레드를 보호하지 않으므로 스레드 보호는 사용자가 직접 신경 써야 합니다.
다음 샘플에서는 COM_VERIFY 매크로를 사용하여 메서드에서 검사가 필요한 HRESULT를 반환하는 지점을 명확하게 확인합니다. 이 부분은 예외를 발생시키거나 직접 HRESULT를 반환하는 등의 적절한 오류 처리로 대체할 수 있습니다.

XML 읽기
XmlLite는 다음과 같이 IXmlReader 인터페이스 구현을 반환하는 CreateXmlReader 함수를 제공합니다.
CComPtr<IXmlReader> reader;
COM_VERIFY(::CreateXmlReader(__uuidof(IXmlReader),
                             reinterpret_cast<void**>(&reader),
                             0));
CComPtr 클래스 템플릿은 선택적으로 사용할 수 있으며 인터페이스 포인터가 즉각 해제되도록 합니다.
CreateXmlReader는 void 포인터에 대한 포인터, 그리고 IID(인터페이스 식별자)를 받습니다. 이는 반환할 인터페이스 포인터의 유형을 호출자가 지정할 수 있도록 하는 COM 프로그래밍의 일반적인 패턴입니다. 필자의 샘플에서는 __uuidof 연산자를 사용합니다. 이 연산자는 유형과 연결된 GUID를 추출하는 Microsoft 고유의 키워드로, 여기에서는 인터페이스에 대한 IID를 가져오는 데 사용됩니다. CreateXmlReader에 대한 마지막 인수는 선택적인 IMalloc 구현을 받아 호출자가 메모리 할당을 제어할 수 있도록 합니다.
판독기를 만들었으면 이 판독기에서 입력으로 사용할 저장소를 지정해야 합니다. IStream 인터페이스는 저장소를 나타내며, 다음과 같이 이를 통해 원하는 모든 스트림 구현을 XmlLite에서 사용할 수 있습니다.
CComPtr<IStream> stream;

// Create stream object here...

COM_VERIFY(reader->SetInput(stream));
스트림에 대해서는 이 기사의 뒷부분에서 설명하겠습니다.
XML 판독기를 위한 입력을 설정한 다음 Read 메서드를 반복적으로 호출하여 읽기 작업을 할 수 있습니다. Read 메서드는 성공한 각 호출의 노드 유형을 반환하는 선택적인 인수를 취하며 스트림에서 다음 노드를 성공적으로 읽었을 경우는 S_OK를 반환하고 스트림의 끝에 도달했을 경우는 S_FALSE를 반환합니다. 다음은 노드를 열거하는 방법을 보여 주는 예입니다.
HRESULT result = S_OK;
XmlNodeType nodeType = XmlNodeType_None;

while (S_OK == (result = reader->Read(&nodeType)))
{
    // Get node-specific info
}
현재 노드의 특성을 열거하려면 MoveToFirstAttribute와 MoveToNextAttribute 메서드를 사용합니다. 두 메서드 모두 판독기의 위치가 성공적으로 변경되면 S_OK를, 더 이상 특성이 없으면 S_FALSE를 반환합니다. 다음 예는 지정한 노드의 특성을 열거하는 방법을 보여 줍니다.
for (HRESULT result = reader->MoveToFirstAttribute(); 
     S_OK == result;
     result = reader->MoveToNextAttribute())
{
    // Get attribute-specific info
}
IXmlReader의 Read 메서드를 호출하면 모든 노드 특성이 자동으로 내부 컬렉션에 저장됩니다. 따라서 MoveToAttributeByName 메서드를 사용하여 이름에 따라 특정 특성으로 판독기를 이동할 수 있습니다. 그러나 일반적으로는 특성을 열거하고 응용 프로그램별 데이터 구조에 저장하는 편이 더 효율적입니다. GetAttributeCount 메서드를 사용해도 현재 노드에 있는 특성의 수를 확인할 수 있습니다.
일단 노드나 특성을 알면 이 노드나 특성의 정보를 얻는 것은 간단합니다. 다음은 지정한 노드에 대한 네임스페이스 URI와 로컬 이름을 가져오는 방법을 보여 주는 예입니다.
PCWSTR namespaceUri = 0;
UINT namespaceUriLength = 0;

COM_VERIFY(reader->GetNamespaceUri(&namespaceUri, 
                                   &namespaceUriLength));

PCWSTR localName = 0;
UINT localNameLength = 0;

COM_VERIFY(reader->GetLocalName(&localName, 
                                &localNameLength));
문자열 값을 반환하는 모든 IXmlReader 메서드는 이 패턴을 따릅니다. 첫 번째 인수는 와이드 문자 포인터 상수에 대한 포인터를 받습니다. 두 번째 인수는 옵션이며, 0이 아닌 경우 문자열에서 null 종결자를 제외한 부분을 문자 단위로 측정한 길이를 반환합니다.
성능에 중점을 둔 다른 예를 살펴보겠습니다. IXmlReader 메서드에서 반환된 문자열 포인터는 사용자가 판독기를 다른 노드로 옮기거나 기타 다른 방법(새 입력 스트림을 설정하거나 IXmlReader 인터페이스를 해제하는 등)으로 현재 노드를 무효화하기 전까지만 유효합니다. 즉, IXmlReader는 호출자에게 스트림의 복사본을 반환하지 않습니다.
IXmlReader는 .NET Framework에서 제공하는 유사 기능과 달리 형식이 지정된 내용을 읽을 수 있는 기능을 제공하지 않습니다. 예를 들어 특정 요소 또는 특성에 숫자나 날짜 값이 있는 경우 먼저 이 값의 문자열 표현을 얻은 다음 필요에 따라 직접 변환해야 합니다. 이 밖에도 .NET Framework의 XmlReader 클래스에는 있지만 IXmlReader에는 없는 도우미 메서드가 많으므로 필요할 경우 도우미 메서드를 직접 작성해야 합니다. XmlLite는 최소 인터페이스 설계라는 C++의 원칙을 확실히 지키고 있습니다.
그림 2는 IXmlReader를 사용하여 XML 문서를 읽는 작업과 관련된 개체 및 추상화를 보여 줍니다. 단, IStream은 어떠한 저장소라도 추상화할 수 있으며 여기에 표시된 파일은 일반적인 예일 뿐입니다.
그림 2 판독기 

XML 쓰기
XmlLite는 IXmlWriter 인터페이스 구현을 반환하는 다음과 같은 CreateXmlWriter 함수를 제공합니다.
CComPtr<IXmlWriter> writer;

COM_VERIFY(::CreateXmlWriter(__uuidof(IXmlWriter),
                             reinterpret_cast<void**>(&writer),
                             0));
작성기를 만들었으면 이 작성기에서 출력으로 사용할 저장소를 지정해야 합니다.
CComPtr<IStream> stream;

// Create stream object here

COM_VERIFY(writer->SetOutput(stream));
작성을 시작하기 전에 작성기 속성을 수정할 수 있습니다. XmlWriterProperty 열거형은 사용 가능한 속성을 정의합니다. 예를 들어 SetProperty 메서드를 사용하여 사람이 읽기 편하도록 XML 출력을 들여쓰기할 수 있습니다.
COM_VERIFY(writer->SetProperty(XmlWriterProperty_Indent, TRUE));
그 다음으로 IXmlWriter 메서드를 사용하여 기본 스트림 작성을 시작할 수 있습니다. XmlLite는 XML 조각을 지원합니다. 완전한 XML 문서를 작성할 계획이라면 XML 선언 작성을 처리하는 WriteStartDocument 메서드를 호출하여 시작해야 합니다. 선언은 사용 중인 인코딩에 따라 달라지지만 기본값은 UTF-8이며, 이는 대부분의 경우에 적합합니다. 텍스트 인코딩에 대해서는 잠시 후 설명하겠습니다. 다양한 노드 유형, 특성 및 값을 위한 여러 가지 WriteXxx 메서드가 제공됩니다.
다음 예를 참고하십시오.
COM_VERIFY(writer->WriteStartDocument(XmlStandalone_Omit));
COM_VERIFY(writer->WriteStartElement(0, L"html", 
                                     L"http://www.w3.org/1999/xhtml"));

COM_VERIFY(writer->WriteStartElement(0, L"head", 0));
COM_VERIFY(writer->WriteElementString(0, L"title", 0, L"My Web Page"));
COM_VERIFY(writer->WriteEndElement()); // </head>

COM_VERIFY(writer->WriteStartElement(0, L"body", 0));
COM_VERIFY(writer->WriteElementString(0, L"p", 0, L"Hello world!"));

COM_VERIFY(writer->WriteEndDocument());
WriteStartDocument 메서드는 스트림에 XML 선언을 작성하는 작업을 처리합니다. 이 메서드의 단일 인수는 독립 문서 선언이 표시될지를 나타내는 XmlStandalone 열거형의 값을 받으며, 표시되는 경우 해당 값을 포함합니다. XML 조각을 작성하는 경우 보통 WriteStartDocument 호출은 생략합니다.
WriteStartElement 메서드는 세 개의 인수를 받습니다. 첫 번째 인수는 요소에 대한 네임스페이스 접두사(옵션)를 지정하고, 두 번째는 요소의 로컬 이름을 지정하며 세 번째 인수는 네임스페이스 URI(옵션)를 지정합니다. WriteElementString은 XmlLite에서 편의를 위해 제공하는 몇 안 되는 메서드 중 하나입니다. XHTML 문서의 제목을 작성하기 위한 다음 코드는 이전 예에서 사용된 WriteElementString과 같은 기능을 담당합니다.
COM_VERIFY(writer->WriteStartElement(0, L"title", 0));
COM_VERIFY(writer->WriteString(L"My Web Page"));
COM_VERIFY(writer->WriteEndElement());
WriteElementString 메서드는 꼭 필요하지는 않지만 유용한 것은 확실합니다.
마지막에는 WriteEndDocument 메서드로 문서를 닫습니다. body와 html 요소가 명시적으로 닫히지 않음을 알 수 있는데, 열려 있는 모든 요소는 WriteEndDocument가 자동으로 닫기 때문입니다. 또한 작성기를 해제하는 경우에도 남아 있는 요소가 모두 닫힙니다. 그러나 스트림의 수명과 작성기의 수명이 다른 경우가 많기 때문에 이러한 요소를 명시적으로 닫지 않는 습관은 주의하지 않을 경우 버그로 이어질 수 있습니다. 기본 스트림을 대상으로 모든 쓰기가 확실히 수행되도록 하려면 IXmlWriter의 Flush 메서드만 호출하면 된다는 점도 알아 두십시오.
그림 3은 IXmlWriter로 XML 문서를 작성할 때 수반되는 개체 및 추상화의 흐름을 보여 줍니다. IStream은 어떠한 저장소라도 추상화할 수 있으며 이 파일은 일반적인 예일 뿐입니다.
그림 3 작성기 

스트림 작업
지금까지 스트림에 대해서는 별로 설명하지 않았습니다. 모든 기능을 제공하는 몇몇 XML 라이브러리와 달리 XmlLite는 파일 또는 네트워크 프로토콜을 통한 액세스 위치와 같은 일반적인 저장 위치에서 읽고 쓰는 작업을 수행하기 위한 지원 기능은 제공하지 않습니다. 따라서 읽거나 쓰려는 저장소가 어떤 것이든 관계없이 IStream 구현을 제공해야 합니다. IStream 인터페이스 구현은 복잡하지는 않지만 이미 구현되어 있는 경우가 많으므로 직접 구현할 필요는 없습니다.
CreateStreamOnHGlobal 함수는 가상 메모리를 사용하는 IStream 구현을 제공합니다. 첫 번째 인수는 GlobalAlloc 함수를 사용하여 생성되는 메모리 핸들(옵션)입니다. 이 값으로 0을 전달하면 CreateStreamOnHGlobal이 메모리 개체를 생성합니다. 다음 예는 시스템 메모리의 지원을 받고 필요에 따라 동적으로 확장되는 IStream 구현을 생성합니다.
CComPtr<IStream> stream;
COM_VERIFY(::CreateStreamOnHGlobal(0, TRUE, &stream));
스트림을 해제하면 메모리도 해제됩니다.
SHCreateStreamOnFile 함수는 또 다른 유용한 IStream 구현을 제공합니다. 이 함수는 다음과 같이 파일을 사용하는 IStream을 만듭니다.
CComPtr<IStream> stream;
COM_VERIFY(::SHCreateStreamOnFile(L"D:\\Sample.xml",
                                  STGM_WRITE | STGM_SHARE_DENY_WRITE,
                                  &stream));

읽기 작업의 텍스트 인코딩
기본적으로 XmlLite는 읽기를 위해 텍스트 인코딩을 감지할 때와 쓰기 작업을 할 때 UTF-8을 사용하지만 이러한 기본 동작은 다시 정의할 수 있습니다. 우선 자동으로 수행되는 부분을 살펴보겠습니다. 스트림이 지정되면 IXmlReader는 바이트 순서 표시를 통한 인코딩 힌트를 XML에 대한 프리앰블로 감지합니다. 또한 IXmlReader는 XML 선언에 인코딩이 지정된 경우 이를 따르게 됩니다. 이러한 두 가지 특징은 모든 XML 파서에 일반적으로 적용되는 것입니다. 정의된 인코딩 정보가 입력 스트림에 없고 사용 중인 인코딩을 XmlLite가 찾아서 확인하지 못하는 경우 IXmlReader에서 특정 인코딩을 사용하도록 코드 페이지나 인코딩 이름을 지정할 수 있습니다.
IXmlReader로 스트림을 직접 전달하는 대신 IXmlReaderInput 인터페이스의 형태로 XML 판독기 입력 개체를 만들 수 있습니다. 입력 스트림을 래핑하는 입력 개체를 만들기 위해 제공되는 함수는 두 개이며, CreateXmlReaderInputWithEncodingCodePage 함수는 코드 페이지 번호 형식으로 인코딩을 받고 CreateXmlReaderInputWithEncodingName 함수는 정식 이름을 사용하여 인코딩을 받는다는 점을 제외하면 두 함수의 사용법은 동일합니다. 정리하자면, 일반적으로 XML 판독기에 대한 입력 스트림은 다음과 같이 설정합니다.
CComPtr<IStream> stream;

// Create stream object here

COM_VERIFY(reader->SetInput(stream));
인코딩을 다시 정의하려면 다음과 같이 코드를 변경합니다.
CComPtr<IStream> stream;

// Create stream object here

CComPtr<IXmlReaderInput> input;

COM_VERIFY(::CreateXmlReaderInputWithEncodingName(stream,
                                                  0, // default allocator
                                                  L"ISO-8859-8",
                                                  TRUE, // hint
                                                  0, // base URI
                                                  &input));

COM_VERIFY(reader->SetInput(input));
첫 번째 인수는 XML 판독기가 읽는 대상 스트림을 나타냅니다. 두 번째 인수는 선택 사항인 IMalloc 구현을 받습니다. 이 구현을 지정하면 XML 판독기의 자체 구현을 다시 정의할 수 있습니다. 세 번째 인수는 인코딩 이름을 지정합니다. 기본적으로 지원되는 인코딩 목록은 msdn2.microsoft.com/ms752827.aspx의 문서에서 볼 수 있습니다. 다른 인코딩을 지원하려면 IMultiLanguage2 인터페이스 구현을 제공하면 됩니다. 그 다음 인수는 지정된 인코딩이 반드시 사용되어야 하는지, 또는 단순한 힌트에 불과한지를 나타냅니다. TRUE로 지정하면 파서는 제시된 인코딩을 사용하려 시도하며, 이 시도가 실패할 경우 실제 인코딩을 스스로 찾아 확인할 수 있습니다. FALSE로 지정하면 파서는 제시된 인코딩을 사용하려 시도하며, 입력 스트림과 해당 인코딩이 일치하지 않을 경우 오류를 반환합니다. 다음 인수는 외부 엔터티를 확인하는 데 사용되는 선택적인 기본 URI를 받습니다. 마지막 인수는 SetInput 메서드로 전달할 입력 개체를 나타내는 인터페이스 포인터를 반환합니다.

쓰기 작업의 텍스트 인코딩
XML 작성기는 SetOutput 메서드에 전달된 개체를 통해 사용할 인코딩을 결정합니다. 이 개체가 IStream 인터페이스를 구현하거나 제한적인 ISequentialStream 인터페이스를 구현하는 경우 XML 작성기는 UTF-8 인코딩을 적용합니다. XML 작성기 출력 개체를 만들어 이 동작을 다시 정의할 수 있습니다. 출력 스트림을 래핑하는 출력 개체를 만들기 위해 제공되는 함수는 두 개이며, CreateXmlWriterOutputWithEncodingCodePage 함수는 코드 페이지 번호 형식으로 인코딩을 받고 CreateXmlWriterOutputWithEncodingName 함수는 정식 이름을 사용하여 인코딩을 받는다는 점을 제외하면 두 함수의 사용법은 동일합니다. 일반적으로 XML 작성기에 대한 출력 스트림은 다음과 같이 설정합니다.
CComPtr<IStream> stream;

// Create stream object here

COM_VERIFY(writer->SetOutput(stream));
기본 인코딩을 다시 정의하려면 다음과 같이 코드를 작성합니다.
CComPtr<IStream> stream;

// Create stream object here

CComPtr<IXmlWriterOutput> output;

COM_VERIFY(::CreateXmlWriterOutputWithEncodingName(stream,
                                                   0,
                                                   L"ISO-8859-8",
                                                   &output));

COM_VERIFY(writer->SetOutput(output));
첫 번째 인수는 XML 작성기가 쓰는 대상 스트림을 나타내며 두 번째 인수는 선택 사항인 IMalloc 구현을 받습니다. 이 구현은 XML 작성기의 자체 구현을 다시 정의합니다. 세 번째 인수는 인코딩 이름을 지정합니다. 마지막 인수는 SetOutput 메서드로 전달할 출력 개체를 나타내는 인터페이스 포인터를 반환합니다.

큰 데이터 값 처리
큰 데이터 값을 읽을 때 메모리 사용량을 제한하기 위해 XML 판독기는 값을 청크로 읽는 메커니즘을 제공합니다. IXmlReader ReadValueChunk 메서드는 설정된 최대 문자 수까지 읽고 다음 호출을 위해 판독기를 앞으로 옮깁니다. 다음 예는 ReadValueChunk를 반복적으로 호출하여 큰 데이터 값을 읽는 방법을 보여 줍니다.
CString value;

WCHAR chunk[256] = { 0 };
HRESULT result = S_OK;
UINT charsRead = 0;

while (S_OK == (result = reader->ReadValueChunk(chunk,
                                                countof(chunk),
                                                &charsRead)))
{
    value.Append(chunk, charsRead);
}
더 이상 데이터가 없으면 ReadValueChunk는 S_FALSE를 반환합니다. 이 예에서는 CString 개체에 청크를 썼는데, 이는 청크 길이가 관리되는 방식을 설명하고 실제 사용 시 길이로 인해 청크 사용의 장점이 희석될 수 있음을 보여 주기 위함입니다.

보안 관련 고려 사항
XML 중심의 응용 프로그램은 출처를 신뢰할 수 없는 XML을 처리해야 하는 경우가 많습니다. XmlLite는 알려진 취약점 및 예상되는 취약점으로부터 응용 프로그램을 보호하기 위한 몇 가지 기능을 제공합니다.
XML 문서는 외부 엔터티에 대한 참조를 포함할 수 있으며 일부 XML 파서는 이러한 참조를 자동으로 확인합니다. 이러한 방식은 유용하기도 하지만 XML 확인자가 다양한 위협을 완화하도록 신중하게 작성되지 않은 경우 악용의 빌미를 제공할 수 있습니다. 때문에 XmlLite는 외부 엔터티를 자동으로 확인하지 않으며 XML 확인자도 제공하지 않습니다. 따라서 필요한 경우 직접 확인자 구현을 제공하려면 IXmlResolver 인터페이스를 구현하고 IXmlReader SetProperty 메서드와 함께 XmlReaderProperty_XmlResolver 속성을 사용하여 판독기가 이 확인자를 사용하도록 지정해야 합니다.
XML 문서에는 DTD 처리 명령도 포함될 수 있습니다. XmlLite는 XML 스키마나 DTD를 사용한 문서 유효성 확인은 지원하지 않지만 DTD 엔터티 확장과 기본 특성은 지원합니다. 이러한 DTD는 외부 엔터티에 대한 참조를 포함할 수 있으므로 응용 프로그램이 다양한 공격에 노출될 수 있습니다. XmlLite는 기본적으로 DTD를 처리하지 않도록 설정되어 있으며 XmlReaderProperty_DtdProcessing 속성을 DtdProcessing_Parse 값으로 설정하면 DTD 처리를 허용할 수 있습니다. 또한 DTD 엔터티 확장 공격(Billion Laughs 공격이라고도 함)의 위험을 낮추는 기능도 기본적으로 제공되며 이러한 기능은 XmlReaderProperty_MaxEntityExpansion을 통해 제어됩니다. 이 속성의 기본값은 100,000입니다.
XML을 사용한 또 다른 응용 프로그램 공격 방법은 매우 긴 이름의 문서를 만드는 것입니다. 이러한 공격이 차단되지 않으면 막대한 메모리를 소모하여 서비스 거부 공격을 유발할 수 있습니다. 이를 차단하는 방법에 대해서는 이미 힌트를 드렸습니다. 이러한 위협을 완화하는 한 가지 확실한 방법은 앞 섹션에서 설명한 대로 데이터 값을 청크로 읽는 것입니다. 또 다른 유용한 방법은 메모리 할당에 제한을 둔 사용자 지정 IMalloc 구현을 제공하는 것입니다. 입력 스트림이 임의 액세스를 지원한다면 XmlReaderProperty_RandomAccess 속성을 사용하여 특성을 캐시하지 않도록 XML 판독기에 지시할 수 있습니다. 이렇게 하면 시작 요소 태그를 읽는 데 사용되는 메모리의 양은 줄지만 요청에 따라 여러 가지 특성을 검색하기 위해 파서가 앞뒤로 탐색해야 하므로 구문 분석 속도가 느려집니다.
지나치게 깊은 XML 계층도 시스템 리소스를 빠르게 소모하는 요인이 됩니다. 지나치게 깊은 계층을 가진 XML 문서를 이용한 공격을 차단하려면 XmlReaderProperty_MaxElementDepth 속성을 사용하여 파서의 허용 깊이를 제한하면 됩니다. 이 속성의 기본값은 256입니다.

결론
XmlLite는 네이티브 C++ 응용 프로그램을 위한 강력한 XML 파서를 제공합니다. XmlLite는 성능을 우선시하는 XML 파서로, 사용하는 시스템 리소스 사용량을 파악하고 이러한 특징을 유연하게 제어할 수 있도록 지원합니다. 모든 일반적인 텍스트 인코딩을 지원하는 XmlLite는 네이티브 C++ 응용 프로그램에서 간편하게 XML을 사용할 수 있도록 지원하는 매우 유용하고 실용적인 도구입니다. 자세한 내용은 msdn2.microsoft.com/ms752872.aspx의 XmlLite 설명서를 참조하십시오.

Kenny Kerr은 Windows용 소프트웨어 개발을 전문으로 하는 소프트웨어 개발자로, 프로그래밍과 소프트웨어 설계 분야에서 왕성한 집필 및 교육 활동을 하고 있습니다. 문의 사항이 있으면 http://weblogs.asp.net/kennykerr로 연락하시면 됩니다.

Page view tracker