2009.04.08 10:19

xml 출력하기


오랫만에 하는 asp 라 이래 저래 예전에 어떻게 했었는지 기억을 더듬으며(어딘가 쳐박혀 있을 소스코드를 뒤지며가 더 정확하겠지만...ㅋㅋㅋ)주섬 주섬 기능을 만들어 내고 있다. 요즘은 플래쉬가 활용도가 높아서 플래쉬 내에서 기능을 처리하는 경우도 많기에 플래쉬와 DB 데이타의 연동이 필수적으로 인식되는 듯 하다. Jquery 같은 것을 이용해서 멋진 화면 구성이 가능하고 우겨 봐야... 플래쉬에서 되는 것 처럼 모든게 다 되는 것은 아니기에 한수 접도록 하자....ㅋㅋㅋㅋ
어쨌거나 플래쉬와 데이타 연동에 있어서 가장 편리한 방법은 xml 인것 같다. 플래쉬 제작자(액셔너 란 말을 사용하던데...)가 xml 에 대해 이해 하고 있다면 말이다.

그래서 request(post 나 get 이나) 를 받아서 xml 컨텐츠를 뿌려주는 페이지를 만들어 보도록 하자.

일단 asp 출력을 xml 양식이 되도록 컨텐츠 타입을 지정해 보자.

 Response.expires = -1
 Response.AddHeader "Pragma", "no-cache"
 Response.AddHeader "cache-control", "no-store"
 Response.buffer = true
 Response.ContentType="text/xml"
 Response.Charset= "utf-8"
뭐 크게 새로운 것도 다를 것도 없다. 캐쉬 없이 바로 바로 xml 이 되도록 지정해 주는 부분이라....
다음은 출력을 위한 기본 Dom 을 만들어 보자....


 Set oXml = Server.CreateObject("MSXML2.DOMDocument")
 oXml.async = False
 oXml.loadXML("<xml><serviceInfos/><userInfos/><items/><errors/></xml>")
<xml> 을 루트로 하는 xml 을 먼저 로드 하자....xlm 을 로드 하는 방법은 load, loadXML 이 있는데 문자열을 로드 할때는 후자(loadXML) 을 사용한다. 난 요청 받은 내용(servieInfos) 와 인정정보(userInfos) 출력할 데이타(items) , 처리 결과(errors) 를 보내주기로 약속했기에.....이렇게 추가 했다....

먼저 노드를 생성하고 속성을 추가하는 함수를 만들어보자.

'-----------------------------------------------------------------------------------
' fnGetMakeNode() / XML Node 생성
'-----------------------------------------------------------------------------------
Function fnGetMakeNode(x1,node1,name,text)
 Dim node2, i
 if isNull(text) then text = ""

 name = Replace(name,"(","")
 name = Replace(name,")","")
 name = Replace(name,"'","")

 Set node2 = x1.CreateElement(name)
 if Len(text)>0 then node2.text = text
 node1.appendchild node2
 Set fnGetMakeNode = node2
End Function

'-----------------------------------------------------------------------------------
' fnGetMakeAttribute() / XML Attribute 생성
'-----------------------------------------------------------------------------------
Function fnGetMakeAttribute(x1,node1,name,text)
 Dim a1
 if isNull(text) then text = ""
 If IsObject(text) Then text = TypeName(text)
 If IsEmpty(text) Then text = ""
 If IsArray(text) Then text = TypeName(text)
 
 Set a1 = x1.CreateAttribute(name)
 a1.text = text
 node1.attributes.setNameditem a1
 Set fnGetMakeAttribute = a1
End Function

인자는 전체 xml(x1), 추가하려고 하는 노드(node1), 이름(name), 내용(text) 이다. 부른 함수에 따라 노드가 추가되기도 하고 속성(attribute) 가 추가되기도 한다. 이제 xml 을 편리하게 추가할 방법이 생겼으니 DB에 있는 데이타를 꺼내서 만들어 놓은 xml 에 추가해 보도록 하자.

참... asp 에서는 오류 제어가 조금 애매하다. C# 이나 java 처럼 try ... catch 가 않되는 단점이 있다. 그래서 사용하는게 On Error Resume Next 다. 단점이라면 에러를 무시하고 마지막 에러만 반환한다는 단점이 있기는 하지만, xml 생성하는 데에는 크게 지장이 없으니...또 고려해야 할 사항이 플래셔(액션너)와 받기로 한 값들의 유효성 부분도 고려해야 한다. 난 serviceMethod 란 파라메터로 값을 받아 처리하기로 했으니까....이름이야 정하기 나름 아니겠습니까....ㅋㅋㅋ
지금까지 이야기 한것을 바탕으로 DB 연동 부분까지 해결해 보도록 하자....

serviceMethod = Request("serviceMethod")

Set oXml = Server.CreateObject("MSXML2.DOMDocument")
oXml.async = False
oXml.loadXML("<xml><serviceInfos/><userInfos/><items/><errors/></xml>")

'##서비스 인포 세팅
Set oNode = fnGetMakeNode(oXml, oXml.selectSingleNode("xml/serviceInfos"), "serviceInfo", null)
 Call fnGetMakeAttribute(oXml, oNode, "serviceMethod", cstr(serviceMethod))
Set oNode = Nothing

Dim Dbcon, SQL, Cmd, Rs
Set Dbcon = Server.CreateObject("ADODB.Connection")
Dbcon.Open (fn_DOA_GetDBString())

requestdt =  Request("requestdt")
potoIdx =  Request("photoIdx")

On Error Resume Next
select case serviceMethod
 '#############################################################3
 ' 리스트
 'http://localhost/service.asp?servicemethod=list&userparam=1
 case "list"
  userparam = request("userparam")
  '##  DB  로직 추가하세요...
   Set Cmd = Server.CreateObject("ADODB.Command")
   
    Cmd.ActiveConnection = Dbcon
    Cmd.CommandText = "UPFR_List"
    Cmd.Commandtype = 4
    
    'Cmd.Parameters.Refresh()
    Cmd.Parameters("@param") = userparam '추가 협의된 param
  
   Set Rs = Cmd.Execute()
   
   if Rs.Eof then
    Err.Raise 302, "", "준비된 리스트가 없습니다."
   else
    do while not Rs.eof
     Set oNode = fnGetMakeNode(oXml, oXml.selectSingleNode("xml/items"), "item", null)
      Call fnGetMakeAttribute(oXml, oNode, "rownum", Cstr( Rs("rownum")))
      Call fnGetMakeAttribute(oXml, oNode, "title", Cstr( Rs("title")))
      Call fnGetMakeAttribute(oXml, oNode, "name", Cstr( Rs("name")))
     Set oNode = Nothing
     Rs.moveNext
    Loop
   end if
  end if
  
 '#############################################################3
 '  serviceMethod 오류
     case Else
      Err.Raise 900, "", "파라메터 오류"
  '''/<errorCode="900" description="파라메터 오류">
end select


Dbcon.Close()
Set Dbcon = Nothing

일단 serviceMethod 를 받아서 case 문으로 분기 시키고, 쿼리를 위한 추가 파라메터를 userparam 으로 받아서 저장 프로시져를 실행하는 내용이다. 지정된 serviceMethod 가 아닌 경우는 Err.Raise 를 통해서 강제로 에러를 발생 시키도록 한다. 앞에서 On Error Resume Next 를 지정해 두었으므로 500 에러 페이지가 표시되진 않을 것이다. 지정된 serviceMethod 라 하더라도 상황에 따라 Err.Raise 를 활용하면 상황에 따른 내용을 처리하기 편리하다. 단, Err 는 최종 것만 남는다는 것을 기억하면된다.

마지막 단계 준비된 Xml 을 출력하자.

if (Err.Number <> 0) then
  Set oNode = fnGetMakeNode(oXml, oXml.selectSingleNode("xml/errors"), "error", null)
   Call fnGetMakeAttribute(oXml, oNode, "errorCode", Err.Number)
   Call fnGetMakeAttribute(oXml, oNode, "description", Err.Description)
  Set oNode = Nothing
 else
  Set oNode = fnGetMakeNode(oXml, oXml.selectSingleNode("xml/errors"), "error", null)
   Call fnGetMakeAttribute(oXml, oNode, "errorCode", "000")
   Call fnGetMakeAttribute(oXml, oNode, "description", "" & strResultDescription)
  Set oNode = Nothing
 end if
 
 
 oXml.Save Response
 Set oXml = Nothing
앞에서 Err.Raise 로 발생 시켰던 Err 의 내용을 xml 에 추가하고 처음에 협의 하였던 xml 양식으로 출력해 주는 부분이다. 플래셔와 협의에 따라 처리 방법을 늘린다면 serviceMethod 를 늘려나가면 될 것이고 각 종류별 파라메터는 필요에 따라 더 추가하면 될 것이다. 플래셔는 출력된 xml 데이타에서 errors 노드 밑의 error 노드의 errorCode 를 먼저 확인해서 협의된 정상 코드에 대해서만 처리를 하고(위 코드 상으로는 "000" 이군요...) 나머지 경우는 플래셔의 재량껏(기획서상에 나온 정의에 따라...)처리하면 될 것입니다.
저의 경우 아래와 같이 미리 정의된 에러 코드를 보내줍니다.

<errors>
<errorCode="000" description="코드000 이면 정상입니다. description 도 빈칸입니다.">
<errorCode="300" description="DB 에러">
<errorCode="900" description="파라메터 오류">
</errors>

전체 코드 보기


저작자 표시
신고
Trackback 0 Comment 0
2009.04.06 13:18

xml 로 받은 레코드 셋 어떻게 처리하나.


오랫만에 asp 를 하다 보니 이래 저래 불편한게 한두가지가 아니네...그 여러가지 문제들 중에 인코딩 관련 문제가 가장 크고(실제로은 if 뒤에 then 을 까먹는 문제가 더 심각하긴 하다...ㅋㅋㅋ) xml 에 대한 처리 문제가 그렇다.

나름 xml 에는 익숙하다고 생각하고 있었는데 asp 에서 xml 을 다루는 양식의 차이 뭐 그런 것으로 인해 여러가지 문제를 겪기도 했다. 그래서 몇자 기록이라도 남겨 보려고 한다.

일단 asp 에서 recordset 으로 xml 데이타를 가져오는 부분이다. 뭐 dom 을 생성하고 레코드셋 돌면서 dom 을 채워 나가도 상관 없겠지만...그렇게 하려고 xml 을 이용한다는건 우스운 일인 것 같고, 쿼리에서 지원하는 for xml auto 를 사용해보기로 하자....
select * from tableA for xml auto
JOIN 문의 상황이나 관계키의 설정등 여러가지 경우에 따라 표시되는 xml 의 모습이 다양하긴 하겠지만, 위의 경우 단일에 테이블에서 읽은 것이니 간단한 xml 이 출력되리라(테이블 명이 태그로 컬럼들이 속성으로 표시되는 xml 이 출력될 것이다.).
 
그럼 이렇게 만들어진 xml 은 레코드 셋에서 어떻게 읽어야 할까? 아래와 같이 실행 시켰다면 Rs 는 뭐가 들어 올까 하느냔 말이지....ㅋㅋㅋㅋ
 Set Dbcon = Server.CreateObject("ADODB.Connection")
 Dbcon.Open (strConnectionstring) 'strConnectionstring OLE DB 구문...


 Set Cmd = Server.CreateObject("ADODB.Command")
 
  Cmd.ActiveConnection = Dbcon
  Cmd.CommandText = "UPFR_PROCEDURE"  '위에 있는 select 문이 들어 있는 프로시져..
  Cmd.Commandtype = 4
  
  Cmd.Parameters("@param") = "all"
 Set Rs = Cmd.Execute()

위 코드에  Response.write Rs(0) 을 실행하면 어떤 모습일까....한글까지 출력되어 나온다면 정말 희한한 외계어를 보게 될 것이다....ㅋㅋㅋㅋ
우리가 돌려 받고자 하는 것은 분명 xml 이다... 단순한 문자열을 돌려 받고자 한게 아니라 xml Dom 을 돌려 받으려는 것이었다.


 Set xmlDom = CreateObject("MSXML2.DOMDocument")
 Set oStrm = CreateObject("ADODB.Stream")
 oStrm.Open()

즉 위와 같이 받을 xml 을 준비해 두어야 한다는 뜻이다. 제목에서 벌써 사기를 쳐버렸네....레코드셋으로 받는게 아닌데 ㅋㅋㅋㅋ 실제로는 ADODB.Stream 으로 받아서 Dom 에 올리는 형식이 되어야 한다는 것이다.


 Set xmlDom = CreateObject("MSXML2.DOMDocument")
 Set oStrm = CreateObject("ADODB.Stream")
 oStrm.Open()
 
 Set Dbcon = Server.CreateObject("ADODB.Connection")
 Dbcon.Open (strConnectionstring)


 Set Cmd = Server.CreateObject("ADODB.Command")
 
  Cmd.ActiveConnection = Dbcon
  Cmd.CommandText = "UPFR_PROCEDURE"
  Cmd.Commandtype = 4
  
  Cmd.Parameters("@param") = "all"
  
  Cmd.Properties("xml root") = "x"
  Cmd.Properties("Output Stream") = oStrm
  Cmd.Execute,,1024
 Set Cmd = Nothing
 
 oStrm.Position = 0
 xmlDom.loadXML(oStrm.ReadText)
 oStrm.Close()
 Set oStrm = Nothing


이렇게 말이지.
중간에 보면 좀 낯선 것들이 보인다. 특히 Cmd.Properties("xml root") = "x" 부분... 쿼리의 for xml auto 는 select 된 레코드 갯수만큼 Row 를 만들어 내기 때문에 xml 에 최상위 엘리먼트를 추가해 주어야 한다. 내가 배운 전통(한수석님과 멍멍이님...)에 따라 최상위 레코드는 "x" 를 사용했다... 더 궁금한건 물어보면 답해주리라....
더블어 그렇게 읽은 Dom 을 이용하는 VB script 예제도 같이 올려 본다.


<%
    Dim eventNodes, itemNode, itemInfoNedes, itemInfoNode
    Set eventNodes = eventDom.selectNodes("x/firstElement")
    If (eventNodes Is Nothing) Then
     
%>
  <div class="sMtype40 cr">
   <p class="cType2 sMtype10">레코드가 없습니다.</p>
  </div>
<%    
    else 
     For Each itemNode In eventNodes
     
      itemCnt = 1
%>  
  <div class="sMtype40 cr">
   <p class="cType2 sMtype10"><%=itemNode.Attributes.GetNamedItem("name").text%></p>
   
   <div class="fl">
   <%
    Set itemInfoNedes = itemNode.selectNodes("secondElement")
    For Each itemInfoNode In itemInfoNedes
   %>
    <p class="sMtype10">ID : <%=itemInfoNode.Attributes.GetNamedItem("name").text%></p>
   <%
      if (itemInfoNedes.Length /2) <=  itemCnt then
       itemCnt = 0
   %>
   </div>
   <div class="fr">
   <%
      end if

    itemCnt = itemCnt + 1
    Next
   %>
   </div>
  </div>
<%
     Next
    end if
%>


이렇게 루프를 돌릴 수 있다.  출력 결과물의 경우 계층형으로 두번째 엘리먼트의 경우 2단으로 표시되는 것이라 이렇게 이용했다. 위에서 사용한 쿼리예와는 다른 적용이지만 적용 내용만으로도 충분히 내용을 알 수 있으리라 생각한다.








저작자 표시
신고

'뭐하는데 > 이따위로 만든다' 카테고리의 다른 글

Microsoft의 새로운 검색 서비스 Bing  (0) 2009.09.01
xml 출력하기  (0) 2009.04.08
xml 로 받은 레코드 셋 어떻게 처리하나.  (0) 2009.04.06
sql 2008  (0) 2009.03.18
xslt 문서 변환  (0) 2009.03.12
오랫만에 다시 하는 ASP  (0) 2009.03.09
Trackback 0 Comment 0
2009.03.12 10:08

xslt 문서 변환

xml 을 다루다 보면 참 여러가지 문제가 발생한다. 그 중 내가 주로 겪었던 대부분의 문제는 encoding 에 대한 문제인데... 한글처리가 만만치 않다는 점이다. 매번 골치를 썪고 해결을 어떻게 어떻게 해결은 하면서도 막상 비슷한 문제를 겪기에 이번엔 좀 정리를 해놓으려고 이렇게 적는다. 물론 이렇게 적어 놓고 곧 잊어버리긴 하겠지만, xslt 로 모든 걸 해결할 수 있다고 생각하고 데이타와 형식의 분리를 꿈꾸며 도전해 왔었는데...막상 그 적용이 그리 많지는 않았기에...그 조금 쌓았던 지식 마져 매번 먼지처럼 날려가는 기분이다...ㅋㅋㅋ

이번에도 몇가지 작업을 하면서 골치아픈 문제를 만났는데...DB 에 들은 메뉴 정보를 트리형태로 보이기 위한 작업이었는데... 트리형태라는게 계층형이다 보니 재귀호출이 귀찮게 느껴지고(내가 은근 재귀 호출로 피본 경험이 많기에...)xml 이 편할 것 같아 xml auto 로 읽은 것을 xslt 로 변경하는 작업을 했다...
VB script 내용은 아래와 같이 간단히 적용 가능하다. xml, xslt 둘다 하자 없는 파일이라면...



 Set xmlDoc = Server.CreateObject("Msxml2.DOMDocument")
 
 xmlDoc.async = False
 xmlDoc.load(server.MapPath("menu.xml"))
 
 
 Set xslDoc = Server.CreateObject("Msxml2.FreeThreadedDOMDocument")
 xslDoc.async = False
 xslDoc.load(Server.MapPath("xslt.xsl"))
 
 call xmlDoc.transformNodeToObject(xslDoc, xmlDoc)
 Response.write xmlDoc.xml

문서의 변환은 transformNode와 transformNodeToObject 두가지가 지원되던데 전자의 경우 변환된 결과가 문자열 형태로 나타나고 후자의 경우 dom 객체로 리턴되는 함수이다. 난 후자의 경우를 이용했다.
xslt 는 아래와 같이 이용했다. 뭐 어려운 일은 아닌데...문제는 인코딩이다. 내가 생각하기엔 모두 utf-8 로 처리되게 한거 같은데 transformNodeToObject  만 거처서 나오면 utf-16 으로 변환되어 몽땅 깨져 버리는 ...문제를 해결했으니 이 글을 적는 것이다...ㅋㅋㅋ... 문법 자체를 마크업 형식으로 적다니...오랫만에 다시 봐도 참 매력적인 넘이다....ㅋㅋㅋ...

 <xsl:template match="/">
  <ul id="leftMenu">
   <xsl:apply-templates select="x/LIST/Menu"/>
  </ul>
 </xsl:template>
 <xsl:template match="Category">
   <xsl:choose>
    <xsl:when test="count(Category) = 0">
     <li><a  target="pgm_page">
      <xsl:attribute name="href">
       <xsl:value-of select="@cate_linkurl"/>
      </xsl:attribute>
     <xsl:value-of select="@cate_name"/>
     </a></li>
    </xsl:when>
    <xsl:when test="count(Category) &gt; 0">
     <li><a  target="pgm_page">
     <xsl:value-of select="@cate_name"/>
     </a>
     <ul>
      <xsl:apply-templates select="Category"/>
     </ul></li>
    </xsl:when>
   </xsl:choose>
 </xsl:template>


문제는 xslt 의 output 이다. 인코딩을 지정할 수 있었던 것이다...바보처럼....ㅋㅋㅋㅋ 그것도 모르고 있었다니...전에도 비슷한 문제를 겪고 해결을 했던것 같은데...다시 걸린 것 같다. 오전 내내 머릿속에서 맴돌던 넘을 해결하고 나니 속이 시원하긴 한데...혹시라도 어려움을 겪을 사람들(어쩌면 다시 나...)을 위해 몇자 남겨 본다.

xslt output 스키마


 관련 링크 : http://www.w3schools.com/xsl/xsl_w3celementref.asp

신고
Trackback 0 Comment 0