Hey, Scripting Guy!자동차 뒤쫓기... 그리고 XML

Microsoft Scripting Guys

이 기사의 코드 다운로드: HeyScriptingGuy2007_02.exe (150KB)

오래된 농담 중에 "우리 집 개가 왜 자동차를 뒤쫓는지 모르겠어. 따라잡아서 뭘 하려고 계속 쫓는 걸까?"라는 말이 있습니다.

좋은 질문이지만 Scripting Guys는 이 질문에 대해서는 답할 수 없습니다. 뭐, 우리가 알고 있는 루시라는 개가 자동차를 따라잡는다면 모를까... 아마도 술집을 턴 후에 도망가는 데 사용하지 않을까요? 그리고 우리 앞마당의 상태를 봤을 때 다른 곳에 들렀다 올 시간은 없을 것 같습니다.

이 오래된 농담을 오늘날의 최첨단 기술 시대에 맞게 각색한다면 "시스템 관리자가 왜 계속 XML을 배우려고 애쓰는지 모르겠어. 배워서 뭘 하려고 그러는 걸까?" 정도로 바꿀 수 있을 것 같습니다.

식상한 농담을 각색한다고 했지 재미있을 것이라는 말은 하지 않았다는 점은 분명히 밝혀 두고 싶습니다.

사실 XML은 적어도 시스템 관리와 관련해서는 다소 과장된 측면이 있는 기술이라 할 수 있습니다. 실제로 XML 파일과 상호 작용하는 스크립트를 한 번도 작성해 본 적이 없는 시스템 관리자도 여전히 많이 있으며 당분간 이러한 상황은 바뀌지 않으리라 생각합니다.

반면 XML을 표준 데이터 저장소로 채택하는 응용 프로그램이 점점 더 많아지고 있는 것도 사실입니다. 그렇게 하지 않을 이유가 없으니까요. 잔뜩 꾸민 텍스트 파일에 지나지 않는 XML 데이터 파일은 빠르고 손쉽게 만들 수 있고 플랫폼 간에 이동이 용이하고 비용이 적게 드는 것은 물론이거니와 복잡한 데이터베이스 프로그램이 필요하지도 않습니다. 따라서 운영 체제와 텍스트 편집기만 있으면 누구나 XML 데이터베이스를 만들 수 있습니다.

물론 시스템 관리자의 경우에도 XML을 사용하여 수행할 수 있는 작업이 한 가지는 있습니다. 즉, XML 파일을 완전한 데이터베이스처럼 쿼리하는 스크립트를 작성할 수 있습니다. 이 작업에 대한 내용을 Scripting Guys보다 더 잘 설명할 사람들이 있을까요?

루시라고요? 뭐, 그럴 수도 있겠습니다만 루시는 다른 집 화단을 뒤지느라 너무 바빠 XML에 대해 칼럼을 쓸 시간은 없을 것 같습니다.

먼저 매일 연재되는 Hey, Scripting Guy! 칼럼(영문)에 사용된 네 가지 스크립트 관련 데이터베이스 파일인 XML 파일(그림 1 참조)을 살펴보겠습니다.

Figure 1 Hey, Scripting Guy! XML 스크립트

<?xml version="1.0" encoding="ISO-8859-1"?>
<Repository>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Access</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Print a Microsoft Access Report?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1020.mspx</URL>
  </Script>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Access</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Compact a Microsoft Access Database?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1009.mspx</URL>
  </Script>
  <Script>
    <Category>Microsoft Office</Category>
    <Subcategory>Microsoft Word</Subcategory>
    <Keyword>hyperlinks</Keyword>
    <Title>How Can I Change an Existing Hyperlink in a Microsoft Word Document?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1016.mspx</URL>
  </Script>
  <Script>
    <Category>Enterprise Servers</Category>
    <Subcategory>Microsoft SQL Server</Subcategory>
    <Keyword>databases</Keyword>
    <Title>How Can I Create a Table in a SQL Server Database?</Title>
    <URL>https://www.microsoft.com/technet/scriptcenter/resources/qanda/oct06/hey1016.mspx</URL>
  </Script>
</Repository>

보시다시피 매우 간단하고 작은 파일입니다. 각각의 개별 스크립트는 <Script> 태그 아래에 있습니다. 지금부터 이러한 스크립트를 레코드라고 하겠습니다. 맞습니다. XML 전문가들은 절대로 이러한 항목을 레코드라고 부르지 않습니다. 그렇지만 여기에서는 이미 알고 있는 지식(데이터베이스 쿼리 기법)을 활용하여 새로운 데이터 원본에 적용하는 방법을 배우는 것이 목표이므로 이렇게 부르도록 하겠습니다. 작업을 수행하는 방법을 먼저 설명하고 나중에 다시 이 주제에 대해 설명하고 올바른 용어를 알려 드리겠습니다.

자, 그럼 이제 다시 레코드로 돌아와서, 각 레코드에는 <Category>, <Subcategory>, <Keyword>, <Title> 및 <URL> 필드가 있습니다.

흠, 예상보다 독자들의 반응이 신통치 않은 것 같군요. XML 파일을 열고 내용을 출력하는 다음 스크립트가 이해하는 데 도움이 되리라 생각합니다.

Set xmlDoc = CreateObject("Microsoft.XMLDOM")
xmlDoc.Async = "False"
xmlDoc.Load("C:\Scripts\Scripts.xml")

Set colNodes = xmlDoc.selectNodes _
("/Repository/Script/*")

For Each objNode in colNodes
   Wscript.Echo objNode.Text
Next

여기서 태그 이름은 대/소문자를 구분한다는 점을 유의해야 합니다. 즉, "/Repository/Script"와 "/repository/script" 또는 "/REPOSITORY/SCRIPT"는 다릅니다.

아직도 그다지 감명을 받은 듯한 반응은 아니군요. 어쨌든 중요한 점은 생각보다 어렵지 않다는 것입니다.

위의 코드에서 알 수 있듯이 XML 파일 작업에 사용되는 COM 개체인 Microsoft.XMLDOM 개체의 인스턴스를 맨 먼저 만들었습니다. 그리고 Async 속성을 False로 설정하여 제어 권한이 스크립트로 반환되기 전에 전체 파일이 메모리로 읽히도록 했습니다. 그런 다음 Load 메서드를 사용하여 C:\Scripts\Scripts.xml 파일을 읽습니다.

xmlDoc.Load("C:\Scripts\Scripts.xml")

파일이 메모리에 로드되면 selectNodes 메서드를 사용하여 데이터베이스에서 지정된 레코드를 선택할 수 있습니다.

Set colNodes = xmlDoc.selectNodes _
("/Repository/Script/*")

selectNodes에 전달된 매개 변수를 주의 깊게 살펴보면 XML 파일 내의 경로를 나타낸다는 것을 알 수 있습니다. 예제의 XML 파일의 구조는 다음과 같습니다.

<Repository>
    <Script>
        <Category></Category>
        <Subcategory></Subcategory>
        <Keyword></Keyword>
        <Title></Title>
        <URL></URL>
    </Script>
</Repository>

이러한 구조가 중요할까요? 결론부터 말하자면, 그렇습니다! XML 파일을 처리할 때는 구조가 매우 중요합니다. 예를 들어 레코드의 개별 속성을 가져오려면 어떻게 해야 할까요? 간단합니다. 개별 속성 태그와 <Script> 태그 앞에 있는 <Repository> 태그에 액세스하면 됩니다. 바로 이것이 selectNodes에 대한 매개 변수로 지정한 경로와 동일합니다. 간단히 해당 경로에 /*를 사용하여 "이 레코드의 모든 속성을 선택"하도록 지시한 것입니다. 다시 말해 반환되는 컬렉션은 데이터베이스의 모든 레코드에 대한 모든 속성으로 구성됩니다. 이는 WMI(Windows® Management Instrumentation)에서 "Select * From" 쿼리를 실행한 결과와 비슷합니다.

이 간단한 스크립트를 실행해 보면 정확한 의미를 이해할 수 있을 것입니다.

스크립트의 나머지 부분은 이해하기 쉽습니다. For Each 루프를 설정하여 컬렉션 전체에 대해 실행하고 각 항목과 속성에 대해 Text 속성 값을 출력합니다.

For Each objNode in colNodes
   Wscript.Echo objNode.Text
Next

자동차를 뒤쫓는 것보다 훨씬 쉽지 않습니까?

하지만 파일의 전체 내용을 출력하는 것이 전부였다면 XML과 관련된 스크립트를 모두 생략하고 FileSystemObject만 사용했을지도 모릅니다. 물론 이 경우 아주 약간만 더 복잡해질 뿐입니다. 어쨌든 독자로 하여금 "와! XML 기능이 상당히 유용하다"라고 생각하도록 만들려면 아직 갈 길이 멉니다.

다시 본론으로 돌아와 독자를 놀라게 만들 수 있을지 한번 보겠습니다. 첫 번째 스크립트에서 모든 레코드의 모든 속성 값을 출력했습니다. 이 경우에도 문제는 없지만 스크립트 제목만 출력하면 되는 경우에는 어떻게 해야 할까요? 가능하기는 할까요? 자, 한번 알아봅시다.

아! 루시가 말하기를 selectNodes 명령을 수정하여 경로 끝에 원하는 속성을 추가하기만 하면 된다는군요. 즉, 다음과 같습니다.

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/Title")

결국 처음에 모든 속성이 출력되었던 이유는 * 와일드카드 문자를 사용했기 때문임을 알 수 있습니다. 이제 Title 속성만 출력되는 이유는 무엇일까요? 바로 해당 속성 값만 출력하도록 요청했기 때문입니다.

흠, 보십시오. XML은 우리가 요청한 항목만 반환했습니다. 루시에게는 미안하지만 이제 인간의 가장 좋은 친구는 XML이 될 것 같군요.

물론 속성을 두 개 이상 반환할 수도 있습니다. 예를 들어 다음과 같이 수정한 명령을 사용하면 Title 속성과 URL 속성의 값이 모두 반환됩니다.

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/(Title | URL)")

구문이 다소 이상해 보이긴 하지만 익숙해진다면 불편하지 않을 것입니다. 먼저 앞에서 한 것처럼 경로의 부모 부분을 지정합니다.

    /Repository/Script/

그리고 괄호를 추가하고 괄호 안에 반환할 속성을 지정합니다. 이때 수직선 구분 기호(| 문자)를 사용하여 개별 속성을 구분합니다. 즉, 다음과 같습니다.

    (Title | URL)

이미 설명했듯이 스크립트가 다소 어색해 보이지만 결과는 정확합니다.

반환할 수 있는 속성 수가 두 개로 제한되지는 않습니다. 수직선 구분 기호만 계속 추가한다면 원하는 모든 정보를 반환할 수 있습니다. 예를 들어 다음 명령은 세 가지 속성(Title, URL 및 Keyword)을 반환합니다.

    Set colNodes=xmlDoc.selectNodes _    ("/Repository/Script/(Title | URL |          Keyword)")

멋지지 않습니까?

뭐, 적어도 우리는 멋지다고 생각했습니다. 독자들의 수준이 너무 높은가 보군요. 그래도 좋습니다. 지금까지는 데이터베이스의 모든 레코드에 대한 정보를 검색한 것에 불과하다는 점은 인정합니다. 그러나 이것이 쓸데없는 일은 아닙니다. 정확히 이러한 결과가 필요한 경우도 많으니까요. 반면 이러한 레코드 중 일부에 대한 정보만 검색해야 하는 경우도 많습니다. 예를 들어 Keyword 특성이 databases인 스크립트에 대한 정보만 출력해야 할 수도 있습니다. 아시다시피 다음과 같은 쿼리를 사용하는 경우입니다.

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’]")

이번에도 구문이 다소 이상합니다. 그러나 동시에 쉽고 간단하기도 합니다. 부모 경로를 지정한 후에 다음과 같이 대괄호 안에 "Where" 절(예: x = y)을 추가합니다.

/Repository/Script [Keyword = ‘databases’]

이미 알고 계실지도 모르지만 Where 절은 "Keyword 특성이 databases인 항목만 반환"하도록 지시하는 역할을 합니다. 이때 databases는 작은따옴표로 표시해야 합니다. 이러한 작은따옴표는 사용해도 아무 문제가 없을 뿐만 아니라 여러 가지 면에서 필요하기도 합니다. 필터 용어를 큰따옴표로 표시하면 스크립트를 실행할 때 구문 오류가 발생합니다.

이유가 무엇일까요? 바로 전체 쿼리 문자열("/Repository/Script [Keyword = ‘databases’]")이 큰따옴표로 묶여 있기 때문입니다.

물론 데이터를 필터링할 때 등호(=)는 마음대로 사용할 수 있습니다. 뿐만 아니라 보다 큼(>) 또는 보다 작음(<) 기호, 그리고 누구나 즐겨 사용하는 작거나 같음(<=) 또는 크거나 같음(>=) 기호도 사용할 수 있습니다. 또한 이러한 기호 앞에 느낌표를 추가하여 해당 기호를 부정할 수도 있습니다. 예를 들어 다음 명령은 Keyword가 databases가 아닌(!=) 모든 스크립트를 요청합니다.

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword != ‘databases’]")

혹시 "물론 필터를 설정할 수는 있겠지만 필터를 설정하고 반환할 속성을 지정할 수는 없을걸?"이라고 생각하는 독자가 있을지 모르겠습니다. 당연히 이러한 작업도 가능합니다.

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’]/Title")

이 명령이 정상적으로 실행되면 databases라는 Keyword가 있는 모든 스크립트의 Title 속성이 반환됩니다. 제대로 작동하는지 실행해 보겠습니다.

    How Can I Print a Microsoft Access Report?
    How Can I Compact a Microsoft Access Database?
    How Can I Create a Table in a SQL Server     Database?

칼럼을 마치기 전에 마지막으로 명령을 하나만 더 살펴보겠습니다. 지금까지 몇 가지 내용을 설명했지만 반환되는 레코드의 범위를 더 좁혀야 할 수도 있습니다. 결과적으로 위에서 설명한 명령은 Microsoft® Access® 스크립트와 Microsoft SQL Server™ 스크립트를 반환합니다. 그 이유는 이러한 모든 스크립트에 databases라는 Keyword가 사용되기 때문입니다. 그렇다면 databases라는 Keyword가 있고 Subcategory가 Microsoft SQL Server인 스크립트로 반환되는 데이터를 제한하려면 어떻게 해야 할까요? 여러 가지 조건을 기준으로 필터링할 수도 있을까요?

다음과 같이 필터링하면 됩니다.

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Keyword = ‘databases’ and " & _
    "Subcategory = ‘Microsoft SQL Server’]")

기본 구문은 동일합니다. 여기에 Keyword = ‘databases’과 Subcategory = ‘Microsoft SQL Server’이라는 두 가지 조건을 사용하고 이 두 조건을 AND 연산자로 연결했습니다.

그렇습니다. 이와 같이 작고 간단한 XML 파일로 Subcategory가 Microsoft SQL Server인 모든 스크립트를 요청할 수 있습니다. 그리고 교육적인 내용이지만 재미있는 시간이 되셨으리라 믿습니다.

OR 절도 사용할 수 있습니다. 예를 들어 Subcategory가 Microsoft Excel®, Microsoft PowerPoint®, Microsoft Outlook® 등에 해당하는 여러 Microsoft Office 스크립트가 있다면 Microsoft Word 또는 Microsoft Access 하위 범주의 스크립트만 반환되도록 제한할 수 있을까요? 물론입니다.

    Set colNodes=xmlDoc.selectNodes _
    ("/Repository/Script " & _
    "[Subcategory = ‘Microsoft Word’ " & _
    "or Subcategory = ‘Microsoft Access’]")

이제 마음에 드십니까? 이 정도의 내용만으로 여러분의 인생을 바꿔 놓을 만한 칼럼이 되지는 않겠지만 언젠가는 이러한 XML 파일 쿼리 방법이 유용하게 사용될 가능성은 충분히 있습니다. 물론 이러한 기본적인 XML 기법을 익힐지 아니면 자동차를 뒤쫓을지를 결정하는 것은 여러분의 몫입니다. 후자를 선택하여 거리에서 루시를 만나게 된다면 Scripting Guys를 대신해 인사를 전해 주시기 바랍니다. 그리고 우리 앞마당에서 좀 나가 달라고 전해 주십시오.

Hey, Scripting Guy! - 일일 칼럼

이번 달 Hey, Scripting Guy! 칼럼을 읽고 지금까지 읽으신 기술 자료 중 단연 최고라고 생각하셨으리라 믿습니다. 나아가 지금까지 읽은 어떤 글보다도 훌륭하다고 생각하실지도 모르겠습니다. 너무 감명받은 나머지 이런 기사를 더 읽기 위해 다음 호 TechNet Magazine이 배달되기만을 손꼽아 기다리는 독자도 있으리라 생각합니다.

새로운 세상을 경험하고 싶으십니까? 이웃집 개 루시에 대해 자세히 알고 싶거나 Scripting Guy의 최신 칼럼을 읽고 싶으십니까? 매년 열리는 Turducken Bowl 행사가 작년에 어떻게 진행되었는지 알고 계십니까? 그렇다면 매일 게시되는 Hey, Scripting Guy! 칼럼을 읽어 보십시오. 주요 공휴일과 Scripting Guy의 휴가 기간을 제외하고 월요일에서 금요일까지 매일 고등학교 야구, 대학 축구나 농구 리그 소식뿐만 아니라 지방 날씨에 대한 소식까지 전해 들을 수 있습니다. 물론 레드먼드에 거주하는 분들에게는 "지역" 소식이겠지만 레드먼드에 대해 별로 알고 싶지 않은 독자에게는 무슨 필요가 있냐고요?

이것이 다가 아닙니다. 일상적인 스크립트 작성 업무에 대해서도 새롭고 유용한 내용이 많습니다. 맞습니다. 위에 나열한 흥미로운 내용 외에도 실질적인 스크립트 정보가 포함되어 있습니다. 각각의 칼럼에서 Scripting Guys는 실제 사용자가 가질 수 있는 궁금증에 대한 해답을 제공합니다. 이러한 일일 칼럼은 microsoft.com/technet/scriptcenter/resources/qanda(영문)에서 읽을 수 있습니다. 또한 이미 수백 개의 질문에 대해 칼럼이 연재되었기 때문에 상당히 방대한 양의 스크립팅 정보를 얻을 수 있습니다. 목록을 보려면 microsoft.com/technet/scriptcenter/resources/qanda/hsgarch.mspx(영문)를 참조하십시오.

Scripting Guys에 질문할 내용이 있으시면 scripter@microsoft.com으로 보내 주시기 바랍니다. 가능성이 적긴 하지만 혹시라도 칼럼을 통해 답변을 받을지도 모르니까요. 질문하지 않으면 아예 가능성이 없는 것이니 손해를 볼 것은 없지 않습니까? 게다가 복권과는 달리 돈이 들지도 않고 당첨 가능성도 좀 더 높습니다.

Microsoft Scripting Guys는 Microsoft의 업무를 담당하는, 정확히 말하면 Microsoft에 고용된 직원입니다. 이들은 야구 경기를 하거나, 감독하거나, 관람하는 등의 일상적인 다른 활동을 하지 않을 때 TechNet 스크립트 센터를 운영합니다. 자세한 내용은 https://www.microsoft.com/korea/technet/scriptcenter/default.mspx에서 확인하십시오.

© 2008 Microsoft Corporation 및 CMP Media, LLC. All rights reserved. 이 문서의 전부 또는 일부를 무단으로 복제하는 행위는 금지됩니다..