Office Space: Office Open XML 파일 만들기
Office Space
Office Open XML 파일 만들기
Ted Pattison

코드 다운로드 위치: OfficeSpace2007_02.exe (338 KB)
Browse the Code Online
이번 달부터 새로 시작하는 칼럼인 Office Space에서는 Microsoft® Office system에서의 개발 작업에 대해 집중적으로 살펴보겠습니다. 이 칼럼에서 필자는 Microsoft Office system 응용 프로그램과 파일 형식을 확장하고 사용자 지정하는 방법에 대해 설명할 것입니다. Microsoft는 2007 Microsoft Office system에 Office Open XML 파일 형식을 기반으로 새로운 Word, Excel® 및 PowerPoint®용 파일 형식을 도입하였습니다. 이러한 새 파일 형식에 저장되는 Office 문서는 패키지라고 하는 ZIP 압축 파일 내에 구조화되어 있습니다. 패키지 내에서 실제 콘텐츠는 파트라고 하는 구성 요소에 저장되어 있습니다. 파트는 일반적으로 내부 XML 문서로 저장되며 이 문서의 콘텐츠는 게시된 XML 스키마에 따라 구조화됩니다.
)에는 Office Open XML 파일 형식의 기본 개념에 대해 설명되어 있습니다. 또한 Microsoft .NET Framework 3.0의 일부인 새로운 패키징 API를 사용하여 이러한 파일 형식을 만드는 방법에 대해서도 다루고 있습니다. Office Open XML 파일 형식을 처음 접해보는 사용자라면 11월호 칼럼을 먼저 읽어야 이번 칼럼 내용에 대해 이해할 수 있습니다.

.docx 파일 생성
11월호 Basic Instincts 칼럼에는 .docx 파일을 처음 생성할 경우 코드를 작성하는 방법이 나와 있습니다. 기본 단계를 살펴보기 위해 여기에 기본 코드의 기본 구조(그림 1 참조)를 추가하였습니다. 우선 .NET Framework 3.0의 일부인 WindowsBase 어셈블리에서 제공하는 Package 클래스를 사용하여 새로운 패키지 파일을 만든 다음, 해당 패키지 내에 하나 이상의 파트를 만들고 파트에 필요한 모든 콘텐츠를 작성합니다. 간단한 .docx 파일의 예로, /word/document.xml의 URI(Uniform Resource Identifier)를 사용하여 파트를 만든 다음 WordprocessingML 콘텐츠를 이 파트에 넣어 "Hello World"와 같은 Word 문서를 만들 수 있습니다.
Dim pack As Package = Nothing
pack = Package.Open("basic.docx", FileMode.Create, FileAccess.ReadWrite)

'*** create main document part (document.xml) ...
Dim uri As Uri = New Uri("/word/document.xml", UriKind.Relative)
Dim partContentType As String
partContentType = "application/vnd.openxmlformats" & _
                  "-officedocument.wordprocessingml.document.main+xml"
Dim part As PackagePart = pack.CreatePart(uri, partContentType)

'*** get stream for document.xml
Dim streamPart As New StreamWriter( _
    part.GetStream(FileMode.Create, FileAccess.Write))

Dim xmlPart As XmlDocument = New XmlDocument()
'*** code to generate 'Hello World' WordProcessingML omitted for clarity
xmlPart.Save(streamPart)

streamPart.Close()
pack.Flush()

'*** create the relationship part
Dim relationshipType As String
relationshipType = "http://schemas.openxmlformats.org" & _
                   "/officeDocument/2006/relationships/officeDocument"
pack.CreateRelationship(uri, TargetMode.Internal, _
                        relationshipType, "rId1")
pack.Flush()

'*** close package
pack.Close()

이때 주의할 것은 패키지 내에 새 파트를 만들 때마다 콘텐츠의 유형을 지정해주어야 한다는 점입니다. 그림 1의 코드에서는 CreatePart 호출의 두 번째 매개 변수로 /word/document.xml 파트에 필요한 콘텐츠 유형을 지정하고 있습니다. 이 메서드를 호출하고 콘텐츠 유형을 전달할 때, 모든 패키지 파일 루트에 있는 [Content_Types].xml이라는 콘텐츠 유형 항목에 어떤 항목을 추가하든 패키징 API가 이를 처리합니다.
마지막으로 주의할 점은 그림 1에 나와 있는 CreateRelationship 메서드에 대한 호출입니다. 이 호출 부분은 코드에서 가장 중요한 부분으로, 패키지와 /word/document.xml 파트 간 상위/하위 관계를 설정하는 역할을 합니다. 관계 생성은 중요한 작업입니다. 이에 대해서는 다음 섹션에서 설명합니다. 이러한 관계 생성 작업이 제대로 이루어지지 않으면, Word에서 문서를 로드하거나 렌더링할 수 없습니다.

파트 간 관계에 대해 자세히 살펴보기
Office Open XML 파일 형식의 패키지 구조는 관계에 따라 크게 좌우됩니다. 앞서 언급한 것처럼 파트를 만들 때 각 파트를 서로 간의 관계를 바탕으로 패키지에 적절히 연결하지 못할 경우 Word와 같은 응용 프로그램이 각 파트를 인식하지 못하게 됩니다. 각 파트는 자신을 포함하는 패키지와의 관계 또는 관계망을 갖고 있어야 하기 때문입니다.
이러한 관계는 두 가지 종류로 나뉩니다. 우선 첫 번째는 관계 패키지와 최상위 파트 간의 연결을 정의하는 패키지 관계입니다. 그림 2는 Microsoft Office Word 2007에서 생성한 .docx 파일에 있는 일반적인 최상위 파트인 /docProps/app.xml, /docProps/core.xml , /word/document.xml을 보여 줍니다. 두 번째는 한 패키지 내에서 두 개의 파트 간 상/하위 관계를 정의하는 파트 관계입니다. /word/document.xml 파트에는 일반적으로 /word/settings.xml이나 /word/styles.xml 등 여러 가지 하위 파트가 있습니다.
그림 2 패키지 콘텐츠 
Office Open XML 파일 형식 사양에 따르면 한 패키지 내의 모든 파트는 패키지와 직접 혹은 간접적으로 연결되어 있어야 합니다. /word/document.xml과 같은 파트는 패키지 관계를 통해 해당 패키지와 직접 연결됩니다. /word/styles.xml과 같은 파트는 패키지와 간접적으로 연결되어 있는데, 이는 이러한 파트가 /word/document.xml과 같은 최상위 파트와 연결되고, 이 최상위 파트가 패키지와 연결되기 때문입니다.
여기서 매우 중요한 것은 소비자 응용 프로그램에서 패키지 관계를 나열함으로써 해당 패키지 내 모든 파트를 검색할 수 있어야 한다는 점입니다. 응용 프로그램을 직접 작성할 때 Word나 Excel 등의 다른 응용 프로그램에서 만든 패키지를 읽을 경우 각 패키지 관계를 나열하여 기존 파트를 검색하는 것이 좋습니다.
한 가지 예를 들어 보겠습니다. 기존 .docx 파일을 열고 /word/document.xml 파트 XML 콘텐츠를 콘솔 창에 표시하는 코드를 만든다고 가정하면 하드 코딩된 URI를 사용하여 해당 파트에 쉽게 액세스할 수 있습니다.
Dim pack As Package = Package.Open("c:\Data\Hello.docx", _
                                   FileMode.Open, FileAccess.Read)
Dim partUri As Uri = New Uri("/word/document.xml", UriKind.Relative)
Dim part As PackagePart = pack.GetPart(partUri)

'** now add code to program against part
이 코드를 통해 /word/document.xml 파트에 액세스할 수 있으므로 스트림을 열어 파트의 콘텐츠를 읽을 수 있습니다. 그럼 이 파트를 다른 방법으로 액세스해보도록 하겠습니다. 이번에는 패키지 관계를 나열하여 특정한 관계 유형을 검색합니다. Word 문서의 주 문서 파트에 사용되는 관계 유형을 가진 패키지와 관련된 파트를 검색할 것입니다.
Dim pack As Package = Package.Open("c:\Data\Hello.docx", _
    FileMode.Open, FileAccess.Read)

Dim relationshipType As String = _
    "http://schemas.openxmlformats.org" & _
    "/officeDocument/2006/relationships/officeDocument"

Dim rel As PackageRelationship, partUri As Uri

For Each rel In pack.GetRelationshipsByType(relationshipType)
    partUri = PackUriHelper.ResolvePartUri( _
        rel.SourceUri, rel.TargetUri)
    Dim part As PackagePart = pack.GetPart(partUri)
    '** now add code to program against part
Next
보시다시피, 이 코드는 Package 개체를 대상으로 GetRelationshipsByType 메서드에 관계 유형의 문자열 이름을 전달하여 호출합니다. 이러한 방법을 통해 원하는 유형의 모든 패키지 관계를 나열할 수 있습니다. 여기서와 같이 officeDocument 관계 유형을 갖는 파트를 검색할 경우 패키지 내에 해당 파트가 하나만 있을 것입니다.
올바른 PackageRelationship 개체를 검색한 다음에는 코드에서 .NET Framework 패키징 API에서 제공하는 PackUriHelper 클래스를 사용하여 대상 파트에 대한 URI를 동적으로 생성합니다. 필요한 URI 개체는 ResolvePartUri를 호출하고 현재 PackageRelationship 개체의 SourceUri와 TargetUri를 전달하여 가져올 수 있습니다. 대상 파트에 대한 URI를 동적으로 생성한 후에는 GetPart 메서드를 호출할 수 있습니다.
패키지 개체는 삭제 가능한 개체이므로 적절히 관리해야 합니다. 따라서 패키지 내 항목에 액세스하는 코드는 아래와 같은 Using 문 내에서 구조화되어야 합니다.
    Using pack As Package = _
    Package.Open("c:\Data\Hello.docx", _
    FileMode.Open, FileAccess.Read)
    '*** code to access package goes here
End Using
각각의 개별 식을 살펴보았으므로 이제 이들이 서로 어떻게 조합되는지 알아보겠습니다. 그림 3에 이번 달 칼럼에서 제공되는 DocumentReader라는 샘플 콘솔 응용 프로그램의 코드가 나와 있습니다. 이 코드는 패키지를 열고 대상 관계 유형을 사용하여 대상 파트를 검색하기 위해 작성되었습니다. 대상 파트를 검색한 후에는 URI를 생성하여 대상 파트를 열고 해당 파트의 콘텐츠에 스트림 기반 방식으로 액세스할 수 있습니다. 이 콘솔 응용 프로그램을 실행하면 그림 4와 같은 출력이 생성됩니다.
Dim relType As String
Dim rel As PackageRelationship 
Dim partUri As Uri

'*** define target relationship type
relType = "http://schemas.openxmlformats.org" & _
    "/officeDocument/2006/relationships/officeDocument"

Using pack As Package = Package.Open("minimal.docx", _
    FileMode.Open, FileAccess.Read)

    For Each rel In pack.GetRelationshipsByType(relType)
        partUri = PackUriHelper.ResolvePartUri( _
            rel.SourceUri, rel.TargetUri)
        Dim part As PackagePart = pack.GetPart(partUri)
        Dim partStream As Stream, partReader As StreamReader
        partStream = part.GetStream(FileMode.Open, FileAccess.Read)
        partReader = New StreamReader(partStream)
        '*** print contents of part to console window
        Console.WriteLine(partReader.ReadToEnd)
        '*** close stream
        partStream.Close()
    Next

End Using

그림 4 콘솔 응용 프로그램 출력 (더 크게 보려면 이미지를 클릭하십시오.)

Package Viewer 샘플 응용 프로그램
지금까지 패키지 내에서 대상 파트의 관계를 통해 파트를 검색하고 액세스하는 방법에 대해 설명했습니다. 이 방법에 대해 좀 더 자세히 살펴보겠습니다. 앞서 말했듯이 Open XML 파일 형식의 사양에 따르면 패키지 내 모든 파트는 각 파트의 관계를 통해 검색할 수 있어야 합니다. 따라서 패키지를 검사하고 패키지 내 모든 파트를 표시하는 응용 프로그램을 작성할 수 있습니다.
이번 달 칼럼에서 제공하는 두 번째 샘플 응용 프로그램은 Package Viewer로, 이는 패키지와 패키지 내 모든 파트를 계층적인 방식의 노드로 표현하여 그 관계를 보여 주는 Windows® 폼 기반의 응용 프로그램입니다. 그림 5는 실행 중인 Package Viewer 응용 프로그램의 모습을 보여 줍니다. 이 응용 프로그램의 트리 보기에서 노드를 클릭하면 패키지와 특정 파트에 대한 자세한 정보를 볼 수 있습니다. 각 파트에는 해당 콘텐츠 유형, 상위 파트, 상위 파트와 연결된 관계 종류 등이 표시되어 있습니다.
그림 5 패키지 내의 파트를 검사하는 Package Viewer (더 크게 보려면 이미지를 클릭하십시오.)
Package Viewer에서는 표준적인 파일 열기 대화 상자에서 패키지 파일을 선택할 수 있습니다. 사용자가 원하는 파일을 선택하면 응용 프로그램에서 모든 패키지 관계를 나열하여 최상위 파트를 검색합니다. 그림 6에 코드의 기본 구조가 나와 있습니다. 여기에는 TreeNode 개체를 사용하여 Windows 트리 보기 컨트롤을 채우는 작업에 대한 자세한 코드가 생략되어 있습니다.
Dim rootNode As TreeNode 
rootNode = New TreeNode(PackageName, 0, 0)

For Each rel As PackageRelationship _
    In CurrentPackage.GetRelationships()

    Dim PartUri As Uri = PackUriHelper.ResolvePartUri( _
        rel.SourceUri, rel.TargetUri)
    Dim part As PackagePart = CurrentPackage.GetPart(PartUri)
    Dim topLevelPartNode As TreeNode = New TreeNode( _
        part.Uri.OriginalString, 1, 1)

    '*** call helper method to discover child controls
    PopulateChildPartNode(part, topLevelPartNode)

    '*** add node to 
    rootNode.Nodes.Add(topLevelPartNode)
Next

treePackageContents.Nodes.Add(rootNode)

이 응용 프로그램에서는 Package 클래스에서 GetRelationships 메서드를 호출하여 패키지에 있는 모든 최상위 파트를 나열합니다. 각 루프를 살펴보면 특정 최상위 파트에 대한 TreeNode 개체 생성 작업이 반복되고 또한 PopulateChildPartNode라는 도우미 메서드를 호출하여 모든 관련 하위 파트를 검색하고 각 하위 파트에 대한 TreeNode 개체를 생성함을 알 수 있습니다. 그럼 이제 PopulateChildPartNode 메서드(그림 7 참조)의 코드에 대해 알아보겠습니다.
Private Sub PopulateChildPartNode( _
    ByVal part As PackagePart, ByVal partNode As TreeNode)

    For Each rel As PackageRelationship In part.GetRelationships()

        Dim ChildPartUri As Uri = _
            PackUriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri)
        Dim ChildPart As PackagePart = part.Package.GetPart(ChildPartUri)

        Dim ChildPartNode As TreeNode = _
            New TreeNode(rel.TargetUri.OriginalString, 1, 1)             

    '** use recursion 
        PopulateChildPartNode(ChildPart, childPartNode)

        partNode.Nodes.Add(ChildPartNode)

    Next

End Sub

보이는 것처럼 각 PackagePart 개체는 GetRelationships 메서드를 제공합니다. 이를 통해 각 최상위 파트에 대한 하위 파트를 나열할 수 있습니다. 그러나 여기서 파트 내의 계층적인 관계는 하위 파트가 자신의 하위 파트를 가지고, 이 하위 파트가 또 하위 파트를 가지는 관계 때문에 계속 늘어나게 됩니다. 따라서 이 응용 프로그램은 자체적인 PopulateChildPartNode 호출을 통해 재귀 방식을 사용하도록 설계되었습니다. 즉, 하나의 메서드로 현재 패키지 내에서 검색되는 모든 파트를 트리 보기로 표현할 수 있습니다.
Package Viewer 응용 프로그램에 대한 코드를 보면 사용자 정의 구조와 각 TreeNode 개체에 대한 Tag 속성이 적용되어 있어 각 노드가 나타내는 엔터티에 대한 다양한 특성을 추적할 수 있습니다. 예를 들어 각 노드가 패키지, 파트 또는 외부 관계 중 무엇인지 파악할 수 있습니다. 파트를 나타내는 각 노드에서는 파트의 콘텐츠 유형, 상위 파트 및 상위 파트와의 관계 종류를 추적할 수 있으며 트리에서 노드를 클릭하면 이러한 정보가 응용 프로그램 주 폼의 오른쪽에 있는 컨트롤을 통해 표시됩니다.
Package Viewer는 또한 패키지 내에 있는 모든 XML 기반 파트의 콘텐츠를 표시하는 기능을 제공합니다. 이 과정은 패키지를 다시 열어 대상 파트에 스트림 기반 방식으로 액세스하여 해당 파트의 XML 기반 콘텐츠를 임시 파일에 기록하고, 최종적으로 파일을 Windows Forms WebBrowser 컨트롤로 로드하여 여기에 XML 콘텐츠를 색이 구분된 코드로 축소된 섹션에 표시하는 방식으로 진행됩니다. Package Viewer가 Office Open XML 파일 형식 문서를 작업하는 데 필요한 파트와 XML에 대해 이해하는 데 많은 도움이 되기를 바랍니다.
이제 Office Open XML 파일 형식을 사용하여 Word나 Excel 또는 PowerPoint에서 문서를 만드는 데 필요한 작업에 대해 어느 정도 이해가 되었을 것입니다. 이 과정은 이론상으로는 매우 간단합니다. 새 패키지 파일을 만들고 필요한 파트를 추가한 다음 파트에 XML 스키마에 따라 구조화된 XML 콘텐츠를 채우기만 하면 됩니다. 물론 간단한 개념이지만 이를 익히기까지는 다소 시간이 걸립니다. 또한 파일 형식 사양에 대한 관련 섹션을 모두 읽어봐야 합니다. 이 사양은 OpenXmlDeveloper.org(영문)에서 다운로드할 수 있습니다.
Word 문서를 작업할 경우 어떤 파트 유형이 패키지 내에 추가되고 콘텐츠 유형 및 관계에 따라 어떻게 구조화되는지 알아야 하며 이러한 파트 내에 삽입되는 WordprocessingML을 생성하는 방법에 대해서도 익혀야 합니다. 사용자가 Excel 스프레드시트를 사용할 경우에는 콘텐츠 유형 및 관계가 달라지므로 WordprocessingML 대신에 SpreadsheetML에 대해 배워야 합니다. 테이블, 그래픽, 서식 등을 포함한 문서를 처음부터 만들려면 XML 생성 방법을 익히는 데 많은 시간과 노력이 필요합니다.

Word 2007의 새로운 개발자 기능
이 칼럼에서 마지막으로 다룰 내용은 Word 2007에서 WordprocessingML을 생성하여 문서를 좀 더 전문적으로 보일 수 있게 만드는 기법에 대한 것입니다. 이때 코드를 작성할 필요는 없습니다. 먼저 Word 2007의 두 가지 새로운 기능을 살펴보겠습니다. 이러한 기능은 새 Office Open XML 파일 형식으로 저장된 문서를 작업할 때 사용됩니다. 첫 번째 기능은 XML 데이터 저장소로, 하나 이상의 사용자 정의 XML 문서를 .docx 파일 내의 파트로 삽입할 수 있습니다. 두 번째 기능은 콘텐츠 컨트롤로, 데이터 항목과 데이터 바인딩을 지원하는 /word/document.xml 파트 내에 정의되어 있는 사용자 인터페이스 구성 요소입니다.
콘텐츠 컨트롤을 테스트하려면 우선 Word 2007의 Word 옵션 대화 상자를 연 다음 자주 사용하는 메뉴 탭에서 리본 메뉴에 개발 도구 표시 옵션이 선택되어 있는지 확인합니다. 개발 도구 탭에 그림 8과 같은 컨트롤 집합이 보일 것입니다. 이러한 컨트롤은 모두 Word 문서에 추가할 수 있습니다.
그림 8 개발 도구 탭 컨트롤 
콘텐츠 컨트롤은 새 형식으로 저장된 Word .docx 파일에만 추가할 수 있으며 기존 이진 형식을 사용하여 이 컨트롤을 정의할 수 없기 때문에 .doc 파일에서는 지원되지 않습니다. 새 형식에서 작업할 경우 콘텐츠 컨트롤을 추가하여 Word 문서에 사용자 입력 요소를 넣을 수 있습니다. 예를 들어 날짜를 선택하는 달력과 같이 사용자의 입력 정보를 수집하여 비즈니스 문서를 완성하는 Word 문서를 작성할 수 있습니다
콘텐츠 컨트롤에는 편집 및 디스플레이 모드라는 두 가지 모드가 있습니다.. 편집 모드에서는 사용자가 텍스트 형태로 입력한다거나, 날짜 선택기에서 날짜를 선택한다거나, 드롭다운 목록에서 항목을 선택하는 등의 작업을 할 수 있습니다. 디스플레이 모드는 렌더링과 인쇄 작업에 최적화되어 있습니다. 즉, 사용자가 편집을 마치고 편집 모드에서 빠져 나올 경우 콘텐츠 컨트롤의 편집 양상을 볼 수 없습니다.

Word 2007에서 데이터 분리
Word 2007의 새로운 기능인 XML 데이터 저장소와 콘텐츠 컨트롤은 서로 독립적으로 사용할 수 있는 별도의 기능이지만 함께 사용할 경우 강력한 효과를 발휘합니다. 예를 들어 Word 문서에 고객 정보나 청구서 등의 데이터를 포함한 XML 파일을 삽입할 수 있습니다. 그런 다음 XPath 식을 사용하여 XML 파일 내의 데이터에 컨트롤을 바인딩할 수 있습니다. 이렇게 하면 Word에 데이터 표시 방법을 지정하는 서식 명령과 데이터를 효과적으로 분리할 수 있습니다.
간단한 예를 살펴보도록 하겠습니다. 여기서는 이번 달 칼럼에서 제공하는 다운로드에 포함되어 있는 CustomerEntry.docx라는 샘플 Word 문서를 사용하겠습니다. 이 파일을 Package Viewer로 보면 위 과정들이 어떻게 진행되는지 빠르게 파악할 수 있습니다. 실제로 이 문서 중 하나를 만들어 보려면 필자가 2006년 11월호 Basic Instincts 칼럼에서 설명한 것처럼 .docx 파일 확장자를 .zip으로 바꾸어 패키지 파일을 직접 열어야만 파일의 내용을 조작할 수 있습니다.
CustomerEntry.docx에는 간단한 사용자 정의 XML 문서가 포함되어 있으며 여기에는 다음과 같은 고객 데이터가 포함되어 있습니다.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Customer xmlns="http://litware.com/2006/customer">
  <Name>Brian Cox</Name>
  <Address>2732 Baker Blvd</Address>
  <City>Eugene</City>
  <State>OR</State>
  <Zip>97403</Zip>
</Customer>
이 XML 문서는 /customXml/item1.xml이라는 URI를 사용하여 .docx 파일 내에 파트로 삽입되어 있습니다. 물론 사용자가 원하는 대로 이름을 바꿀 수 있습니다. 그러나 XML 문서를 작성하거나 XML 데이터 저장소에 있는 파트 이름을 바꿀 경우 Word 2007에서 사용하는 명명 체계와 일관성을 유지하는 것이 좋습니다.
다음은 고객 XML 문서에서 콘텐츠를 확인하는 방법을 알아내야 합니다. 식별을 위한 GUID를 사용하여 dataStoreItem을 정의함으로써 콘텐츠를 확인할 수 있습니다. 이 작업을 수행하려면 /customXml/itemProps1.xml이라는 파트를 만들고 이 파트와 상위 파트인 /customXml/item1.xml 간의 관계를 설정해야 합니다. /customXml/itemProps1.xml의 콘텐츠는 다음과 유사하게 구성되어 있습니다.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ds:dataStoreItem ds:itemID="{D5CB27EE-AE18-48C7-B53F-E921F4653E70}" 
      xmlns:ds="http://schemas.openxmlformats.org/officeDocument/2006/
      customXml">
  <ds:schemaRefs>
    <ds:schemaRef ds:uri="http://litware.com/2006/customer"/>
  </ds:schemaRefs>
</ds:dataStoreItem>
이제 /word/document.xml과 /customXml/item1.xml 간의 관계를 설정해야 합니다. 이때 /word/document.xml을 상위 파트로, /customXml/item1.xml을 하위 파트로 지정해야 하며, 관계 종류는 다음 문자열을 사용하여 설정해야 합니다.
http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml
앞의 모든 단계를 거쳐 사용자 정의 XML 문서를 식별 가능한 dataStoreItem으로 설정하면 /word/document.xml에서 해당 데이터에 액세스할 수 있습니다. 예를 들어 매크로를 사용하는 Word 2007 문서의 VBA(Visual Basic® for Applications) 숨김 코드를 작성하여 ActiveDocument 개체에서 CustomXmlPart 개체를 검색하고 삽입된 XML 문서에서 필요한 고객 데이터를 가져옵니다. 이에 대한 예로 여기에서는 /word/document.xml에서 콘텐츠 컨트롤을 생성하고 이 컨트롤을 고객 데이터가 있는 XML 문서의 특정 요소로 바인딩할 것입니다.
아쉽게도 Word 2007에서는 사용자 인터페이스를 통해 콘텐츠 컨트롤을 XML 데이터 저장소 내의 요소로 바인딩할 수 있는 방법이 없습니다. 따라서 Visual Studio® 2005와 같은 XML 편집기를 사용하여 /word/document.xml을 직접 편집해야 합니다. 사용자 정의 XML 문서의 Customer 요소 내에 있는 Name 요소에 바인딩하기 위해 /word/document.xml 파트에 추가할 수 있는 WordprocessingML 요소의 예는 다음과 같습니다.
<w:sdtPr>
  <w:id w:val="256125470"/>
  <w:dataBinding 
    w:prefixMappings="xmlns:ns0='http://litware.com/2006/customer'"
    w:xpath="/ns0:Customer[1]/ns0:Name" 
    w:storeItemID="{D5CB27EE-AE18-48C7-B53F-E921F4653E70}"/>
  <w:text/>
</w:sdtPr>
dataBinding 요소에는 고객 데이터가 포함된 dataStoreItem의 GUID를 참조하는 storeItemID 특성을 비롯해 XPath 식을 정의하여 콘텐츠 컨트롤을 사용자 정의 XML 파일 내 특정 요소로 바인딩하는 XPath 특성도 포함되어 있습니다. 사용자 정의 XML 파일의 관련 데이터를 표시하는데 필요한 모든 dataBinding 요소를 포함하도록 /word/document.xml 파일을 업데이트했다면, 이제 Word 문서를 작성할 수 있습니다.
사용자 정의 XML 파일과 바인딩된 콘텐츠 컨트롤을 포함하는 Word 문서를 수동으로 능숙하게 작성하는 방법을 익히는 데는 어느 정도 시간이 필요하지만 그만큼 유용하게 사용할 수 있습니다. 이러한 문서 작성 기법은 데이터의 표시와 데이터를 분리할 수 있는 유용한 솔루션을 제공합니다. 콘텐츠 컨트롤이 바인딩된 Word 문서 작성 작업을 마쳤으면 워드에서 문서를 좀 더 전문적으로 보이도록 나머지 작업만 처리하면 됩니다. 예를 들어 다른 일반 Word 문서처럼 텍스트와 로고를 추가하고 새 표과 구역을 만든 다음 콘텐츠 컨트롤을 표시하고자 하는 위치에 끌어다 놓기만 하면 됩니다.
바인딩된 콘텐츠 컨트롤의 경우 두 가지 방법으로 사용자 정의 XML 파일과 데이터를 동기화할 수 있습니다. 사용자 정의 파일에 추가한 모든 데이터는 바인딩된 콘텐츠 컨트롤에 표시되며, 데이터를 업데이트할 수도 있습니다. 사용자가 콘텐츠 컨트롤에 있는 데이터를 업데이트하고 문서를 저장할 경우 업데이트한 내용이 사용자 정의 XML 파일에 다시 기록됩니다. 이 방법을 통해 업데이트된 데이터를 XML 형식으로 쉽게 추출할 수 있습니다.

프로그래밍 방식으로 XML 데이터 저장소 업데이트
지금까지 콘텐츠 컨트롤을 XML 데이터 저장소의 사용자 정의 XML 문서로 바인딩하는 방법을 설명하였습니다. 마지막 단계는 특정 고객의 데이터가 포함된 XML 문서의 새 인스턴스를 생성하는 코드를 작성하고, 적절한 콘텐츠 컨트롤이 설정된 기존 Word 문서 내에 있는 XML 문서를 덮어쓰는 것입니다.
이번 달 칼럼에서 제공하는 다운로드에는 LetterTemplate.docx라는 Word 문서를 대상으로 작동하는 Letter Generator라는 ASP.NET 2.0 샘플 응용 프로그램이 포함되어 있습니다. 이 Word 문서에는 그림 9와 같이 XML 문서가 삽입되어 있으며 여기에는 Litware 직원이 고객에게 보내는 초안 서신에 필요한 모든 내용이 포함되어 있습니다.
<?xml version="1.0" encoding="utf-8"?>
<LitwareLetter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
               xmlns="http://litware.com/2006/letters">
  <Customer>
    <ContactFirstName>Yoshi</ContactFirstName>
    <ContactLastName>Latimer</ContactLastName>
    <Company>Hungry Coyote Import Store</Company>
    <Address> 516 Main St.</Address>
    <City>Elgin</City>
    <State>OR</State>
    <Zip>97827</Zip>
  </Customer>
  <Date>October 16, 2006</Date>
  <Body>Thanks for your recent purchase at Litware </Body>
  <Employee>
    <Name>Rob Verhoff</Name>
    <Title>Director of Accounting</Title>
  </Employee>
</LitwareLetter>

Letter Generator 응용 프로그램은 Access™ MDB 파일에 있는 3개의 테이블에서 정보 데이터를 가져와 일정한 형식으로 XML 문서를 만들기 위해 작성되었습니다. 물론 이 샘플 응용 프로그램은 SQL Server™나 사용자 지정 웹 서비스 등의 다른 데이터 원본에서 데이터를 추출하는 데에도 사용할 수 있습니다.
사용자가 GenerateLetter.aspx라는 페이지를 열면 고객, 서신 유형, 서신에 서명할 직원 등을 선택할 수 있는 WebControls가 표시됩니다. 일단 사용자가 서신 초안을 작성하는 데 필요한 올바른 매개 변수를 선택한 후에는 Generate Letter라는 단추를 클릭하여 요청의 서버 쪽 처리를 시작할 수 있습니다.
Generate Letter 단추를 클릭하여 시작되는 서버 쪽 코드에서는 LetterTemplate.docx라는 Word 2007 문서를 MemoryStream 개체로 로드하고 .NET 패키징 API를 사용하여 이 문서를 엽니다. 그런 다음 코드에서 사용자가 요청한 매개 변수에 따라 새로운 XML 문서를 만들고, /customXML/item1.xml 파트를 열어 해당 파트의 콘텐츠를 새로 만든 XML 문서로 덮어씁니다. 마지막으로 이 코드는 패키지 파일을 닫은 다음 2006년 11월호 Basic Instincts 칼럼에서 필자가 설명한 기법을 사용하여 Word 문서를 다시 사용자한테 보냅니다. 이를 통해 사용자는 고객 서신의 초안을 작성하는 프로세스를 자동으로 처리할 수 있게 됩니다.

요약
지난 칼럼에 이어 이번 달 칼럼에서도 Office Open 파일 형식으로 작업하는 방법을 설명하였습니다. 이제 Word 2007 문서를 정의하는 데 있어 파트나 패키지 간의 관계나 관계 유형이 얼마나 중요한 역할을 하는지 이해하셨을 것입니다. 또한 사용자 정의 XML 문서의 요소에 바인딩된 콘텐츠 컨트롤을 사용하여 데이터와 데이터 표현이 분리된 Word 2007 문서를 만들 수 있는 강력한 새 기술에 대해서도 다루었습니다. 이를 적절히 활용하면 Word, Excel 또는 PowerPoint 사용자의 표준 비즈니스 문서를 자동으로 만드는 응용 프로그램과 구성 요소를 만들 수 있습니다.

Ted에게 질문이나 의견이 있으면 다음 전자 메일 주소로 보내시기 바랍니다: mmoffice@microsoft.com.


Ted Pattison은 SharePoint MVP이기도 하며 현재 플로리다 탬파에 거주하고 있습니다. Ted는 현재 Inside Windows SharePoint Services 3.0(Microsoft Press)을 집필 중이며 자신의 회사(Ted Pattison Group)에서 SharePoint 고급 강좌를 담당하고 있습니다.

Page view tracker