[Android] 공공기관 API 신청 및 파싱하기(검색기능구현)
- Mobile/Android
- 2017. 10. 18.
공공 데이터 홈페이지라는 것이 있습니다. 국가 혹은 공공기관에서 만들어놓은 정보들을 국민들에게 개방해 둔 것인데 여기서 무료로 제공하는 공공 API를활용하여 쉽고 간단하게 유용한 앱을 만들 수 있습니다. 이번 글에서 공공기관 API를 파싱하고 검색하는 것까지 구현하는 방법에 대해 포스팅하려 합니다 공공기관 API는 아무거나 파싱 해와도 상관없는데 저는 전기차 충전소 정보를 파싱 해왔습니다.
공공 API 신청 및 파싱 & 검색 구현하기
1. 우선 위에 사진에 나와있는 것처럼 차례대로 공공기관 API를 신청해서 승인을 받습니다. 공공데이터를 신청하면 거기에 딸려있는 참고 문서가 있는데 참고 문서에 필수 요청 변수와 파싱 할 때 필요한 여러 가지 정보들이 있으니 꼭 챙겨보세요.
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로 데이터를 파싱 하는 방식입니다.
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. 그런 뒤 자바 코드에서 버튼을 클릭할 때 검색을 할 수 있도록 코딩을 해주면 검색 기능도 간편하게 구현할 수 있습니다
최종 완성본
위 사진은 최종 완성본을 때의 앱의 모습입니다. 검색 기능이 잘 구현된 것을 볼 수 있습니다.
'Mobile > Android' 카테고리의 다른 글
[Android] LayoutInflater로 동적으로 레이아웃(뷰) 추가하기 (5) | 2017.10.24 |
---|---|
[Android] 안드로이드에서 웹뷰로 자신이 만든 웹사이트 띄우기 (2) | 2017.10.19 |
[Android] Soundpool으로 만드는 드럼 앱 (12) | 2017.10.18 |
[Android] Soundpool으로 만드는 피아노 앱 (30) | 2017.10.18 |