๋ชฉ์ฐจ
android compose์์ ๋ ์จ api๋ฅผ ์ฐ๋ํ๋ ๊ณผ์ ์ ๊ธฐ๋กํด๋ณด์๋ค.
1.JSON response์ Data Class๋ฅผ ์ ์ํด์ค๋ค.
<?xml version="1.0" encoding="UTF-8"?>
<response>
<header>
<resultCode>0</resultCode>
<resultMsg>NORMAL_SERVICE</resultMsg>
</header>
<body>
<dataType>XML</dataType>
<items>
<item>
<baseDate>20210628</baseDate>
<baseTime>0500</baseTime>
<category>TMP</category>
<fcstDate>20210628</fcstDate>
<fcstTime>0600</fcstTime>
<fcstValue>21</fcstValue>
<nx>55</nx>
<ny>127</ny>
</item>
</items>
<numOfRows>10</numOfRows>
<pageNo>1</pageNo>
<totalCount>742</totalCount>
</body>
</response>
๋ ์จ ๋ฐ์ดํฐ์ response๋ ์์ ๊ฐ์๋ค. ๋ฐ๋ผ์ ์ด๋ฅผ ์ํด ์๋์ ๊ฐ์ด dataClass๋ฅผ ์ ์ํด์ฃผ์๋ค.
๋จผ์ ํฐ ํ์ data class๋ฅผ ์ ์ํด์ฃผ๊ณ ๋ด๋ถ์ ์์ data class๋ฅผ ์ ์ํด์ฃผ๋ฉด ๋๋ค!
๊ถ๊ธํ ์ : ์ด ๋ numOfRows์ pageNo์ totalCount์ ๋ํด์๋ ๋ฐ๋ก ์ ์ํด์ฃผ์ง ์์๋ ์ ๋์ํ๋ ์ด์ ๊ฐ ๊ถ๊ธํ๋ค.
package com.example.sejong2washertimer.data
data class Weather(
val response: Response
) {
data class Response(
val header : Header,
val body : Body
) {
data class Header(
val resultCode : Int,
val resultMessage : String,
)
data class Body(
val dataType : String,
val items : Items
){
data class Items(
val item : List<Item>
){
data class Item(
val baseDate : Int,
val baseTime : Int,
val category: String,
val fsctDate : Int,
val fcstTime : Int,
val fcstValue : Int,
val nx : Int,
val ny : Int
)
}
}
}
}
2. Retrofit interface๋ฅผ ๋ง๋ ๋ค : @Query์ ์ฉ๋
- URL endpoint๋ฅผ ์ด์ฉํ๋ค.
- ๋น๋๊ธฐ ํจ์
interface WeatherApi {
@GET("getVilageFcst?serviceKey=$API_KEY")
suspend fun getWeather(
@Query("dataType") dataType : String,
@Query("numOfRows") numOfRows : Int,
@Query("pageNo") pageNo : Int,
@Query("base_date") baseDate : Int,
@Query("base_time") baseTime : Int,
@Query("nx") nx : String,
@Query("ny") ny : String
) : Response<Weather>
}
๊ถ๊ธํ ์ : @Query๋ ์ ์ ์ด์ค์ผํ ๊น?
๊ทธ๋์ @Query๋ถ๋ถ์ ์ฃผ์ ์ฒ๋ฆฌ ํ ํ ์คํํด๋ณด์๋ค. ๊ฒฐ๊ณผ๋ ์๋์ฒ๋ผ None of the following functions can be called with the arguments supplied ์๋ฌ๊ฐ ๋ด๋ค.
์์ํด๋ณธ Query์ ๊ธฐ๋ฅ์ @Query์ ์ ์ด์ฃผ๋ ๋ณ์๋ฅผ ํจ์ ํธ์ถ ์ ์ธ์๋ก ๋๊ฒจ์ค์ผํ ๋ ์ฌ์ฉํ๋ ๊ฒ ๊ฐ๋ค!
์ฐพ์๋ณธ Query์ ๊ธฐ๋ฅ์ ์ด ๋งค๊ฐ๋ณ์๋ค์ด HTTP ์์ฒญ์ ์ฟผ๋ฆฌ ์คํธ๋ง์ผ๋ก ๋งคํ๋๋๋ก ํ๋ ๊ฒ์ด๋ค
์ฆ ์ด ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ์ ํ ๋ ํจ์์ ์ ๋ฌํ๋ ๊ฐ๋ค์ด HTTP ์์ฒญ์ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ก ๋ค์ด๊ฐ๋ค.
์๋์ ์๋ฌ๋ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์๊ฐ ํ์ํ๋ฐ ์ ๋ฌ๋์ง ์์์์ ๋ํ๋ธ๋ค.
3. Retrofit ๊ฐ์ฒด๋ฅผ ์ป๊ธฐ ์ํ ํ์ผ
- retrofit ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ํจ์ ์์ฑ
object RetrofitHelper {
fun provideRetrofit(): Retrofit =
Retrofit.Builder()
.baseUrl(WEATHER_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
fun provideWeatherApi(retrofit: Retrofit): WeatherApi =
retrofit.create(WeatherApi::class.java)
}
- provideRetrofit ํจ์๋ Retrofit ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ตฌ์ฑํ๋ค.
4. ๋ ํธ๋กํ ๊ฐ์ฒด์ ๋ ํธ๋กํ ์ธํฐํ์ด์ค๋ฅผ ์ฐ๊ฒฐํด์ค๋ค.
- provideWeatherApi ํจ์๋ Retrofit ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ WeatherApi ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ค๋๋ค .
5. Repository ์์ฑํด์ฃผ๊ธฐ
๋ฐ์ดํฐ ๋ช ๋ น์ด๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ repository class๋ฅผ ์์ฑํด์ค๋ค.
@Singleton
class WeatherRepository @Inject constructor(
private val weatherApi: WeatherApi
) {
suspend fun getWeather(
dataType:String,
numOfRows:Int,
pageNo:Int,
baseDate:Int,
baseTime:Int,
nx:String,
ny:String
) : Response<Weather> {
return weatherApi.getWeather(dataType,numOfRows,pageNo,baseDate,baseTime,nx,ny)
}
}
6. ViewModel ๊ตฌํํ๊ธฐ
composable UI๋ฅผ ์ํ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ viewModel์ ๊ตฌํํด์ค๋ค.
@HiltViewModel
class WeatherViewModel @Inject constructor(
private val repository : WeatherRepository):ViewModel() {
private val _weatherResponse: MutableLiveData<Response<Weather>> = MutableLiveData()
private val _temperature : MutableState<String> = mutableStateOf("")
@DrawableRes private var _weatherImageResource : MutableState<Int> = mutableIntStateOf(R.drawable.sunny)
val temperature :MutableState<String> get() = _temperature
val weatherImageResource : MutableState<Int> get() = _weatherImageResource
fun makeWeatherRequest(
dataType: String, numOfRows: Int, pageNo: Int,
baseDate: Int, baseTime: Int, nx: String, ny: String
) {
viewModelScope.launch {
try {
val response =
repository.getWeather(dataType, numOfRows, pageNo, baseDate, baseTime, nx, ny)
_weatherResponse.value = response
updateTemperature(response.body()?.response?.body?.items?.item)
}
catch (e:Exception){
Log.d("๋ ์จ", e.toString())
}
}
}
private fun updateTemperature(items: List<Weather.Response.Item>?) {
if ((items != null) && items.isNotEmpty()) {
val filteredItems = items.filter {
it.category in listOf("TMP")
}
for (i in filteredItems) {
// ํ์ฌ ๊ธฐ์จ
if (i.category == "TMP") {
val temperature = i.fcstValue + "°C"
_temperature.value = temperature
}
//๊ฐ์ ์ฒ๋ฆฌ
if (i.category == "PTY") {
if (i.fcstValue == "1" || i.fcstValue == "4") {
rainStateText = "๋น"
_weatherImageResource.value = R.drawable.rainy
}
if (i.fcstValue == "2") {
rainStateText = "๋น/๋"
_weatherImageResource.value = R.drawable.rainy_snow
}
if (i.fcstValue == "3") {
rainStateText = "๋"
_weatherImageResource.value = R.drawable.snowy
}
}
}
}
}
7. Composabe UI ๋ง๋ค์ด์ฃผ๊ธฐ : rememberUpdatedState
์ฑ๋ฐ์์ ๋ ์จ ์์ ฏ์ ๊ตฌํํด์ฃผ๊ธฐ ์ํ์ฌ Main ์ฝ๋๋ฅผ ์์ฑํด์ฃผ์๋ค.
์ฌ๊ธฐ์ rememberUpdateState๋ผ๋ ํจ์๋ฅผ ์๊ฒ๋์๋ค!
val imageResource by rememberUpdatedState(newValue = weatherViewModel.weatherImageResource)
Icon(
painterResource(imageResource.value),
contentDescription = "weather"
)
val temperature by rememberUpdatedState(newValue = weatherViewModel.temperature)
Text(
text = temperature.value,
fontSize = 10.sp
)
rememberUpdatedState ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ์จ ์ํ์ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ์ด ์ํ๋ฅผ ์ฌ์ฉํ๋ ๋ถ๋ถ๋ง ๋ค์ ๊ทธ๋ ค์ง๋ค๊ณ ํ๋ค!
์ดํ์ ๋ ์จ ์์ ฏ์ ์ ๋ฌ๋๋ ๋ ์ง์ ์๊ฐ ๊ฐ ๋ก์ง์ ์ถ๊ฐํ ๋ค ๊ฒฐ๊ณผ๋ฅผ ๋ด์ผ๊ฒ ๋ค
๊ฐ์ด ๋ณด๋ฉด ์ข์ ์๋ฃ
https://developer.android.com/jetpack/compose/side-effects?hl=ko#rememberupdatedstate
์ฌ์ง ์ถ์ฒ
์๊ฐ macrovector</a> ์ถ์ฒ Freepik
'๐ค2024 ์๋๋ก์ด๋ > ๊ธฐ์์ฌ ํ๋ก์ ํธ ๊ธฐ๋ก' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Compose์์ ๊ตฌ์ฑ๋ณ๊ฒฝ ์ ์ ์ฅ๋์ง ์๋ ๋ฌธ์ ํด๊ฒฐํ๊ธฐ (2) | 2024.01.21 |
---|---|
android Flow ์์๋ณด๊ธฐ (2) | 2024.01.21 |
compose์ preferences datastore ์ด์ฉํ๊ธฐ (1) | 2024.01.18 |
kotlin coroutines basic ๊ณต์๋ฌธ์ ์ฝ์ด๋ณด๊ธฐ (0) | 2024.01.17 |
๐ Android FCM ์ ์ฉ๊ธฐ 2ํ : FCM + Retrofit (0) | 2024.01.07 |