π‘λ€νΈμν¬ μμ² μ€μ λ°μν μ μλ μλ¬μ λν΄ μμλ³΄κ³ , ν΄κ²° λ°©μμ κΈ°λ‘νμμ΅λλ€.
HTTP
- Androidμμ HTTP μλ¬λ μ£Όλ‘ λ€νΈμν¬ μμ² μ€μ λ°μνλ©°, μ΄λ μλ² κ°μ ν΅μ μ΄ μ€ν¨νκ±°λ μλ²κ° μμ²μ μ²λ¦¬νμ§ λͺ»ν λ λνλ λλ€.
- μ΄λ HTTP μν μ½λ, λ€νΈμν¬ μ°κ²° λ¬Έμ , μλ²μ μλ΅ μ²λ¦¬ μ€λ₯ λ±μ μ΄μ κ° μμ μ μμ΅λλ€.
- μλμ κ°μ μΌλ°μ μΈ μ€λ₯λ‘ λΆλ₯ν μ μκ³ , JSON νμ±μ΄λ SSL μΈμ¦μ μ€λ₯λ λ°μν μ μμ΅λλ€.
4XX
- HTTP μν μ½λ 4XXλ₯Ό κ°μ§λ©΄ ν΄λΌμ΄μΈνΈ μ€λ₯λ‘ λΆλ₯ν©λλ€.
- 400 : Bad Resquest
- 401 : Unauthorized
- 403 : Forbidden
- 404 : Not Found
5XX
- HTTP μν μ½λ 5XXλ₯Ό κ°μ§λ©΄ μλ² μ€λ₯λ‘ λΆλ₯ν©λλ€.
- 500 : Internal Server Error
- 502 : Bad Getway
- 503 : Service Unabilable
- 504 : Getway Timeout
λ€νΈμν¬ μ€λ₯
- λ€νΈμν¬ μ°κ²°μ΄ λΆμμ νκ±°λ, Wi-Fi λλ λͺ¨λ°μΌ λ°μ΄ν°κ° λκ²Όμ λ λ€νΈμν¬ μ°κ²° μ€ν¨ μλ¬κ° λ°μν©λλ€.
- μλ²κ° μΌμ μκ° λ΄μ μλ΅νμ§ μμΌλ©΄ νμ μμμ΄ λ°μν©λλ€.
λΆμ μ ν μμΈμ²λ¦¬
- μλλ‘μ΄λμμλ HTTP μμ²μ ν λ μ£Όλ‘ Retrofitμ΄λ OKHttpμ κ°μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©ν©λλ€.
- μ΄λ¬ν λΌμ΄λΈλ¬λ¦¬λ₯Ό ν΅ν΄ HTTP μμ² λ° μλ¬λ₯Ό μ²λ¦¬ν μ μμ΅λλ€.
- viewμ λ°μ΄ν°λ₯Ό μ°κ²°νλ ViewModelμμ μλμ κ°μ΄ μλ¬ μ²λ¦¬λ₯Ό μ§νν μ μμ΅λλ€.
// Retrofit API Interface
interface ApiService {
@GET("example/data")
suspend fun getData(): Response<Data>
}
// ViewModel
class MyViewModel(private val apiService: ApiService) : ViewModel() {
fun fetchData() {
viewModelScope.launch {
try {
val response = apiService.getData()
if (response.isSuccessful) {
// μ±κ³΅μ μΌλ‘ λ°μ΄ν°λ₯Ό λ°μ κ²½μ°
val data = response.body()
// μ²λ¦¬ λ‘μ§
} else {
// HTTP μλ¬ μ²λ¦¬
handleHttpError(response.code())
}
} catch (e: Exception) {
// λ€νΈμν¬ μ€λ₯ λλ κΈ°ν μμΈ μ²λ¦¬
handleNetworkError(e)
}
}
}
private fun handleHttpError(code: Int) {
when (code) {
400 -> Log.e("Error", "Bad Request")
401 -> Log.e("Error", "Unauthorized")
404 -> Log.e("Error", "Not Found")
500 -> Log.e("Error", "Internal Server Error")
else -> Log.e("Error", "Unknown HTTP Error: $code")
}
}
private fun handleNetworkError(e: Exception) {
if (e is SocketTimeoutException) {
Log.e("Error", "Request Timeout")
} else if (e is UnknownHostException) {
Log.e("Error", "No Internet Connection")
} else {
Log.e("Error", "Unknown Network Error: ${e.message}")
}
}
}
- μ΄λ μν μ½λμ λ€νΈμν¬, νμμμμ λν μλ¬λ₯Ό λΆκΈ°μ²λ¦¬ν λͺ¨μ΅μ΄μ§λ§, μλμ κ°μ λ¬Έμ κ° λ°μν©λλ€.
μλ² μλ΅ μ½λλ₯Ό ViewModelμ μ ν
μ μ½λλ μλ² μλ΅ μ½λλ₯Ό ViewModelμμ μκ³ μμ΅λλ€. ViewModelμ μν€ν μ²μμ UIμ λ°μ΄ν° μ¬μ΄μ μ€μ¬μ μν μ μνν©λλ€. λ°λΌμ ViewModelμ΄ μλ² μλ΅ μ½λλ λ°μ΄ν° μΆμ²μ λν΄ μλ κ²μ λ°λμ§νμ§ μμΌλ©°, ViewModelμ λ°μ΄ν°κ° μ΄λμ μ€λμ§ μκ΄μμ΄ UIλ₯Ό μ λ°μ΄νΈνλ μν μ μ§μ€ν΄μΌ ν©λλ€.
κ΄μ¬μ¬ λΆλ¦¬
- ViewModelμ UIμ μνλ₯Ό κ΄λ¦¬νκ³ , λ°μ΄ν°κ° UIμ μ΄λ»κ² νμλ μ§λ₯Ό κ²°μ νλ μν μ ν΄μΌ ν©λλ€.
- μλ², λ‘컬 λ°μ΄ν°λ² μ΄μ€ λ±μ λ°μ΄ν° μΆμ²μ μλ΅μ½λλ Repositoryλ DataSource κ³μΈ΅μμ κ΄λ¦¬ν΄μΌ νλ©°, ViewModelμ λ°μ΄ν°μ μΆμ²μ 무κ΄νκ² νμν λ°μ΄ν°λ₯Ό μ 곡λ°μμΌ ν©λλ€.
λΉμ¦λμ€ λ‘μ§μ΄ ViewModelλ‘ μ ν
λ§μ½ ViewModelμ΄ μλ² μλ΅ μ½λλ₯Ό μκ² λλ€λ©΄, μ΄λ λΉμ¦λμ€ λ‘μ§μ΄ ViewModelμ ν¬ν¨λμ΄ κ΄μ¬μ¬μ λΆλ¦¬κ° 무λμ§ μ μμ΅λλ€. νΉμ λ°μ΄ν° μΆμ²μ μ’ μμ μ΄ λμ΄, ν μ€νΈνκΈ° μ΄λ ΅κ³ μ½λκ° λ³΅μ‘ν΄μ§ μ μμ΅λλ€.
Result κ°μ²΄ νμ©
Result κ°μ²΄?
- Result κ°μ²΄λ λ°μ΄ν°μ μ²λ¦¬ κ²°κ³Όλ₯Ό μ±κ³΅κ³Ό μ€ν¨λ‘ λλμ΄ νννλ νμ€νλ λ°μ΄ν° λνΌμ λλ€.
- μ΄λ₯Ό νμ©ν΄μ ν¨μλ λ©μλκ° μ±κ³΅μ μΈ κ²°κ³Όμ μ€ν¨ μν©μ λͺ ννκ² κ΅¬λΆνκ³ , μ΄λ₯Ό νλμ λ°ν κ°μΌλ‘ μ²λ¦¬ν μ μμ΅λλ€.
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
}
- Kotlinμμλ κΈ°λ³Έμ μΌλ‘ Kotlin.Resultλ₯Ό μ 곡νμ§λ§, μ΄λ₯Ό 컀μ€ν νμ¬ λμ± μ μ°νκ² νμ©ν μ μμ΅λλ€.
μΈλΆνλ Result κ°μ²΄
- Result κ°μ²΄λ₯Ό μλ¬ μ²λ¦¬μ μ ν©νκ² νμ©νκΈ° μν΄μ μΈλΆνν μ μμ΅λλ€.
sealed class Result<out T> {
data class Success<out T>(val data: T) : Result<T>()
data class HttpError(val statusCode: Int) : Result<Nothing>() // HTTP μν μ½λ μλ¬
data class NetworkError(val exception: Throwable) : Result<Nothing>() // λ€νΈμν¬ μλ¬ (νμμμ, μ°κ²° μ€ν¨ λ±)
}
- μ΄λ₯Ό νμ©νλ©΄ HTTP μν μ½λ, λ€νΈμν¬ μ€λ₯, νμμμμ λ§κ² μΈλΆνν μ μμΌλ©°, ꡬ체μ μΈ μλ¬ μ²λ¦¬κ° κ°λ₯ν©λλ€.
Repositoryμμ μλ¬ μ²λ¦¬
- Repositoryμμ μλ¬μ²λ¦¬λ₯Ό μ§ννμ¬ λΉμ¦λμ€ λ‘μ§μ΄ ViewModelλ‘ μ νλλ κ²μ λ°©μ§ν μ μμ΅λλ€.
- ViewModelλ‘ Resultλ‘ κ°μΈμ§ κ°μ²΄λ₯Ό μ λ¬νμ¬ ViewModelμμλ μλ¬ μ½λλ₯Ό μ μ μκ² ν μ μμ΅λλ€.
class DataRepository(private val apiService: ApiService) {
suspend fun getData(): Result<List<String>> {
return try {
val response = apiService.getData() // μλ²μ λ°μ΄ν° μμ²
if (response.isSuccessful) {
Result.Success(response.body() ?: emptyList())
} else {
Result.HttpError(response.code()) // HTTP μν μ½λ μλ¬ μ²λ¦¬
}
} catch (e: IOException) {
// λ€νΈμν¬ μμΈ μ²λ¦¬ (λ€νΈμν¬ μ°κ²° μ€ν¨, νμμμ λ±)
Result.NetworkError(e)
}
}
}
UIλ‘ μ ν
- κ°μΈμ§ Resultκ°μ²΄λ₯Ό λΆμνμ¬ μ¬μ©μμκ² λ€λ₯Έ UIλ₯Ό 보μ¬μ€ μ μμ΅λλ€.
// ViewModelμμ λ°μ΄ν°λ₯Ό κ°μ Έμ€κ³ Resultμ λ°λΌ λΆκΈ° μ²λ¦¬
class MyViewModel(private val repository: DataRepository) : ViewModel() {
private val _uiState = MutableLiveData<Result<List<String>>>()
val uiState: LiveData<Result<List<String>>> get() = _uiState
fun loadData() {
viewModelScope.launch {
_uiState.value = Result.Loading
val result = repository.getData()
_uiState.value = result // λ€νΈμν¬ μλ¬, μν μ½λ λ±μ μ²λ¦¬ν Result λ°ν
}
}
}
// UIμμ Result κ°μ²΄μ μνμ λ°λΌ UI κ°±μ
@Composable
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
val uiState by viewModel.uiState.observeAsState(Result.Loading)
when (uiState) {
is Result.Success -> ShowData((uiState as Result.Success).data) // μ±κ³΅ μ λ°μ΄ν° λ λλ§
is Result.HttpError -> ShowError("HTTP Error: ${(uiState as Result.HttpError).statusCode}") // HTTP μν μ½λ μ²λ¦¬
is Result.NetworkError -> ShowError("Network Error: ${(uiState as Result.NetworkError).exception.message}") // λ€νΈμν¬ μλ¬ μ²λ¦¬
}
}
μ΄λ° κ³Όμ μ΄ νμν μ΄μ λ?
λΆλ¦¬λ μλ¬ μ²λ¦¬
- ViewModelμ λ¨μν UI μνλ₯Ό κ΄λ¦¬νκ³ , μΈλΆμ μΈ λ€νΈμν¬ λ° HTTP μν μ½λμ λν μ²λ¦¬λ Repositoryλ‘ λΆλ¦¬λ©λλ€.
- μ΄λ μ μ§λ³΄μλ₯Ό μ©μ΄νκ² νκ³ , μ½λμ κ΄μ¬μ¬ λΆλ¦¬λ₯Ό μ μ§νλ λ° λμμ μ€λλ€.
ν μ€νΈ μ©μ΄μ±
- κ° μνλ₯Ό λΆλ¦¬ν¨μΌλ‘μ¨ κ°λ³μ μΈ ν μ€νΈκ° κ°λ₯ν©λλ€.
- μ±κ³΅ μλ΅, HTTP μν μλ¬, λ€νΈμν¬ μλ¬λ₯Ό κ°κ° ν μ€νΈν μ μμ΅λλ€.
μ¬μ©μ κ²½ν ν₯μ
- λ€μν μλ¬ μν©μ λ°λΌ μ μ ν λ©μμ§λ UI νΌλλ°±μ μ 곡ν μ μμ΅λλ€.
- μ΄λ μ¬μ©μκ° μμμΉ λͺ»ν μν©μ λ°©μ§νλ©°, μΉμ ν μμΈ μ λ¬λ‘ μ¬μ©μ κ²½νμ ν¬κ² ν₯μμν¬ μ μμ΅λλ€.
Sandwich?
- 볡μ‘ν μ ν리μΌμ΄μ μν€ν μ²μμ API μ²λ¦¬λ₯Ό μ§νν λ, λ§€λ² μμΈμ²λ¦¬ μ½λλ₯Ό μμ±νλ κ²μ λ§μ μκ°μ μλͺ¨ν μ μμ΅λλ€.
- Sandwichλ Multi-Layred μν€ν μ²μμ λ°μ΄ν° νλ¦κ³Ό Retrofit μλ΅ λ° μλ¬λ₯Ό κ°κ²°νκ³ λͺ μμ μΌλ‘ λ€λ£¨λ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ 곡νκ³ μμ΅λλ€.