[Android][Kotlin] Get Data from the Internet

hy3.nji·2023년 9월 10일
0

Jetpack Compose 🎀

목록 보기
6/6

🍒 Introduction to web service

  • ViewModel의 역할
    • ViewModel은 data layer와 communicate한다.
    • ViewModel에서는 네트워크를 호출해야한다.
    • ViewModel에서는 MutableState를 통해 앱 UI를 업데이트한다.

🍒 Web services and Retrofit

  • 통신
    • 대부분의 웹서버는 stateless web architecture인 REST를 사용하여 통신
    • URI를 통해 RESTful 웹 서비스 Request가 만들짐
    • URI는 서버에 있는 리소스를 찾도록 해줌
    • URL은 URI의 일부분

🍒 Web service request

  • 웹 서비스 요청에는 URI가 포함되며, 동일한 HTTP 프로토콜을 통해 서버에 요청이 전송된다.
  • HTTP 요청에는 서버가 무엇을 하면 되는지에 대한 작동이 기술되어있다.

일반적인 HTTP 요청들

  • GET
  • POST
  • PUT
  • DELETE

Retrofit Library

  • Retrofit은 REST 백엔드와 통신한다.
  • Dependency 추가가 필요하다.
// Retrofit 
implementation("com.squareup.retrofit2:retrofit:2.9.0")
// Retrofit with Scalar Converter
implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
  • scalar converter는 JSON 결과를 String으로 바꿔줌

🍒 Connecting the internet

레트로핏의 작동

  1. Retrofit은 network API를 만듦.
  2. API는 데이터를 패치하여 converter library로 보냄.
  3. converter library는 데이터를 decode하여 오브젝트 형태로 반환함.
  4. Retrofit은 XML과 JSON에 대한 데이터 형식에 대해 지원함.
  5. 결과적으로 Retrofit은 서비스를 call하고 consume할 코드를 생성.

API Service 만들기

  1. network 패키지 하위에 APIService.kt 파일 생성
  2. Base URL 선언
private const val BASE_URL = 
   "https://android-kotlin-fun-mars-server.appspot.com"
  1. retrofit 선언
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()
  • Retrofit은 웹 서비스 API를 만들기 위해 base URI와 converter factory를 필요로 한다.
  • converter는 Retrofit에 응답받은 데이터로 무엇을 해야하는지 알려준다
  • ScalarsConverterFactory는 응답받은 JSON 데이터를 String 등의 원시 타입으로 바꿔준다.
  1. 통신 인터페이스 추가
import retrofit2.http.GET

interface MarsApiService {
    @GET("photos") 
    fun getPhotos(): String
}

Object declarations

  • object 선언

    • 싱글톤 오브젝트를 만들 때 사용되는 키워드
  • 예제

// Example for Object declaration, do not copy over

object SampleDataProvider {
    fun register(provider: SampleProvider) {
        // ...
    }// ...
}

// To refer to the object, use its name directly.
SampleDataProvider.register(...)
  • 싱글톤 패턴
    • 단 하나의 객체만 만들어지는 것을 보장한다.
    • 오브젝트에 접근하는 단 하나의 global point만 존재
    • 오브젝트 초기화는 thread-safe이고 첫 접근에 이루어짐
    • 단점
      • 테스트에서 예측이 어려움
    • 주의

API 생성

object MarsApi {
    val retrofitService : MarsApiService by lazy { 
       retrofit.create(MarsApiService::class.java)
    }
}
  • object 키워드
    • Retrofit API Servide는 한 가지 instance만 필요함
    • Retrofit object의 create() 함수를 호출하는 것은 메모리, 비용, 성능 면에서 비효율적.
    • object declation을 활용
  • lazy 키워드
    • 처음 사용할 때 초기화되도록 함

🍒 Call the web service in MarsViewModel

ViewModelScope

  • 특징

    • built-in coroutine scope이다.
    • 앱에 있는 뷰모델 하나마다 각각 하나씩 존재
    • 뷰모델이 사라지면 ViewModelScope 코루틴은 자동적으로 취소
    • configuration 변화가 일어나도 요청은 유지
  • 필요성

    • coroutine을 시작하고, 백그라운드에서 통신 요청을 보내기 위함.
  • 활용하기

  1. APIService 인터페이스의 통신 함수들을 suspend 함수로 정의한다.
  • suspend 키워드를 통해 비동기 함수를 만들 수 있다.
  1. ViewModel에 다음과 같은 형태로 작성한다.
private fun getMarsPhotos() {
  viewModelScope.launch {
    val listResult = MarsApi.retrofitService.getPhotos()
    marsUiState = listResult // 서버에서 받아온 결과를 UiState에 저장한다.
  }
}

🍒 Add Internet permission and Exception Handling

Android Permissions

  • 권한의 필요성

    • 안드로이드 사용자의 privacy를 지키기 위함.
  • 권한의 작동

  • 인터넷 권한 사용하기

    • Manifest 파일에 다음과 같이 선언
<uses-permission android:name="android.permission.INTERNET" />

Exception Handling

  • exceptions

    • runtime중에 발생할 수 있는 에러로 발생하면 앱이 바로 꺼진다.
  • 예외의 해결

    • try - catch 문을 이용하여 대응한다.
try {
    // some code that can cause an exception.
}
catch (e: SomeException) {
    // handle the exception to avoid abrupt termination.
}
// 위의 네트워크 연결과 같은 경우 다음과 같이 대응한다.

viewModelScope.launch {
  try {
    val listResult = MarsApi.retrofitService.getPhotos()
    marsUiState = listResult
  } catch (e: IOException) {
  
  }
}

Add State UI

  • 네트워크 상태 대응
  1. ViewModel에 sealed interface 추가
sealed interface MarsUiState {
  data class Success(val photos: String): MarsUiState
  object Error : MarsUiState
  object Loading : MarsUiState
}
  • sealed interface: 가능한 값을 제한하여 state 관리에 도움
  • loading, success, error의 세 가지 state로 응답 제한
    • 상태 설명
      • Loading: 앱이 데이터를 기다리는 상태
      • Success: 앱이 성공적으로 서버에서 데이터를 받아온 상태
      • Error: 네트워크 연결 에러 상태
    • 상태 대응
      • success의 경우 서버에서 받아온 데이터를 저장하기 위해 생성자 매개변수가 필요
      • loading, error의 경우 웹의 응답을 저장하기 위해 object를 사용
  1. ViewModel에서 UiState 정의 수정
var marsUiState: MarsUiState by mutableStateOf(MarsUiState.Loading)
  private set
  • 디폴트 값을 UiState.Loading으로 변경
  • UiState에 입력 방지 위해 private 설정
  1. ViewModel에서 네트워크 호출부 수정
val listResult = MarsApi.retrofitService.getPhotos()
marsUiState = MarsUiState.Success(listResult)
  • 서버에서 성공적으로 받아온 데이터값을 이용해 marsUiState 재할당
  1. catch문에서 네트워크 통신 실패 예외 핸들링
catch (e: IOException) {
   marsUiState = MarsUiState.Error
}
  1. 통신 상태에 따른 화면 조정
fun HomeScreen(
  marsUiState: MarsUiState,
  modifier: Modifier = Modifier
) {
  when (marsUiState) {
    is MarsUiState.Loading -> LoadingScreen(modifier = modifier.fillMaxSize())
    is MarsUiState.Success -> ResultScreen(
      marsUiState.photos, modifier = modifier.fillMaxWidth()
    )
    is MarsUiState.Error -> ErrorScreen( modifier = modifier.fillMaxSize() )
  }
}

🍒 Parse the JSON response with kotlinx.serialization

JSON

  • Json 이란?

    • 서버 응답 데이터 형식 중 하나
  • deserialization

    • JSON으로 표현된 데이터를 Kotlin object로 변경하는 작업
  • serialization

    • 앱 데이터를 네트워크를 통해 전송할 수 있는 형태로 변환하는 것
  • kotlinx.serialization 라이브러리 사용하기

  1. 플러그인 추가
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10"
  1. dependency 설정
// Kotlin serialization 
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")

Implement the Mars Photo data class

@Serializable
data class MarsPhoto(
  val id: String, val img_src: String
)
  • 데이터 클래스를 만드는 이유?

    • 파싱된 JSON data를 저장해두어야, Kotlin object로의 convert가 가능하다.
  • 데이터 클래스의 생성

    • 서버에서 받아오는 데이터의 형식에 맞춘다.
      • JSON을 파싱 후, key값에 맞춰 data object를 채운다.
@SerialName(value = "img_src")
val imgSrc: String
  • @SerialName 의 이용
    • Kotlin object의 property명과 JSON 응답의 key값이 맞지 않을 때 이용함.

Update MarsApiService and MarsViewModel

  1. Retrofit Builder 수정 - kotlinx.serialization을 사용할 수 있도록.
private val retrofit = Retrofit.Builder()
        .addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
        .baseUrl(BASE_URL)
        .build()
profile
Android Developer

0개의 댓글