# Table of Contents
# Retrofit
Retrofit
은 안드로이드에서의 HTTP 통신을 위한 라이브러리다.
# 설정
Retrofit
을 사용하기 위해 다음 의존성을 추가한다.
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
그리고 AndroidManifest.xml
에 인터넷 권한을 추가해야한다.
<uses-permission android:name="android.permission.INTERNET"/>
에뮬레이터에서 로컬 호스트로 통신할 때는 다음 IP를 사용한다.
val BASE_URL = "http://10.0.2.2:${YOUR_PORT}"
# 사용법
Retrofit
을 사용하려면 먼저 인터페이스를 정의해야한다. HTTP 메소드에 따른 정의 방법은 다음과 같다.
# GET
HTTP GET 메소드의 경우 @GET
어노테이션을 사용한다. @Query
어노테이션으로 Query Parameter를 함께 전송할 수 있다.
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
interface PostApi {
// HTTP GET with query parameter
@GET("posts")
fun getPosts(@Query("page") page: Int, @Query("size") size: Int): Call<GetPostsResponse>
}
@Path
어노테이션으로 Path Variable도 설정할 수 있다.
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Path
interface PostApi {
// HTTP GET with path variable
@GET("post/{id}")
fun getPostById(@Path("id") id: Long): Call<GetPostByIdResponse>
}
# POST
HTTP POST 메소드의 경우 @POST
어노테이션을 사용한다. @Body
어노테이션으로 HTTP Body도 설정할 수 있다.
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
interface AuthApi {
@POST("/auth/login")
fun login(@Body request: LoginRequest): Call<LoginResponse>
}
import com.google.gson.annotations.SerializedName
data class LoginRequest (
@SerializedName("email") val email: String,
@SerializedName("password") val password: String
)
import com.google.gson.annotations.SerializedName
data class LoginResponse (
@SerializedName("user_id") val userId: Long,
@SerializedName("access_token") val accessToken: String,
@SerializedName("refresh_token") val refreshToken: String
)
HTTP Body에 실제로 포함되어 전송되는 데이터 형태는 다음과 같다.
{
"email": "paul@gmail.com",
"password": "1234"
}
@FormUrlEncoded
, @Field
어노테이션을 사용하면 application/x-www-form-urlencoded
타입의 데이터도 전송할 수 있다.
import retrofit2.http.POST
import retrofit2.http.FormUrlEncoded
import retrofit2.http.Field
interface PostApi {
@FormUrlEncoded
@POST("auth/login")
fun login(
@Field("email") email: String,
@Field("password") password: String
): Call<LoginResponse>
}
HTTP Body에 실제로 포함되어 전송되는 데이터 형태는 다음과 같다.
email=paul@gmail.com&password=1234
@Multipart
, @Part
어노테이션을 사용하면 multipart/form-data
타입의 데이터도 전송할 수 있다. 이 타입은 보통 이미지 업로드에 많이 사용된다.
import retrofit2.http.POST
import retrofit2.http.Multipart
import retrofit2.http.Part
interface PostApi {
@Multipart
@POST("post")
fun addPost(
@Part("writer_id") writerId: RequestBody,
@Part("title") title: RequestBody,
@Part("content") content: RequestBody,
@Part images: List<MultipartBody.Part>?
): Call<AddPostResponse>
}
# PATCH
HTTP PATCH 메소드의 경우 @PATCH
어노테이션을 사용한다.
import retrofit2.http.PATCH
interface PostApi {
@PATCH("post/{id}")
fun updatePostById(
@Path("id") id: Long,
@Body UpdatePostByIdRequest)
: Call<UpdatePostByIdResponse>
}
# DELETE
HTTP DELETE 메소드의 경우 @DELETE
어노테이션을 사용한다.
import retrofit2.http.DELETE
interface PostApi {
@DELETE("post/{id}")
fun deletePostById(@Path("id") id: Long)
}
# Header
@Headers
를 사용하여 헤더를 지정할 수 있다.
import retrofit2.http.Headers
@Headers({
"Accept: application/json",
"User-Agent: Your-App-Name",
"Cache-Control: max-age=640000"
})
interface PostApi {
@GET("post/{id}")
fun getPostById(
@Header("Authorization") String authorization,
@Path("id") id: Long
): Call<GetPostByIdResponse>
}
@Header
를 사용하여 헤더를 동적으로 추가할 수 있다. 파리미터가 null
이면 헤더가 추가되지 않는다.
import retrofit2.http.Header
interface PostApi {
@GET("post/{id}")
fun getPostById(
@Header("Authorization") String authorization,
@Path("id") id: Long
): Call<GetPostByIdResponse>
}
# 통신하기
다음과 같이 인터페이스를 정의했다고 가정하자.
import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.POST
interface AuthApi {
@POST("/auth/login")
fun login(@Body request: LoginRequest): Call<LoginResponse>
}
이 인터페이스로 다음과 같이 API 역할을 할 구현체를 생성한다.
val client = OkHttpClient.Builder().build()
val authApi = Retrofit.Builder()
.client(client)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(AuthApi::class.java)
이제 구현체로 통신을 할 수 있다. execute()
를 사용하면 현재 스레드에서 동기적으로 통신한다.
val response: LoginResponse = authApi.execute()
enqueue()
를 사용하면 별도의 작업 스레드에서 비동기적으로 통신한다. 따라서 통신이 끝났을 때 실행할 Callback을 함께 전달해야한다.
authApi.enqueue(object: Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
if(response.isSuccessful()) {
// 2xx Success
} else {
// 4xx, 5xx Error
val code = response.code()
val errorBody = response.errorBody()
}
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
// Connection Error
}
})