Unit 5: Get data from the internet (1)

์ •์ƒ์ค€ยท2023๋…„ 2์›” 10์ผ
0
post-thumbnail
post-custom-banner

๐Ÿ“š Retrofit ์‚ฌ์šฉํ•˜์—ฌ ์›น ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ

Unit 5: ์ธํ„ฐ๋„ท์—์„œ Retrofit Library๋ฅผ ํ™œ์šฉํ•˜์—ฌ Image๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์‹ค์Šต์„ ํ•ฉ๋‹ˆ๋‹ค.

  1. web service๋ฅผ ์œ„ํ•œ URL์„ ์ƒ์ˆ˜๋กœ ์ถ”๊ฐ€
private const val BASE_URL =
    "https://android-kotlin-fun-mars-server.appspot.com"
  1. Retrofit ๋นŒ๋” ์ถ”๊ฐ€
    Retrofit์€ converter factory๊ฐ€ ์žˆ์–ด์•ผ ์›น ์„œ๋น„์Šค API๋ฅผ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. converter์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ Retrofit์—๊ฒŒ data๋กœ ํ•ด์•ผํ•˜๋Š” ๊ฒƒ์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์ด๋ฒˆ ์•ฑ์—์„œ๋Š” JSON์„ String์œผ๋กœ ๋ฐ”๊ฟ”์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ScalarsConverter๋ผ๋Š” Converter๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
private val retrofit = Retrofit.Builder().addConverterFactory(ScalarsConverterFactory.create())
  1. baseURL์„ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉฐ, build()๋ฅผ ํ†ตํ•ด retrofit์„ ๋นŒ๋“œ
private val retrofit =
    Retrofit.Builder().addConverterFactory(ScalarsConverterFactory.create()).baseUrl(
        BASE_URL
    ).build()
  1. retrofit์ด http๋ฅผ ์‚ฌ์šฉํ•ด ์›น ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•  interface๋ฅผ ์„ ์–ธ
interface MarsApiService {
    fun getPhotos() : String
}
  1. @Get("photos") Retroft์—๊ฒŒ Get์š”์ฒญ์ž„์„ ์•Œ๋ฆฌ๊ณ  ๊ธฐ๋ณธ URL์˜ ์—”๋“œํฌ์ธํŠธ์— "photos"๋ฅผ ์ถ”๊ฐ€ํ•ด ์›ํ•˜๋Š” ์™„๋ฒฝํ•œ URL์„ ๋งŒ๋“ฌ
interface MarsApiService {
    @GET("photos")
    fun getPhotos() : String
}

โ—๏ธ์‹ฑ๊ธ€ํ†ค ์„ ์–ธ์€ ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ด์ฃผ๋ฉฐ ์ด ๊ฐ์ฒด๋Š” ํ•˜๋‚˜์˜ ์ „์—ญ ํฌ์ธํ„ฐ๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค. ๊ฐ์ฒด ์ดˆ๊ธฐํ™”๋Š” ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ ๋ณด์žฅ๋ฐ›์œผ๋ฉฐ access์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ์•ฑ์—์„œ Retrofit ์ธ์Šคํ„ด์Šค๋Š” ํ•˜๋‚˜๋งŒ ํ•„์š”ํ•˜๋ฏ€๋กœ ์‹ฑ๊ธ€ํ†ค์„ ํ†ตํ•ด ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

  1. retrofit์˜ create()๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŽ์ด ์†Œ๋น„ํ•˜๋Š” ์ž‘์—…์ด๋ฏ€๋กœ ์‹ฑํดํ†ค์œผ๋กœ ์„ ์–ธ
object MarsApi {
    val retrofitService: MarsApiService by lazy {
        retrofit.create(MarsApiService::class.java)
    }
}

โ—๏ธViewModelScope
viewModelScope์€ ์•ฑ์˜ ๊ฐ ViewModel์„ ๋Œ€์ƒ์œผ๋กœ ์ •์˜๋œ ๊ธฐ๋ณธ ์ œ๊ณต ์ฝ”๋ฃจํ‹ด ๋ฒ”์œ„์ž…๋‹ˆ๋‹ค. ์ด ๋ฒ”์œ„์—์„œ ์‹คํ–‰๋œ ๋ชจ๋“  ์ฝ”๋ฃจํ‹ด์€ ViewModel์ด ์‚ญ์ œ๋˜๋ฉด ์ž๋™์œผ๋กœ ์ทจ์†Œ๋ฉ๋‹ˆ๋‹ค.

  1. getPhotos()๋ฅผ suspend funtion์œผ๋กœ ๋งŒ๋“ค์–ด ๋น„๋™๊ธฐ์ฒ˜๋ฆฌ
interface MarsApiService {
    @GET("photos")
    suspend fun getPhotos(): String
}
  1. viewModel์—์„œ viewModelScope๋ฅผ ์‚ฌ์šฉํ•ด ์•ž์„œ ๋งŒ๋“ค์—ˆ๋˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜๋ฅผ ์ฝ”๋ฃจํ‹ด์œผ๋กœ ์‹คํ–‰ํ•จ
fun getMarsPhotos() {
        viewModelScope.launch {
            val listResult = MarsApi.retrofitService.getPhotos()
            marsUiState = listResult
        }
    }

๊ทธ๋Ÿผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ Jsonํ˜•ํƒœ๋กœ ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋„๊ณ  ์•ฑ์„ ์‹คํ–‰ํ•œ๋‹ค๋ฉด ์•ฑ์ด ๊ณง๋ฐ”๋กœ ์ข…๋ฃŒ๋˜๋Š” ๋ฒ„๊ทธ๊ฐ€ ๋‚จ์•„์žˆ์Šต๋‹ˆ๋‹ค.์šฐ๋ฆฌ๋Š” ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์„ ํ•ด๊ฒฐํ•  ์ˆœ ์—†์ง€๋งŒ ์ตœ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์นœํ™”์ ์ด๊ฒŒ ์•ฑ์ด ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์—ฌ์ค€๋‹ค๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๋Š” ๋™์•ˆ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • API์— ์‚ฌ์šฉ๋œ URL ๋˜๋Š” URI๊ฐ€ ์ž˜๋ชป๋จ
  • ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด ์•ฑ์„ ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†์Œ
  • ๋„คํŠธ์›Œํฌ ์ง€์—ฐ ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ
  • ๊ธฐ๊ธฐ์˜ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์ด ๋ถˆ์•ˆ์ •ํ•˜๊ฑฐ๋‚˜ ๊ธฐ๊ธฐ๊ฐ€ ์ธํ„ฐ๋„ท์— ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Œ
  1. ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ˆ์™ธ๊ฐ€ ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ try-catch๋ฌธ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.
viewModelScope.launch {
   try {
       val listResult = MarsApi.retrofitService.getPhotos()
       marsUiState = listResult
   } catch (e: IOException) {

   }
}

๐Ÿ“š State UI ์ถ”๊ฐ€ํ•˜๊ธฐ

์•„์ง ์ด ์•ฑ์—์„œ๋Š” ์„œ๋ฒ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•  ๋•Œ ์š”์ฒญ ์ƒํƒœ๋ฅผ ๋ณด์—ฌ์ฃผ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

  • Loading ์ƒํƒœ๋Š” ์•ฑ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ์Œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • Success ์ƒํƒœ๋Š” ์›น ์„œ๋น„์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๊ฐ€์ ธ์™”์Œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • Error ์ƒํƒœ๋Š” ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜ ๋˜๋Š” ์—ฐ๊ฒฐ ์˜ค๋ฅ˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  1. uiState๋Š” ์œ„์™€ ๊ฐ™์€ ์ƒํƒœ๋งŒ ๋ณด์—ฌ์ฃผ๋ฉด ๋˜๋ฏ€๋กœ sealed interface๋ฅผ ์‚ฌ์šฉํ•ด uiState์˜ ๋ฒ”์œ„๋ฅผ ์ œํ•œ
sealed interface MarsUiState {
    data class Success(val photos: String) : MarsUiState
    object Error : MarsUiState
    object Loading : MarsUiState
}
    var marsUiState: MarsUiState by mutableStateOf(MarsUiState.Loading)
        private set
  1. try-catch๋ฌธ์—์„œ ์„ฑ๊ณต์‹œ Success๋ฅผ ์‹คํŒจ์‹œ Error๋ฅผ ๋„ฃ๊ธฐ
private fun getMarsPhotos() {
   viewModelScope.launch {
       marsUiState = try {
           val listResult = MarsApi.retrofitService.getPhotos()
           MarsUiState.Success(listResult)
       } catch (e: IOException) {
           MarsUiState.Error
       }
   }
}
  1. uiState์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ํ™”๋ฉด์— ๋ฌด์—‡์„ ๊ทธ๋ ค์ค„์ง€ ์„ ํƒ
fun HomeScreen(
   marsUiState: MarsUiState,
   modifier: Modifier = Modifier
) {
    when (marsUiState) {
        is MarsUiState.Loading -> LoadingScreen(modifier)
        is MarsUiState.Success -> ResultScreen(marsUiState.photos, modifier)
        is MarsUiState.Error -> ErrorScreen(modifier)
    }
}
-์‹คํŒจ ํ™”๋ฉด- -๋กœ๋”ฉ ํ™”๋ฉด- -์„ฑ๊ณต ํ™”๋ฉด-

๐Ÿ“š Serialization์‚ฌ์šฉํ•˜์—ฌ Json์„ Kotlin ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜

  1. Json์„ Kotlin ๊ฐ์ฒด๋กœ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•ด Data Class ๋งŒ๋“ค๊ธฐ
@Serializable
data class MarsPhoto(
    val id: String,  val img_src: String
)

โ—๏ธ @SerialName ์–ด๋…ธํ…Œ์ด์…˜ : Json์˜ Key์ด๋ฆ„์ด Kotlin ๊ฐ์ฒด์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋“ค์˜ ์ด๋ฆ„๊ณผ ๊ฐ™์•„์•ผ ๊ฐ๊ฐ์— ๋งž๊ฒŒ ํŒŒ์‹ฑ์ด ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Json๋Š” ๋ณ€์ˆ˜๋ช…์„ ์Šค๋„ค์ดํฌ ํ‘œ๊ธฐ๋ฒ•์œผ๋กœ ํ•˜๋Š”๋ฐ Kotlin์˜ ๊ฒฝ์šฐ Camel ํ‘œ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Kotlin ๊ฐ์ฒด๊ฐ€ ๋ฌด์กฐ๊ฑด์ ์œผ๋กœ Json์˜ Key์ด๋ฆ„์„ ๋”ฐ๋ผํ•œ๋‹ค๋ฉด Kotlin์˜ ์†์„ฑ์„ ์–ด์ง€๋Ÿฝ๊ฒŒ ํ•˜๊ฑฐ๋‚˜ ๊ถŒ์žฅ ์†์„ฑ์„ ๋ฒ—์–ด๋‚  ์ˆ˜ ์žˆ์–ด @SerialName ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ Json์˜ Key์ด๋ฆ„๊ณผ Kotlin ๊ฐ์ฒด์˜ ๋ณ€์ˆ˜๋ช…์„ ๋งตํ•‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. Json์˜ Key ๋งตํ•‘
@Serializable
data class MarsPhoto(
    val id: String,
    @SerialName(value = "img_src")
    val imgSrc: String
)
profile
์•ˆ๋“œ๋กœ์ด๋“œ๊ฐœ๋ฐœ์ž
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€