[Android] Retrofit2 Empty Response

👻·2022년 5월 26일
0

Android

목록 보기
4/11
post-thumbnail

📌 개요

Http 관련된 것들을 Retrofit으로 교체했다.

Retrofit은 type-safe한 Http Client library이다.
OkHttp Client를 default로 선언하여 그 위에서 동작한다.

빠른 성능과 쉬운 구현, 가독성이 좋다는 장점이 있다.

기존 Http 메소드를 Retrofit으로 변환하는데 있어서 어려움은 없었다.

하지만, 일부 API에 있어 문제가 발생했다.

서버에서 보내는 response가 null이 아닌, 단순 공백을 보낼 때가 있었다.

개인적인 생각으로는 서버에서 보내는 response가 공백이 있는 경우는 없어야 한다고 생각한다.

뭐.. 데이터 값에 단순히 데이터 하나만 오는게 아니라 status 등 구분할 값 자체는 반드시 필요하다고 생각한다.

당장 내가 서버쪽 API를 관리하는게 아니니 변경해달라고 하지 않고 그냥 쓰기로 했다.

이런 경우가 번번치 않게 있긴 하기 때문이다. (공공데이터포털이라던가^^)


📌 원인파악

일단, 현재 내가 구현해놓은 방식은 Retrofit 객체 생성 관련해서 클래스를 따로 빼놓았다.

public class HttpRetrofitManager {
    private String base_url;
    private Retrofit retrofit;
    private Gson gson = new GsonBuilder().setLenient().create();

    public HttpRetrofitManager(String base_url) {
        this.base_url = base_url;
    }

    public Retrofit getInstance() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(base_url)
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        }

        return retrofit;
    }
}

Retrofit Interface도 하나의 인터페이스에서 관리할 수 있도록 해놓았다.

public interface HttpRetrofitInterface {
	...
    
    @POST([myUrl])
    Call<String> getMyValue(@Body [myValue]);
}

문제가 된건 String으로 Response를 받는 부분이였다.
다른 API들은 전부 body 자체가 비어있을 경우가 없다.
애초에 값 하나만 받는 경우도 아니고, 구체적으로 예외처리가 잘 되어있기 때문이다.

retrofit에서 call handler는 onResponse(), onFailure() 크게 두가지로 분류된다.

private void requestMediaId() {
        retrofitManager = new HttpRetrofitManager(server_url);

        retrofit = retrofitManager.getInstance();
        retrofitInterface = retrofit.create(HttpRetrofitInterface.class);
        httpCall = retrofitInterface.getMyValue(new RequestData(param1, param2);

        httpCall.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                if(response.isSuccessful()) {
                    if(response.body() == null || response.body().isEmpty()) {
                        return;
                    }

                    Log.d(TAG, response.body());
                }
            }

            @Override
            public void onFailure(Call<String> call, Throwable throwable) {
                handler.sendEmptyMessage(RESPONSE_FAILED..);
            }
        });
    }

response 자체는 정상이지만, 값이 없는 empty의 경우에 onResponse가 아닌 onFailure로 빠져서 콜백처리가 된다.

하지만, 값이 onFailure로 빠지게 되면 응답 자체를 받지 못한 경우와 동일하게 처리가 되버린다.
정상 응답이지만 값이 비어있는 경우를 구분해줘야 하는 상황이다.

따라서 지금 현재 내가 할 일은 empty response의 경우 onFailure가 아닌 onResponse로 처리가 되도록 해야한다.


📌 해결방안

깃허브에서 바로 해결 방안을 찾았다.
https://github.com/square/retrofit/issues/1554

JakeWharton에 의하면 Converter.Factory를 커스텀해서
response body의 length가 0인 경우 오류가 아닌 null로 처리하도록 하는 부분을 적용했다.

바로 적용해보자.

public class HttpRetrofitManager {
    private String base_url;
    private Retrofit retrofit;
    private Gson gson = new GsonBuilder().setLenient().create();

    public HttpRetrofitManager(String base_url) {
        this.base_url = base_url;
    }

    public Retrofit getInstance() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(base_url)
                    .addConverterFactory(new NullOnEmptyConverterFactory())
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();
        }

        return retrofit;
    }

    public class NullOnEmptyConverterFactory extends Converter.Factory {
        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
            return new Converter<ResponseBody, Object>() {
                @Override
                public Object convert(ResponseBody body) throws IOException {
                    if (body.contentLength() == 0) return null;
                    return delegate.convert(body);
                }
            };
        }
    }
}

retrofit 객체 생성하는 클래스에서 NullOnEmptyConverterFactory 클래스를 추가했다.

ResponseBody의 contentLength()가 0인 경우 null로 처리하여 response를 받을 수 있다.

Retrofit.Builder()에서 addConverterFactory(new NullOnEmptyConverterFactory)를 사용하여 커스텀한 Converter를 적용시켜주었다.

적용 후 empty response는 null 형태로 반환되어 onResponse()로 정상적으로 처리되었다.

profile
Software Developer

0개의 댓글