2026/02/22

오늘의 이야기

앱에서 지원하고 싶은 것중 우선 나의 주변에 버스 정류소를 찾는다. 어떻게 찾을까 ?   모든 데이터는 data.go.kr 공공데이터 포털을 중심으로 ... 찾아보니 제주도에서 제공하는 버스 정보가 있다.


 


http://www.jeju.go.kr/help/open.htm?page=3&act=view&seq=967654 


다만, 가이드의 정보를 기준으로 보면 http:// 으로 시작하는 기본url 과 xml 형식으로 자료를 전송한다는 것이 조금 예전 방식인 것 같은 생각이 들었다.  


 


이제 앱에 retrofit 통신을 하기 위한 준비를 해 보자.  gradle 파일에 수정을 


    // 데이터 주고 받기
implementation 'com.squareup.retrofit2:retrofit:2.7.2'
implementation 'com.squareup.retrofit2:converter-simplexml:2.1.0'

retrofit 과 xml 파싱을 위해서 추가 했다.


 


다음은 manifest 에 internet 접속을 위한 권한 등록


 


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

이제 준비는 되었고.  그럼 데이터를 가져오기 위해서 일단 open api 가 제공하는 데이터 구조를 확인해 보아야겠다.


 


xml 샘플


xml 마지막 부분



데이터는 이렇게 조회가 되고 있고, open api 가이드의 내용도 이와 같다.  그래서 일단은 데이터를 받아올 class 을 만들어 보았다. 


 


맨 먼저 제일 바깥쪽에 구성되는 class 부터


import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;


@Root(name="response", strict = false)
public class StationBean {
@Element(name="header")
HeaderClass headerClass;
@Element(name="body")
BodyClass bodyClass ;

public HeaderClass getHeaderClass() {
return headerClass;
}

public BodyClass getBodyClass() {
return bodyClass;
}

public void setHeaderClass(HeaderClass headerClass) {
this.headerClass = headerClass;
}

public void setBodyClass(BodyClass bodyClass) {
this.bodyClass = bodyClass;
}
}

Root 는 response 로 구성 되며, 그 안에는 header 와 body 가 들어간다. 


다음은 header class


import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root(name="header", strict = false)
public class HeaderClass {
@Element(name="resultCode")
String resultCode;
@Element(name="resultMsg")
String resultMsg ;

public String getResultCode() {
return resultCode;
}

public String getResultMsg() {
return resultMsg;
}

public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}

public void setResultMsg(String resultMsg) {
this.resultMsg = resultMsg;
}
}

header 에는 resultCode, 와 resultMsg 만 들어 있고, 실제 데이터는 body 에 들어간다.  다음은 body class


import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;

import java.util.ArrayList;

@Root(name="body", strict = false)
public class BodyClass {
@ElementList(entry = "items")
ArrayList<ItemClass> items ;
@Element(name="numOfRows")
String numOfRows ;
@Element(name="pageNo")
String pageNo ;
@Element(name="totalCount")
String totalCount ;

public ArrayList<ItemClass> getItems() {
return items;
}

public String getNumOfRows() {
return numOfRows;
}

public String getPageNo() {
return pageNo;
}

public String getTotalCount() {
return totalCount;
}

public void setItems(ArrayList<ItemClass> items) {
this.items = items;
}

public void setNumOfRows(String numOfRows) {
this.numOfRows = numOfRows;
}

public void setPageNo(String pageNo) {
this.pageNo = pageNo;
}

public void setTotalCount(String totalCount) {
this.totalCount = totalCount;
}
}

body 에는 데이터가 들어가는 items 와 건수등을 표시하는 데이터 들이 들어 있는데, 구현하면서 조금 방황(?)한 부분은 ArrayList 로 표시하는 items 에 대한 구현을 어떻게 해야 하는 가에 대한 부분 이었다.  gson 으로 파싱을 할 때는 그런거 없이 수월했는데, xml 형식에서는 EntryList 가 표현이 되도록 구현을 해야 되는 것이었다.


 


다음은 item이 들어가는 item class 


import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root(name="item", strict = false)
public class ItemClass {
@Element(name="dirTp")
String dirTp ;
@Element(name="govNm")
String govNm;
@Element(name="localX")
double localX ;
@Element(name="localY")
double localY ;
@Element(name="mobiNum")
String mobiNum ;
@Element(name="stationId")
String stationId ;
@Element(name="stationNm")
String stationNm ;
@Element(name="upd")
String upd ;
@Element(name="useYn")
String useYn ;

public double getLocalX() {
return localX;
}

public double getLocalY() {
return localY;
}

public String getMobiNum() {
return mobiNum;
}

public String getDirTp() {
return dirTp;
}

public String getGovNm() {
return govNm;
}

public String getStationId() {
return stationId;
}

public String getStationNm() {
return stationNm;
}

public String getUpd() {
return upd;
}

public String getUseYn() {
return useYn;
}

public void setDirTp(String dirTp) {
this.dirTp = dirTp;
}

public void setGovNm(String govNm) {
this.govNm = govNm;
}

public void setLocalX(double localX) {
this.localX = localX;
}

public void setLocalY(double localY) {
this.localY = localY;
}

public void setMobiNum(String mobiNum) {
this.mobiNum = mobiNum;
}

public void setStationId(String stationId) {
this.stationId = stationId;
}

public void setStationNm(String stationNm) {
this.stationNm = stationNm;
}

public void setUpd(String upd) {
this.upd = upd;
}

public void setUseYn(String useYn) {
this.useYn = useYn;
}
}

이렇게 구현 하면 데이터를 받아올 준비는 되었다.  이제 interface 을 구현해 볼 차례 인데,    openapi 에는 호출시 파라미터를 last 와 serviceKey 를 받는다고 되어 있지만, 현재( 2021.12.19 )에는 아무것도 전달하지 않아도 되고, last 뒤에 날자만 전달해도 된다. last 을 넣어보내면 최근 업데이트 된 정보만 받아오는 것으로 확인이 되었다.  다만, 나의 앱에서는 정보가 처음이라, 전체 데이터를 받기 위해서 아무것도 파라미터로 사용하지 않다. 


 


import retrofit2.Call;
import retrofit2.http.GET;

public interface RetrofitApi {
String baseURL = "http://busopen.jeju.go.kr";
String getStation = "/OpenAPI/service/bis/Station";

@GET(getStation)
Call<StationBean> getStationInfo();
}

 interface class 는 이렇게 구성을 했다. query 가 없어서 아무것도 전달하지는 않았다.


이번에는 activity 에서 호출을 해 보도록 하겠다.


 


public class MainActivity extends AppCompatActivity {

...

Retrofit retrofit ;
RetrofitApi service ;

...


private void doGetBusStation() {

retrofit = new Retrofit.Builder()
.baseUrl(RetrofitApi.baseURL)
.client(new OkHttpClient())
.addConverterFactory(SimpleXmlConverterFactory.create())
.build();
service = retrofit.create(RetrofitApi.class);

service.getStationInfo().enqueue(new Callback<StationBean>() {
@Override
public void onResponse(Call<StationBean> call, Response<StationBean> response) {
Log.e(TAG, "response=" + response.code() + " Total=" + response.body().getBodyClass().getTotalCount());
for(ItemClass item : response.body().getBodyClass().getItems()) {
Log.e(TAG, item.getStationNm());
}
}

@Override
public void onFailure(Call<StationBean> call, Throwable t) {
Log.e(TAG, "ERROR=" + t.toString()) ;
}
});

}


...


}

아직 데이터를 받아와서 어떻게 하고자 하는 지 결정을 하지 못했다. 일단, 데이터가 들어 오는 것은 확인을 하였다.  이 구현을 하는데,  CLEARTEXT communication to XXXX not permitted by network security policy 을 만나게 되면, 어떻게 할 것인가를 고민하지 말고 다음을 따라해 보면 된다. 이유는 http:// 으로 된 주소에 통신을 시도하기 때문에 발생하는 보안 문제인데,  나 앱이 모든 호출에서 http:// 로 할 것이 아니기 때문에 특정 url 만 http 로 호출할 것이라고 등록을 해 주면 된다. 


 


먼저 xml 파일을 하나 만든다.  res/xml/network_config.xml 이라고 .. 그안에는 다음과 같이 넣어 준다. 


<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">busopen.jeju.go.kr</domain>
</domain-config>
</network-security-config>

특정 주소는 http 로 통신을 하겠다는 것이다.  보안정책 때문에 누군가 싫어할지 모르지만.


그리고 다음은 manifest 에 다음을 추가해 준다. 


 


    <application
android:allowBackup="true"
android:icon="@mipmap/ic_info_logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_info_logo_round"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_config"

networkSecurityConfig 을 추가해 주면 끝.


 


응답 로그



제주도에는 4358개이 정류소 정보가 있나 보다.  다음엔 이것을 저장해서 데이터로 활용해 보도록 하겠다.


 





댓글 없음:

댓글 쓰기

오늘의 이야기

예전에 어떤 앱을 만들기 할 때 사용했던 음력 변환 하는 소스를 어딘가에서 찾았던 거 같은데... 다시 한번 기억해 보기 위해서 적어 보기로 했다.    https://billcorea.tistory.com/4   구글로간 음력 설명서 새로운 버젼으로...