Unit 4: Internet (2)

quokka·2021년 11월 24일
0

Android Basics in Kotlin

목록 보기
19/25
post-thumbnail

REST

  • 대부분의 웹 서버는 REST(REpresentational State Transfer)라는 일반적인 Stateless 웹 아키텍처를 사용해 웹 서비스를 실행한다.

  • Stateless란 client의 state를 기억할 필요 없다는 것

  • 이 아키텍처를 제공하는 웹 서비스를 RESTful 서비스라고 한다.

  • Restful 서비스는 Client-server architecture로 이루어진다.

  • URI를 통해 RESTful 웹 서비스에 요청을 전송한다.

  • URI(Uniform Resource Identifier)는 리소스 위치나 리소스에 액세스하는 방법을 암시하는 것이 아니라 서버의 리소스를 이름으로 식별한다.

  • URL(Uniform Resource Locator)은 리소스 표현을 획득하거나 표현에 관해 조치를 취하는 수단을 지정하는 URI이다. 즉, 기본 액세스 메커니즘과 네트워크 위치를 모두 지정한다.

웹 서비스 요청

일반적인 HTTP 작업에는 GET, POST, PUT, DELETE가 있다.

  • GET: 서버 데이터를 검색
  • POST PUT: 서버에 새로운 데이터를 추가/생성/업데이트
  • DELETE: 서버에서 데이터를 삭제

HTTP status codes

  • 200-299 : Success
  • 400-499 : Client Errors
  • 500-599 : Server Errors

Retrofit

Retrofit 라이브러리를 사용해 앱의 네트워크 계층을 구현한다. Retrofit으로 REST 웹 서비스에 연결하고 응답을 받을 수 있다.

Retrifit은 웹 서비스의 컨텐츠를 기반으로 앱의 네트워크 API를 만든다.
웹 서비스에서 데이터를 가져와 별도의 converter 라이브러리를 통해 라우팅한다. 이 converter 라이브러리는 데이터를 디코딩하여 String 같은 객체 형식으로 반환하는 방법을 알고있다.

1. retrofit 종속 항목 추가

build.gradle (Project)

repositories {
   google()
   jcenter()
}

build.gradle (Module) dependencies

// Retrofit2 라이브러리
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit 스칼라 변환기. JSON을 String으로 변환
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"

2. 자바 8 관련 내장 기능 추가

3. Network layer - MarsApiService class 생성

Network 패키지를 생성하고 그 아래 MarsApiService.kt를 생성한다.

4. BASE_URL

클래스 바깥에 웹 서비스의 base url을 상수 형태로 작성한다.

private const val BASE_URL = "https://android-kotlin-fun-mars-server.appspot.com"

5. Retrofit builder

BASE_URL 아래 Retrofit object를 빌드하고 생성할 Retrofit bilder를 작성한다.

  • 웹 서비스 API를 빌드하려면 converter factory가 필요하다.
  • converter는 웹 서비스에서 받아온 데이터로 무엇을 해야할지 retrofit에게 알려준다.
  • ScalarsConverter는 strings나 다른 원시 타입을 지원한다.
  • baseUrl()에 base URL을 담는다.
  • build()를 호출해 Retrofit object를 생성한다.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()

6. MarsApiService interface

여기서는 Retrofit이 HTTP requests를 사용해 어떻게 웹 서버와 통신하는지를 정의한다.

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

getPhotos()가 호출되면 retrofit은 base URL 끝에 photos를 더해 요청을 시작한다.

7. Object declarations - MarsApi

Singleton 객체 선언을 위해 object declaration을 한다. Singleton pattern은 객체의 인스턴스가 오직 하나만 생성되고, 그 객체에 접근하는 global point가 하나임을 보장한다.
Object declaration은 object 키워드를 사용한다.

  • Retrofit service를 initialize하기 위한 public object MarsApi를 정의한다.
  • retrofitService: lazily initialized retrofit object property
    lazy initialize로 첫 사용시에 initialize 되도록 한다.
object MarsApi {
    val retrofitService : MarsApiService by lazy { 
       retrofit.create(MarsApiService::class.java) }
}

ViewModel에서 웹 서비스 호출

Retrofit에서 반환된 JSON 문자열을 처리하는 getMarsPhotos() 메서드를 구현한다.

1. suspend 함수

MarsApiService의 getPhotos()를 코루틴에서 호출할 수 있도록 suspend 함수로 수정한다.

@GET("photos")
suspend fun getPhotos(): String

2. viewModelScope

ViewModelScope는 앱의 각 ViewModel을 대상으로 정의된 코루틴 범위이다. 이 범위에서 실행된 모든 코루틴은 ViewModel이 삭제되면 자동으로 취소된다.

ViewModelScope를 사용하여 코루틴을 실행하고 백그라운드에서 Retrofit 네트워크 호출을 실행한다.

  • viewModelScope.launch{}를 사용하여 코루틴을 실행한다.

  • viewModelScope 내부에서 MarsApi를 사용하여 retrofitService 인터페이스에서 getPhotos() 메서드를 호출하고 반환된 응답을 listResult 변수에 저장한다.

  • 결과를 _status.value에 할당한다.

    private fun getMarsPhotos() {
        viewModelScope.launch {
            val listResult = MarsApi.retrofitService.getPhotos()
            _status.value = listResult
        }
    }

3. 인터넷 권한

AndroidManifest.xml에 INTERNET 권한을 추가한다.

<uses-permission android:name="android.permission.INTERNET" />

이제 데이터가 포함된 JSON 텍스트가 화면에 표시된다.

예외처리 try-catch

예외는 런타임 시 발생할 수 있는 오류로, 앱이 튕기면서 종료하는 경우를 말한다.
다음과 같이 try catch로 예외를 처리하면 인터넷이 연결안됨 등의 예외 상황 발생시 앱을 종료하는 대신 화면에 error message를 띄운다.

    private fun getMarsPhotos() {
        viewModelScope.launch {
            try{
                val listResult = MarsApi.retrofitService.getPhotos()
                _status.value = listResult
            } catch (e: Exception){
                _status.value = "Failure:( ${e.message}"
            }

        }
    }

Moshi - Json 응답 파싱

Moshi 라이브러리를 사용해 JSON 응답을 LiveData 객체로 파싱한다.

https://android-kotlin-fun-mars-server.appspot.com/photos 서버에서 데이터를 보면, JSON 형식은 다음과 같다.

Moshi는 JSON 문자열을 Kotlin 객체로 변환하는 JSON parser다. Retrofit은 Moshi와 연동되는 변환기가 있으니 코드를 수정해보자.

1. build.gradle

Moshi 종속 항목을 추가한다.

// Moshi
implementation 'com.squareup.moshi:moshi-kotlin:1.9.3'

이전에 작성한 Retrofit과 Retrofit with Moshi Converter를 지우고 다음으로 바꾼다.

// Retrofit with Moshi Converter
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'

2. MarsPhoto data 클래스

Moshi는 JSON 데이터를 파싱해 Kotlin 객체로 변환하다. 파싱된 결과를 저장하려면 이를 담을 data 클래스가 필요하다.

network 패키지 아래 MarsPhoto.kt를 생성했다. 코드는 다음과 같다.

data class MarsPhoto (
    val id: String, @Json(name = "img_src") val imgSrcUrl: String
)
  • idid 그대로 받아오면 되므로 그냥 string 변수로 작성했다.
  • img_src는 코틀린 네이밍 컨벤션(카멜 표기법)과 맞지 않다. 따라서 Json의 img_srcimgSrcUrl 변수로 받는다.

3. MarsApiService 업데이트

Moshi 빌더를 사용해 Moshi 객체를 만드는 코드를 추가해야 한다.

  • ScalarsConverterFactory관련 코드과 import를 삭제하고, Moshi 객체를 만든다.
  • 생성한 moshi 인스턴스를 Retrofit 빌더에 전달한다.
private val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    . build()

private val retrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .baseUrl(BASE_URL)
    .build()
  • 이제 JSON 문자열을 반환하는 것이 아니라 MarsPhoto 객체 목록을 반환하도록 MarsApiService 인터페이스를 수정한다.
interface MarsApiService {
    @GET("photos")
    fun getPhotos(): List<MarsPhoto>
}

4. OverviewViewModel 업데이트

listResult가 더 이상 String이 아니므로 에러가 나는 코드를 수정한다.

전체솔루션코드

0개의 댓글