유지보수 중인 소스의 API 연동 NetworkManager 클래스 관련된 소스들이 오래전에 사용하던 소스코드여서 리팩토링 해보았습니다.

 

RxJava 및 retrofit2 를 이용해서 간단하게 적용했습니다. RxJava 기능이 너무 광범위해서 간단한 기능만 사용해 봅니다.

 

프로젝트에 포함한 라이브러리

 

def retrofit_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"
implementation "com.squareup.retrofit2:converter-scalars:$retrofit_version"
implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofit_version"
implementation "io.reactivex.rxjava2:rxandroid:2.0.1"

 

인터페이스 구현 

  프로젝트에서 API 연동하는 부분이 한군대라 간단하게 구현해 놨습니다.

Observable<JsonObject> 로 받는 방법도 있고 Call<ResponseBody> 형태로도 구현해봤습니다.

 

Observable< T > 

Call< T >

T 에 모델 TestModel 를 바로 넣어서 받아도 됩니다.

 

public class TestModel {
    //    {"title":"title","body":"body","userId":"1","id":101}
    public String title;
    public String body;
    public String userId;
    public int id;
}

 

public interface APIInterface {

    ////////////////////////////////////////////////    
    // rxjava2 Observable 통해서 받을 경우 
    @FormUrlEncoded
    @Headers({
            "accept: application/json",
            "content-type: application/x-www-form-urlencoded; charset=utf-8"
    })
    @POST
    Observable<JsonObject> getPostGsonObject(@Url String suburl, @FieldMap Map<String, String> param);
    
    ////////////////////////////////////////////////
    // rxjava2 Observable 방식으로 Model Class 받을 경우
    @FormUrlEncoded
    @POST
    Observable<TestModel> getPostObservable(@Url String suburl, @FieldMap Map<String, String> param);
    
    ////////////////////////////////////////////////
    // retrofit2 Call<ResponseBody> 방식으로 받을 경우
    @FormUrlEncoded
    @Headers({
            "accept: application/json",
            "content-type: application/x-www-form-urlencoded; charset=utf-8"
    })
    @POST
    Call<ResponseBody> getPostResponseBody(@Url String suburl, @FieldMap Map<String, String> param);
    
    ////////////////////////////////////////////////
    // Call 방식으로 Model Class 받을 경우
    @FormUrlEncoded
    @POST
    Call<TestModel> getPostCall(@Url String suburl, @FieldMap Map<String, String> param);
}

 

[API 연동 클래스]

 

구문에 .client(TrustOkHttpClientUtil.getUnsafeOkHttpClient())  해당 하는 부분은 개발서버 연동할때 SSL 인증서가 없거나 만료되서 SSL 에러 발생으로 SSL 인증서 예외처리하는 클래스 입니다. 

 

public class API {

    static Retrofit retrofit = null;

    public static APIInterface getInstance() {
        return getRetrofitInstance().create(APIInterface.class);
    }

    private static synchronized Retrofit getRetrofitInstance() {
        if (retrofit == null) {
            Gson gson = new GsonBuilder().setLenient().create();
            retrofit = new Retrofit.Builder()
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addConverterFactory(ScalarsConverterFactory.create())
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .baseUrl("https://jsonplaceholder.typicode.com") // 테스트 API 서버 (구동됩니다.)
                    .client(TrustOkHttpClientUtil.getUnsafeOkHttpClient()) // 개발서버 SSL 인증서 없는 경우 예외처리 적용
                    .build();
        }
        return retrofit;
    }
}

 

[TrustOkHttpClientUtil.java]

public class TrustOkHttpClientUtil {

    public static OkHttpClient getUnsafeOkHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { //
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { //throws CertificateException
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            // Create an ssl socket factory with our all-trusting manager
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
            builder.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    // 그냥 true 리턴해도 되지만 취약점 점검시 취약점으로 걸림.
                    if (hostname.equalsIgnoreCase("m.운영도메인.co.kr") ||
                            hostname.equalsIgnoreCase("m.개발도메인.co.kr")) {
                        return true;
                    } else {
                        return false;
                    }
                }
            });

            return builder.build();
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }
    };

}

 

 

[사용 방법]

총 4가지 방식으로 API 받아서 처리하는 방법을 소개하겠습니다. 간단한 예제니 한번씩 테스트 해보세요.

1. Call<ResponseBody>

2. Call<TestModel>

3. Observable<JsonObject>

4. Observable<TestModel>

 

 

1. Call<ResponseBody> 사용 방법

Map params = new HashMap();
params.put("userId" , "1");
params.put("title" , "title");
params.put("body" , "body");

// "/posts" 서브 URL 입니다.
Call<ResponseBody> result = API.getInstance().getPostResponseBody("/posts", params);

result.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        
        try {
            String result = response.body().string();
            JSONObject jsonObject = new JSONObject(result);
            
            DLog.d(new Exception(), jsonObject.toString());
        } catch (JSONException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        DLog.e(new Exception(), call.toString());
    }
});

/* 결과
	{
	  	"title": "title",
	  	"body": "body",
	  	"userId": "1",
	  	"id": 101
	}
*/

 

2. Call Model Class 사용 방법

TestModel 클래스를 다이렉트로 받아서 저리하는 방식

 
Map params = new HashMap();
params.put("userId", "1");
params.put("title", "title");
params.put("body", "body");

API.getInstance()
        .getPostCall("/posts", params)
        .enqueue(new Callback<TestModel>() {
            @Override
            public void onResponse(@NonNull Call<TestModel> call, @NonNull Response<TestModel> response) {
                if (response.isSuccessful()) {
                    TestModel testModel = response.body();
                    
                    DLog.d(new Exception(), testModel.title + " / " + 
                                            testModel.body + " / " + 
                                            testModel.userId + " / " + 
                                            testModel.id);
                } else {
                    Toast.makeText(getApplicationContext(), "서버에 문제가 발생했습니다.", Toast.LENGTH_SHORT).show();
                    DLog.d(new Exception(), "Retrofit2 - Failure " + response.code() + " : " + response.message());
                }
            }
            //통신 실패
            @Override
            public void onFailure(@NonNull Call<TestModel> call, @NonNull Throwable t) {
                Toast.makeText(getApplicationContext(), "서버에 문제가 발생했습니다.", Toast.LENGTH_SHORT).show();
                t.printStackTrace();
            }
        });
        
/* 결과
      title / body / 1 / 101
*/

 

 

3. Observable<JsonObject>  사용 방법

// Retrofit 테스트 코드
Map params = new HashMap();
params.put("userId", "1");
params.put("title", "title");
params.put("body", "body");

Disposable disposable = API.getInstance()
        .getPostGsonObject("/posts", params)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(result -> {
                    // JSONObject 객체 저장 해서 사용
                    JSONObject jsonObject = new JSONObject(result.toString());
                    DLog.d(new Exception(), jsonObject.toString());
					
                    // Gson JsonObject 객체 저장 
//                    JsonObject resultObject = (JsonObject) result;
//                    DLog.d(new Exception(), resultObject.toString());

//                    String title = resultObject.get("title").getAsString();
//                    String body = resultObject.get("body").getAsString();
//                    String userId = resultObject.get("userId").getAsString();
//                    int id = resultObject.get("id").getAsInt();

//                    DLog.d(new Exception(), title + " / " + body + " / " + userId + " / " + id);
                },
                error -> {
                    DLog.e(new Exception(), "onError() : err : " + error.toString());
                },
                () -> {
                    DLog.d(new Exception(), "onComplete()");
                }
        );
        

/* 결과
	{"title":"title","body":"body","userId":"1","id":101}
*/

 

4. Observable<TestModel>  Class Model  사용 방법

Map params = new HashMap();
params.put("userId", "1");
params.put("title", "title");
params.put("body", "body");

Disposable disposable = API.getInstance()
        .getPostObservable("/posts", params)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(result -> {
                    TestModel testModel = (TestModel) result;
                    if (testModel != null) {
                        DLog.d(new Exception(), testModel.title + " / " + testModel.body + " / " + testModel.userId + " / " + testModel.id);
                    }
                },
                error -> {
                    DLog.e(new Exception(), "onError() : err : " + error.toString());
                },
                () -> {
                    DLog.d(new Exception(), "onComplete()");
                }
        );
        
/* 결과
   title / body / 1 / 101
*/

+ Recent posts