[Android] 공공기관 API 신청 및 파싱하기(검색기능구현)

공공 데이터 홈페이지라는 것이 있습니다. 국가 혹은 공공기관에서 만들어놓은 정보들을 국민들에게 개방해 둔 것인데 여기서 무료로 제공하는 공공 API를활용하여 쉽고 간단하게 유용한 앱을 만들 수 있습니다. 이번 글에서 공공기관 API를 파싱하고 검색하는 것까지 구현하는 방법에 대해 포스팅하려 합니다 공공기관 API는 아무거나 파싱 해와도 상관없는데 저는 전기차 충전소 정보를 파싱 해왔습니다.

 

공공 API 신청 및 파싱 & 검색 구현하기

공공기관api
공공기관api2

1. 우선 위에 사진에 나와있는 것처럼 차례대로 공공기관 API를 신청해서 승인을 받습니다. 공공데이터를 신청하면 거기에 딸려있는 참고 문서가 있는데 참고 문서에 필수 요청 변수와 파싱 할 때 필요한 여러 가지 정보들이 있으니 꼭 챙겨보세요.

https://www.data.go.kr/

 

공공기관api3

2. 참고 문서에 있는 URL을 웹상에서 쳤을 때 왼쪽과 같은 그림이 나오면 성공입니다. 저는 처음에 URL을 잘못 쳐서 계속 오른쪽 화면만 나왔었습니다. 처음에는 원래 그런 건가 싶었는데 알고 보니 아니더라고요. 꼭 왼쪽처럼 데이터가 나오는 것을 확인하고 다음 Step으로 넘어갑시다.

 

<uses-permission android:name = "android.permission.INTERNET"/>

3. 파싱을 하기 위해서는 먼저 androidManifest에서 인터넷 사용을 허용시켜줘야 합니다. Mainifest에 위에 있는 코드를 복사해 넣어주세요. (웹에서 데이터를 요청받아 조회해야 하기 때문에 인터넷 권한 설정을 반드시 추가해줘 합니다.)

 

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"
    >

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <TextView
        android:id="@+id/result"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
    </ScrollView>
</LinearLayout>

4. XML코드에는 파싱 한 데이터를 뿌릴 TextView정도만 간략하게 만들어주도록 하겠습니다.

 

JAVA코드

package com.example.user.url_passing;
import android.app.Activity;
import android.os.Bundle;
import android.os.StrictMode;
import android.widget.TextView;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import java.net.URL;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   StrictMode.enableDefaults();

    TextView status1 = (TextView)findViewById(R.id.result); //파싱된 결과확인!

    boolean initem = false, inAddr = false, inChargeTp = false, inCpId = false, inCpNm = false;
    boolean inCpStat = false, inCpTp = false, inCsId = false, inCsNm = false, inLat=false;
    boolean inLongi = false, inStatUpdateDatetime = false;

    String addr = null, chargeTp = null, cpId = null, cpNm = null, cpStat = null, cpTp=null, csId=null, csNm=null;
    String lat = null, longi = null, statUpdateDatetime = null;


    try{
        URL url = new URL("http://openapi.kepco.co.kr/service/evInfoService/getEvSearchList?"
                + "&pageNo=1&numOfRows=10&ServiceKey="
                + "iOsw4MlgRU0JZpvuR5AkLUfkX%2FAOl0Q03HF78VRzR2g0dz6iD0esiw6HmLHKly6PVvGVP2PPgRpqtpULJBWSHg%3D%3D"
        ); //검색 URL부분

        XmlPullParserFactory parserCreator = XmlPullParserFactory.newInstance();
        XmlPullParser parser = parserCreator.newPullParser();

        parser.setInput(url.openStream(), null);

        int parserEvent = parser.getEventType();
        System.out.println("파싱시작합니다.");

        while (parserEvent != XmlPullParser.END_DOCUMENT){
            switch(parserEvent){
                case XmlPullParser.START_TAG://parser가 시작 태그를 만나면 실행
                    if(parser.getName().equals("addr")){ //title 만나면 내용을 받을수 있게 하자
                        inAddr = true;
                    }
                    if(parser.getName().equals("chargeTp")){ //address 만나면 내용을 받을수 있게 하자
                        inChargeTp = true;
                    }
                    if(parser.getName().equals("cpId")){ //mapx 만나면 내용을 받을수 있게 하자
                        inCpId = true;
                    }
                    if(parser.getName().equals("cpNm")){ //mapy 만나면 내용을 받을수 있게 하자
                        inCpNm = true;
                    }
                    if(parser.getName().equals("cpStat")){ //mapy 만나면 내용을 받을수 있게 하자
                        inCpStat = true;
                    }
                    if(parser.getName().equals("cpTp")){ //mapy 만나면 내용을 받을수 있게 하자
                        inCpTp = true;
                    }
                    if(parser.getName().equals("csId")){ //mapy 만나면 내용을 받을수 있게 하자
                        inCsId = true;
                    }
                    if(parser.getName().equals("csNm")){ //mapy 만나면 내용을 받을수 있게 하자
                        inCsNm = true;
                    }
                    if(parser.getName().equals("lat")){ //mapy 만나면 내용을 받을수 있게 하자
                        inLat = true;
                    }
                    if(parser.getName().equals("longi")){ //mapy 만나면 내용을 받을수 있게 하자
                        inLongi = true;
                    }
                    if(parser.getName().equals("statUpdateDatetime")){ //mapy 만나면 내용을 받을수 있게 하자
                        inStatUpdateDatetime = true;
                    }
                    if(parser.getName().equals("message")){ //message 태그를 만나면 에러 출력
                        status1.setText(status1.getText()+"에러");
                        //여기에 에러코드에 따라 다른 메세지를 출력하도록 할 수 있다.
                    }
                    break;

                case XmlPullParser.TEXT://parser가 내용에 접근했을때
                    if(inAddr){ //isTitle이 true일 때 태그의 내용을 저장.
                        addr = parser.getText();
                        inAddr = false;
                    }
                    if(inChargeTp){ //isAddress이 true일 때 태그의 내용을 저장.
                        chargeTp = parser.getText();
                        inChargeTp = false;
                    }
                     if(inCpId){ //isMapx이 true일 때 태그의 내용을 저장.
                        cpId = parser.getText();
                        inCpId = false;
                    }
                    if(inCpNm){ //isMapy이 true일 때 태그의 내용을 저장.
                        cpNm = parser.getText();
                        inCpNm = false;
                    }
                    if(inCpStat){ //isMapy이 true일 때 태그의 내용을 저장.
                        cpStat = parser.getText();
                        inCpStat = false;
                    }
                    if(inCpTp){ //isMapy이 true일 때 태그의 내용을 저장.
                        cpTp = parser.getText();
                        inCpTp = false;
                   }
                    if(inCsId){ //isMapy이 true일 때 태그의 내용을 저장.
                        csId = parser.getText();
                        inCsId = false;
                    }
                    if(inCsNm){ //isMapy이 true일 때 태그의 내용을 저장.
                        csNm = parser.getText();
                        inCsNm = false;
                   }
                    if(inLat){ //isMapy이 true일 때 태그의 내용을 저장.
                       lat = parser.getText();
                       inLat = false;
                    }
                    if(inLongi){ //isMapy이 true일 때 태그의 내용을 저장.
                        longi = parser.getText();
                        inLongi = false;
                    }
                    if(inStatUpdateDatetime){ //isMapy이 true일 때 태그의 내용을 저장.
                        statUpdateDatetime = parser.getText();
                        inStatUpdateDatetime = false;
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if(parser.getName().equals("item")){
                        status1.setText(status1.getText()+"주소 : "+ addr +"\n 충전기 타입: "+ chargeTp +"\n 충전소ID : " + cpId
                               +"\n 충전기 명칭 : " + cpNm +  "\n 충전기 상태 코드 : " + cpStat+ "\n 충전 방식 : " + cpTp
                               +"\n 충전소 ID : " +csId + "\n 충전소 명칭 : " + csNm + "\n 위도 : " +lat
                               +"\n 경도 : " +longi +"\n 충전기상태갱신시각 : " +statUpdateDatetime+"\n");
                        initem = false;
                    }
                    break;
            }
            parserEvent = parser.next();
        }
    } catch(Exception e){
        status1.setText("에러가..났습니다...");
    }
  }
}

5. 데이터를 파싱 해옵니다.. 파싱 해오는 방식은 여러 가지 방식이 있는데 위 예제는 XmlPullParser로 데이터를 파싱 하는 방식입니다.

 

공공기관api4

6. 이까지 진행하면 파싱이 완료될 것입니다. 위의 화면까지 나오면 일차적으로 파싱 하는 부분까지는 성공적으로 잘 진행된 거라고 생각하면 됩니다. 

이제 이렇게 파싱한코드를 검색 기능을 구현해보겠습니다. 공공기관 API에는 요청 변수라는것이 있는데 이 요청변수 URL에 자신이 검색하고 싶은 값을 입력하면 간편하게 검색을 할 수 있습니다.

 

XML코드

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="10dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:orientation="horizontal"
        android:weightSum="10">
                
        <EditText
        android:id="@+id/edit"
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:hint="enter text to search"/>
            
        <Button
        android:id="@+id/button"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:text="search"
        android:textSize="12sp"
        android:onClick="mOnClick"/>
                
    </LinearLayout>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
            
        <TextView
        android:id="@+id/result"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="8sp"
        android:textStyle="bold"/>
                
    </ScrollView>
</LinearLayout>

7. 우선 XML에 검색할 키워드를 입력할 EDIT TEXT를 만들어주겠습니다.

 

JAVA코드

package com.example.user.url_passing;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLEncoder;

public class MainActivity extends Activity {

EditText edit;
TextView text;

XmlPullParser xpp;
String key="iOsw4MlgRU0JZpvuR5AkLUfkX%2FAOl0Q03HF78VRzR2g0dz6iD0esiw6HmLHKly6PVvGVP2PPgRpqtpULJBWSHg%3D%3D"; 
//Naver 개발자센터 검색 키

String data;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    edit= (EditText)findViewById(R.id.edit);
    text= (TextView)findViewById(R.id.result);
}

 //Button을 클릭했을 때 자동으로 호출되는 callback method....
public void mOnClick(View v){
   switch( v.getId() ){
       case R.id.button:

        //Android 4.0 이상 부터는 네트워크를 이용할 때 반드시 Thread 사용해야 함
        new Thread(new Runnable() {

           @Override
            public void run() {
                // TODO Auto-generated method stub
                data= getXmlData();//아래 메소드를 호출하여 XML data를 파싱해서 String 객체로 얻어오기

                //UI Thread(Main Thread)를 제외한 어떤 Thread도 화면을 변경할 수 없기때문에
                //runOnUiThread()를 이용하여 UI Thread가 TextView 글씨 변경하도록 함
                runOnUiThread(new Runnable() {
                   @Override
                    public void run() {
                         // TODO Auto-generated method stub
                         text.setText(data); //TextView에 문자열  data 출력
                    }
                });
            }
         }).start();
       break;
    }
}//mOnClick method..


//XmlPullParser를 이용하여 Naver 에서 제공하는 OpenAPI XML 파일 파싱하기(parsing)
String getXmlData(){

    StringBuffer buffer=new StringBuffer();

    String str= edit.getText().toString();//EditText에 작성된 Text얻어오기
    String location = URLEncoder.encode(str);//한글의 경우 인식이 안되기에 utf-8 방식으로 encoding..
    String query = "%EC%A0%84%EB%A0%A5%EB%A1%9C";

    String queryUrl="http://openapi.kepco.co.kr/service/evInfoService/getEvSearchList?"//요청 URL
            +"addr="+location
            +"&pageNo=1&numOfRows=1000&ServiceKey=" + key;

    try {
        URL url= new URL(queryUrl);//문자열로 된 요청 url을 URL 객체로 생성.
        InputStream is= url.openStream(); //url위치로 입력스트림 연결

        XmlPullParserFactory factory= XmlPullParserFactory.newInstance();
        XmlPullParser xpp= factory.newPullParser();
        xpp.setInput( new InputStreamReader(is, "UTF-8") ); //inputstream 으로부터 xml 입력받기

        String tag;

        xpp.next();
        int eventType= xpp.getEventType();

        while( eventType != XmlPullParser.END_DOCUMENT ){
            switch( eventType ){
                case XmlPullParser.START_DOCUMENT:
                    buffer.append("파싱 시작...\n\n");
                    break;

                case XmlPullParser.START_TAG:
                    tag= xpp.getName();//테그 이름 얻어오기

                    if(tag.equals("item")) ;// 첫번째 검색결과
                    else if(tag.equals("addr")){
                        buffer.append("주소 : ");
                        xpp.next();
                        buffer.append(xpp.getText());//title 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                            buffer.append("\n"); //줄바꿈 문자 추가
                    }
                    else if(tag.equals("chargeTp")){
                        buffer.append("충전소타입 : ");
                        xpp.next();
                        buffer.append(xpp.getText());//category 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n");//줄바꿈 문자 추가
                    }
                    else if(tag.equals("cpId")){
                        buffer.append("충전소ID :");
                        xpp.next();
                        buffer.append(xpp.getText());//description 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n");//줄바꿈 문자 추가
                   }
                    else if(tag.equals("cpNm")){
                        buffer.append("충전기 명칭 :");
                        xpp.next();
                        buffer.append(xpp.getText());//telephone 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n");//줄바꿈 문자 추가
                    }
                    else if(tag.equals("cpStat")){
                        buffer.append("충전기 상태 코드 :");
                        xpp.next();
                        buffer.append(xpp.getText());//address 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n");//줄바꿈 문자 추가
                    }
                    else if(tag.equals("cpTp")){
                        buffer.append("충전 방식 :");
                        xpp.next();
                        buffer.append(xpp.getText());//mapx 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("  ,  "); //줄바꿈 문자 추가
                    }
                    else if(tag.equals("csId")){
                        buffer.append("충전소 ID :");
                        xpp.next();
                        buffer.append(xpp.getText());//mapy 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n"); //줄바꿈 문자 추가
                    }
                    else if(tag.equals("cpNm")){
                        buffer.append("충전소 명칭 :");
                        xpp.next();
                        buffer.append(xpp.getText());//mapy 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n"); //줄바꿈 문자 추가
                    }
                    else if(tag.equals("lat")){
                        buffer.append("위도 :");
                        xpp.next();
                        buffer.append(xpp.getText());//mapy 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n"); //줄바꿈 문자 추가
                    }
                    else if(tag.equals("longi")){
                        buffer.append("경도 :");
                        xpp.next();
                        buffer.append(xpp.getText());//mapy 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n"); //줄바꿈 문자 추가
                    }
                    else if(tag.equals("statUpdateDatetime")){
                        buffer.append("충전기상태갱신시각 :");
                        xpp.next();
                        buffer.append(xpp.getText());//mapy 요소의 TEXT 읽어와서 문자열버퍼에 추가 
                        buffer.append("\n"); //줄바꿈 문자 추가
                    }
                    break;

                case XmlPullParser.TEXT:
                    break;

                case XmlPullParser.END_TAG:
                    tag= xpp.getName(); //테그 이름 얻어오기

                    if(tag.equals("item")) buffer.append("\n");// 첫번째 검색결과종료..줄바꿈
                    break;
            }

            eventType= xpp.next();
         }

      } catch (Exception e) {
          // TODO Auto-generated catch blocke.printStackTrace();
    }
 
    buffer.append("파싱 끝\n");
    return buffer.toString();//StringBuffer 문자열 객체 반환

    }//getXmlData method....

}//MainActivity class..

8. 그런 뒤 자바 코드에서 버튼을 클릭할 때 검색을 할 수 있도록 코딩을 해주면 검색 기능도 간편하게 구현할 수 있습니다

 

최종 완성본

공공기관 api 검색기능

위 사진은 최종 완성본을 때의 앱의 모습입니다. 검색 기능이 잘 구현된 것을 볼 수 있습니다.

댓글

Designed by JB FACTORY