#1 ์ด์ ๊ธ
[Android] Retrofit - ๋ฐฐ๊ฒฝ๊ณผ ๊ตฌ์กฐ
#1 Restrofit์ ๋ฐฐ๊ฒฝ#1-1 REST API REST API (REpresentational State Transfer Application Programming Interface)#1 ๋ฌด์(What)์ ๋ํ API์ธ๊ฐ?#1-1 ๊ฐ์REST(REpresentational State Transfer) ๋๋ RESTful API๋ ์๋ฒ์ ์์(Resource)์ ๋๊ณ
kenel.tistory.com
์ด์ ๊ธ์์ Retrofit์ ๋ฐฐ๊ฒฝ๊ณผ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ดค๋ค. ์ด๋ฒ์ ์ค์ Retrofit์ ์ฌ์ฉํ ์ฑ์ ๋ง๋ค์ด๋ณธ๋ค.
#2 ํต์ ํ ์๋ฒ
JSONPlaceholder - Free Fake REST API
{JSON} Placeholder Free fake and reliable API for testing and prototyping. Powered by JSON Server + LowDB. Serving ~3 billion requests each month.
jsonplaceholder.typicode.com
์ ์ฌ์ดํธ๋ ์ฑ ํ๋กํ ํ์ดํ์ด๋ ๊ณต๋ถ๋ฅผ ํ๋ ์ฌ๋๋ค์๊ฒ ์ ๊ณตํ๋ ๋ชฉ์ ์ผ๋ก, JSON ํ์์ผ๋ก ๋ ์์(Resource)์ ์ ๊ณตํ๋ค. ๋ณธ ๊ฒ์๊ธ์์๋ ์ด URL์ ๋ฐ์ดํฐ๋ฅผ GETํ๋ ๊ฑธ ๋ชฉํ๋ก ์ผ๋๋ค.
#3 Retrofit ์ฌ์ฉ์ ์ํ ํ๊ฒฝ ์ค์
#3-1 AndroidManifest.xml์์ ๊ถํ ํ์ฉ
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
...
</application>
</manifest>
์ธํฐ๋ท, ๋คํธ์ํฌ ์ํ, ์์ดํ์ด ์ํ์ ์ ๊ทผํ ์ ์๋ ๊ถํ์ ๋ฃ๋๋ค.
#3-2 Retrofit ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ (๋ชจ๋ ์์ค build.gradle)
plugins {
...
}
android {
...
}
dependencies {
...
// Retrofit
val retrofitVersion = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.retrofit2:converter-gson:$retrofitVersion")
}
Retrofit ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ชจ๋ ์์ค build.gradle์ ์ถ๊ฐํ๋ค. ์ฌ๊ธฐ(๊ณต์ ์ฌ์ดํธ)์์ Retrofit ์ฌ์ฉ์ ์ํด ์ถ๊ฐํด์ผํ๋ dependencies ๋ชฉ๋ก์ ํ์ธํ ์ ์๋ค. GSON Converter ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ถ๊ฐํ๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ JSON์ Kotlin์ ๋ฐ์ดํฐ ํ์์ผ๋ก ํ์ฑํ๋ ์ญํ ์ ์ํํ๋ค.
๋ง์ฝ, ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ๋ก ๋ณด๋ด๋ ๋ฐ์ดํฐ๊ฐ JSON ํ์์ด ์๋๋ผ XML์ด๋ผ๋ฉด, ์ ๊ณต์ ๋ฌธ์์ ์ค๋ช
๋๋ก GSON ๋์ Simple XML Converter ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋ค.
#3-3 Coroutine, LiveData ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ (๋ชจ๋ ์์ค build.gradle)
plugins {
...
}
android {
...
}
dependencies {
...
// Retrofit
...
// Coroutines
val coroutinesVersion = "1.6.4"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
// ViewModel, LiveData
val lifecycleVersion = "2.5.1"
implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion")
}
์๋ฒ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ ์์
์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์งํํ ๊ฒ์ด๋ฏ๋ก, ์ฝ๋ฃจํด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฐ์์จ ๋ฐ์ดํฐ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ํ์ํ๊ธฐ ์ํ ViewModel ๋ฐ LiveData ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ถ๊ฐํ๋ค.
๋ง์ง๋ง์ผ๋ก, ์๋๋ก์ด๋ ์คํ๋์ค ์๋จ์ ๋จ๋ 'Sync Now' ๋ฒํผ์ ํด๋ฆญํ๋ค.
#4 Data Class ๋ง๋ค๊ธฐ
#4-1 AlbumsItem.kt
package com.example.retrofitbasics
import com.google.gson.annotations.SerializedName
data class AlbumsItem(
@SerializedName("userId")
val userId: Int,
@SerializedName("id")
val id: Int,
@SerializedName("title")
val title: String,
)
/*
{
"userId": 1,
"id": 1,
"title": "Album Title"
}
โ ์ด ํด๋์ค๋ ์์ ๊ฐ์ JSON ๋ฐ์ดํฐ๋ฅผ, ์๋์ ๊ฐ์ Kotlin ๊ฐ์ฒด๋ก ๋ฐ๊พผ๋ค. โ
val myAlbum = AlbumsItem(id = 1, title = "Album Title", userId = 1)
*/
GSON ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ด๋
ธํ
์ด์
์ธ @SerializedName()์ ํ์ฉํด, GSON ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋ฒ๊ฐ ๋ณด๋ด์ค JSON์ ํ์ฑํ ๋ ์ฐธ์กฐํ Key ์ ๋ณด๋ฅผ ๋ฃ๋๋ค.
#4-2 Albums.kt
// package com.example.retrofitbasics
class Albums : ArrayList<AlbumsItem>()
AlbumsItem๋ฅผ ArrayList์ ๋ฃ์ ๊ฐ์ฒด๋ค. ์ด๋ ๊ฒ ๊ตณ์ด ๋๋๋ ์ด์ ๋, ์๋ฒ๋ก๋ถํฐ ๋ฐ์์ฌ JSON ์์ดํ
ํ๋์ @SerializedName()๊ฐ ์ฐ์ธ #4-1์ data class ํ๋๊ฐ ๋งค์นญ๋๊ฒ ๋ง๋๋ ๊ฒ, ์ฆ JSON ๊ฐ์ฒด ํ๋์ ์ฝํ๋ฆฐ ๊ฐ์ฒด๊ฐ 1๋1๋ก ๋งค์นญ๋๊ฒ ๋ง๋๋ ๊ฒ์ด ์ง๊ด์ ์ด๊ธฐ ๋๋ฌธ์ด๋ค. #4-1๊ณผ #4-2๋ฅผ ํ๋๋ก ํฉ์น๋ฉด ์คํ๋ ค ๋ณต์กํด์ง ๊ฒ์ด๋ค. ํ์ฌํผ ์์ผ๋ก Albums ํด๋์ค๋ ์ดํ๋ก AlbumsItem์ ํ๋ํ๋ ๋ด๋ ๊ทธ๋ฆ์ผ๋ก ๊ธฐ๋ฅํ๊ฒ ๋ ๊ฒ์ด๋ค.
#4-3 (์ฌ๋ด) kapt ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ์ด๋ ธํ ์ด์ (@)์ด ์กด์ฌํ๋ ์ด์
์ด๋ ธํ ์ด์ (@)์ ์ฒ๋ฆฌํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ kapt๋ฅผ build.gradle์ dependencies { ... }์ ์ถ๊ฐํ์ง ์์์์๋ #4-1์ ์ฝ๋๊ฐ ์๋ฌ์์ด ์ปดํ์ผ์ด ๋๋ ์ด์ ๊ฐ ๋ญ๊น?
kapt๋ ์ด๋
ธํ
์ด์
์ ๊ทธ์ '์ฝ๊ฒ ํด์ฃผ๋' ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋๊ธฐ ๋๋ฌธ์ด๋ค. kapt ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฐ์
์์ด ์์ค ์ฝ๋์ ์ด๋
ธํ
์ด์
(@)์ด ์๋ค๊ณ ์ปดํ์ผ์ด ๋์ง ์๋ ๊ฒ์ด ์๋๋ค. kapt์ ์ง์ง ์ฉ๋๋, ์ปดํ์ผ ํ์์ ํน์ ์ฝ๋๋ฅผ ์์ฑํ๊ฑฐ๋ ๊ฒ์ฆํ๋ ๋ฐ์ ์๋ค. ์๋ฅผ ๋ค์ด, Room์์ ์ฐ์ด๋ ์ด๋
ธํ
์ด์
๋ค์, ์ปดํ์ผ ๋์ Sqlite ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ด๋ จ๋ ์ฝ๋๋ฅผ ์์์ ์ผ๋ก ์์ฑํ๊ฑฐ๋ Room์ ๊ตฌ์กฐ๊ฐ ์ ๋๋ก ์ง์ผ์ง๋ ์ง ํ์ธํ๋ ์ฉ๋๋ก ์ฌ์ฉ๋๋ค. ์ด ๋ kapt๊ฐ ๊ทธ ๊ณผ์ ์ ์ํํ๋ ๊ฒ์ด๋ค.
๋ฐ๋ฉด, ์ ์ฝ๋์ ์ด๋
ธํ
์ด์
์ธ @SerializedName()์ ์ปดํ์ผ ๋ ๋ญ๊ฐ ํ์ง ์๋๋ค. ๋์ ๋ฐํ์ ์์ ์, @SerializedName()์ ์ ๋ณด๋ฅผ ์ฐธ๊ณ ํ์ฌ JSON์ ํ์ฑ์ ํ์ฉํ๋ค.
#5 Service Interface ๋ง๋ค๊ธฐ
// package com.example.retrofitbasics
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Query
interface AlbumService {
@GET("/albums")
suspend fun getAlbums(): Response<Albums>
@GET("/albums")
suspend fun getSortedAlbums(@Query("userId") userId: Int): Response<Albums>
}
HTTP ๋ฉ์๋์ ํจ๊ป ๋ค๋ฃฐ ์์์ ์์น(์ด ๊ฒ์๊ธ์ #2-2 ์ฐธ์กฐ)๋ฅผ ๋ฉ์๋์ ํํ๋ก ํ๋์ฉ ๊ธฐ์ ํ๋ค. ์ด ํจ์๋ฅผ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์คํ์ํฌ ๊ฒ์ด๋ฏ๋ก suspend ํค์๋๋ฅผ ๋ถ์ธ๋ค. ๋ฐํํ์ Response<>๋ก, Retrofit์ ํญ์ Response<>์ ํํ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ค. Query Parameter๊ฐ ์๋ ๊ฒฝ์ฐ getSortedAlbums()์ ๊ฐ์ ํ์์ผ๋ก ๋ฉ์๋๋ฅผ ์์ฑํ๋ค.
#6 Retrofit Instance ๋ง๋ค๊ธฐ
// package com.example.retrofitbasics
import com.google.gson.GsonBuilder
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class RetrofitInstance {
companion object {
val BASE_URL = "https://jsonplaceholder.typicode.com/"
fun getRetrofitInstance(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
}
}
}
Retrofit์ ์ธ์คํด์ค๋ Retrofit.Builder()๋ผ๋ ๋น๋ ํด๋์ค๋ฅผ ํตํด ์์ฑ๋๋ค. Service Interface์์ ๊ธฐ์ ํ๋ '๋ค๋ฃฐ ์์์ ์์น'์ ๋ํด ๊ทธ ๊ธฐ๋ฐ์ด ๋๋ ๊ธฐ๋ณธ URL๊ณผ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ JSON ๋ฑ์ ํ์ผ ํ์์ ์ฝํ๋ฆฐ ๊ฐ์ฒด๋ก ๋ฐ๊ฟ์ฃผ๋ Converter๋ฅผ Retrofit.Builder()์ ๋ฌ์์ค ๋ค์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค.
#7 Service Class ๋ง๋ค๊ธฐ
// package com.example.retrofitbasics
...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
val retrofitService = RetrofitInstance.getRetrofitInstance().create(AlbumService::class.java)
}
}
#6์์ ์์ฑ๋ Retrofit์ ์ธ์คํด์ค์์ Service Interface๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋ Retrofit.create() ๋ฉ์๋๋ฅผ ์ํํ๋ค. ์ด๋ฅผ ํตํด Service Interface์ ๊ตฌํ์ฒด(Service Class)๋ฅผ ๋ง๋ ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๊ตฌํ์ฒด์ ๋ฉ์๋๋ค(= Service Interface์์ ์ ์ํ๋ ๋ฉ์๋๋ค)๋ก ์๋ฒ์ ํต์ ํ ๊ฒ์ด๋ค.
#8 ์๋ฒ๋ก๋ถํฐ ๋ฐ์์จ ๋ฐ์ดํฐ ํ๋ฉด์ ํ์ํ๊ธฐ
#8-1 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
๊ฐ๋จํ๊ฒ ๊ตฌ์ฑํ๋ค. ScrollView ํ๋ ๊ทธ๋ฆฌ๊ณ ๊ทธ ์์ TextView ํ๋๋ฅผ ๋ฐฐ์นํ๋ค.
#8-2 MainAcitivity.kt
...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val retrofitService =
RetrofitInstance.getRetrofitInstance().create(AlbumService::class.java)
val textView = findViewById<TextView>(R.id.textView)
val responseLiveData: LiveData<Response<Albums>> = liveData {
val response1 = retrofitService.getAlbums()
val response2 = retrofitService.getSortedAlbums(7)
emit(response1)
}
responseLiveData.observe(this, Observer {
appendListOnTextView(textView, it)
})
}
private fun appendListOnTextView(textView: TextView, list: Response<Albums>) {
val albumsList = list.body()?.listIterator()
if (albumsList != null) {
while (albumsList.hasNext()) {
val albumsItem = albumsList.next()
val result =
"User id : ${albumsItem.userId}" + "\n" +
"Album id : ${albumsItem.id}" + "\n" +
"Album Title : ${albumsItem.title}" + "\n\n\n"
textView.append(result)
}
}
}
}
์๋ฒ๋ก๋ถํฐ ๋ฐ์ ๋ฐ์ดํฐ Response<Albums>์ LiveData๋ก ์ ์ธํ๋ค. LiveData Builder๋ ์ฌ์ฉํ๋ค. ํด๋น LiveData์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ๊ฑฐ๋ ๋ณํํ ๋๋ง๋ค ๋ฐ์ํ๋ ์์
์ ๊ธฐ์ ํ๊ธฐ ์ํ LiveData.observe() ๋ฉ์๋๋ ์ ์ํ๋ค. response1์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ, response2๋ userId๊ฐ 7์ธ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค. ์ฌ๊ธฐ์๋ ์ด๋ ๊ฒ userId๋ฅผ ํ๋ ์ฝ๋ฉํ์ง๋ง, ์ด์ด์ง๋ ๊ธ(#12 ์ฐธ์กฐ)์์๋ ์ฌ์ฉ์๊ฐ ์ํ๋ userId๋ฅผ ์์ฒญํ๊ณ ๊ทธ์ ์๋ตํ ์ ์๊ฒ ๋ง๋ค์ด๋ณธ๋ค.
#9 ์๋ ํ์ธ

#10 ์์ฝ
Retrofit์ ํด๋ผ์ด์ธํธ-์๋ฒ ๊ฐ ํต์ ์ ๊ฐํธํ๋ฉฐ ์ ์ฐํ ๊ตฌํํ๋ ๋๊ตฌ๋ค.
#11 ์์ฑ๋ ์ฑ
android-practice/retrofit/RetrofitBasics at master ยท Kanmanemone/android-practice
Contribute to Kanmanemone/android-practice development by creating an account on GitHub.
github.com
#12 ์ด์ด์ง๋ ๊ธ
#12-1 userId ์ํํธ ์ฝ๋ฉ ๋ฐ ํ๋ก์ ํธ๋ฅผ MVVM ๊ตฌ์กฐ๋ก ๊ตฌํ
[Android] Retrofit - MVVM ๊ตฌ์กฐ
#1 ์ด์ ๊ธ [Android] Retrofit - ๊ธฐ์ด#1 ์ด์ ๊ธ [Android] Retrofit - ๋ฐฐ๊ฒฝ๊ณผ ๊ตฌ์กฐ#1 Restrofit์ ๋ฐฐ๊ฒฝ#1-1 REST API REST API (REpresentational State Transfer Application Programming Interface)#1 ๋ฌด์(What)์ ๋ํ API์ธ๊ฐ?#1-1 ๊ฐ์REST(
kenel.tistory.com
์ด์ด์ง๋ ๊ธ์์ userId๋ฅผ ํ๋ ์ฝ๋ฉํ๋ ๋์ , ์ฌ์ฉ์์ ์ ๋ ฅ์ ๋ฐ๋ผ ๋ณํ ์ ์๋๋ก ๋ง๋ ๋ค. ๋, ํ๋ ๊น์ MVVM์ ์ธ ๊ตฌ์กฐ๋ก ๋ง๋ค์ด๋ณธ๋ค.
#12-2 Logging๊ณผ Timeout ์ค์
[Android] Retrofit - Logging, Timeout ๊ด๋ฆฌ (Interceptor)
#1 ์ด์ ๊ธ [Android] Retrofit - ๊ธฐ์ด#1 ์ด์ ๊ธ [Android] Retrofit - ๋ฐฐ๊ฒฝ๊ณผ ๊ตฌ์กฐ#1 Restrofit์ ๋ฐฐ๊ฒฝ#1-1 REST API REST API (REpresentational State Transfer Application Programming Interface)#1 ๋ฌด์(What)์ ๋ํ API์ธ๊ฐ?#1-1 ๊ฐ์RE
kenel.tistory.com
Retrofit Instance๊ฐ ํต์ ๊ธฐ๋ก(Log)์ ๋จ๊ธฐ๊ฒ ๋ง๋ ๋ค. ๋, Retrofit Instance๊ฐ ์๋ฒ-ํด๋ผ์ด์ธํธ ๊ฐ ํต์ ์ ์ฑ๊ณต ๋๋ ์คํจ(Timeout)๋ก ํ๋จํ๋ ๊ท์น์ ๋ฐ๊ฟ๋ณธ๋ค.
#12-3 Post ์์ฒญ ๊ตฌํ
[Android] Retrofit - Post
#1 ์ด์ ๊ธ [Android] Retrofit - ๊ธฐ์ด#1 ์ด์ ๊ธ [Android] Retrofit - ๋ฐฐ๊ฒฝ๊ณผ ๊ตฌ์กฐ#1 Restrofit์ ๋ฐฐ๊ฒฝ#1-1 REST API REST API (REpresentational State Transfer Application Programming Interface)#1 ๋ฌด์(What)์ ๋ํ API์ธ๊ฐ?#1-1 ๊ฐ์RE
kenel.tistory.com
์๋ฒ์ Post ์์ฒญ์ ํด๋ณธ๋ค. (์ฐธ์กฐ: REST API)
'๊นจ์ ๊ฐ๋ ๐ > Android' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] Retrofit - Logging, Timeout ๊ด๋ฆฌ (Interceptor) (0) | 2024.06.11 |
---|---|
[Android] Retrofit - MVVM ๊ตฌ์กฐ (0) | 2024.06.05 |
[Android] Retrofit - ๋ฐฐ๊ฒฝ๊ณผ ๊ตฌ์กฐ (0) | 2024.05.28 |
[Android] Room - AutoMigrationSpec (0) | 2024.05.08 |
[Android] Room - AutoMigration ๊ธฐ์ด (0) | 2024.05.06 |