티스토리 뷰

+ Figure 1 XML 파일 만들기

Set xmlDoc = _
  CreateObject("Microsoft.XMLDOM") 
 
Set objRoot = _
  xmlDoc.createElement("ITChecklist") 
xmlDoc.appendChild objRoot 

Set objRecord = _
  xmlDoc.createElement("ComputerAudit")
objRoot.appendChild objRecord
 
Set objName = _
  xmlDoc.createElement("ComputerName") 
objName.Text = "atl-ws-001"
objRecord.appendChild objName 

Set objDate = _
  xmlDoc.createElement("AuditDate") 
objDate.Text = Date 
objRecord.appendChild objDate 

Set objIntro = _
  xmlDoc.createProcessingInstruction _
  ("xml","version='1.0'") 
xmlDoc.insertBefore _
  objIntro,xmlDoc.childNodes(0) 

xmlDoc.Save "C:\Scripts\Audits.xml" 

먼저 Microsoft.XMLDOM 개체의 인스턴스를 만듭니다. 짐작하셨겠지만 이것은 우리가 XML 파일을 사용하여 작업할 수 있게 해주는 개체입니다. 여기서 우리의 목표는 그림 2와 같이 간단한 XML 파일을 만드는 것입니다.
그림 2 목표: 간단한 XML 파일 
이런 XML 파일을 만들려면 우선 루트 노드(ITChecklist)를 생성해야 합니다. 어떻게 하냐고요? 다음과 같습니다.

Set objRoot = _
  xmlDoc.createElement("ITChecklist") 
xmlDoc.appendChild objRoot 
아주 간단하죠? 루트 노드에 지정할 이름을 createElement 메서드에 전달하고 호출한 다음 appendChild 메서드의 유일한 메서드 매개 변수에 새 요소(objRoot)에 대한 개체 참조를 지정하고 메서드를 호출하면 됩니다. 그러면 이제 루트 노드가 생성된 것입니다.
그러나 아직 끝난 것은 아니죠. 이제 ComputerAudit 노드를 생성해야 합니다. 이 노드는 ITChecklist 노드의 하위 노드로, 단일 컴퓨터의 정보를 표시합니다. 보시다시피 이 노드를 생성하는 코드는 루트 노드를 만드는 코드와 비슷합니다.

Set objRecord = _
  xmlDoc.createElement("ComputerAudit")
objRoot.appendChild objRecord
유 일한 차이점은 루트 노드를 만들 때는 XML 문서 자체에서 appendChild를 호출했는데(개체 참조 xmlDoc 확인), 루트에 새 하위 노드를 추가할 때는 XML 문서가 아닌 루트 노드(objRoot)에서 appendChild를 호출한다는 것입니다. 너무 쉽죠?
ComputerName 노드와 AuditDate 노드를 ComputerAudit의 하위 노드로 추가하는 방법도 역시 간단합니다. 비슷한 절차를 거치는데요, createElement를 호출하여 새 노드를 만들고, appendChild를 호출하여 이 새 노드를 파일에 추가하면 됩니다.
(돌발 퀴즈: 이때 appendChild는 어디서 호출할까요? 그렇습니다. 방금 생성한 상위(ComputerAudit) 노드인 objRecord에서 호출합니다.)
그리고 ComputerName 노드와 AuditDate 노드는 값을 포함해야 하기 때문에 문서에 추가하기 직전 이들 각 노드의 Text 속성 값도 반드시 지정해야 합니다.
ComputerName 노드를 실제로 생성하는 아래 코드를 보시면 보다 명확히 이해할 수 있습니다.

Set objName = _
  xmlDoc.createElement("ComputerName") 
objName.Text = "atl-ws-001"
objRecord.appendChild objName 
예상하셨겠지만, AuditDate를 생성하는 코드 또한 거의 동일합니다. createElement를 호출할 때 노드 이름을 다르게 지정하고 Text 속성에 다른 값을 지정하면 되는 것입니다.
이거 너무 쉬운거 아닌가요?
첫 번째이자 이 예에서는 유일한 레코드의 노드를 생성했으면 아래의 간단한 코드 블록을 실행합니다.

Set objIntro = _
xmlDoc.createProcessingInstruction _
  ("xml","version='1.0'") 
xmlDoc.insertBefore _
  objIntro,xmlDoc.childNodes(0) 
이 코드는 파일의 시작 부분에 <?xml version="1.0" ?> 태그를 삽입하여 정확한 형식의 XML 문서가 생성되도록 합니다.
이제 남은 일은 Save 메서드를 호출하고 새 파일을 C:\Scripts\Audits.xml로 저장하는 것입니다.

xmlDoc.Save "C:\Scripts\Audits.xml" 
이렇게 간단히 새로운 XML 문서가 탄생했습니다.
이 정보는 사실상 매우 유용합니다. 이제 스크립트를 사용하여 새 XML 파일을 만들 수 있는 것입니다. 물론, 새 XML 파일을 만드는 것보다는 기존 파일에 단순히 새 데이터를 추가해야 하는 경우가 더 많기는 하죠. 따라서 Scripting Guys가 지금부터 기존 파일에 새 데이터를 추가하는 방법을 알려드리겠습니다.
원 래 저희는 기존 XML 파일에 데이터를 추가하는 방법까지는 설명하지 않을 생각이었어요. 그렇지만 너무 마음이 여린 저희는 TechNet Magazine 독자들께 한 가지 제안을 해보기로 했습니다. 지금 이 매거진을 읽고 계신 여러분 모두 2008 Winter Scripting Games에 참여하신다면, 그 보답으로 저희는 스크립트를 사용하여 XML 파일에 데이터를 추가하는 방법을 보여드리는 것이죠. 자, 그럼 모두 Scripting Games에 참여하기로 약속하신겁니다?
저희는 끝까지 기다릴거예요. 거기 서울에 계신 분, 약속하시죠?
꼭 참여하시는 겁니다? Scripting Games에서 여러분 모두 좋은 시간 보내실 거예요. 그건 저희가 약속드리죠.
자, 그럼 약속은 약속이니까 이제 스크립트로 기존 XML 파일에 데이터를 추가하는 방법에 대해 설명하겠습니다. 그림 3을 한번 보세요.

Set xmlDoc = _
  CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load("C:\Scripts\Audits.xml")

Set objRoot = xmlDoc.documentElement
  
Set objRecord = _
  xmlDoc.createElement("ComputerAudit")
objRoot.appendChild objRecord

Set objFieldValue = _
  xmlDoc.createElement("ComputerName")
objFieldValue.Text = "atl-ws-100"
objRecord.appendChild objFieldValue

Set objFieldValue = _
  xmlDoc.createElement("AuditDate")
objFieldValue.Text = Date
objRecord.appendChild objFieldValue
  
xmlDoc.Save "C:\Scripts\Audits.xml"  

보다시피 XML 파일 생성 스크립트와 크게 다르지는 않습니다. 먼저 Microsoft.XMLDOM 개체의 인스턴스를 만든 다음, Async 속성을 False로 설정하여 문서를 비동기가 아닌 동기적으로 로드하도록 스크립트에 지시합니다. 이것에 어떤 차이가 있을까요? 문서를 비동기적으로 로드하면 문서가 완전히 로드되지 않았는데도 스크립트가 계속 실행될 수 있습니다. 아직 생성되지도 않은 문서에 작업을 수행하려 하면 당연히 문제가 발생하지요. 따라서 XML 파일이 반드시 동기적으로 로드되게 하여 파일이 완전히 로드된 후에 스크립트가 진행되도록 해야 하는 것입니다.
이제 Load 메서드를 호출하여 C:\Scripts\Audits.xml 파일을 엽니다. 파일이 열리면 아래의 코드 줄을 사용하여 documentElement 클래스의 인스턴스를 생성합니다. 이 인스턴스는 우리를 이 문서 루트에 묶어 두는 효과가 있습니다. 여기서는 물론 ITChecklist 노드가 되겠죠.

Set objRoot = xmlDoc.documentElement
이 제부터는 일사천리로 진행됩니다. 새 노드를 파일에 추가하는 appendChild 메서드를 사용하여 ComputerAudit 노드의 새 인스턴스를 만듭니다. 그런 다음 ComputerName 노드와 AuditDate 노드에 대해 적절한 값(각각 atl-ws-100 및 현재 날짜)을 지정하여 두 노드의 새 인스턴스를 만듭니다. 생성된 이 두 항목을 조금 전에 만든 새 ComputerAudit 노드에 넣고 Save 메서드를 호출하여 파일을 저장하면 끝입니다. 이제 Scripting Games나 준비하러 가면 되는 거죠.
예, 그렇죠. 2월 15일부터 3월 3일까지 TechNet Script Center에서 열린다는 그 이벤트말입니다.
지금까지 아주 좋습니다. 이제 새 XML 파일을 만들고 이 파일에 새 레코드를 추가할 수도 있습니다. 그런데 이것이 전부는 아닙니다. 예를 들어 파일의 기존 레코드를 수정해야 할 때는 어떻게 하죠? 그림 4에는 파일의 기존 레코드를 수정하는 방법 중 하나가 나와 있습니다.

Set xmlDoc = _
  CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load("C:\Scripts\Audits.xml")

Set colNodes=xmlDoc.selectNodes _
  ("/ITChecklist/ComputerAudit " & _
   "[ComputerName = 'atl-ws-100']/AuditDate")

For Each objNode in colNodes
   objNode.Text = Date
Next
  
xmlDoc.Save "C:\Scripts\Audits.xml" 
이 스크립트의 핵심 동작은 쿼리의 반환 레코드를 결정하는 selectNodes 메서드를 호출할 때 발생합니다. 그림과 같이 selectNodes를 호출할 때는 두 가지 조건을 주의해서 지정해야 합니다. 즉, 레코드의 ComputerName 특성이 atl-ws-100이어야 하고, AuditDate 특성만 반환되도록 해야 합니다.
왜 그런지 잘 모르겠다면 XML 파일 작업에 대한 Scripting Guys의 첫 번째 기사(technetmagazine.com/issues/2007/02/HeyScriptingGuy)를 읽어 보십시오. 여기에는 selectNodes 쿼리 구문에 대한 자세한 설명이 나와 있습니다.
이 제 원하는 대로 selectNodes는 지정한 조건에 맞는 모든 XML 레코드의 컬렉션을 반환합니다. 이는 곧 AuditDate 특성(유일하게 요청한 특성)의 값을 업데이트할 수 있음을 의미합니다. 즉, 컬렉션의 모든 항목을 대상으로 반복하도록 For Each 루프를 설정하고 이 루프에서 AuditDate에 새 값을 지정하여 업데이트할 수 있습니다.

For Each objNode in colNodes
   objNode.Text = Date
Next
그런데 한 번에 두 개 이상의 특성을 수정할 수도 있을까요? 물론입니다. 그러나 이 내용은 다음 칼럼에서 다루도록 하겠습니다.
혹시 말이죠, 파일에서 모든 컴퓨터의 감사 날짜를 업데이트하는 방법도 알고 싶으세요? 간단합니다. 그림 5의 스크립트를 사용하면 됩니다.

Set xmlDoc = _
  CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load("C:\Scripts\Audits.xml")

Set colNodes=xmlDoc.selectNodes _
  ("/ITChecklist/ComputerAudit/AuditDate")

For Each objNode in colNodes
   objNode.Text = Date
Next
  
xmlDoc.Save "C:\Scripts\Audits.xml"

이 스크립트와 앞서 살펴본 XML 수정 스크립트의 유일한 차이점이 무엇인지 아시겠어요? 이 스크립트에서는 ComputerName이 atl-ws-100인 레코드만 표시하도록 지정하지 않는다는 점이 다르죠. 이 조건을 지정하지 않기 때문에 기본적으로 모든 레코드가 반환됩니다.
마지막으로 스크립트를 하나만 더 살펴보겠습니다. atl-ws-100이라는 이 끈질긴 조건이 마지막 제 역할을 다한 후 저세상으로 떠났다고 가정해 봅시다.
여 기서 잠깐, 컴퓨터는 죽으면 어디로 갈까요? 대개는 Scripting Guys 중 한 명에게 가는 것 같습니다. 실제로 본 칼럼을 쓰고 있는 이 Scripting Guy도 전에 "Microsoft를 위해 애써왔으니 랩톱 컴퓨터를 한 대 선사한다"면서 랩톱 컴퓨터를 받은 적이 있습니다. 고마운 일이었지만 컴퓨터가 고장나서 켜지지도 않는다는 것이 문제였죠. 그정도는 괜찮았을지도 모르는데, 알고보니 하드 디스크도 없더군요.
컴퓨터라는 거대한 실리콘 집합체의 수명을 완전히 끝내려면 XML 파일에서 atl-ws-100을 삭제해야 합니다. 구체적으로 어떻게요? 그림 6을 보십시오.

Set xmlDoc = _
  CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load("C:\Scripts\Audits.xml")

Set colNodes=xmlDoc.selectNodes _
  ("/ITChecklist/ComputerAudit/AuditDate")

For Each objNode in colNodes
   objNode.Text = Date
Next
  
xmlDoc.Save "C:\Scripts\Audits.xml"
보시다시피 이 스크립트는 AuditDate 속성을 수정하는 이전 스크립트와 비슷합니다. 다만 두 가지가 다릅니다. 첫째는 selectNodes 쿼리에 속성 값을 지정하지 않기 때문에 atl-ws-100에 대해 전체 XML 레코드가 반환된다는 것입니다.

Set colNodes=xmlDoc.selectNodes _
  ("/ITChecklist/ComputerAudit " & _
   "[ComputerName = 'atl-ws-100']")
둘째는 For Each 루프에서 removeChild 메서드를 호출하여 ComputerName이 atl-ws-100인 레코드를 모두 삭제한다는 점입니다.

xmlDoc.documentElement.removeChild _
  (objNode)
이것으로 끝입니다. 이제 atl-ws-100은 우리에게서 완전히 떠나는 것이죠.

[출처] http://technet.microsoft.com/ko-kr/magazine/2008.02.heyscriptingguy.aspx?pr=PuzzleAnswer
댓글