모바일 디바이스와 플랫폼은 매번 새로운 제품이 나올 때마다 더욱 다양한 기능으로 시선을 끌며 업계를 선도하는 모바일 벤더에서는 단지 몇 달 만에 중요한 발표를 하곤 한다. 헤드라인에는 대부분 멀티터치 기능 및 Adobe® Flash® 기술과 같은 UI 기능과 프로세서 속도 및 스토리지 용량과 같은 하드웨어 개선사항에 관한 내용을 채워진다. 그러나 중요한 점은 컨텐츠가 핵심이라는 사실이다. 애플리케이션과 서버, 모바일 디바이스 그리고 사용자 간에는 계속해서 컨텐츠 즉, 데이터의 교환이 이루어진다. 데이터를 처리할 수 있는 기능이 없으면 Apple의 iPhone이나 Google의 Android와 같은 스마트폰은 값은 비싸지만 성능은 이에 미치지 못하는 일반적인 휴대전화가 된다.

자주 사용하는 약어

  • API: Application Programming Interface
  • DOM: Document Object Model
  • HTML: HyperText Markup Language
  • IDE: Integrated development environment
  • SAX: Simple API for XML
  • SDK: Software Developer Kit
  • UI: User Interface
  • XML: Extensible Markup Language

경이적으로 성공한 Facebook, LinkedIn 및 Twitter와 같은 소셜 네트워킹 플랫폼을 생각해보자. 기능 면에서만 보면 이러한 플랫폼은 매우 단조롭다. 구성원과 사이트 방문자가 사이트에서 발행된 컨텐츠에서 가치를 추구한다는 점에서 이러한 플랫폼이 인기를 얻고 있다. 그리고 모바일 디바이스 덕택에 이러한 컨텐츠를 액세스하는 경우가 점차 늘어나고 있다.

이 기사에서는 Android 플랫폼에서 XML과 JSON 데이터 교환 형식을 사용하는 과정을 설명한다. 예제 애플리케이션의 데이터 소스는 Twitter 계정의 상태 업데이트 피드이다. Twitter에서는 이 피드 데이터를 XML과 JSON 형식으로 사용할 수 있다. 아는 바와 같이 이 두 가지 형식은 프로그래밍을 통해 데이터를 처리하는 방식이 매우 다르다.

여기서는 독자가 이 기사에 있는 예제 코드를 실행하기 위해 Eclipse와 Android SDK 버전 1.5 이상을 설치했다고 가정한다. 환경 설정에 관한 자세한 내용은 Android Developers 웹 사이트를 참조한다. 또한, 활성화된 Twitter 계정이 있으면 예제를 따라 하는 데 도움이 되지만 반드시 필요한 것은 아니다. 관련 링크는 참고자료를 확인한다.

먼저, 두 가지 데이터 형식 중 XML을 살펴보기로 하자. 이미 XML과 JSON에 익숙한 경우에는 애플리케이션 기회: Twitter 피드로 건너 뛰어 바로 Android에서 이 두 형식을 사용해볼 수 있다.

XML: 오랜 친구

힘든 작업은 이제 그만!

일반적으로 XML을 채택하기 전 기술이 상태와 비교해보면 XML의 자기 기술적 특성이 가치가 있다는 것을 분명하게 확인할 수 있다. 그 당시에는 데이터를 교환하려면 워드 프로세서나 스프레드시트 애플리케이션으로 번거로운 데이터 기술 문서를 직접 작성하고 유지보수해야 했다. 이러한 문서는 보통 인터페이스 스펙이라고 하며 여기에는 필드 이름과 길이, 구분 기호, 계층 구조 등이 기술되었다. 사용자는 그들이 적합하다고 생각하는 것을 따랐으며 표준에 가장 근접한 것은 CSV(Comma-Separated-Value) 형식이었다. 그러나 CSV 파일 조차도 매우 다양하다. CSV 파일을 스프레드시트 프로그램으로 가져오면서 사용 가능한 모든 옵션을 확인하면 이점을 확인할 수 있다.

사실상 최근에 엔터프라이즈이나 웹, 모바일 분야에서 프로그래밍을 수행한 개발자라면 누구나 XML을 사용했을 것이다. XML은 이제 어디에서나 사용되고 있다.

XML 문서에는 인식 가능한 구조 즉, 속성과 하위 요소를 선택적으로 포함할 수 있는 일련의 요소가 있다. 유효한 모든 XML 문서의 첫 번째 줄에는 <?xml version="1.0" encoding="utf-8"?>이라고 선언되어 있다. 첫 번째 줄 다음부터는 애플리케이션마다 다르다. XML의 장점은 이 형식이 자기 기술적이라는 점에 있다.

XML 스키마

XML 문서가 자기 기술적이라고 하더라도 XML 문서는 특정 규칙과 지침을 따라야 한다. XML 스키마는 이러한 규칙과 지침을 지정한다. XML 스키마는 특정 XML 파일의 구조가 기술된 문서이다. 이러한 구조는 때로는 장황하고 복잡하다. (이론의 여지가 있지만, XML로 인해 매우 설명적인 데이터 구조 개념이 만연하고 부분적으로는 지난 10년간 디스크 스토리지 기술 비용이 대폭 감소하면서 데이터가 급격히 늘어나는 좋지 못한 상황이 발생하였다.)

이와 같은 크고 복잡한 파일을 일반적으로 사용하게 되면서 이러한 파일을 수동으로 처리하는 기술은 프로그래머와 분석가에게 일고의 가치도 없는 기술로 간주되곤 했다. 이러한 문제를 처리하기 위해 XML 편집기와 유효성 확인 도구를 사용하여 이러한 파일과 이 파일과 관련된 작업(레거시 형식으로 문서화하거나 변환하는 작업)을 관리하게 되었다.

또한, XML을 사용하면 CDATA 태그 세트를 통해 일반적인 텍스트 데이터 외에 2진 데이터를 저장할 수 있다. XML 문서 내에 있는 CDATA 태그에는 텍스트에 CDATA 자체가 포함되지만 않으면 다른 마크업 텍스트를 포함한 모든 유형의 데이터가 포함될 수 있다.

API에서는 요청/응답 쿼리를 수행하기 위한 구조로 XML을 사용하여 이러한 기능을 활용하는 것이 일반적이다. 응답 데이터는 XML 구조를 포함하고 있으며 이 XML 구조는 CDATA 태그 내에 포함된다. 예를 들면, API 호출을 통해 이름이 Mott인 고객 레코드를 요청할 수 있다. 데이터가 발견되면 데이터는 XML 구조로 패키지되어 Listing 1에 있는 것과 같은 응답 요소에 저장된다.


Listing 1. 데이터를 XML 구조로 패키지하여 응답 요소 내에 저장하기

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<request>
<query>
<lastname>Mott</lastname>
<maxhits>100</maxhits>
</query>
</request>

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
<returncode>200</returncode>
<query>
<lastname>Mott</lastname>
<hits>1</hits>
</query>
<data>
<![CDATA[
<contact>
<firstname>Troy</firstname>
<lastname>Mott</lastname>
<age>not telling</age>
</contact>
]]>
</data>
</response>

작업영역에서의 XML

오늘날에는 XML을 기본적인 데이터 형식으로 사용한다. 같은 데이터를 다른 형식으로도 사용할 수 있지만 XML 구조의 사용 가능성을 고려하는 것이 안전하다.

ERP(Enterprise Resource Planning) 패키지에서는 데이터 가져오기와 내보내기 작업에 XML을 많이 사용한다. 인터넷 뉴스 사이트에서는 데이터를 RSS(Really Simple Syndication) 피드 즉, 뉴스 판독기에서 처리할 수 있게 설정한 사전 정의된 형식이 있는 XML 문서로 사용할 수 있다. OpenOffice.org 및 Microsoft® Office와 같은 워드 프로세싱 애플리케이션도 XML을 사용한다.

현재의 Microsoft Office 문서는 PKZIP 호환 가능 파일이며 여기에는 다수의 XML 문서가 포함되어 있다. 각 XML 파일의 첫 번째 줄에는 공통으로 선언 문구가 포함된다. Listing 2에서 알 수 있는 바와 같이 속성은 수행하기에 다소 어려울 수 있다.


Listing 2. 각 XML 파일의 첫 번째 줄에 있는 공통 선언

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <w:document xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
 xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
 xmlns:v="urn:schemas-microsoft-com:vml"
 xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
 xmlns:w10="urn:schemas-microsoft-com:office:word"
 xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
 xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml">
 <w:body><w:p w:rsidR="00B6337C" w:rsidRDefault="00663F0E"><w:r>
 <w:t xml:space="preserve">This is a sample </w:t></w:r><w:r
 w:rsidRPr="006906EA"><w:rPr><w:i/></w:rPr><w:t>Microsoft 
 Word document</w:t></w:r><w:r><w:t xml:space="preserve"> used
 to </w:t></w:r><w:r w:rsidRPr="006906EA"><w:rPr><w:b/>
 <w:u w:val="single"/></w:rPr><w:t>demonstrate</w:t></w:r>
 <w:r><w:t xml:space="preserve"> some XML topics.</w:t></w:r>
 </w:p><w:p w:rsidR="00B14B2A" w:rsidRDefault="00B14B2A"/><w:p 
 w:rsidR="00B14B2A"w:rsidRDefault="00B14B2A"><w:r><w:rPr>
 <w:noProof/></w:rPr><w:drawing><wp:inline distT="0" distB="0" 
 distL="0" distR="0"><wp:extent cx="3276600" cy="3838575"/><wp:effectExtent
 l="19050" t="0" r="0" b="0"/><wp:docPr id="1" name="Picture 0"
 descr="frankableson.jpg"/><wp:cNvGraphicFramePr><a:graphicFrameLocks
 xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
 noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic
 xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData
 uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic
 xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
 <pic:nvPicPr><pic:cNvPrid="0"name="frankableson.jpg"/><pic:cNvPicPr/>
 </pic:nvPicPr><pic:blipFill><a:blip r:embed="rId4"
 cstate="print"/><a:stretch><a:fillRect/></a:stretch>
 </pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/>
 <a:ext cx="3276600" cy="3838575"/></a:xfrm><a:prstGeom
 prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic>
 </a:graphicData></a:graphic></wp:inline></w:drawing>
 </w:r></w:p><w:p w:rsidR="00663F0E" w:rsidRDefault="00663F0E"/>
 <w:p w:rsidR="00CC16CE" w:rsidRDefault="00CC16CE"/><w:sectPr 
 w:rsidR="00CC16CE" w:rsidSect="00B6337C"><w:pgSz w:w="12240" w:h="15840"/>
 <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" 
 w:footer="720" w:gutter="0"/><w:cols w:space="720"/><w:docGrid
 w:linePitch="360"/></w:sectPr></w:body></w:document>

XML은 자기 기술적이지만 그렇다고 해서 태그를 판독하기 쉬운 것은 아니다. 또한, 이처럼 복잡한 예제에서는 다수의 XML 네임스페이스를 사용하고 있으며 이 때문에 특수한 도구가 없으면 XML 문서를 처리하기가 훨씬 더 어려워진다.

XML은 어디에서나 사용할 수 있지만 Android 프로그래머에게는 좋지 못한 선택이 될 수도 있다. 특히, XML의 구조적 특성으로 인해 데이터가 증가하게 되면서 데이터 구조를 희생해야 하는 경우에는 더욱 그렇다. Android와 같이 자원이 제약된 플랫폼은 일반적으로 셀룰러 데이터 네트워크에서 작동하며 대량의 XML 데이터를 저장하거나 구문 분석할 수 없다. 그러나 특정 프로그래밍 작업에서 텍스트와 2진 데이터를 모두 교환해야 하는 경우에는 XML을 사용하는 것이 유용하다.

이제 다른 데이터 교환 형식인 JSON을 살펴보자.


JSON: 인터넷에 새롭게 등장한 기술

JSON을 데이터 형식 옵션으로 제공하는 인터넷 API 제공자가 더욱 많아지고 있다. JSON은 그 자체로 Ajax(Asynchronous JavaScript and XML) 웹 프로그래밍 커뮤니티에서 유명하다. Ajax 기술을 이용하면 웹 페이지에서 전체 페이지를 새로 고치 대신 선택한 부분에 있는 데이터만 다시 고침으로써 웹 페이지를 동적으로 업데이트할 수 있다. 보다 적은 데이터가 전송되고 따라서 훨씬 더 적은 데이터가 구문 분석되어 브라우저 창에 표시되기 때문에 Ajax를 사용하는 애플리케이션은 기존 웹 애플리케이션보다 훨씬 더 우수한 사용자 경험을 제공할 수 있다. 사실상, 제대로 작성된 Ajax 애플리케이션이라면 사용자 경험에 있어서 스마트 애플리케이션이나 팻 클라이언트 애플리케이션에 못지않다.

Ajax 애플리케이션은 웹 서버를 이용하여 데이터를 교환하며 때로는 데이터와 같은 것을 다시 고치도록 요청하지만 형식화는 원칙적으로 필요 없다. 사전 형식화된 HTML을 웹 서버에서 서비스하는 것은 일반적으로 좋지 않다. 그 대신 제대로 작성한 애플리케이션을 통해 데이터 컨텐츠를 브라우저에 전송하고 CSS(Cascading Style Sheet) 파일을 적용하여 색상 및 글꼴 특성과 같은 시각적 효과를 제공해야 한다.

애플리케이션에서 가상의 인물인 Mott의 연락처 레코드를 요청한다고 가정하자. 이 애플리케이션에는 브라우저로 반송할 둘 이상의 데이터 요소가 있다. 그렇다면 이 데이터를 어떻게 패키지해야 할까? Listing 1에 있는 예제에서는 XML로 된 간단한 요청/응답 구조를 사용하였다. 이러한 방식도 매우 적합하지만 이런 경우에는 서버에서 응답을 전송할 때마다 구문 분석하여 DOM과 같은 구조에 데이터를 저장한 후, 웹 페이지 컨텐츠를 업데이트해야 한다.

그 대신 서버에서 Javascript를 일부 되가져와서 직접 처리할 수도 있다. 다음은 이름이 Mott인 남자를 대상으로 하는 쿼리(http://<yourserver/app/searchcontact?Mott)에 응답하는 가상 애플리케이션의 샘플 응답이다. 이 응답은 문자열로 표현된 Javascript 오브젝트 즉, JSON 문자열이며 여기서는 기사의 페이지 넓이에 맞춰 두 줄로 나누어져 있다.

[{"firstname":"Troy","lastname":"Mott","age":"don't ask!"},{"firstname":"Apple seed",
   "lastname":"Mott's","age":"99"}]

XML에서는 문자열이 장황하게 기술되는 반면에 JSON은 읽기가 다소 어렵다는 문제가 있다. JSON 오브젝트는 : 쌍 형식으로 구성된다. JSON 오브젝트의 요소는 쉼표로 분리하고 각 오브젝트는 중괄호({}) 안에 포함된다. 그리고 오브젝트의 배열은 대괄호 안에 포함된다. 이렇게 하는 것이 데이터베이스에 있는 일련의 행을 오브젝트의 배열로 전송하는 일반적인 방법이며 오브젝트의 배열에서 각 배열 요소는 데이터베이스 행에 해당하며 오브젝트의 각 특성은 데이터 열을 나타낸다.

Listing 3에는 이러한 오브젝트를 HTML 페이지 내에서 사용하는 예제가 표시되어 있다. 단순하게 하기 위해 서버와 통신하는 부분은 추가하지 않았으며 그 대신 문자열 변수(serverresponse)를 사용하여 JSON 데이터를 제공한다.


Listing 3. HTML 페이지에서 JSON 오브젝트 사용하기

<html>
<head>
<script language="JavaScript">
var serverresponse = "[{\"firstname\":\"Troy\",\"lastname\":\"Mott\",\"age\":\"don't
ask!\"},{\"firstname\":\"Apple seed\",\"lastname\":\"Mott's\",\"age\":\"99\"}]";
function updatepage()
{
    var contacts = eval!(serverresponse );
    var i;
    var s = "Search Results:<br />";
    for (i=0;i<contacts.length;i++)
    {
        s = s + contacts[i].firstname + " " + contacts[i].lastname + "'s age is ... " 
+ contacts[i].age + "<br />";
    }
    document.getElementById("target").innerHTML = s;
}
</script>
</head>
<body>
<button onclick="updatepage();">Search for Mott</button><br />
<span id="target"> </span>
</body>
</html>

이 예제에서는 eval!() Javascript 함수를 사용하여 문자열을 Javascript 배열로 변환한다. JSON 라이브러리를 사용하면 더욱 신속하고 안전하게 이 단계를 수행할 수 있다. Listing 3에 있는 방식은 우수 사례가 아니다. 이 예제는 Ajax 애플리케이션에서 JSON 오브젝트를 어떻게 사용할 수 있는지 보여주기 위한 것이며 JSON 구조는 클라이언트 코드에서 교환되고 구문 분석되어 처리된다.

요약하면 JSON에는 다음과 특징이 있다.

  • 데이터 교환 형식이다.
  • Javascript 오브젝트를 문자열로 인코드하는 수단이다.
  • 텍스트와 숫자 값으로 제한된다. 2진 값은 명백하게 허용되지 않는다. JSON에는 CDATA에 해당하는 것이 없다.
  • 데이터 크기 면에서 XML보다 더 경제적이지만 가독성은 떨어진다.
  • Twitter와 같은 API 제공자가 옵션으로 제공하는 사례가 증가하고 있다.

Listing 3에서 클라이언트는 클라이언트 측 스크립트를 실행하는 웹 브라우저이다. 이제 본론으로 돌아가서 Android 애플리케이션에서 XML과 JSON을 사용하는 방법을 살펴보도록 하자.


애플리케이션 가능성: Twitter 피드

Twitter는 사람들이 아침 식사로 무엇을 먹는지 그리고 어린이 스포츠 팀이 운동장에서 어떻게 활동하는지부터 인접 국가에서 발생한 정치적 폭동과 관련된 최신 정보나 장기 이식과 관련된 상세한 보도와 같은 심각한 주제에 이르기까지 거의 모든 최신 정보를 제공하면서 국제적인 영향력을 갖게 되었다.

이 기사에 있는 샘플 코드와 함께 사용할 XML과 JSON 문서를 얻기 위한 가장 쉬운 방법은 http://twitter.com/statuses/user_timeline/userid.format URL을 이용하는 것이며 여기서 userid는 Twitter 사용자 ID이고 format은 XML이나 JSON에 해당한다.

또한, 이 페이지를 가리키는 링크를 그림 1과 같은 Twitter 페이지에서 직접 찾을 수 있으며 여기에서 자신의 Twitter 사용자 ID를 확인할 수 있다.


그림 1. 자신의 Twitter 페이지에 있는 피드 페이지를 가리키는 링크
오른쪽 하단 모서리에 'RSS feed of fableson's tweets' 링크가 있는 fableson Twitter 페이지의 스크린 캡처 

전체 피드 파일은 매우 장황하므로 다음 두 개의 Listing에서는 필자의 Twitter 계정에서 가져온 피드의 첫 번째 항목만 표시한다. Listing 4에는 XML 스니펫이 포함되어 있다.


Listing 4. XML 스니펫

<?xml version="1.0" encoding="UTF-8"?>
<statuses type="array">
<status>
  <created_at>Thu Apr 29 05:25:29 +0000 2010</created_at>
  <id>13052369631</id>
  <text>Wrapping up new article on JSON for Android
 programmers...</text>
  <source><a href="http://www.linkedin.com/"rel="nofollow">
   LinkedIn</a></source>
  <truncated>false</truncated>
  <in_reply_to_status_id/>
  <in_reply_to_user_id/>
  <favorited>false</favorited>
  <in_reply_to_screen_name/>
  <user>
    <id>15221439</id>
    <name>fableson</name>
    <screen_name>fableson</screen_name>
    <location>Byram Township, NJ</location>
    <description/>

<profile_image_url>http://a3.twimg.com/profile_images/260492935
/bookcover_normal.jpg</profile_image_url>
    <url>http://msiservices.com</url>
    <protected>false</protected>
    <followers_count>52</followers_count>
    <profile_background_color>9ae4e8
    <profile_text_color>000000</profile_text_color>
    <profile_link_color>0000ff</profile_link_color>
    <profile_sidebar_fill_color>e0ff92
</profile_sidebar_fill_color>
    <profile_sidebar_border_color>87bc44
</profile_sidebar_border_color>
    <friends_count>10</friends_count>
    <created_at>Tue Jun 24 17:04:11 +0000 2008</created_at>
    <favourites_count>0</favourites_count>
    <utc_offset>-18000</utc_offset>
    <time_zone>Eastern Time (US & Canada)</time_zone>

   <profile_background_image_url>http://s.twimg.com/a/1272044617/
images/themes/theme1/bg.png</profile_background_image_url>
   
<profile_background_tile>false</profile_background_tile>
    <notifications>false</notifications>
    <geo_enabled>false</geo_enabled>

    <verified>false</verified>
    <following>false</following>
    <statuses_count>91</statuses_count>
    <lang>en</lang>
    <contributors_enabled>false</contributors_enabled>
  </user>
  <geo/>
  <coordinates/>
  <place/>
  <contributors/>
</status>
</statuses>

Listing 5에는 동일한 데이터가 JSON 형식으로 표시되어 있다.


Listing 5. JSON 형식의 피드 데이터

[
{"in_reply_to_status_id":null,
"favorited":false,
"created_at":"Thu Apr 29 05:25:29 +0000 2010",
"in_reply_to_screen_name":null,
"geo":null,
"source":"<a href=\"http://www.linkedin.com/\" rel=\"nofollow\
          ">LinkedIn</a>",
"contributors":null,
"place":null,
"truncated":false,
"coordinates":null,
"user":
{
    "friends_count":10,
    "description":"",
    "lang":"en",
    "statuses_count":91,
    "time_zone":"Eastern Time (US & Canada)",
    "profile_link_color":"0000ff",
    "favourites_count":0,
    "created_at":"Tue Jun 24 17:04:11 +0000 2008",
    "contributors_enabled":false,
    "profile_sidebar_fill_color":"e0ff92",
    "following":null,
    "geo_enabled":false,
    "profile_background_image_url":"http://s.twimg.com/a/1272044617/images/themes
/theme1/bg.png",
    "profile_image_url":"http://a3.twimg.com/profile_images/260492935
/bookcover_normal.jpg",
    "notifications":null,
    "profile_sidebar_border_color":"87bc44",
    "url":"http://msiservices.com",
    "verified":false,
    "profile_background_tile":false,
    "screen_name":"fableson",
    "protected":false,
    "location":"Byram Township, NJ",
    "profile_background_color":"9ae4e8",
    "name":"fableson",
    "followers_count":52,
    "id":15221439,
    "utc_offset":-18000,
    "profile_text_color":"000000"
},
"in_reply_to_user_id":null,
"id":13052369631,
"text":"Wrapping up new article on JSON for Android programmers..."}
]

두 개의 Listing에 상태 업데이트 외에 얼마나 많은 추가 데이터가 삽입되었는지 확인한다. 게시물이 작성되고 나면 날짜/시간과 해당 게시물의 텍스트 자체만 신경 쓰면 된다. 다음은 이 데이터를 구문 분석하는 Android 애플리케이션의 관련 부분을 살펴보자. 전체 프로젝트는 다운로드링크를 통해 얻을 수 있다.


XMLvsJSON 애플리케이션

실시간 업데이트

샘플 애플리케이션은 웹에서 실시간으로 데이터를 가져오지 않지만 실제 애플리케이션에서는 실시간으로 데이터를 가져오게 된다. 데이터 피드는 raw 자원 폴더에서 가져오므로 샘플 애플리케이션에서는 구문 분석 특성에 집중할 수 있다. Android에서의 네트워크 연결과 관련된 정보는 참고자료를 참조한다.

이 Android 애플리케이션은 단순하다. 이 애플리케이션은 XML과 JSON 데이터 피드의 전체 사본을 포함하고 있으며 사용자에게 이 두 사본 중 어느 하나를 구문 분석할 수 있는 옵션을 제공한다. 그림 2에는 Eclipse에 있는 프로젝트 파일의 구조가 표시되어 있다. (그림 2의 텍스트 전용 버전 보기)


그림 2. Eclipse 프로젝트의 파일 구조
Eclipse에 있는 파일의 구조를 캡처한 화면 

그림 3에는 구문 분석 옵션을 선택하기 전의 애플리케이션 UI가 표시되어 있다.


그림 3. 구문 분석 옵션을 선택하기 전의 애플리케이션 UI
구문 분석 옵션을 선택하기 전의 애플리케이션 사용자 인터페이스를 캡처한 화면 

애플리케이션 UI에는 먼저, Parse XML과 Parse JSON file 단추가 표시되고 그다음에 default text가 표시된다. Listing 6에는 UI에 맞는 레이아웃이 포함되어 있으며 해당 프로젝트의 res/layout 폴더에 있는 main.xml 파일에서 이 레이아웃을 확인할 수 있다.


Listing 6. UI 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal">

<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
 android:id="@+id/btnXML" android:text="Parse XML"></Button>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
 android:id="@+id/btnJSON" android:text="Parse JSON file"></Button>
</LinearLayout>

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/ScrollView01" android:layout_width="fill_parent"
android:layout_height="wrap_content">

<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="default text" 
    android:layout_gravity="center_horizontal"
    android:id="@+id/txtData" 
    />


</ScrollView>

</LinearLayout>

Parse XML과 Parse JSON file 단추는 TextView 제어가 포함된 ScrollView 위에 정의되어 있다. 이렇게 하는 이유는 사용자가 결과 데이터를 스크롤할 수 있게 하려는 것이다.

여러 개의 LinearLayout 구조를 사용했다는 점에 주목한다. 첫 번째는 수직 맞춤이고 여기에는 수평 구조가 있는 LinearLayoutScrollView가 모두 포함되어 있다. 내부에 있는 LinearLayout에는 두 개의 Button 위젯이 포함되어 있다. 이 레이아웃은 Listing 7에 있는onCreate() 메소드에서 확대되어 연결된다.


Listing 7. onCreate() 메소드

    Button btnXML;
    Button btnJSON;
    TextView tvData;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);


        tvData = (TextView) findViewById(R.id.txtData);
        btnXML = (Button) findViewById(R.id.btnXML);
        btnXML.setOnClickListener(new Button.OnClickListener()
        {
            public void onClick(View v)
            { 
                examineXMLFile();
            }
        });


        btnJSON = (Button) findViewById(R.id.btnJSON);
        btnJSON.setOnClickListener(new Button.OnClickListener()
        {
            public void onClick(View v)
            {
                examineJSONFile();
            }
        });

    }

examineXMLFile() 메소드는 XML 구문 분석을 제어한다.


XML 구문 분석

SAX 대 DOM

또한, Android는 DOM 구문 분석기를 지원한다. 이 구문 분석기를 사용하면 메모리 용량이 많이 필요하지만 SAX 구문 분석기를 사용할 때보다는 덜 복잡해진다. XMLvsJSON과 같은 애플리케이션은 대용량 데이터 피드의 매우 작은 서브세트에만 관여하며 SAX 방식은 이러한 작업에 적합하다고 할 수 있다.

XML 데이터를 구문 분석하는 작업은 SAX 방식의 구문 분석기를 사용하여 수행되는 경우가 많다. 여기서는 이러한 유형의 구문 분석기를 사용하여 소스 XML 데이터를 가리키는 InputSource를 설정하며 해당 문서가 "처리"될 때 특정 이벤트를 수신하는 핸들러를 제공한다. Listing 8에는 examineXMLFile() 메소드가 있으며 이 메소드는 다음과 같은 작업을 수행한다.

  • 원시 자원의 XML 파일을 사용하여 InputSource를 설정
  • Listing 9에 있는 twitterFeedHandler 핸들러와 관련하여 SAXParser 작성
  • 구문 분석기를 호출하여 TextView 위젯에 해당 결과를 표시. 이 위젯은 레이아웃 파일에서는 R.id.txtData로 식별되고 코드에서는tvData로 참조한다.
  • TextView에 모든 오류 표시


Listing 8. examineXMLFIle() 메소드

void examineXMLFile()
    {
        try {
            InputSource is = new InputSource(getResources()
.openRawResource(R.raw.xmltwitter));
            // create the factory
            SAXParserFactory factory = SAXParserFactory.newInstance();
            // create a parser
            SAXParser parser = factory.newSAXParser();
            // create the reader (scanner)
            XMLReader xmlreader = parser.getXMLReader();
            // instantiate our handler
            twitterFeedHandler tfh = new twitterFeedHandler();

            // assign our handler
            xmlreader.setContentHandler(tfh);
            // perform the synchronous parse
            xmlreader.parse(is);
            // should be done... let's display our results
            tvData.setText(tfh.getResults());
        }
        catch (Exception e) {
            tvData.setText(e.getMessage());
        }
    }

examineXMLFile() 메소드는 여러 가지 작업을 수행하지만 애플리케이션의 Perspective에서 수행되는 실제 구문 분석 작업은 twitterFeedHandler.java 파일에서 구현한 핸들러에서 처리된다. 이 클래스에서 Listing 9에 있는 DefaultHandler 인터페이스가 구현된다.


Listing 9. twitterFeedHandler 클래스

public class twitterFeedHandler extends DefaultHandler {

    StringBuilder sb = null;
    String ret = "";
    boolean bStore = false;
    int howMany = 0;

    twitterFeedHandler() {
    }

    String getResults()
    {
        return "XML parsed data.\nThere are [" + howMany + "] status updates\n\n" + ret;
    }
    @Override

    public void startDocument() throws SAXException {
        // initialize "list"
    }

    @Override
    public void endDocument() throws SAXException {

    }

    @Override
    public void startElement(String namespaceURI, String localName, String qName, 
Attributes atts) throws SAXException {

        try {
            if (localName.equals("status")) {
                this.sb = new StringBuilder("");
                bStore = true;
            }
            if (localName.equals("user")) {
                bStore = false;
            }
            if (localName.equals("text")) {
                this.sb = new StringBuilder("");
            }
            if (localName.equals("created_at")) {
                this.sb = new StringBuilder("");
            }
        } catch (Exception ee) {

            Log.d("error in startElement", ee.getStackTrace().toString());
        }
    }

    @Override

    public void endElement(String namespaceURI, String localName, String qName)
throws SAXException {

        if (bStore) {
            if (localName.equals("created_at")) {

                ret += "Date: " + sb.toString() + "\n"; 
                sb = new StringBuilder("");
                return;

            }

            if (localName.equals("user")) {
                bStore = true;
            }

            if (localName.equals("text")) {

                ret += "Post: " + sb.toString() + "\n\n";
                sb = new StringBuilder("");
                return;

            }


        }
        if (localName.equals("status")) {
            howMany++;
            bStore = false;
        }
    }

    @Override

    public void characters(char ch[], int start, int length) {

        if (bStore) {
            String theString = new String(ch, start, length);

            this.sb.append(theString);
        }
    }

}

Listing 9에는 몇 가지 주목해야 할 항목이 포함되어 있다. 첫 번째로 주목해야 할 사항은 SAX 구문 분석기가 이벤트 기반 구문 분석기라는 점이다. 따라서 구문 분석될 때와 같은 실제 문서를 작성해야 한다. 이벤트는 데이터가 발견될 때마다 그리고 문서의 시작과 끝, 태그의 시작과 끝에서 개시된다. 따라서 중요한 데이터는 유지하고 나머지 데이터는 버리도록 데이터 구조를 정의해야 한다.

특정 데이터 소스를 InputSource에서 여러 번 읽어서 처리할 수 있으므로 StringBuilder를 사용하여 데이터를 추가한다. characters()메소드를 호출할 때마다 모든 데이터가 제공되는 것은 아니다.

이 애플리케이션은 데이터를 수집하여 형식화된 문자열로 간단히 변환한다. 그 대신 다른 예제에서는 이러한 항목을 콜렉션 클래스나 데이터베이스에 삽입할 수 있으며 특히, 구문 분석을 한 후에 대량의 데이터를 처리해야 하는 경우에 그렇게 한다.

getResults() 메소드는 이 클래스를 사용자 정의한 것이다. 이 메소드는 이러한 데이터를 어셈블된 형태로 수집하여 애플리케이션에 제공하기 위해 사용된다. 이 메소드는 DefaultHandler 인터페이스의 일부가 아니다.

그림 4는 구문 분석된 XML 데이터를 나타낸다. (그림 4의 텍스트 전용 버전 보기)


그림 4. 구문 분석된 XML 데이터
구문 분석된 XML 데이터가 표시된 휴대전화 화면을 캡처한 화면 

최종 구조를 구성, 관리하고 탐색한다는 면에서 보면 SAX 구문 분석기를 사용하여 XML을 구문 분석하는 것이 사소한 작업은 아니지만 이 구문 분석기의 중요한 장점은 구문 분석을 하는 과정과 그 이후 과정에서 필요한 램 용량을 대폭 줄일 수 있다는 점과 속도에 있다.

이제 Android 방식으로 JSON 데이터를 구문 분석하는 과정을 살펴보자.


JSON 구문 분석

애플리케이션에서 JSON 데이터를 구문 분석하는 과정은 사용자가 JSON 단추를 선택할 때 시작한다. JSON 단추를 클릭하면 Listing 10에 있는 examineJSONFile() 메소드가 호출된다. 모든 구문 분석과 문서 관리는 Android에서 제공하는 라이브러리 내에서 수행되고 JSON과 관련된 모든 코드가 이 메소드에 포함되어 있기 때문에 핸들러 클래스가 추가로 필요하지는 않다.


Listing 10. examineJSONfile() 메소드 호출

void examineJSONFile()
    {
        try
        {
            String x = "";
            InputStream is = this.getResources().openRawResource(R.raw.jsontwitter);
            byte [] buffer = new byte[is.available()];
            while (is.read(buffer) != -1);
            String jsontext = new String(buffer);
            JSONArray entries = new JSONArray(jsontext);

            x = "JSON parsed.\nThere are [" + entries.length() + "]\n\n";

            int i;
            for (i=0;i<entries.length();i++)
            {
                JSONObject post = entries.getJSONObject(i);
                x += "------------\n";
                x += "Date:" + post.getString("created_at") + "\n";
                x += "Post:" + post.getString("text") + "\n\n";
            }
            tvData.setText(x);
        }
        catch (Exception je)
        {
            tvData.setText("Error w/file: " + je.getMessage());
        }
    }

앞서 살펴본 XML 루틴과 마찬가지로 이 코드는 원시 자원에 있는 파일에서 데이터를 읽는다. 메모리로 데이터를 완전히 읽고java.lang.String으로 변환한 후, 구문 분석하여 JSONArray에 삽입할 수 있다. 이 예제에서와 같이 특정 문자열을 직접 구문 분석하여 배열에 삽입하거나 문자열을 구문 분석하여 JSONObject로 변환할 수 있다. Twitter 데이터는 오브젝트로 구성된 배열이기 때문에 전체 문자열을 구문 분석하여 배열에 삽입한 후, 원래의 위치에 따라 개별적으로 오브젝트를 액세스하는 것이 일반적이다.

이 메소드의 플로우는 간단하며 일단 데이터가 구문 분석되면 해당 코드는 XML 구문 분석기 핸들러에서 처리하는 방식과 비슷하게 문자열 표현을 구성한다. 여기서 중요한 점은 데이터가 관리되고 있다는 점이며 따라서 데이터가 포함될 메모리 구조를 개발자가 추가로 구성할 필요가 없다. 마찬가지로 애플리케이션은 JSONArray에 항목이 몇 개가 있는지 미리 인식한다. (이 예제에서는 20개의 항목이 있다.)

프로그램 면에서 JSON 구문 분석이 훨씬 더 간단하지만 대가가 없는 것은 아니다. JSON 구문 분석을 사용하면 데이터를 처리하기 전에 전체 데이터 스트림을 읽는 과정에서 메모리 소비량이 더 늘어나게 되며 모든 데이터를 저장하는 데도 메모리가 더 필요하게 된다. 이와는 대조적으로 SAX XML 방식에서는 관련된 데이터만을 사용한다. 이러한 문제를 제외하면 특정 JSON 오브젝트를 구문 분석하는 데 필요한 메모리가 충분한 경우, 대부분의 애플리케이션에서는 JSON 구문 분석 방식이 적합하며 특히, DOM과 함께 사용하고 싶지 않은 경우에 유용하다.

+ Recent posts