스퀘어에서 만든 HTTP 통신을 간편하게 만들어 주는 라이브러리이다.
동작 방식은 아래와 같다.
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을 의미한다.
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() 함수가 호출된다.
@GET("users/list?sort=desc")
fun test1(): Call<UserModel>
val call: Call<UserModel> = networkService.test1()
@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")
@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
@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
@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로 지정한 모델의 데이터는 데이터 스트림으로 서버에 전송된다.