์๋ ํ์ธ์ ๐
์ค๋์ ์๋๋ก์ด๋์์ Rest ํต์ ์ ํ๋ ๋ฒ๊ณผ
Retrofit2์ ์ฌ์ฉ๋ฐฉ๋ฒ์ ๋ํด ๊ณต๋ถํ ๋ด์ฉ์ ํฌ์คํ ํ๋ ค ํฉ๋๋ค.

REST(’RE’presentational ‘S’tate ‘T’ransfer)๋?
HTTP URI(Uniform Resource Identifier)๋ฅผ ํตํด ์์(Resource)์ ๋ช ์ํ๊ณ , HTTP Method(POST, GET, PUT, DELETE)๋ฅผ ํตํด ํด๋น ์์์ ๋ํ CRUD Operation์ ์ ์ฉํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
๊ทธ๋ ๋ค๋ฉด Retrofit2์ด๋ ๋ฌด์์ผ๊น์?
Retrofit2 ๋?
OkHttp๋ฅผ ๋ง๋ Square์ฌ์์ ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก
Annotation์ ์ด์ฉํ์ฌ REST๋ฅผ ๋ค๋ฃฐ ์ ์์ต๋๋ค.
(OkHttp๋ฅผ ๊ธฐ๋ฐ์ผ๋ก reflect๋ฅผ ์ ์ฉํ์ฌ ๊ฐ๋ฐ)
๊ทธ๋ ๋ค๋ฉด Retrofit2๋ฅผ ์ฌ์ฉํ์ฌ Http ํต์ ์ ํ๋ ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค. ๐
1. ์ธํฐ๋ท ํต์ ์ ์ฌ์ฉํ ์ ์๋ permission์ manifestํ์ผ์ ์ถ๊ฐํด์ค๋๋ค.
//์ธํฐ๋ท ์ฌ์ฉ Permission ์ถ๊ฐ
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
2. ์ ๋ฌ๋ฐ์ JSON ๋ฐ์ดํฐ์ ๋ฐ๋ผ model class๋ฅผ ํ๋ ์์ฑํด์ค๋๋ค.
(์ ๊ฐ ์ฌ์ฉํ ๋ฐ์ดํฐ๋ ๊ณต๊ณต๋ฐ์ดํฐ ํฌํธ์ ์๋ 'ํด์์์ฅ ํผ์ก๋'๋ฅผ ๋ํ๋ด๋ ๋ฐ์ดํฐ๋ก JSON ๋ฐ์ดํฐ๋ ์๋์ ๊ฐ์ต๋๋ค.)
"Beach7": {
"etlDt": "202209021330",
"seqId": 33,
"poiNm": "๋ด์๋",
"uniqPop": 691,
"congestion": "1"
},
"Beach8": {
"etlDt": "202209021330",
"seqId": 34,
"poiNm": "๋ดํฌ๋ฆฌ",
"uniqPop": 1152,
"congestion": "1"
},
"Beach9": {
"etlDt": "202209021330",
"seqId": 35,
"poiNm": "์ผํฌ",
"uniqPop": 738,
"congestion": "1"
},
์์ ๊ฐ์ JSON์ ๋ด์ class๋ฅผ ํ๋ ๋ง๋ค์ด ์ค๋๋ค.
data class BeachCongestionModel(
val etlDt : String = "",
val seqId : Int = -1,
val poiNm : String = "",
val uniqPop : Int = -1,
var congestion : String = ""
)
class BeachCongestionList {
private lateinit var mBeachList : MutableList<BeachCongestionModel>
@SerializedName("Beach0")
val beach0 : BeachCongestionModel? = null
... // ๋ฐ์ ๋ ๋ง์ ๋ฐ์ดํฐ๊ฐ ์์ง๋ง ์ํ ํ๋ฉด์ ์ํ์ฌ ์๋ตํ๊ณ
// ์ด๋ฒ ํฌ์คํ
์์๋ ํด๋ณ์ ๋ณด ํ๋๋ง์ ๊ฐ์ ธ์ ์ฌ์ฉํ์์ต๋๋ค.
}
3. ๋ ํธ๋กํ์ ํตํด ํต์ ํ ์ ์๋ interface๋ฅผ ํ๋ ๋ง๋ค์ด ์ค๋๋ค.
interface BeachCongestionService {
@GET("/seantour_map/travel/getBeachCongestionApi.do")
suspend fun getBeachCongestion() : Response<BeachCongestionList>
companion object{
private lateinit var instance : BeachCongestionService
fun getInstance() : BeachCongestionService{
if(!::instance.isInitialized){
val retrofit = Retrofit.Builder()
.baseUrl(BEACH_CONGESTION_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
instance = retrofit.create(BeachCongestionService::class.java)
}
return instance
}
}
}
์์ ๊ฐ์ด annotaion์ ํตํด ํต์ ํ ๋ฐฉ์์ ์ ํ์ฌ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
(@GET ์ธ์ @POST , @DELETE , @PUT ๋ฑ์ด ์์ต๋๋ค.)
4. Retrofit2๋ฅผ ์ฌ์ฉํ์ฌ ํต์ ํ๋ ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
private fun startRetrofitButton() {
binding.progressBar.visibility = View.VISIBLE
val response = RetrofitService.getInstance().getBeachCongestion()
response.enqueue(object : Callback<BeachCongestionList>{
override fun onResponse( // ์ฑ๊ณต์์ ์คํ๋๋ ์ฝ๋
call: Call<BeachCongestionList>,
response: Response<BeachCongestionList>
) {
val model = response.body()?.beach0
CoroutineScope(Dispatchers.Main).launch {
with(binding){
beachName.text = model?.poiNm
congestionText.text = convertCongestion(model?.congestion as String)
timeText.text = model.etlDt
uniqPopText.text = model.uniqPop.toString()
seqIdText.text = model.uniqPop.toString()
}
Toast.makeText(this@MainActivity , "์ ์์ ์ผ๋ก ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ต๋๋ค." , Toast.LENGTH_SHORT).show()
binding.progressBar.visibility = View.GONE
}
}
override fun onFailure(call: Call<BeachCongestionList>, t: Throwable) {
// ์คํจ์์ ์คํ๋๋ ์ฝ๋
Toast.makeText(this@MainActivity , "์๋ฒ์์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋๋ฐ ์คํจํ์ต๋๋ค" , Toast.LENGTH_SHORT).show()
}
})
}
์ด๋ฒ ํฌ์คํ ์์๋ Response์์ Call๊ฐ์ฒด๋ฅผ ๋ฐํํ๋๋ก ๊ตฌํํ์๊ธฐ์ ์์ ๊ฐ์ด
enqueue()๋ฅผ ํตํด '๋น๋๊ธฐ'๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ์์ต๋๋ค. ์ด ์ธ์๋
execute()๋ฅผ ํตํด '๋๊ธฐ'๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ๋ ๊ฐ๋ฅํฉ๋๋ค.
excute()๋ก ๊ฐ์ ์์ ์ response.isSuccessful์ ํตํด ์ฑ๊ณต ์ฌ๋ถ๋ฅผ ํ์ธํ ์ ์์ผ๋ฉฐ
๊ทธ์ ๋ฐ๋ผ ๋์์ ๊ตฌํํด์ฃผ๋ฉด ๋ฉ๋๋ค. ๐
์ ์ฒด ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
<?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">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:indeterminateTint="#000"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone"
/>
<TextView
android:id="@+id/timeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/seqIdText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/timeText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="20dp"
/>
<TextView
android:id="@+id/beachName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/seqIdText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="20dp"
/>
<TextView
android:id="@+id/congestionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/beachName"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="20dp"
/>
<TextView
android:id="@+id/uniqPopText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/congestionText"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="20dp"
/>
<Button
android:id="@+id/startButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="์์"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.lee.retrofitexamplepj
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import com.lee.retrofitexamplepj.databinding.ActivityMainBinding
import com.lee.retrofitexamplepj.retrofit.model.BeachCongestionList
import com.lee.retrofitexamplepj.retrofit.model.BeachCongestionModel
import com.lee.retrofitexamplepj.retrofit.model.RetrofitService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater).also {
setContentView(it.root)
}
addListeners()
}
private fun addListeners() {
with(binding){
startButton.setOnClickListener {
startRetrofitButton()
}
}
}
private fun startRetrofitButton() {
binding.progressBar.visibility = View.VISIBLE
val response = RetrofitService.getInstance().getBeachCongestion()
response.enqueue(object : Callback<BeachCongestionList>{
override fun onResponse(
call: Call<BeachCongestionList>,
response: Response<BeachCongestionList>
) {
val model = response.body()?.beach0
CoroutineScope(Dispatchers.Main).launch {
with(binding){
beachName.text = model?.poiNm
congestionText.text = convertCongestion(model?.congestion as String)
timeText.text = model.etlDt
uniqPopText.text = model.uniqPop.toString()
seqIdText.text = model.uniqPop.toString()
}
Toast.makeText(this@MainActivity , "์ ์์ ์ผ๋ก ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ต๋๋ค." , Toast.LENGTH_SHORT).show()
binding.progressBar.visibility = View.GONE
}
}
override fun onFailure(call: Call<BeachCongestionList>, t: Throwable) {
Toast.makeText(this@MainActivity , "์๋ฒ์์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋๋ฐ ์คํจํ์ต๋๋ค" , Toast.LENGTH_SHORT).show()
}
})
}
/**
* ์์ ๊ฐ์ ๋ฐ๋ผ text๋ฅผ ๋ฌ๋ฆฌํด์ฃผ๋ ํจ์
**/
fun convertCongestion(congestion : String) : String {
return when(congestion){
"1" -> "๋ณดํต"
"2" -> "์กฐ๊ธ ํผ์ก"
"3" -> "ํผ์ก"
else -> "Error"
}
}
}
๋ง์ง๋ง์ ์คํํ๋ฉด์ผ๋ก ํฌ์คํ ์ ๋ง๋ฌด๋ฆฌํ๋๋ก ํ๊ฒ ์ต๋๋ค.
์ค๋๋ ์ฆ์ฝํ์ธ์ :)