Today's

길을 나서지 않으면 그 길에서 만날 수 있는 사람을 만날 수 없다

모바일 앱(안드로이드)

안드로이드 앱 만들기 API 연동을 위한 retrofit 구현 이야기.

Billcorea 2021. 5. 15. 00:27
반응형

안드로이드 폰에서 Restful 호출을 위해서 StringRequest 을 사용해 보기도 했지만, Retrofit 을 알고 나서는 간편하게 잘 쓰게 되었다.

 

data.go.kr 에서 제공하는 공공데이터를 이용해서 앱을 만들어 보고자 했던 건데... 문제는 covid19 관련된 앱은 공공기관에서 제출하는 것만 구글스토어에 등재를 할 수 있다는 것이다. ㅋ~ 뭐 그래도 일단 만들어 보면서 느꼈던 것들을 정리해 두고자 한다. 그래야 나중에 또 하게 되면 쉽게 삽집하지 않고 접근을 할 수 있을테니...

 

먼저 데이터 포털에서 사용인증을 신청해 보자

 

일단 내가 보고싶은 건 코로나 관련 병원정보 이력과 코로나 환자 발생현황 이다. 그중에 하나 신청한 내역은 위와 같다.

일단 신청은 되었고, 사용 승인도 금새 되었다.  안드로이드 앱을 만들어 가는 과정중에서 필수적으로 이해가 되어야 하는 부분만 기록해 둔다.

 

manifest 파일에서 중요한 건 internet 사용을 위한 권한 설정 부분과

http 통신을 하기 위해서 usesCleartextTraffic 을 설정하는 것이 우선 되어야 한다.

    <uses-permission android:name="android.permission.INTERNET" />
    
    ...
    
        <application
        android:allowBackup="false"
        android:icon="@mipmap/ic_main_logo"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_main_logo_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Corona19Exam"
        android:usesCleartextTraffic="true">

usesCleartextTraffic 설정은 cleartext HTTP와 같은 cleartext 네트워크 트래픽을 사용할지 여부를 나타내는 flag로 이 플래그가 flase 로 되어 있으면, 플랫폼 구성 요소 (예 : HTTP 및 FTP 스택, DownloadManager, MediaPlayer)는 일반 텍스트 트래픽 사용에 대한 앱의 요청을 거부하게 되어 true 설정해야 된다고 설명이 되어 있다.

 

그 다음은 build.gradle (module) 부분의 설정중에 다른 것들도 필요에 따라 들어가야 하지만, 아래 부분은 필수.

그래야 retrofit 을 사용할 수 있다. ( 버전인 2.7.2는 2021년 5월중순 현재 기준임)

dependencies {

    implementation 'com.squareup.retrofit2:retrofit:2.7.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.7.2'
    implementation 'com.squareup.retrofit2:converter-simplexml:2.1.0'
    implementation 'com.google.code.gson:gson:2.8.6'
    
}

 

이제 호출하는 본문의 예제를 보자

MainActivity 에서 아래 처럼 CallAPi 함수를 만들어 쓰는 방법이 있을 테고... 

암튼 Retrofit 이용하여 호출할 떄 APIInterface 을 만들어서 호출할 떄 전달해야 되는 파라미터를 전달하고

 

아래 예시 처럼 onResponse 에서 수신된 데이터를 구조체로 받아서 데이터로 활용할 수 있다.

    public void CallApi(Context context) throws IOException {

        UtilStrValue utilStrValue = new UtilStrValue();
        Date date = Calendar.getInstance().getTime();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd") ;
        String eDate = simpleDateFormat.format(date) ;
        Log.d(TAG, "Today=" + eDate) ;
        String bDate = eDate.substring(0, 6).toString() + "01";

        mDatabase = FirebaseDatabase.getInstance().getReference();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UtilStrValue.BaseURL)
                .addConverterFactory(SimpleXmlConverterFactory.create()) // 공공데이터 포털의 응답 방식 xml 이라서 이렇게 하는 것이고...json 이면 다른 방법으로 GsonConverterFactory 을 활용
                .build() ;

        APIInterface service = retrofit.create(APIInterface.class) ;
        service.getData(utilStrValue.serviceKey, "1", "10", bDate, eDate).enqueue(new Callback<ResData>() {
            @Override
            public void onResponse(Call<ResData> call, Response<ResData> response) {
                if ("00".equals(response.body().header.resultCode)) {
                    Log.d(TAG, "resultCode=" + response.body().header.resultCode + "\nresultMsg=" + response.body().header.resultMsg) ;
                    Log.d(TAG, "item Size=" + response.body().body.bodyItems.item.size()) ;
                    for(int i=0 ; i < response.body().body.bodyItems.item.size() ; i++) {
                        mDatabase.child("covid19").child(response.body().body.bodyItems.item.get(i).getSeq()).setValue(response.body().body.bodyItems.item.get(i)) ;
                    }
                }
            }

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

위 예제 소스의 콜백에서 선언된 ResData 는 수신되는 데이터의 구조체 선언인데, 구조체는 공공데이터 포털에서 제공한 응답데이터 구조를 그대로 구성하면 수신된 데이터를 쉽게 활용할 수 있다.

 

ResData예시는 아래와 같다.   작성할 때 이노테이션을 잘 정리해야 한다.  이노테이션의 변수 이름은 공공데이터 포털에서 알려준 전문 구조체의 변수과 일치 되어야 하고, 이노테이션 아래 선언된 변수는 내 프로그램에서 사용할 변수명이다.

(예시의 Hederlist 나 BodyData 는 또 다른 구조를 가지고 있는 클래스 임을 참고하시길)

import com.exam.corona19exam.util.HeaderList;

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

@Root(name="response")
public class ResData {

    @Element(name = "header")
    public HeaderList header = new HeaderList();
    @Element(name = "body", required = false)
    public BodyData body = new BodyData() ;

}

 

호출할 때 사용한 APIInterface의 구조체는  Call 을 할 떄 데이터 구조체를 미리 정해서 호출을 하기 때문에 사용은 수월하게 할 수 있으며, 아래 예시 처럼 Query String 을 전달 파라미터로 설정하여 호출 할 수 있다.

import com.exam.corona19exam.hospitem.ResDataHosp;
import com.exam.corona19exam.covid19res.ResData;
import com.exam.corona19exam.placesapi.ResPlaces;

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Query;

public interface APIInterface {

    @Headers("Content-Type:application/json")
    @GET("/openapi/service/rest/Covid19/getCovid19InfStateJson")
    Call<ResData> getData (@Query(value = "serviceKey", encoded = true) String serviceKey // encoded = true 하면 % 기호가 그대로 전달됨
            , @Query("pageNo") String pageNo
            , @Query("numOfRows") String numOfRows
            , @Query("startCreateDt") String startCreateDt
            , @Query("endCreateDt") String endCreateDt) ;

}

 

이런 정도까지만 기록해 두면 훗날에서 쉽게 기억을 되살려 코드 작업을 할 수 있을 것 같다.

반응형