Retrofit은 OkHttp 기반의 라이브러리로 OkHttp보다 간편하게 사용할 수 있고, 가독성 높은 구조로 네트워크 통신을 가능하게 해준다.
특징
- API 인터페이스 : 요청과 응답을 인터페이스로 쉽게 구현할 수 있다.
- 어노테이션 : HTTP Method와 Header 등 다양한 작업을 어노테이션으로 정의할 수 있다.
- 비동기 처리 : Call 객체 및 Coroutine, RxJava와 같이 비동기 처리 가능
- 직렬화/역직렬화 : Converter Factory 등록으로 데이터를 JSON으로 직렬화 및 역직렬화를 손쉽게 변환할 수 있다.
- OkHttp Interceptor 추가 작업
- 다른 HTTP 라이브러리에 비해 속도가 빠르다.
1️⃣ Retrofit Builder
class RetrofitManager {
fun getRetrofit(){
Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(Converter.Factory)
.client(OkHttpClient)
.build()
}
}
기본 URL 등록
baseUrl에 메인 URL(Host, Port) 등록
class RetrofitManager {
fun getRetrofit(){
Retrofit.Builder()
.baseUrl(URL)
.build()
}
companion object{
private const val URL = "https://api.github.com/"
}
}
ConverterFactory 등록
- GsonConverterFactory
- Kotlin Serialization
- Moshi
OkHttp Interceptor
API Key와 같이 매번 헤더에 전송해야 하는 경우와 로깅 등 추가적인 작업을 원하는 경우 OkHttp의 Interceptor를 Retrofit과 같이 사용할 수 있다.
class RetrofitManager {
fun getRetrofit(){
Retrofit.Builder()
.baseUrl(URL)
.client(provideOkhttpClient(HeaderInterceptor()))
.build()
}
private fun provideOkhttpClient(interceptor: HeaderInterceptor): OkHttpClient =
OkHttpClient.Builder()
.addInterceptor(interceptor)
.build()
class HeaderInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain)
: Response = with(chain) {
val newRequest = request().newBuilder()
.addHeader("Authorization", "Bearer ${BuildConfig.GIT_TOKEN}")
.build()
proceed(newRequest)
}
}
companion object{
private const val URL = "https://api.github.com/"
}
}
2️⃣ API 인터페이스
Retrofit은 요청과 응답을 아래와 같이 인터페이스로 간단하고 가독성 있게 구현할 수 있으며, 다양한 어노테이션으로 요청 형식을 정의할 수 있다.
@GET
interface ApiService {
@GET("repos/OWNER/REPO/issues")
suspend fun getIssues(): Response<List<Issues>>
}
@POST
interface ApiService {
@FormUrlEncoded
@POST("repos/OWNER/REPO/issues")
suspend fun postFieldIsuue(
@Field("title") title: String,
@Field("body") body: String?,
@Field("assignees") assignees: List<String>
): Response<Issues>
@POST("repos/OWNER/REPO/issues")
suspend fun postBodyIssue(
@Body postIssueData: PostIssueData
): Response<Issues>
}
- @Body
데이터를 객체에 담아 전송 - @Field
Body 데이터를 각각 전송할 때 - @FormUrlEncoded
데이터를 URL 인코딩 형태로 만들어 전송할 때 사용
(@Field 의 데이터를 형식에 맞춰 requset body에 담아 보내야하기 때문에 형식에 맞추는 인코딩 작업을 해주어야한다.)
@PUT
interface ApiService {
@PUT("repos/OWNER/REPO/issues/{issue_number}/lock")
suspend fun lockIssue(){
@Path("issue_number") number: Int,
@Body lockReason: LockReason = LockReason()
}
}
data class LockReason{
val lock_reason: String = "off-topic"
}
- @Path
URL에 들어가는 값을 지정한다.
ex) https://api.github.com/repos/OWNER/REPO/issues/1/lock
@PATCH
일부만 수정
interface ApiService {
@PATCH("repos/OWNER/REPO/issues/{issue_number}")
suspend fun closeIssue(
@Path("issue_number") number: Int,
@Body closeIssue: CloseIssue = CloseIssue()
): Response<Issues>
}
data class CloseIssue(
val state: String = "closed"
)
@DELETE
interface ApiService {
@DELETE("repos/OWNER/REPO/issues/{issue_number}/lock")
suspend fun unlockIssue(){
@Path("issue_number") number: Int
}
}
기타 어노테이션
- @Quary
interface ApiService {
@GET("repos/OWNER/REPO/issues/")
fun queryIssue(
@Query("key") query: String = "value"
): Response<Issue>
}
URL의 Quary Parameter로 형식은 아래와 같다.
ex) https://api.github.com/repos/OWNER/REPO/issues/?key=value
- @QuaryMap
interface ApiService {
@GET("repos/OWNER/REPO/issues/")
fun queryIssue(
@QueryMap values: Map<String,String>
): Response<Issue>
}
Query가 여러 개인 경우 사용
- @Header
interface ApiService {
@GET("repos/OWNER/REPO/issues/")
fun queryIssue(
@Header("Authorization") token: String,
@QueryMap values: Map<String,String>
): Response<Issue>
}
파일 전송
@Multipart
@POST("/api/posts/")
fun writePost(
@Part photo : MultipartBody.Part,
@Part("caption") caption : RequestBody?, // 사진과 함께 보내는 추가적인 데이터
@Part("location") location : RequestBody
):Call<Any>
val photofile = File(경로)
val photo = RequestBody.create("image/jpeg".toMediaTypeOrNull(), photofile)
val filePart : MultipartBody.Part = MultipartBody.Part.createFormData("photo","photo.jpg", photo)
val caption = RequestBody.create(
"text/plain".toMediaTypeOrNull(),
text
)
val location = RequestBody.create(
"text/plain".toMediaTypeOrNull(),
text
)
retrofit.writePost(filePart, caption, location)
3️⃣ Data Transfer Object
data class Issues(
@SerializedName("id") val id: Long?,
@SerializedName("node_id") val nodeId: String?,
@SerializedName("number") val number: Int?,
@SerializedName("title") val title: String?,
@SerializedName("body") val body: String?,
@SerializedName("user") val user: User,
@SerializedName("labels") val labels: List<Label>,
@SerializedName("created_at") val createdAt: String?,
@SerializedName("closed_at") val closedAt: String?,
@SerializedName("updated_at") val updatedAt: String?,
@SerializedName("comments") val comments: Int?,
@SerializedName("assignee") val assignee: Assignee?,
@SerializedName("assignees") val assignees: List<Assignee>,
@SerializedName("locked") val locked: Boolean?,
@SerializedName("state") val state: String?,
@SerializedName("milestone") val milestone: Milestone?,
)
- @SerializedName
Response JSON 객체의 Key 이름을 작성
'안드로이드 > Network' 카테고리의 다른 글
OkHttp (0) | 2025.05.13 |
---|---|
HttpUrlConnection (0) | 2025.05.12 |
[안드로이드] 서버 통신 Retrofit2 (0) | 2023.11.06 |