Retrofit

k_hyun·2023년 3월 29일
0

스퀘어에서 만든 HTTP 통신을 간편하게 만들어 주는 라이브러리이다.

동작 방식은 아래와 같다.

  1. 통신용 함수를 선언한 인터페이스를 작성한다.
  2. Retrofit에 인터페이스를 전달한다.
  3. Retrofit이 통신용 서비스 객체를 반환한다.
  4. 서비스의 통신용 함수를 호출 후 Call 객체를 반환한다.
  5. Call 객체의 enqueue() 함수를 호출하여 네트워크 통신을 수행한다.

라이브러리 선언

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

Retrofit은 JOSN이나 XML 데이터를 모델 객체로 변환해 주는데, 이때 gson 라이브러리 및 컨버터를 사용한다.

모델 클래스 선언

모델 클래스는 서버와 주고받는 데이터를 표현하는 클래스이다.
VO (value-object) 클래스라고도 한다.

원래는 JSON 데이터를 코드에서 직접 파싱해서 이용해야 하지만, 데이터를 담을 모델 클래스를 선언하고 클래스 정보만 알려주면 모델 클래스의 객체를 알아서 생성하고 그 객체에 데이터를 담아준다.

{
	"id": 7,
    "email": "abcde@gmail.com",
    "first_name": "hk",
    "last_name": "lee"
}

위의 JSON 정보를 담을 모델 클래스는 아래와 같이 작성할 수 있다.

data class UserModel(
	var id: String,
    @SerializedName("first_name")
    var firstName: String,
    var lastName: String
)

// 아래와 같이 클래스를 분리해서 작성할 수 있다.
data class UserListModel(
	var page: String,
    var perPage: String,
    var total: String,
    var totalPages: String,
    var data: List<UserModel>?
    
)

@SerializedName 애너테이션을 통해 first_name이라는 키의 데이터가 firstName에 저장된다고 표시한다.
email 프로퍼티는 존재하지 않지만, 문제는 없다.
키가 last_name이면 자동으로 lastName 프로퍼티에 저장된다.

서비스 인터페이스 정의

네트워크 통신이 필요한 순간에 호출할 함수를 포함하는 서비스 인터페이스를 작성해야 한다.

interface INetworkService {
	@GET("api/users")
    fun doGetUserList(@Query("page") page: String): Call<UserModel>
    @GET
    fun getAvatarImage(@Url url:String): Call<ResponseBody>
}

이 인터페이스를 구현해 실제로 통신하는 클래스는 Retrofit이 자동으로 만들어 준다. 이때 애너테이션을 참조한다.

@GET은 서버와 연동할 때 GET 방식으로 해달라는 의미이다.
@Query는 서버에 전달되는 데이터
@Url은 요청 URL을 의미한다.

Retrofit 객체 생성

val retrofit: Retrofit
	get() = Retrofit.Builder()
    	.baseUrl("https://reqres.in/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()

baseUrl을 위 처럼 선언하고 @GET("api/users")처럼 경로를 지정한다면 서버 요청 URL은 "https://reqres.in/api/users" 가 된다.

인터페이스 타입의 서비스 객체 얻기

var networkService: INetworkService = retrofit.create(INetWorkService::class.java)

Retrofit의 create()함수에 앞에서 만든 서비스 인터페이스 타입을 전달한다.

그러면 이 인터페이스를 구현한 클래스의 객체를 반환해준다.

네트워크 통신 시도

// Call 객체 얻기
val userListCall = networkService.doGetUserList("1")

인터페이스에 선언한 함수를 호출하면 Call 객체가 반환된다.
실제 통신은 이 Call 객체의 enqueue() 함수를 호출하는 순간 이뤄진다.

userListCall.enqueue(object : Callback<UserListModel> {
	override fun onResponse(call: Call<UserListModel>, response: Response<UserListModel>) {}
    override fun onFailure(call: Call<UserListModel>, t: Throwable) {}

통신이 성공하면 onResponse() 함수가, 실패하면 onFailure() 함수가 호출된다.

Retrofit 애너테이션

@GET, @POST, @PUT, @DELETE, @HEAD

@GET("users/list?sort=desc")
fun test1(): Call<UserModel>

val call: Call<UserModel> = networkService.test1()

@Path

@GET("group/{id}/users/{name}")
fun test2(
	@Path("id") userId: String,
    @Path("name") arg2: String,
): Call<UserModel>

val call: Call<UserModel> = networkService.test2("10", "lee")

@Query

@GET("group/users")
fun test3(
	@Query("sort") arg1: String,
    @Query("name") arg2: String
): Call<UserModel>

val call: Call<UserModel> = networkService.test3("age", "lee")

// 서버 요청 URL
https://reqres.in/group/users?sort=age&name=lee

@QueryMap

@GET("group/users")
fun test4(
	@QueryMap options: Map<String, String>,
    @Query("name") name: String
): Call<UserModel>

val call: Call<UserModel> = networkService.test4(
		mapOf<String, String> ("one" to "hello", "two" to "world"),
        "lee"
)

// 서버 요청 URL
https://reqres.in/group/users?one=hello&two=world&name=lee

@Body

@POST("group/users")
fun test5(
	@Body user: UserModel,
    @Query("name") name: String
): Call<UserModel>

val call: Call<UserModel> = networkService.test5(
	UserModel(id="1", firstName="gildong", lastName="hong"),
    "lee"
)    

@Body 애너테이션을 사용하면 서버 요청 URL은 바뀌지 않는다.
@Body로 지정한 모델의 데이터는 데이터 스트림으로 서버에 전송된다.

0개의 댓글