메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

Android 애플리케이션에서 인터넷 데이터 사용하기

XML, JSON 및 프로토콜 버퍼 데이터 구문 분석하기

Michael Galpin, 개발자, eBay
Michael Galpin 사진
Michael Galpin은 1998년부터 전문적으로 자바 소프트웨어를 개발하고 있으며 이베이에서 근무중이다. 캘리포니아 공대에서 수학을 전공하였다.

요약:  Android 애플리케이션 중 다수는 다양한 형식으로 제공되는 인터넷 데이터와 상호작용해야 합니다. 이 기사에서는 두 가지 유명한 데이터 형식인 XML 및 JSON(JavaScript Object Notation)과 Google의 색다른 프로토콜 버퍼 형식을 사용하여 작업하는 Android 애플리케이션을 빌드합니다. 이 기사에서는 각각의 형식과 연관된 성능과 코딩의 상충 관계에 대해 살펴봅니다.

원문 게재일:  2010 년 6 월 29 일 번역 게재일:   2010 년 9 월 14 일
난이도:  중급 영어로:  보기 PDF:  A4 and Letter (174KB | 20 pages)Get Adobe® Reader®
페이지뷰:  8960 회
의견:  


자주 사용하는 약어

  • Ajax: Asynchronous JavaScript + XML
  • API: Application Programming Interface
  • CSV: Comma-separated value
  • CSS: Cascading stylesheet
  • DOM: Document Object Model
  • HTML: HyperText Markup Language
  • HTTP: Hypertext Transfer Protocol
  • IDL: Interface Description Language
  • JSON: JavaScript Object Notation
  • SAX: Simple API for XML
  • SDK: Software Developer Kit
  • UI: User Interface
  • URL: Uniform Resource Locator
  • XML: Extensible Markup Language
  • 3G: 3세대 휴대전화 기술 표준

Android 애플리케이션은 인터넷에 있는 데이터에 액세스해야 하는 경우가 자주 있으며 인터넷 데이터는 여러가지 다른 형식으로 구조화될 수 있다. 이 기사에서는 Android 애플리케이션에서 세 가지 데이터 형식을 사용하여 작업하는 방법에 대해 살펴본다.

  • XML
  • JSON
  • Google의 프로토콜 버퍼

먼저 CSV 데이터를 XML, JSON 및 프로토콜 버퍼 형식으로 변환하는 웹 서비스를 개발한다. 그런 다음 이러한 형식으로 웹 서비스에서 데이터를 끌어낼 수 있는 샘플 Android 애플리케이션을 빌드한 후 사용자에게 표시하기 위해 구문 분석한다.

이 기사에 있는 실습을 수행하려면 최신 Android SDK(참고자료 참조)와 Android 2.2 플랫폼이 필요하다. SDK를 사용하려면 JDK(Java™ Development Kit)도 설치되어 있어야 한다. 이 기사에서는 JDK 1.6.0_17을 사용했다. 실제 Android 장치는 필요하지 않으며 모든 코드는 SDK의 Android 에뮬레이터에서 실행된다. 이 기사에서는 Android 개발 자체에 대해서는 설명하지 않으므로 Android 프로그래밍에 익숙하도록 권장한다. 하지만 Java 프로그래밍 언어에 대한 지식만으로도 이 내용을 이해할 수 있을 것이다.

Android 애플리케이션이 사용하는 웹 서비스를 실행하는 Java 웹 애플리케이션 서버도 필요하다. 또는 서버측 코드를 Google App Engine에 전개할 수 있다. 전체 소스 코드를 얻으려면 다운로드를 참조한다.

Day Trader 애플리케이션

Day Trader라는 단순한 Android 애플리케이션을 개발할 것이다. Day Trader를 사용하면 사용자가 하나 이상의 종목 기호를 입력하여 이러한 기호가 표시하는 종목의 최신 가격 정보를 검색할 수 있다. 사용자는 데이터에 사용할 형식(XML, JSON 또는 프로토콜 버퍼)을 지정할 수 있다. 실제 Android 애플리케이션은 일반적으로 이러한 선택사항을 제공하지 않지만 이를 구현하면 애플리케이션이 각 형식을 사용하여 작업하도록 만드는 방법을 알게 된다. 그림 1에서는 Day Trader 사용자 인터페이스를 보여 준다.


그림 1. 작동 중인 Day Trader 애플리케이션
AAPL, IBM, MSFT 및 GOOG의 주가 정보가 표시된 작동 중인 Day Trader 애플리케이션의 화면 캡처

텍스트 상자와 이 상자 옆에 있는 Add Stock 단추를 사용하면 관심 있는 각 종목의 기호를 입력할 수 있다. 사용자가 Download Stock Data 단추를 누르면 해당 종목 모두에 대한 데이터가 서버로부터 요청되어 애플리케이션에서 구문 분석된 후 화면에 표시된다. 기본적으로 데이터는 XML로 검색된다. 메뉴를 사용하면 데이터 형식을 XML, JSON 및 프로토콜 버퍼 사이에서 전환할 수 있다.

Listing 1에서는 그림 1에 있는 UI를 작성하는 데 사용된 레이아웃 XML을 보여 준다.


Listing 1. Day Trader 레이아웃 XML

<?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 android:orientation="horizontal" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content">
        <EditText android:id="@+id/symbol" android:layout_width="wrap_content"
            android:layout_height="wrap_content" android:width="120dip"/>
        <Button android:id="@+id/addBtn" android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:text="@string/addBtnLbl"/>
    </LinearLayout>
    <LinearLayout android:orientation="horizontal" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content">
        <TextView android:layout_width="wrap_content" 
            android:layout_height="wrap_content" android:id="@+id/symList" />
        <Button android:id="@+id/dlBtn" android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:text="@string/dlBtnLbl"
        />
       </LinearLayout>
    <ListView android:id="@android:id/list" 
        android:layout_height="fill_parent" android:layout_width="fill_parent"
        android:layout_weight="1"
        />
</LinearLayout>

Listing 1에 있는 코드는 대부분 간단하다. 그림 1에 표시된 입력 및 단추를 작성하는 데 필요한 몇 가지 위젯이 표시된다. Android 위젯의 진정한 다용도 도구인 ListView도 표시된다. 이 ListView는 서버에서 다운로드한 주식 데이터로 채워진다. Listing 2에서는 이 보기를 제어하는 Activity를 보여 준다.


Listing 2. Day Trader 기본 활동

public class Main extends ListActivity {

    private int mode = XML; // default
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        final EditText input = (EditText) findViewById(R.id.symbol);
        final TextView symbolsList = (TextView) findViewById(R.id.symList);
        final Button addButton = (Button) findViewById(R.id.addBtn);
        final Button dlButton = (Button) findViewById(R.id.dlBtn);
        addButton.setOnClickListener(new OnClickListener(){
            public void onClick(View v) {
                String newSymbol = input.getText().toString();
                if (symbolsList.getText() == null || 
                        symbolsList.getText().length() == 0){
                    symbolsList.setText(newSymbol);
                } else {
                    StringBuilder sb = 
                                  new StringBuilder(symbolsList.getText());
                    sb.append(",");
                    sb.append(newSymbol);
                    symbolsList.setText(sb.toString());
                }
                input.setText("");
            }
        });
        dlButton.setOnClickListener(new OnClickListener(){
            public void onClick(View v) {
                String symList = symbolsList.getText().toString();
                String[] symbols = symList.split(",");
                symbolsList.setText("");
                switch (mode){
                case JSON :
                    new StockJsonParser().execute(symbols);
                    break;
                case PROTOBUF :
                    new StockProtoBufParser().execute(symbols);
                    break;
                default :
                    new StockXmlParser().execute(symbols);
                    break;
                }
            }
        });
    }
}

Activity는 레이아웃을 Listing 1에 있는 XML 파일로 설정하고 몇 가지 이벤트 핸들러를 연결한다. 먼저 Add Stock 단추에 대해 이 코드는 텍스트 상자에서 기호를 읽은 후 이러한 기호를 쉼표로 분리한 상태로 symList TextView에 추가 추가한다. 다음으로 Download 단추에 대해 핸들러는 해당 symList TextView에서 데이터를 읽은 후 mode 변수를 기반으로 세 가지 다른 클래스 중 하나를 사용하여 서버에서 데이터를 다운로드한다. 이 메뉴는 mode 변수의 값을 설정하지만 코드가 매우 평범하기 때문에 Listing 2에서 생략했다. 다양한 데이터 다운로드/구문 분석 클래스를 살펴보기 전에 서버에서 이 데이터를 제공하는 방법에 대해 알아본다.


주식 데이터 제공하기

애플리케이션을 위한 서버는 두 가지 사항을 수행할 수 있어야 한다. 먼저 종목 기호 목록을 가져와서 각각에 대한 데이터를 검색해야 한다. 다음으로 형식 매개변수를 승인하고 해당 형식을 기반으로 데이터를 인코딩해야 한다. XML 및 JSON 형식의 경우 서버는 텍스트로 직렬화된 주식 데이터를 리턴한다. 프로토콜 버퍼의 경우에는 바이너리 데이터를 전송해야 한다. Listing 3에서는 이러한 단계를 처리하는 서블릿을 보여 준다.


Listing 3. Stock Broker 서블릿

public class StockBrokerServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, 
          HttpServletResponse response) throws IOException {
        String[] symbols = request.getParameterValues("stock");
        List<Stock> stocks = getStocks(symbols);
        String format = request.getParameter("format");
        String data = "";
        if (format == null || format.equalsIgnoreCase("xml")){
            data = Stock.toXml(stocks);
            response.setContentType("text/xml");    
        } else if (format.equalsIgnoreCase("json")){
            data = Stock.toJson(stocks);
            response.setContentType("application/json");
        } else if (format.equalsIgnoreCase("protobuf")){
            Portfolio p = Stock.toProtoBuf(stocks);
            response.setContentType("application/octet-stream");
            response.setContentLength(p.getSerializedSize());
            p.writeTo(response.getOutputStream());
            response.flushBuffer();
            return;
        }
        response.setContentLength(data.length());
        response.getWriter().print(data);
        response.flushBuffer();
        response.getWriter().close();
    }

    public List<Stock> getStocks(String... symbols) throws IOException{
        StringBuilder sb = new StringBuilder();
        for (String symbol : symbols){
            sb.append(symbol);
            sb.append('+');
        }
        sb.deleteCharAt(sb.length() - 1);
        String urlStr = 
                "http://finance.yahoo.com/d/quotes.csv?f=sb2n&s=" + 
                    sb.toString();
        URL url = new URL(urlStr);
        HttpURLConnection conn = 
                (HttpURLConnection) url.openConnection();
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream()));
        String quote = reader.readLine();
        List<Stock> stocks = new ArrayList<Stock>(symbols.length);
        while (quote != null){
            String[] values = quote.split(",");
            Stock s = 
                      new Stock(values[0], values[2],
                           Double.parseDouble(values[1]));
            stocks.add(s);
            quote = reader.readLine();
        }
        return stocks;
    }
}

이 서블릿은 HTTP GET 요청만 지원하는 단순 Java 서블릿이다. 이 서블릿은 주식 및 형식-요청 매개변수의 값을 판독한다. 그런 다음 getStocks() 메소드를 호출한다. 이 메소드는 Yahoo! Finance에 대한 호출을 작성하여 주식 데이터를 가져온다. Yahoo!는 데이터에 대해 CSV 형식만 지원하기 때문에 getStocks() 메소드는 해당 데이터를 Stock 오브젝트의 목록으로 구문 분석한다. Listing 4에서는 단순 데이터 구조를 보여 준다.


Listing 4. Stock 데이터 구조

public class Stock {    
    private final String symbol;
    private final String name;
    private final double price;
      //getters and setters omitted

    public String toXml(){
        return "<stock><symbol>" + symbol + 
"</symbol><name><![CDATA[" +
            name + "]]></name><price>" + price + 
"</price></stock>";
    }

    public String toJson(){
        return "{ 'stock' : { 'symbol' : " +symbol +", 'name':" + name + 
            ", 'price': '" + price + "'}}";
    }

    public static String toXml(List<Stock> stocks){
        StringBuilder xml = new StringBuilder("<stocks>");
        for (Stock s : stocks){
            xml.append(s.toXml());
        }
        xml.append("</stocks>");
        return xml.toString();
    }

    public static String toJson(List<Stock> stocks){
        StringBuilder json = new StringBuilder("{'stocks' : [");
        for (Stock s : stocks){
            json.append(s.toJson());
            json.append(',');
        }
        json.deleteCharAt(json.length() - 1);
        json.append("]}");
        return json.toString();
    }
}

Stock에는 세 개의 특성( symbol, nameprice )과 자체를 XML 문자열 또는 JSON 문자열로 변환하는 편의 메소드가 포함되어 있다. 또한 Stock 오브젝트의 목록을 XML 또는 JSON으로 변환하는 유틸리티 메소드도 포함되어 있다. Listing 3으로 돌아가서 Stock 오브젝트의 목록은 형식 요청 매개변수에 따라 XML 또는 JSON 문자열로 변환된 후 다시 클라이언트에 전송된다.

XML 유스 케이스와 JSON 유스 케이스는 매우 비슷하고 간단하다. 프로토콜 버퍼 케이스의 경우에는 프로토콜 버퍼 형식으로 코드 읽기 및 쓰기 오브젝트를 생성해야 한다. 이를 수행하려면 프로토콜 버퍼 스펙 형식을 사용하여 데이터 구조를 정의해야 한다. Listing 5에 예제가 있다.


Listing 5. 주식에 대한 프로토콜 버퍼 메시지

package stocks;

option java_package = "org.developerworks.stocks";

message Quote{
    required string symbol = 1;
    required string name = 2;
    required double price = 3;
}

message Portfolio{
    repeated Quote quote = 1;
}

IDL(Interface Description Language)과 비슷한 프로토콜 버퍼 메시지 형식은 다양한 언어와 함께 사용할 수 있도록 언어와 독립적이도록 되어 있다. 여기서는 프로토콜 버퍼 컴파일러(protoc)를 실행하여 Listing 5에 있는 코드를 클라이언트 및 서버에 사용할 Java 클래스로 컴파일한다. Java 클래스로의 프로토콜 버퍼 메시지 컴파일에 대한 세부 사항은 프로토콜 버퍼 개발자 안내서(참고자료 참조)를 참조한다.

Listing 3에서는 toProtoBuf() 메소드가 Stock 오브젝트의 목록을 Portfolio 메시지로 변환했다. Listing 6에서는 해당 메소드의 구현을 보여 준다.


Listing 6. 포트폴리오 메시지 작성하기

public static Stocks.Portfolio toProtoBuf(List<Stock> stocks){
    List<Stocks.Quote> quotes = new ArrayList<Stocks.Quote>(stocks.size());
    for (Stock s : stocks){
        Quote q = 
            Quote.newBuilder()
                .setName(s.name)
                .setSymbol(s.symbol)
                .setPrice(s.price)
                .build();
        quotes.add(q);
    }
    return Portfolio.newBuilder().addAllQuote(quotes).build();
}

Listing 6에 있는 코드에서는 Listing 5에 있는 메시지에서 생성된 코드(QuotePortfolio 클래스)를 사용한다. 사용자는 각각의 Stock 오브젝트로부터 Quote를 빌드한 후 Listing 3에 있는 서블릿에 리턴되는 Portfolio 오브젝트에 추가하기만 하면 된다. Listing 3에서 서블릿은 클라이언트에 대해 직접적인 스트림을 열고 생성된 코드를 사용하여 바이너리 프로토콜 버퍼 데이터를 스트림에 작성한다.

지금까지 서버에서 Android 애플리케이션에 전송될 데이터를 작성하는 방법에 대해 살펴봤다. 이제는 이 애플리케이션이 이 데이터를 구문 분석하는 방법에 대해 알아본다.


데이터 형식에 대해 작업하기

Listing 2에 있는 기본 Activity는 서버가 전송할 수 있는 다양한 형식으로 데이터에 대해 작업해야 한다. 또한 적절한 형식으로 데이터를 요청해야 하며 데이터가 구문 분석되면 데이터로 ListView를 채워야 한다. 따라서 데이터 형식과 관계없이 기능의 많은 부분이 공통된다.

먼저 Listing 7과 같이 이 공통 기능을 캡슐화하는 추상 기본 클래스를 작성한다.


Listing 7. 데이터 구문 분석기의 기본 클래스

abstract class BaseStockParser extends AsyncTask<String, Integer, Stock[]>{
    String urlStr = "http://protostocks.appspot.com/stockbroker?format=";

    protected BaseStockParser(String format){
        urlStr += format;
    }

    private String makeUrlString(String... symbols) {
        StringBuilder sb = new StringBuilder(urlStr);
        for (int i=0;i<symbols.length;i++){
            sb.append("&stock=");
            sb.append(symbols[i]);
        }
        return sb.toString();
    }

    protected InputStream getData(String[] symbols) throws Exception{
        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(new URI(makeUrlString(symbols)));

        HttpResponse response = client.execute(request);
        return response.getEntity().getContent();
    }

    @Override
    protected void onPostExecute(Stock[] stocks){
        ArrayAdapter<Stock> adapter = 
            new ArrayAdapter<Stock>(Main.this, R.layout.stock, 
                      stocks );
        setListAdapter(adapter);
    }
}

Listing 7에 있는 기본 클래스는 android.os.AsyncTask를 확장한다. 이 기본 클래스는 비동기 조작에 공통적으로 사용되는 클래스이다. 이 클래스는 기본 UI 스레드에서 요청을 작성하는 데 필요한 스레드 및 핸들러의 작성을 추상화한다. 이 클래스는 입력 및 출력 데이터 유형을 기반으로 매개변수화된다. 모든 구문 분석기에 대해 입력은 동일하다(종목 기호에 대한 문자열). 또한 출력도 항상 동일하다(Stock 오브젝트의 배열). 기본 클래스는 사용할 데이터 형식을 지정하는 문자열인 format을 사용한다. 그러면 이 클래스에는 적절한 HTTP 요청을 작성하고 스트리밍 응답을 리턴하는 데 필요한 메소드가 있게 된다. 마지막으로 이 클래스는 AsyncTaskonPostExecute() 메소드를 대체하고 구문 분석기에서 리턴되는 데이터를 사용하여 ActivityListView에 대한 Adapter를 작성한다.

세 구문 분석기 모두에 공통된 기능에 대해 살펴봤으니 이제는 좀 더 구체적인 구문 분석 코드에 대해 설명한다(XML 구문 분석기부터 시작함).


SAX를 사용하여 XML 구문 분석하기

Android SDK는 표준 DOM 및 SAX를 포함하여 XML에 대해 작업하는 여러가지 방법을 제공한다. 메모리 사용량이 더 많은 상황의 경우에는 SDK의 풀(pull) 구문 분석기를 사용할 수 있다. 대부분의 시간 동안 SAX는 가장 빠른 방법이었기 때문에 Android에는 SAX를 더 편리하게 사용할 수 있게 하는 편의 API가 일부 포함되어 있다. Listing 8에서는 Day Trader 애플리케이션의 XML 구문 분석기를 보여 준다.


Listing 8. XML 구문 분석기 구현

private class StockXmlParser extends BaseStockParser{
    public StockXmlParser(){
        super("xml");
    }

    @Override
    protected Stock[] doInBackground(String... symbols) {
        ArrayList<Stock> stocks = new ArrayList<Stock>(symbols.length);
        try{
            ContentHandler handler = newHandler(stocks);
            Xml.parse(getData(symbols), Xml.Encoding.UTF_8, handler);
        } catch (Exception e){
            Log.e("DayTrader", "Exception getting XML data", e);
        }
        Stock[] array = new Stock[symbols.length];
        return stocks.toArray(array);
    }

    private ContentHandler newHandler(final ArrayList<Stock> stocks){
        RootElement root = new RootElement("stocks");
        Element stock = root.getChild("stock");
        final Stock currentStock = new Stock();
        stock.setEndElementListener(
            new EndElementListener(){
                public void end() {
                    stocks.add((Stock) currentStock.clone());
                }
            }
        );
        stock.getChild("name").setEndTextElementListener(
            new EndTextElementListener(){
                public void end(String body) {
                    currentStock.setName(body);
                }
            }
        );
        stock.getChild("symbol").setEndTextElementListener(
            new EndTextElementListener(){
                public void end(String body) {
                    currentStock.setSymbol(body);
                }
            }
        );
        stock.getChild("price").setEndTextElementListener(
            new EndTextElementListener(){
                public void end(String body) {
                            currentStock.setPrice(Double.parseDouble(body));
                }
            }
        );
        return root.getContentHandler();
    }
}

Listing 8에 있는 코드는 대부분 ContentHandler를 작성하는 newHandler() 메소드에 있다. SAX 구문 분석에 익숙한 경우에는 ContentHandler가 SAX 구문 분석기에 의해 발생한 다양한 이벤트에 대한 반응으로 구문 분석된 데이터를 작성한다는 것을 알고 있다. newHandler() 메소드는 Android 편의 API를 사용하여 이벤트 핸들러를 사용하는 ContentHandler를 지정한다. 이 코드는 단순히 구문 분석기가 다양한 태그를 만나면 발생되는 이벤트를 청취한 후 데이터를 선택하여 Stock 오브젝트의 목록에 추가한다. ContentHandler가 작성되면 Xml.parse() 메소드가 호출되어 기본 클래스에서 제공한 InputStream을 구문 분석하고 Stock 오브젝트의 배열을 리턴한다. 이렇게 하면 신속하게 XML을 구문 분석할 수 있지만 Android에서 제공하는 편의 API를 사용하더라도 여전히 너무 장황하다.


JSON 사용하기

XML은 Android의 중요한 구성원이며 XML에 의존하는 웹 서비스가 얼마나 많은지를 생각하면 이것은 바람직한 것이다. 또한 많은 서비스에서는 자주 사용되는 또다른 형식인 JSON도 지원한다. JSON은 XML보다 좀더 간결하지만 사람이 읽을 수 있기 때문에 작업을 편리하게 하고 JSON을 사용하는 애플리케이션의 디버깅을 쉽게 만든다. Android에는 JSON 구문 분석기가 포함되어 있다. (모바일에서 필요하지 않은 몇 가지 클래스가 제거된 것을 제외하고는 JSON.org 웹 사이트에서 가져올 수 있는 구문 분석기와 동일하다.) Listing 9에서는 작동 중인 JSON을 보여 준다.


Listing 9. JSON 구문 분석기 구현

private class StockJsonParser extends BaseStockParser{
    public StockJsonParser(){
        super("json");
    }
    @Override
    protected Stock[] doInBackground(String... symbols) {
        Stock[] stocks = new Stock[symbols.length];
        try{
            StringBuilder json = new StringBuilder();
            BufferedReader reader = 
                new BufferedReader(
                            new InputStreamReader(getData(symbols)));
            String line = reader.readLine();
            while (line != null){
                json.append(line);
                line = reader.readLine();
            }
            JSONObject jsonObj = new JSONObject(json.toString());
            JSONArray stockArray = jsonObj.getJSONArray("stocks");
            for (int i=0;i<stocks.length;i++){
                JSONObject object = 
                    stockArray.getJSONObject(i).getJSONObject("stock");
                stocks[i] = new Stock(object.getString("symbol"), 
                        object.getString("name"), 
                        object.getDouble("price"));
            }
        } catch (Exception e){
            Log.e("DayTrader", "Exception getting JSON data", e);
        }
        return stocks;
    }
}

Android에서 JSON 구문 분석기를 얼마나 쉽게 사용할 수 있는지 알 수 있다. 스트림을 서버에서 JSON 구문 분석기에 전달하는 문자열로 변환한다. 오브젝트 그래프를 살펴보고 Stock 오브젝트의 배열을 작성한다. XML DOM 구문 분석에 대해 작업한 경험이 있는 경우에는 프로그래밍 모델이 거의 동일하기 때문에 이 작업이 익숙하다.

DOM과 마찬가지로 JSON 구문 분석기를 사용하는 데도 메모리가 많이 소비될 수 있다. Listing 9에서 서버의 모든 데이터는 문자열로 표시된 후 JSONObject로 표시되고 마지막으로 Stock 오브젝트의 배열로 표시된다. 달리 말하면 정확하게 동일한 데이터가 세 가지 다른 방식으로 표시된다. 데이터 양이 많은 경우에 이것이 어떤 문제가 되는지 알 수 있다. 물론 메소드의 끝에 다다르면 이러한 세 가지 데이터 표시 중 두 가지는 범위에서 제거되어 가비지 콜렉터에 의해 회수될 수 있다. 하지만 가비지 콜렉션을 더 빈번하게 트리거하면 비정상적인 속도 저하가 발생하여 사용자 경험에 부정적인 영향을 미칠 수 있다. 메모리 효율 및 성능이 중요한 경우에는 프로토콜 버퍼를 사용하는 구문 분석기가 더 낫다.


프로토콜 버퍼를 사용하여 바이너리화하기

프로토콜 버퍼는 네트워크를 통해 데이터를 전송하기 위해 XML보다 빠르도록 설계된 Google에서 개발한 언어가 특정되지 않은 데이터 직렬화 형식이다. 프로토콜 버퍼는 서버 간 호출에 대한 Google의 사실상의 표준이다. Google은 이 형식과 C++, Java 및 Python 프로그래밍 언어를 위한 해당 바인딩 도구를 오픈 소스로 사용할 수 있도록 했다.

Listing 3Listing 6에서 프로토콜 버퍼가 바이너리 형식이라는 것을 알았다. 예상한 대로 이때문에 데이터가 매우 간결해진다. 클라이언트와 서버 모두에서 gzip 압축을 사용할 수 있는 경우에는 XML 및 JSON을 사용하여 비슷한 크기의 메시지를 얻을 수도 있지만 여전히 프로토콜 버퍼는 크기 면에서 장점이 있다. 또한 프로토콜 버퍼는 매우 신속하게 구문 분석할 수 있는 형식이다. 마지막으로 프로토콜 버퍼는 매우 단순한 API를 제공한다. Listing 10에서는 예제 구문 분석기 구현을 보여 준다.


Listing 10. 프로토콜 버퍼 구문 분석기 구현

private class StockProtoBufParser extends BaseStockParser{
    public StockProtoBufParser(){
        super("protobuf");
    }

    @Override
    protected Stock[] doInBackground(String... symbols) {
        Stock[] stocks = new Stock[symbols.length];
        try{
            Stocks.Portfolio portfolio = 
                Stocks.Portfolio.parseFrom(getData(symbols));
            for (int i=0;i<symbols.length;i++){
                stocks[i] = Stock.fromQuote(portfolio.getQuote(i));
            }
        } catch (Exception e){
            Log.e("DayTrader", "Exception getting ProtocolBuffer data", e);
        }
        return stocks;
    }
}

Listing 3과 마찬가지로 헬퍼 클래스(이 예제에서는 프로토콜 버퍼 컴파일러에 의해 생성됨)를 사용한다. 이 헬퍼 클래스는 서버에서 사용하는 것과 동일한 헬퍼 클래스이다. 한 번 컴파일하고 나면 서버와 클라이언트 모두에서 공유할 수 있다. 따라서 더 편리하게 서버의 스트림에서 직접 읽은 후 Stock 오브젝트로 변환할 수 있다. 이는 뛰어난 성능도 제공하게 되는 단순한 프로그래밍이다. 이제 이러한 성능이 XML 및 JSON과 얼마나 비교되는지 살펴본다.


성능 비교

성능 비교에는 일반적으로 일종의 마이크로 벤치마크가 수반되며 이러한 벤치마크는 매우 편향되거나 실수로 부정확해지기 쉽다. 마이크로 벤치마크가 공정성을 유지하도록 설계되어 있는 경우에도 다수의 임의 요소에 의해 결과의 공정성이 의심될 수 있다. 이러한 경고에도 불구하고 필자는 이 마이크로 벤치마크를 사용하여 XML(약 1300밀리초), JSON(약 1150밀리초) 및 프로토콜 버퍼(약 750밀리초)의 성능을 비교했다. 벤치마크에서는 200개 종목에 대한 요청을 서버에 전송한 후 요청이 전송된 때부터 데이터가 ListView를 위해 Adapter를 작성하는 데 사용할 준비가 될 때까지의 시간을 측정했다. 이러한 작업은 3G 네트워크를 사용하는 두 가지 장치(Motorola Droid와 HTC Evo)에서 각 데이터 형식에 대해 50회씩 수행되었다. 그림 2에 결과가 표시되어 있다.


그림 2. 데이터 형식 속도 비교하기
데이터 형식(XML, JSON 및 프로토콜 버퍼) 속도를 비교하는 막대 그래프

그림 2에 있는 그래프에서는 이 마이크로 벤치마크의 경우 프로토콜 버퍼(약 750밀리초)가 XML(약 1300밀리초)보다 두 배 가까이 빠르다는 것을 보여 준다. 네트워크를 통해 전송되어 휴대용 장치에서 처리되는 데이터의 성능에 영향을 미치는 요인은 많다. 눈에 띄는 요인은 네트워크를 통해 전송되는 데이터의 양이다. 바이너리 형식인 프로토콜 버퍼는 실제로 XML 및 JSON과 같은 텍스트 형식보다 훨씬 적은 데이터를 네트워크를 통해 전송한다. 하지만 텍스트 형식은 웹 서버와 Android 장치에서 모두 지원하는 표준 기술인 gzip을 사용하여 효율적으로 압축할 수 있다. 그림 3에서는 gzip을 사용하거나 사용하지 않은 상태에서 유선으로 전송되는 데이터의 크기를 보여 준다.


그림 3. 형식별 데이터 크기
형식별 데이터 크기를 비교하는 막대 그래프(원래 크기 또는 압축된 크기의 XML 및 JSON과 프로토콜 버퍼 비교)

그림 3을 보면 HTML, JavaScript 및 CSS와 같은 웹 형식은 말할 것도 없이 XML 및 JSON과 같은 텍스트 컨텐츠에 압축이 미치는 영향이 얼마나 큰지 더 잘 알 수 있다. 프로토콜 버퍼의 데이터(약 6KB)는 원래 크기의 XML(약 17.5KB) 또는 JSON(약 13.5KB)보다 훨씬 작다. 하지만 압축을 사용하면 JSON 및 XML(둘 다 약 3KB)은 프로토콜 버퍼보다 더 작다. 이 예제에서는 압축된 XML 및 JSON으로 된 메시지의 크기가 프로토콜 버퍼로 인코딩된 메시지 크기의 절반 정도 밖에 되지 않는다.

그림 2로 돌아가서 살펴보면 속도 차이는 네트워크를 통한 메시지 크기로는 확실하게 설명할 수 없다. 프로토콜 버퍼 메시지는 XML 또는 JSON 인코딩된 메시지보다 크지만 프로토콜 버퍼를 사용하면 여전히 사용자가 데이터를 확인하기 위해 기다리는 시간의 절반을 절감할 수 있다. 이것이 Android 애플리케이션에 프로토콜 버퍼를 사용해야 한다는 것을 의미하는 것인가? 물론 이에 대한 결정은 미리 정해져 있지 않다. 전송되는 데이터의 양이 적으면 세 가지 형식 사이의 차이는 미미하다. 대용량 데이터의 경우에는 프로토콜 버퍼가 차이를 보일 것이다. 하지만 이 경우와 같은 인위적인 벤치마크가 자체 애플리케이션을 테스트하는 것을 대체할 수는 없다.


결론

이 기사에서는 인터넷에서 자주 사용되는 두 가지 데이터 형식인 XML과 JSON을 사용한 작업에 대해 자세히 살펴봤다. 또한 세 번째 형식인 프로토콜 버퍼에 대해서도 살펴봤다. 소프트웨어 엔지니어링의 다른 모든 사항과 마찬가지로 기술을 선택하는 것은 상충 관계에 관한 문제이다. Android와 같은 제한된 환경을 위해 개발하는 경우에는 이러한 의사결정의 결과가 미치는 영향이 크다. 이러한 결과에 대해 지금 알고 있는 추가적인 지식이 우수한 Android 애플리케이션을 작성하는 데 도움이 되길 바란다.



다운로드 하십시오

설명이름크기다운로드 방식
Article source codedaytrader.stockbroker.zip4.81MBHTTP

다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

토론

필자소개

Michael Galpin 사진

Michael Galpin은 1998년부터 전문적으로 자바 소프트웨어를 개발하고 있으며 이베이에서 근무중이다. 캘리포니아 공대에서 수학을 전공하였다.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=XML, 오픈 소스
ArticleID=518050
ArticleTitle=Android 애플리케이션에서 인터넷 데이터 사용하기
publish-date=06292010
author1-email=mike.sr@gmail.com
author1-email-cc=dwxed@us.ibm.com

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.