Retrofit은 OkHttp 기반으로 만들어진 Http 통신 라이브러리다.
OkHttp 란?
OkHttp는 효율적인 Http 클라이언트이다.
- HTTP/2 지원을 통해 동일한 호스트에 대한 모든 요청이 소켓을 공유할 수 있다.
- 연결 풀링은 요청 대기 시간을 줄인다.(HTTP/2를 사용할 수 없는 경우)
- Transparent GZIP는 다운로드 크기를 줄인다.
- 응답 캐싱은 반복적인 요청으로부터 네트워크를 완전히 피한다.
자세한 내용은 아래 링크를 참조 바랍니다.
https://square.github.io/okhttp/
Retrofit2의 장점
- 빠른 성능
- 단순한 구현
- 가독성
사용 방법
인터넷 권한 설정
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.android">
<uses-permission android:name="android.permission.INTERNET" />
<application
...
</application>
</manifest>
인터넷 권한 설정: <uses-permission android:name="android.permission.INTERNET" />
서버와의 통신은 앱 외부와의 네트워크 통신이므로 인터넷 권한이 필요하다.
Gradle 의존성
build.gradle
dependencies {
// Retrofit 라이브러리
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
// Gson 변환기 라이브러리
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// Scalars 변환기 라이브러리
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
}
Retrofit 객체 생성
object NetworkManager {
fun getRetrofitInstance(): Retrofit{
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://localhost:8000")
.build()
}
//RxJava
fun getRetrofitInstance(): Retrofit{
return Retrofit.Builder()
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(url)
.build()
}
}
자주 전송할 헤더가 있다면
예를 들어 Token의 경우 서버에 데이터를 요청할 때마다 Header에 담아 인증 절차를 거쳐야 하므로 특별한 경우를 제외하고 항상 전송해줘야한다.
object NetworkManager {
fun getRetrofitInstance(): Retrofit{
return Retrofit.Builder()
.client(provideOkhttpClient(AppInterceptor()))
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://localhost:8000")
.build()
}
private fun provideOkhttpClient(interceptor: AppInterceptor): OkHttpClient =
OkHttpClient.Builder().run {
addInterceptor(interceptor)
build()
}
class AppInterceptor() : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain)
: Response = with(chain) {
val token = MyApplication.prefs.getString("token", "")
val newRequest = request().newBuilder()
.addHeader("Authorization", "jwt $token")
.build()
proceed(newRequest)
}
}
}
이렇게 하면 Interface에 Header 어노테이션으로 토큰을 따로 넣어주지 않아도 토큰이 헤더에 담겨 서버로 전송된다.
Interface 생성
HTTP 메서드 어노테이션
- @GET
- @POST
- @PUT
- @DELETE
interface ApiService {
@GET("/accounts/mydata/")
fun getMyData() : Call<ArrayList<UserData>>
@FormUrlEncoded
@POST("/accounts/signup/")
fun postSignup(@Field("username") username:String,
@Field("password") password:String): Call<signupDTO>
@PUT("/update/urls/")
fun update(): Call<Any>
@DELETE("/api/posts/{post_id}/like/")
fun unLikePost(@Path("post_id") post_id : Int) : Call<Any>
}
@Header
@GET("/accounts/mydata/")
fun getMyData(@Header("Authorization") token: String) : Call<ArrayList<UserData>>
보통 Header에 토큰을 담아 인증할 때 사용한다.
@Path
Url 사이에 값을 넣고 싶을 때
@DELETE("/api/posts/{post_id}/like/")
fun unLikePost(@Path("post_id") post_id : Int) : Call<Any>
post_id 가 1이라면 Url은 다음과 같다.
"http://localhost:8000/api/posts/1/like/"
@Field
Requst body에 데이터를 담아 보낼 때 사용한다.
@FormUrlEncoded
@POST("/accounts/signup/")
fun postSignup(@Field("username") username:String,
@Field("password") password:String): Single<signupDTO>
@FormUrlEncoded : 데이터를 URL 인코딩 형태로 만들어 전송할 때
사용한다.- @Field 의 데이터를 형식에 맞춰 requset body에 담아 보내야하기 때문에 형식에 맞추는 인코딩 작업을 해주어야한다.
@Query
Url에 데이터를 담아 보내며, 주로 검색할 때 많이 사용된다.
@GET("/accounts/search/user/")
fun searchUser(@Query("key") key:String?) : Call<ArrayList<UserData>>
key의 값이 "Kim" 이라면 Url은 다음과 같다.
"http://localhost:8000/accounts/search/user/?key=Kim"
@QueryMap
Query에 담을 데이터가 많을 때 사용한다.
@GET("/accounts/search/user/")
fun searchUser(@QueryMap values: Map<String,String>) : Call<ArrayList<UserData>>
@Body
데이터 객체를 전송할 때 사용한다.
data class LoginRequest(
val username : String,
val password : String
)
@POST("/accounts/signin")
fun requestLogin(@Body loginRequest: LoginRequest) : Call<LoginResponse>
DTO 생성
import com.google.gson.annotations.SerializedName
data class Comment(
@SerializedName("id") val id: Int,
@SerializedName("author") val author: User,
@SerializedName("message") val message: String,
@SerializedName("created_at") val created_at: String
)
{
data class User(
@SerializedName("id") val id: Int,
@SerializedName("username") val username: String,
@SerializedName("name") val name: String,
@SerializedName("avatar_url") val avatar_url: String
)
}
Json으로 직렬화되어 있는 데이터를 @SerializedName으로 역직렬화시켜 객체로 만들어준다.
+ 안드로이드 스튜디오에는 Json을 입력하면 Data Class로 변환시켜주는 플러그인이 있다. (Json To Kotlin Class)
MainActivity
Basic
private val retrofit = NetworkManager.getRetrofitInstance().create(ApiService::class.java)
retrofit.postSignup("username", "password")
.enqueue(object : Callback<T>{
override fun onResponse(call: Call<T>, response: Response<T>) {
//todo 성공처리
}
override fun onFailure(call: Call<T>, t: Throwable) {
//todo 실패처리
Log.d(TAG,t.toString())
}
})
RxJava
private val retrofit = NetworkManager.getRetrofitInstance().create(ApiService::class.java)
retrofit.postSignup("username", "password")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
//응답 성공
val data = it
},
{
//응답 실패
}
)
Coroutine
coroutine =
try {
CoroutineScope(Dispatchers.IO).launch {
val response = retrofit.postSignup("username", "password")
withContext(Dispatchers.Main){
if(response.isSuccessful){
//TODO
}
}
}
}
catch (e:Exception){ Job() }
파일 전송 방법
@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)
'안드로이드 > Network' 카테고리의 다른 글
[서버 통신] - Http 프로토콜 (0) | 2023.11.03 |
---|