Android - Retrofit2

유의선·2025년 2월 3일

Retrofit2는 안드로이드에서 REST API 통신을 하게 해주는 라이브러리다.

Retrofit2 라이브러리를 사용해 이미지를 보내고 AI로 해당 이미지의 분석 결과를 받는 코드를 작성해보았고, 사용법에 대해 조사해보았다.


build.gradle 의존성 추가

dependencies {

    ...

    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0") // GSON 변환기
    implementation("com.squareup.okhttp3:logging-interceptor:4.9.2") // HTTP 요청 및 응답 로그 출력 인터셉터
}

데이터 모델 클래스

API 응답을 받을 형식의 클래스를 정의한다.

import com.google.gson.annotations.SerializedName;

public class ObjectDetectResponse {
    @SerializedName("name") String name;
    @SerializedName("confidence") double confidence;
    @SerializedName("xmin") double xmin;
    @SerializedName("ymin") double ymin;
    @SerializedName("xmax") double xmax;
    @SerializedName("ymax") double ymax;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getConfidence() {
        return confidence;
    }

    public void setConfidence(double confidence) {
        this.confidence = confidence;
    }

    public double getXmin() {
        return xmin;
    }

    public void setXmin(double xmin) {
        this.xmin = xmin;
    }

    public double getYmin() {
        return ymin;
    }

    public void setYmin(double ymin) {
        this.ymin = ymin;
    }

    public double getXmax() {
        return xmax;
    }

    public void setXmax(double xmax) {
        this.xmax = xmax;
    }

    public double getYmax() {
        return ymax;
    }

    public void setYmax(double ymax) {
        this.ymax = ymax;
    }
}

@SerializedName 어노테이션을 사용해 JSON 응답의 key 값과 매칭되도록 설정하였다.

    @SerializedName("name") String name;
    @SerializedName("confidence") double confidence;
    @SerializedName("xmin") double xmin;
    @SerializedName("ymin") double ymin;
    @SerializedName("xmax") double xmax;
    @SerializedName("ymax") double ymax;

그 후 각 값들의 Getter와 Setter를 만들어주었다.


API 인터페이스

API 통신을 위한 엔드포인트 인터페이스를 정의하였다.

import okhttp3.MultipartBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;

public interface ApiService {
    @Multipart
    @POST("object/detect")
    Call<ObjectDetectResponse> uploadImage(@Part MultipartBody.Part image);
}

서버로 데이터를 전송하기 위해 @Post 어노테이션을 사용하였다.
@Post 후 괄호에 들어가는 "object/detect"가 엔드포인트가 된다.

사진 파일을 서버로 전송하기 때문에 @Multipart@Part 어노테이션을 사용하였다.


Retrofit 인스턴스 생성 & API 호출

Retrofit 인스턴스 생성과 API 호출 역할을 하는 클래스 RetrofitHelper 를 싱글톤 패턴으로 만들었다.

import android.util.Log;

import com.google.gson.GsonBuilder;

import java.util.concurrent.TimeUnit;

import kr.co.edoubles.carlostdetect.utils.ConstantsKt;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitHelper {
    private static final String TAG = "RetrofitHelper";
    private static RetrofitHelper instance;
    private final ApiService apiService;

    private RetrofitHelper() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ConstantsKt.BASE_URL)
                .addConverterFactory(providesConverterFactory())
                .client(providesOkHttpClient().build())
                .build();
        apiService = retrofit.create(ApiService.class);
    }

    public static synchronized RetrofitHelper getInstance() {
        if (instance == null) {
            instance = new RetrofitHelper();
        }
        return instance;
    }

    private static GsonConverterFactory providesConverterFactory() {
        return GsonConverterFactory.create(new GsonBuilder().setLenient().create());
    }

    private static OkHttpClient.Builder providesOkHttpClient() {
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        return new OkHttpClient.Builder()
                .connectTimeout(5, TimeUnit.SECONDS)
                .readTimeout(5, TimeUnit.SECONDS)
                .writeTimeout(5, TimeUnit.SECONDS)
                .addInterceptor(loggingInterceptor);
    }

    public void getResult(MultipartBody.Part image) {
        Call<ObjectDetectResponse> result = apiService.uploadImage(image);
        result.enqueue(new Callback<ObjectDetectResponse>() {
            @Override
            public void onResponse(Call<ObjectDetectResponse> call, Response<ObjectDetectResponse> response) {
                if(response.isSuccessful()) {
                    Log.d(TAG, "Upload Success " + response.body());
                    // 이곳에 성공 시 실행 코드 작성
                } else {
                    Log.d(TAG, "Upload failed: " + response.code());
                }
            }

            @Override
            public void onFailure(Call<ObjectDetectResponse> call, Throwable t) {
                t.getStackTrace();
            }
        });
    }
}

먼저 Retrofit 인스턴스를 생성하였다.

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ConstantsKt.BASE_URL)
                .addConverterFactory(providesConverterFactory())
                .client(providesOkHttpClient().build())
                .build();

.baseUrl() 에는 기본 URL을 설정해준다. ex) https://www.naver.com/

.addConverterFactory() 에는 Json 객체를 변환해주는 Gson 컨버터 객체를 넣어준다.

    private static GsonConverterFactory providesConverterFactory() {
        return GsonConverterFactory.create(new GsonBuilder().setLenient().create());
    }

setLenient()를 사용하여 파싱을 자유롭게 해주었다.

.client() 를 사용하여 Timeout, 로깅 등을 설정하는 OkHttpClient를 설정해주었다.

    private static OkHttpClient.Builder providesOkHttpClient() {
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        return new OkHttpClient.Builder()
                .connectTimeout(5, TimeUnit.SECONDS)
                .readTimeout(5, TimeUnit.SECONDS)
                .writeTimeout(5, TimeUnit.SECONDS)
                .addInterceptor(loggingInterceptor);
    }

그 후 생성한 Retrofit 인스턴스를 사용하여 ApiService 객체를 만들어주었다.

apiService = retrofit.create(ApiService.class);

API 요청을 보내는 메소드 getResult(MultipartBody.Part image) 를 만들었다.

    public void getResult(MultipartBody.Part image) {
        Call<ObjectDetectResponse> result = apiService.uploadImage(image);
        result.enqueue(new Callback<ObjectDetectResponse>() {
            @Override
            public void onResponse(Call<ObjectDetectResponse> call, Response<ObjectDetectResponse> response) {
                if(response.isSuccessful()) {
                    Log.d(TAG, "Upload Success " + response.body());
                    // 이곳에 성공 시 실행 코드 작성
                } else {
                    Log.d(TAG, "Upload failed: " + response.code());
                }
            }

            @Override
            public void onFailure(Call<ObjectDetectResponse> call, Throwable t) {
                t.getStackTrace();
            }
        });
    }

ApiService 객체를 사용해 Retrofit API 요청 객체를 생성하였다.

Call<ObjectDetectResponse> result = apiService.uploadImage(image);

uploadImage() 메소드를 사용하면 기본 URL에 ApiService에서 @Post로 설정한 주소를 합친

기본 URL + object/detect

주소로 요청을 하게 된다.

Retrofit API 요청 객체에 enqueue() 를 사용하여 비동기 네트워크 요청을 실행한다.

        result.enqueue(new Callback<ObjectDetectResponse>() {
            @Override
            public void onResponse(Call<ObjectDetectResponse> call, Response<ObjectDetectResponse> response) {
                if(response.isSuccessful()) {
                    Log.d(TAG, "Upload Success " + response.body());
                    // 이곳에 성공 시 실행 코드 작성
                } else {
                    Log.d(TAG, "Upload failed: " + response.code());
                }
            }

            @Override
            public void onFailure(Call<ObjectDetectResponse> call, Throwable t) {
                t.getStackTrace();
            }
        });

요청에 성공하면 onResponse() 가 실행되고 response.body()에 응답 내용이 들어간다.


그 외 엔드포인트 인터페이스 사용 어노테이션

  1. @GET
    서버에서 데이터를 가져올 때 사용.
@GET("object/{id}")
Call<ObjectDetectResponse> uploadImage(@Path("id") int id);
  1. @Body
    @POST와 함께 서버에 데이터를 전송할 때 사용
@POST("object/detect")
Call<ObjectDetectResponse> uploadImage(@Body String a);
  1. @PUT
    서버에 기존 데이터를 수정할 때 사용
@PUT("object/{id}")
Call<ObjectDetectResponse> uploadImage(@Path("id") int id, @Body ObjectDetectResponse objectDetectResponse);
  1. @DELETE
    서버에 데이터를 삭제할 때 사용.
@DELETE("object/{id}")
Call<Void> uploadImage(@Path("id") int id);

https 가 아닌 http 주소를 사용한다면
AndroidManifest.xml<application> 태그 안에

android:usesCleartextTraffic="true"

를 추가해줘야 한다.

0개의 댓글