๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿค2024 ์•ˆ๋“œ๋กœ์ด๋“œ/๊ธฐ์ˆ™์‚ฌ ํ”„๋กœ์ ํŠธ ๊ธฐ๋ก

๐ŸŒžandroid compose์—์„œ ๋‚ ์”จ api๋ฅผ ์—ฐ๋™ํ•ด๋ณด์ž!

by hyeonha 2024. 2. 1.

๋ชฉ์ฐจ

     

    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

    728x90