728x90
VBA 매크로를 Word 추가 기능으로
Robert Bogue
코드 다운로드 위치: OfficeSpace2008_05.exe (166 KB)
Browse the Code Online
문서 자동화는 매크로가 처음 등장한 시점부터 사용되고 있는 전혀 새로울 것이 없는 기능으로, 1990년대 초반부터 Microsoft® Office 응용 프로그램에서 프로그래밍 모델이 폭넓게 개발되었습니다. 또한 Office 도구는 수년 전부터 키 입력을 재생할 뿐만 아니라 즉석에서 코드를 작성할 수 있는 매크로 레코더를 제공했습니다. 매크로의 기능은 바이러스를 작성하는 데 이용할 수 있을 정도입니다. Word 매크로를 사용한 바이러스 작성은 그 의도는 잘못된 것이지만 어쨌든 뛰어난 기술입니다.
그러나 VBA(Visual Basic® for Applications)의 모든 기능을 활용하더라도 잘 되지 않는 부분이 있습니다. VBA의 초창기는 XML이 아직 개발되지 않았고 인터넷은 갓 태동기에 있었으며 최초의 HTML 페이지가 막 나타나기 시작했던 때였습니다. 당연히 Visual Basic for Applications의 컨텍스트에서 웹 서비스 호출이 제대로 처리되지도 않았습니다.
반면 Microsoft .NET Framework CLR은 이러한 기술을 상당히 잘 처리합니다. 웹 서비스를 호출하는 이러한 기능은 VBA가 아니라 .NET을 대상으로 코드를 작성하게 되는 많은 이유 중 하나일 뿐입니다. 그리고 .NET 대상 코드를 작성하는 것은 곧 VSTO(Visual Studio® Tools for Office)를 사용함을 의미합니다. 그러나 VBA에서 VSTO로 전환한다고 해서 유용한 도구를 버리게 되는 것은 아닙니다. 단지 Office에서 이미 개발 중인 솔루션을 확장하는 자연스러운 방법이라고 할 수 있습니다.
이 칼럼에서는 Word를 사용하여 기본적인 문제를 해결하는 VBA 코드를 캡처하는 방법에 대해 알아봅니다. 그리고 Visual Studio 2008에 포함된 VSTO의 최신 버전을 사용하여 이 코드를 배포 가능한 Word 추가 기능으로 래핑하겠습니다. 또한 매크로 레코더로는 전혀 기록할 수 없거나 최적의 방식으로 기록되지 않는 몇 가지 작업을 실행하는 간단한 코드도 작성합니다.
책을 통한 학습
최근 필자는 이러한 VBA에서 VSTO로의 변환이 당면한 문제에 완벽히 들어맞았던 경우를 경험했습니다. The SharePoint Shepherd's Guide for End Users라는 책을 탈고하는 중이었습니다. 직접 출판하는 책이었기 때문에 Word로 작성한 원고를 프린터에서 사용 가능한 PDF 형식으로 출력해야 했습니다. 이 작업을 위해서는 먼저 원고를 준비하는 몇 가지 단계를 수행해야 했습니다.
우선 여러 파일을 하나의 큰 문서로 만들어야 했습니다. 13개의 단원과 116개의 작업으로 구성된 140개 이상의 개별 파일을 통합하는 작업을 수동으로 처리하기란 어려웠습니다. Microsoft SharePoint®에서 작업할 때는 편집 워크플로를 통해 각 작업을 개별적으로 추적할 수 있으므로 많은 개별 파일을 사용한 작업이 편리했지만 수가 워낙 많다 보니 통합 작업은 자동화하는 편이 효과적이었습니다.
다음으로, 모든 문서에서 추적된 변경 내용이 모두 적용되도록 해야 했습니다. 편집 과정에서 필자는 Word의 변경 내용 추적 기능을 통해 수정 내용 표시를 사용하여 원고의 편집 및 기타 변경 내용을 추적했습니다. 수정 내용 표시는 내용을 최종 감수하는 과정에서 모두 적용해야 하지만 누락되는 항목이 있을 경우 완성된 서적에 눈에 띄는 수정 내용 표시 서식이 보기 싫게 남게 될 것입니다.
셋째로, 문서에 남아 있는 메모를 모두 제거해야 했습니다. 문서에는 여러 편집자들 간에 작동 원리와 용어 표준화에 대해 주고받은 메모가 있습니다. 이러한 메모 역시 검토 초반에 제거되어야 하는 부분이지만 이를 확실히 확인할 수 있는 자동화된 방법이 필요했습니다. 수정 내용 표시와 마찬가지로 메모도 최종 출판본에 남아 있어서는 안 되니까요.
이러한 각 항목을 제작하는 프로세스, 그리고 각 기능에 필요한 코드를 캡처하는 매크로 레코더의 관련 기능을 살펴보겠습니다. 먼저 레코더를 사용하여 기본 자동화 코드를 생성하는 방법부터 시작해서 실행하는 데 문제는 없지만 최적화된 수준으로 생성되지는 않은 코드를 살펴보고 마지막으로 코드를 전혀 생성하지 않는 레코더를 살펴보겠습니다. 그리고 VBA 코드를 VSTO 코드로 변환하여 최종 출판본을 구성하는 데 사용할 Word 추가 기능으로 만들어 보겠습니다. 프로세스의 난이도를 더하기 위해 VSTO로 변환하는 중간에 VBA 코드를 C#으로 변환할 것입니다.
매크로의 기본
이 프로세스의 첫 단계에서는 매크로를 기록할 수 있도록 Word에 개발 도구 탭을 표시해야 합니다. Office 단추를 클릭한 다음 메뉴 아래쪽에 있는 Word 옵션 단추를 클릭합니다. Word 옵션 대화 상자에서 리본 메뉴에 개발 도구 탭 표시(그림 1 참조) 옆에 있는 확인란을 선택하고 확인을 클릭합니다. 그러면 리본 메뉴에 개발 도구 탭이 표시됩니다.
그림 1 리본 메뉴에 개발 도구 탭 표시 (더 크게 보려면 이미지를 클릭하십시오.)
이제 파일을 열고 해당 문서의 내용을 새 파일에 추가하는 과정을 기록해 보겠습니다. 이렇게 하려면 개발 도구 탭의 매크로 기록 단추(그림 2 참조)를 클릭합니다. 그림 3의 대화 상자가 나타나면 AddFiles라는 이름을 입력하고 확인을 클릭합니다. 커서 아이콘이 옆에 작은 카세트 테이프가 있는 포인터로 바뀝니다.
그림 2 개발 도구 탭에서 매크로 기록 (더 크게 보려면 이미지를 클릭하십시오.)
그림 3 매크로 이름 지정
이제 평소와 마찬가지로 파일을 열고 문서의 내용을 복사한 다음, 빈 문서를 새로 만들어 복사한 텍스트를 새 문서에 붙여 넣습니다. 개발 도구 탭의 매크로 기록 단추가 있던 위치에서 기록 중지 단추를 클릭합니다. 이 모든 작업이 끝나면 개발 도구 탭의 맨 왼쪽에 있는 Visual Basic 단추를 클릭하여 Visual Basic for Applications 편집기를 실행할 수 있습니다. 그림 4와 같은 새 AddFiles 함수가 나타납니다.
Figure 4 AddFiles VBA 매크로
Sub AddFiles() ' ' AddFiles 매크로' ' ChangeFileOpenDirectory _ "https://sharepoint.contoso.com/sites/sharepoint/" & _ "Shared%20Documents/SharePoint%20Tasks/" Documents.Open fileName:= _ "https://sharepoint.contoso.com/sites/SharePoint/" & _ "Shared%20Documents/SharePoint%20Tasks/" & _ "Task001%20-%20Create%20a%20Team%20Web%20Site.docx", _ ConfirmConversions:=False, _ ReadOnly:=False, _ AddToRecentFiles:=False, _ PasswordDocument:="", _ PasswordTemplate:="", _ Revert:=False, _ WritePasswordDocument:="", _ WritePasswordTemplate:="", _ Format:= wdOpenFormatAuto, _ XMLTransform:="" Selection.WholeStory Selection.Copy Documents.Add Template:="Normal", NewTemplate:=False, DocumentType:=0 Selection.PasteAndFormat (wdPasteDefault) End Sub
다음 파일 열기 디렉터리를 다시 설정하는 불필요한 코드 줄이 있는 것을 알 수 있습니다. 그 뒤에는 Open 명령과 텍스트를 복사하고 새 문서를 만들고 새 문서에 텍스트를 붙여 넣는 일련의 명령이 있습니다. 매크로 기록을 통해 생성된 코드는 완벽하지는 않지만 나쁘지도 않습니다. 따라서 이 코드를 보강하여 VSTO로 변환하도록 하겠습니다.
매크로를 VSTO로 변환
VSTO를 시작하기 위해 먼저 Word 2007 추가 기능 프로젝트를 만듭니다. Visual Studio를 열고 새 프로젝트를 시작하고 Word 2007 추가 기능 템플릿을 프로젝트에 사용하고 프로젝트 이름을 PublishPrep으로 지정합니다. 새 VSTO 프로젝트가 성공적으로 만들어지면 그림 5와 같은 모습의 Visual Studio 2008 환경이 구성됩니다.
그림 5 새 Word 추가 기능 프로젝트 (더 크게 보려면 이미지를 클릭하십시오.)
프로젝트를 만든 후에는 사용자가 추가 기능에 액세스할 수 있는 수단을 만들어야 합니다. 2007 Office system 응용 프로그램의 경우 이는 리본 탭과 단추를 만드는 것을 의미합니다. 먼저 프로젝트에 새 항목을 추가하고 새 항목 추가 대화 상자에서 리본(비주얼 디자이너) 템플릿을 선택합니다. 새 리본의 이름을 PublishPrep으로 지정합니다.
다음 단계로 리본을 사용자 지정합니다. 여기서는 리본에 고유 탭을 추가하는 대신 추가 기능 탭에 표시되는 그룹을 만들겠습니다.. 이 그룹에는 3개의 단추가 포함됩니다.
리본에서 group1 그룹을 클릭한 다음 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다. 속성 창에서 그림 6과 같이 그룹 이름을 grpPublishPrep으로 변경하고 레이블을 Publishing Preparation으로 변경합니다.
그림 6 PublishPrep 리본 구성 (더 크게 보려면 이미지를 클릭하십시오.)
도구 상자를 열고 Office Ribbon Controls(Office 리본 컨트롤) 그룹으로 스크롤한 다음 3개의 단추 컨트롤을 Publishing Preparation 그룹으로 끌어 놓습니다. 단추는 기본적으로 세로로 쌓입니다.
첫 번째 단추를 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다. 이름 속성을 btnCreateMaster로, 레이블을 Create Master로 설정합니다. 두 번째 단추의 경우 이름을 btnAcceptRevisions로, 레이블을 Accept Revisions로 설정합니다. 마지막 세 번째 단추는 이름 속성을 btnRemoveComments로, 레이블을 Remove Comments로 설정합니다. 그룹이 그림 7과 같이 구성됩니다.
그림 7 추가 기능을 위해 구성된 단추와 그룹 (더 크게 보려면 이미지를 클릭하십시오.)
새 추가 기능 코드 작성
매크로 레코더는 개별 파일을 책으로 통합하는 데 필요한 단계를 기록하는 작업을 훌률히 수행합니다. 그러나 개별 파일을 선택하는 데 필요한 코드는 여기에 포함되지 않습니다. 이를 위해 표준 파일 열기 대화 상자를 사용합니다. 통합할 모든 파일의 이름이 들어 있는 단일 텍스트 파일을 사용자가 선택할 수 있도록 할 것입니다. 이 코드는 크게 두 부분으로 구성됩니다. 첫 번째 부분에서는 실제로 파일을 첨부하고 두 번째 부분에서는 첨부할 파일의 목록을 가져옵니다.
코드의 첫 번째 부분인 AppendFile 함수(그림 8 참조)는 파일 이름을 단일 매개 변수로 받습니다. 얼핏 보기에는 매크로 레코더에서 생성한 코드와 달라 보이지만 그렇지 않습니다.
Figure 8 AppendFile
void AppendFile(string file) { if (string.IsNullOrEmpty(file)) return; Application app = Globals.ThisAddIn.Application; Document activeDoc = app.ActiveDocument; if (activeDoc == null) return; object fileObj = file; object confirmConversions = false; object readOnly = true; object addToRecentFiles = false; object passwordDocument = Missing.Value; object passwordTemplate = Missing.Value; object revert = true; object writePasswordDocument = Missing.Value; object writePasswordTemplate = Missing.Value; object format = Missing.Value; object encoding = Missing.Value; object visible = false; object openAndRepair = false; object documentDirection = Missing.Value; object noEncodingDialog = Missing.Value; object xMLTransform = Missing.Value; Document newDoc = app.Documents.Open(ref fileObj, ref confirmConversions, ref readOnly, ref addToRecentFiles, ref passwordDocument, ref passwordTemplate, ref revert, ref writePasswordDocument, ref writePasswordTemplate, ref format, ref encoding, ref visible, ref openAndRepair, ref documentDirection, ref noEncodingDialog, ref xMLTransform); app.Selection.WholeStory(); app.Selection.Copy(); activeDoc.Select(); object collapseEnd = WdCollapseDirection.wdCollapseEnd; app.Selection.Collapse(ref collapseEnd); app.Selection.Paste(); object saveChanges = WdSaveOptions.wdDoNotSaveChanges; object originalFormat = WdOpenFormat.wdOpenFormatAuto; object routeDocument = Missing.Value; newDoc.Close(ref saveChanges, ref originalFormat, ref routeDocument); object breakType = WdBreakType.wdPageBreak; app.Selection.InsertBreak(ref breakType); }
처음 4줄은 활성 문서를 가져와 오류 검사를 수행합니다. 활성 문서가 없으면 문서에 정확하게 첨부할 수 없고, 첨부할 파일 이름이 없으면 더 이상 할 수 있는 작업이 거의 없습니다. 이 코드 줄에서는 단추 메서드가 호출될 때 VBA에서 가정된 응용 프로그램과 활성 문서에 대한 참조를 가져오는 작업도 실행됩니다. 이는 기록된 매크로 버전에서는 할 필요가 없는 작업입니다.
다음 일련의 줄(개체 변수 선언)은 C#에서 Word가 제공하는 COM 구성 요소를 호출하는 방식 때문에 필요한 코드입니다. 누락된 값을 지정해야 하며, 값이 모두 참조로 전달되므로 값을 보유할 변수가 필요합니다.
그런 다음 코드에서는 매크로 레코더에서 생성한 것과 동일한 복사 작업을 수행합니다. 실질적인 차이점은 추가 기능 코드는 커서의 위치(활성 문서의 끝으로 설정)를 포함한 활성 문서 관리를 담당한다는 것입니다.
마지막 코드 블록은 열었던 문서를 닫고 변경 내용이 저장되지 않도록 합니다. 또한 개별 파일의 내용이 서로 섞이지 않도록 삽입된 내용의 뒤에 페이지 나누기를 추가합니다.
코드의 두 번째 부분에서는 통합할 파일의 목록을 실제로 가져오고 AppendFile을 호출합니다(그림 9 참조). 이 코드는 매크로 레코더로 캡처한 코드가 아니지만 모든 .NET 구문을 활용할 수 있으므로 VSTO의 성능을 확인할 수 있는 부분입니다. 여기서는 OpenFileDialog 컨트롤, 텍스트 파일을 열고 읽는 기능, 제네릭을 사용하여 파일 목록을 만드는 기능, 목록에서 반복 실행되는 다른 작은 메서드를 호출하는 기능 등을 활용합니다.
Figure 9 단추 내부의 VSTO 기능
private void btnCreateMaster_Click(object sender, RibbonControlEventArgs e) { Document activeDoc = Globals.ThisAddIn.Application.ActiveDocument; if ((activeDoc == null)) return; didSelect = false; dlgOpenFile.DefaultExt = ".TXT"; dlgOpenFile.FileOk += new System.ComponentModel.CancelEventHandler( dlgOpenFile_FileOk); dlgOpenFile.ShowDialog(); if ((didSelect)) { string selectedFile = dlgOpenFile.FileName; List<string> files = new List<string>(); using (StreamReader inFile = File.OpenText(selectedFile)) { string fileLine = inFile.ReadLine(); while (fileLine != null && !inFile.EndOfStream) { if ((fileLine.Length > 0)) { // 비어 있지 않은 줄 처리 files.Add(fileLine); } fileLine = inFile.ReadLine(); } } if ((files.Count > 0)) { AppendFiles(files); } } } private bool didSelect = false; void dlgOpenFile_FileOk(object sender, System.ComponentModel.CancelEventArgs e) { didSelect = true; } void AppendFiles(List<string> files) { foreach (string file in files) { AppendFile(file); } }
이 코드의 구조에 대해서는 "파일에 순서대로 나열되어 있고 파일을 읽는 루프에서 바로 AppendFile을 호출하면 간단할 텐데 왜 굳이 파일의 목록을 만드는가"라는 의문이 생길 수 있습니다. 그 이유는 이후에 유연성을 제공하기 위해서입니다. 눈에 띄는 도구 향상 효과 중 하나는 첨부할 파일을 나열하는 제어 파일을 사용하는 대신 사용자가 첨부할 파일을 선택할 수 있다는 점입니다. 이는 VSTO가 VBA보다 뛰어난 부분 중 하나입니다. VSTO는 완전히 .NET Framework를 기반으로 하기 때문에 제네릭, 강력한 형식의 매개 변수 만들기와 같은 기능을 이용할 수 있습니다.
이러한 코드를 이용하려면 몇 가지 단계를 더 수행해야 합니다. 즉, OpenFileDialog를 추가하고 솔루션에 필요한 여러 네임스페이스 모두에 대해 using 문을 추가해야 합니다. PublishPrep.cs 파일의 디자인 모드에서 도구 상자로 이동하고 대화 상자 그룹에서 리본으로 OpenFileDialog를 끌어 놓습니다. 그러면 Visual Studio에서 OpenFileDialog가 아래쪽에 있는 도크로 이동됩니다. 대화 상자의 이름을 dlgOpenFile로 바꾼 다음 FileName을 지웁니다. 앞서 만든 Create Master 단추를 두 번 클릭합니다. 다음과 같이 파일의 맨 위에 using 문을 추가합니다.
using Microsoft.Office.Interop.Word; using System.IO; using System.Reflection;
마지막으로 Visual Studio에서 생성된 메서드 스텁을 그림 9의 코드로 대체합니다.
이제 F5 키를 누르면 추가 기능이 설치된 Word 복사본이 생성됩니다. Create Master 단추를 클릭하여 코드에서 추가할 파일을 가져오는 텍스트 파일을 선택할 수 있습니다. 텍스트가 끝나면 Word를 끝내거나 Visual Studio에서 Shift+F5를 눌러 디버깅을 중지할 수 있습니다.
추가 단추
복잡한 과정은 대부분 끝났고 이제 Accept Revisions 단추를 위한 코드를 추가할 차례입니다. Word 리본 메뉴의 개발 도구 탭으로 돌아가 매크로 레코더를 다시 시작하여 모든 수정 내용을 적용하는 단계를 기록할 수 있습니다. 실제 작업은 검토 탭에서 적용 단추 메뉴를 클릭하고 문서에서 변경 내용 모두 적용을 클릭하여 수행합니다(그림 10 참조). 그러면 다음과 같은 매크로가 기록됩니다.
그림 10 리본 메뉴에 개발 도구 탭 표시 (더 크게 보려면 이미지를 클릭하십시오.)
Sub AcceptAllChanges() ' ' AcceptAllChanges Macro ' WordBasic.AcceptAllChangesInDoc End Sub
매크로 레코더는 WordBasic 개체를 사용하기 위해 이 작업을 기록했습니다. WordBasic 개체는 Word 매크로가 VBA를 사용하도록 수정되기 전의 항목으로, 문제 접근을 위한 최선의 방법은 아닙니다. ActiveDocument를 사용하는 것이 훨씬 더 좋습니다. AcceptAllRevisions 메서드나 Revision 속성의 AcceptAll 메서드를 사용할 수 있습니다. 필자는 Revisions.AcceptAll이라는 명칭이 더 마음에 들지만 어떤 메서드를 사용하더라도 관계없습니다.
VSTO 코드에서 수정 내용을 적용하는 데 필요한 코드는 다음과 같습니다.
private void btnAcceptRevisions_Click( object sender, RibbonControlEventArgs e) { Application app = Globals.ThisAddIn.Application; Document activeDoc = app.ActiveDocument; if (activeDoc == null) return; // 변경 내용 모두 적용 activeDoc.Revisions.AcceptAll(); }
메서드를 한 줄로 작성할 수도 있지만 활성 문서가 있는지 확인하고 디버그를 대비해 개재 변수(intervening variable)를 제공하는 것이 좋습니다. 이 코드를 삽입하려면 Visual Studio의 디자인 모드에서 Publish Prep.cs를 열고 Accept Revisions 단추를 두 번 클릭한 다음 Visual Studio에서 자동으로 추가된 btnAcceptRevisions_Click 메서드 프로토타입을 이 새 코드로 바꾸기만 하면 됩니다. F5 키를 눌러 디버깅을 시작하고 추가 기능 탭으로 이동합니다. 단추를 클릭하여 문서의 모든 수정 내용을 적용할 수 있습니다.
Remove Comments 단추는 매크로 레코더에서 기록되지 않는 항목이기 때문에 조금 더 까다롭습니다. 매크로 레코더를 시작한 후 메모를 마우스 오른쪽 단추로 클릭한 다음 삭제 옵션을 선택하면 제대로 작동하지 않습니다. 이는 제거할 메모를 선택할 수 없음을 의미하므로 직접 VSTO 코드를 새로 작성해야 합니다. 다행히 이 코드는 별로 어렵지 않습니다.
Word의 활성 문서에 대한 참조는 이미 있습니다. 해당 개체에는 메모 컬렉션 속성이 있습니다. 이 컬렉션에서 모든 메모가 제거될 때까지 첫 번째 메모를 계속 삭제할 수 있습니다.
private void btnRemoveComments_Click( object sender, RibbonControlEventArgs e) { Application app = Globals.ThisAddIn.Application; Document activeDoc = app.ActiveDocument; if ((activeDoc == null)) return; // 메모 지우기 while ((activeDoc.Comments.Count > 0)) { app.Selection.Comments[1].Delete(); } }
여기에서 한 가지 흥미로운 사실은 인덱서가 C#의 0이 아니라 1부터 시작한다는 점입니다.
이 기능은 Remove Comments 단추를 두 번 클릭하고 Visual Studio에서 추가된 btnRemoveComments_Click 메서드 프로토타입을 위의 코드로 바꾸면 간단히 솔루션에 추가됩니다.
이제 F5 키를 다시 눌러 계획했던 작업, 즉 파일을 통합하고, 수정 내용을 적용하고, 메모를 제거하는 작업을 모두 실행할 수 있습니다. 놀라운 기능이라고 할 수는 없지만 완성된 추가 기능은 Visual Basic 또는 C#에서 VSTO를 사용하여 더 완성도 높고 정교한 솔루션을 작성하기 위한 프로토타입 VBA 코드를 매크로 레코더로 얼마나 신속하게 생성할 수 있는지 잘 보여 줍니다. 매크로 레코더로 모든 시나리오를 해결할 수는 없지만 지원되는 작업의 경우에는 부담을 크게 덜어 줍니다.
질문이나 의견이 있으면 다음 전자 메일 주소로 보내시기 바랍니다: mmoffice@microsoft.com.
Robert Bogue는 Microsoft Office SharePoint Server 부문의 Microsoft MVP이자 MCSE 및 MCSA:Security이며 100권 이상의 서적 출판과 다른 많은 출판 프로젝트에 참여했습니다. Robert의 블로그 주소는 www.thorprojects.com/blog이며, Rob.Bogue@thorprojects.com을 통해 연락할 수 있습니다. Robert의 최신 서적인 The SharePoint Shepherd's Guide for End Users 프로젝트에 대해 자세히 알아보려면 sharepointshepherd.com을 참조하십시오.
'워드프로세스 > VBA' 카테고리의 다른 글
VBA 매크로 코드사인(codesign) (0) | 2008.04.24 |
---|---|
엑셀개체 생성 후 프로세스가 닫히지 않는 이유/방법 (1) | 2008.03.24 |
모질라/파이어폭스 XPI signing (0) | 2008.03.20 |