사용자가 원하는 지역을 간편하게 입력할 수 있도록 하기 위해 공공 api 중 행정구역 정보를 연동하였다.
해당 지역의 기상정보를 받아와 어플리케이션에 나타내기 위해 기상청의 중기예보 및 단기예보(구, 동네예보)를 연동하였다.
안드로이드 스튜디오에서 Retrofit2를 활용해 연동하면서 실수 했던 점을 적어본다.
응답 데이터 타입과 구조가 다를시, onSuccess로 Http 코드가 200이 잘 나오지만, response.body()에는 null값이 들어온다.
서버에서 보낸 데이터가 잘못된것은 아닐까 별별 생각이 다들지만, 역시나 나의 문제였다...
postman으로 보내보면 아래와 같이 JSON 형태로 응답이 날아온다.
{
"response": {
"service": {
"name": "data",
"version": "2.0",
"operation": "getfeature",
"time": "661(ms)"
},
"status": "OK",
"record": {
"total": "17",
"current": "17"
},
"page": {
"total": "1",
"current": "1",
"size": "1000"
},
"result": {
"featureCollection": {
"type": "FeatureCollection",
"bbox": [
0.0,
0.0,
-1.0,
-1.0
],
"features": [
{
"type": "Feature",
"properties": {
"ctp_eng_nm": "Seoul",
"ctprvn_cd": "11",
"ctp_kor_nm": "서울특별시"
},
"id": "LT_C_ADSIDO_INFO.154"
},
{
"type": "Feature",
"properties": {
"ctp_eng_nm": "Busan",
"ctprvn_cd": "26",
"ctp_kor_nm": "부산광역시"
},
"id": "LT_C_ADSIDO_INFO.155"
},
{
"type": "Feature",
"properties": {
"ctp_eng_nm": "Daegu",
"ctprvn_cd": "27",
"ctp_kor_nm": "대구광역시"
}
]
}
}
}
}
여기서 맨 처음에 보이는 response를 무시한채, 나의 Response 클래스에는 service, status, record, page, result 클래스가 있어서 데이터가 null로 나왔던 것이다.
해당 Response 클래스를 ResponseParams 클래스에 넣으니 위의 JSON과 같은 구조로 완성되어 데이터가 body() 값이 잘 들어오게 되었다.
public class ResponseParams {
@SerializedName("response")
SidoResponse response;
public ResponseParams(SidoResponse response) {
this.response = response;
}
public SidoResponse getResponse() {
return response;
}
public void setResponse(SidoResponse response) {
this.response = response;
}
}
이런식으로 말이다.
@SerializedName("name")
여기서 name과 response jason의 key값이 같으면 된다.
위와같이 오류발생과 함께 메시지를 받게 되었다면, 해당 API의 주소가 https가 아닌 http로 시작하는 건 아닌지 확인해보면 된다.
조치 방법은,
1 http를 https로 바꾸면 된다.
- 하지만 http를 사용해야만 하는 경우라면 2번을 사용한다.
2 manefest에
<application
android:usesCleartextTraffic="true"
.../>
위와 같이 추가한다.
혹은, res의 하위에 new > android resource directory > resource type을 xml로 설정해 추가하고 해당 디렉토리에 network-security-config.xml을 만든다.
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">apis.data.go.kr</domain>
</domain-config>
</network-security-config>
내 경우 apis.data.go.kr 이었지만, 사용하는 baseurl 넣어준다.
해당 xml파일을 manefest에 추가해준다.
<application
android:networkSecurityConfig="@xml/network_security_config"
.../>
response가 onFailure로 자꾸 떨어지면서 다음과 같은 오류메시지를 냈다.
구글링을 해보니,
//추가부분
Gson gson = new GsonBuilder()
.setLenient()
.create();
//여기까지
//아래는 원래 있던 retrofit.builder
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL2)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build(gson);
//여기 바로 윗줄에 build()에 gson을 넣어 build(gson)을 만들어주면 된다.
이렇다 하여 그렇게 했는데,
다음 미션을 맞이하게 되었다.
구글링 해보니 기대한 바가 Object였는데, String이더라? 라고 하는 말이라는데 무튼 이거 내 경우엔 String이 아닌데 이상하다 싶어서.. String으로 전부 다 받아보려 시도도 해보았지만 역시나 뭔가 좀 이상했다.
결국 구글링 도중 Okhttp HttpLoggingInterceptor 발견, 해당 api와의 통신과정을 로그로 다 찍어준다.
retrofit을 빌드하여 리턴해주는 메소드에 다음과 같이 추가하였다.
public static Retrofit getRetrofitForWeather() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
if (retrofit == null) {
//추가부분
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//여기까지
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(5000, TimeUnit.MILLISECONDS)
.connectTimeout(5000, TimeUnit.MILLISECONDS)
.addNetworkInterceptor(new XAccessTokenInterceptor())
//추가부분(매우중요)
.addInterceptor(interceptor)
//여기까지
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL2)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
}
return retrofit;
}
로그에 찍혀 나온 오류 메시지를 보니...
key 값이 잘못되어 있다고 하더라.
공공 api 보면 일반 인증키가 encoding된 것과 decoding 된것을 함께 제공한다.
별 생각없이 postman 사용할 때 encoding버전을 사용했더니 잘되어서 이번에도 사용했더니 안되더라.
encoding되어있는 잘 보시면 맨뒤에 == 이 %3D%3D로 되어있다. 이걸 한번더 encoding하는 듯 하다.
저걸 decoding 인증키로 바꿔주고 api연동을 했더니 값이 아주 잘 들어온다.
이틀간 삽질한걸 정리해본다..
날 죽이지 못하는 고통은 날 강하게 만들어줄 뿐이다...ㅡㅡ