본문 바로가기

Android

코틀린에서 레트로핏 사용하기 (Use retrofit2 with kotlin)

안드로이드 프로젝트에서 REST API를 호출하기 위해 가장 일반적으로 사용하는 라이브러리는 레트로핏(Retrofit) 입니다.
지금까지 자바 기반으로 레트로핏을 사용하다가 코틀린으로 작성하면서 기본적인 사용방법을 익히기 위해 작성된 포스트입니다.
해당 포스트에서는 Github의 프로젝트 Search API를 레트로핏으로 호출하는 방법을 소개합니다.
(Search API는 별도의 key 필요없이 간단하게 호출 가능합니다.)


라이브러리 추가

  • Gradle(app) 파일에 다음과 같은 라이브러리를 추가합니다.
  • retrofit : Retrofit 라이브러리
  • converter-gson : Json데이터를 사용자가 정의한 객채로 변환해주는 컨버터 라이브러리
  • adapter-rxjava2 : Retrofit을 Rx형태로 사용하도록 설정해주는 어댑터
  • okhttp, logging-intercepter : Retrofit을 사용해 받는 HTTP데이터들을 로그상으로 확인하기 위해 사용하는 라이브러리

현재 아래에 적힌 버전들은 2018년 6월 기준 최신 버전이므로 적용전 각각의 라이브러리 최신버전은 별도로 확인하여 작성해주세요.

dependencies {
	...
    
	// Retrofit, Gson, RxAdapter
	implementation 'com.squareup.retrofit2:retrofit:2.4.0'
	implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
	implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    
	// okhttp
	implementation 'com.squareup.okhttp3:okhttp:3.10.0'
	implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
    
	...
}

 


Github API 통신 방식 확인

자바 인터페이스 구현을 통해서 레트로핏 통신을 사용할 수 있습니다. 인터페이스 구현을 하기 위해서 API가 어떻게 호출되는지, 반환 데이터는 어떤 형태인지 파악하고 있어야 인터페이스를 구현할 수 있습니다. 호출 방식에과 반환 형태에 따라 인터페이스 구현이 달라지기 때문입니다. 대부분의 API 제공하는 곳에서는 API문서가 잘 작성되어있어 어떻게 호출하는지, 반환 데이터 형태는 어떠한지 파악하기 쉽습니다. 호출하려는 API의 인자값들을 파악한 후 인터페이스 GithubApi.kt를 정의합니다.

(Github API 설명 : https://developer.github.com/v3/search/#search-repositories)

Search API 호출 방식
Search API 반환 데이터 형태


데이터 클래스 정의

Search API를 통해 데이터를 받기 위해선 API문서에 정의되어있는대로 데이터 클래스를 새로 정의해야합니다. 수신받는 데이터 중 사용하려고 하는 필드들만 구현해도 상관 없습니다. 저의 경우에는 반환 데이터의 JSON 형태가 두개의 depth를 가지고 있으므로 두개의 클래스로 구현하였습니다. 레포 데이터 중에서는 id, name, full_name만 사용합니다.

GithubResponseModel.kt

package com.example.hyunsikyoo.retrofit_example_kotlin.model

import com.google.gson.annotations.SerializedName

class GithubResponseModel {
    @SerializedName("total_count")
    val totalCount: Int =  0

    @SerializedName("items")
    val items: List<GithubRepoModel> = listOf()
}

GithubRepoModel.kt

package com.example.hyunsikyoo.retrofit_example_kotlin.model

import com.google.gson.annotations.SerializedName

class GithubRepoModel {
    @SerializedName("id")
    val id: Long = 0

    @SerializedName("name")
    val name: String = ""

    @SerializedName("full_name")
    val fullName: String = ""
}

API 클래스 정의

데이터 클래스까지 정의했으니 API코드를 구현합니다.

GithubApi.kt

package com.example.hyunsikyoo.retrofit_example_kotlin.retrofit

import com.example.hyunsikyoo.retrofit_example_kotlin.model.GithubResponseModel
import io.reactivex.Observable
import retrofit2.http.GET
import retrofit2.http.Query

class GithubApi {

    interface GithubApiImpl {
        @GET("/search/repositories")
        fun getRepoList(@Query("q") query: String): Observable<GithubResponseModel>
    }

    companion object {
        fun getRepoList(query: String): Observable<GithubResponseModel> {
            return RetrofitCreator.create(GithubApiImpl::class.java).getRepoList(query)
        }
    }
}

레트로핏을 통해 수신받는 데이터의 형태를 스트림 형태로 사용하기 위해 Reactive를 활용하여 Observable형태로 수신 받습니다. 그리고 API 호출의 편의를 위해 companion object를 정의했습니다.


Creator 정의

Retrofit을 호출하기 위한 Creator를 정의합니다. Creator는 API를 바로 호출할 수 있도록 설정해주는 클래스 입니다.

RetrofitCretor.kt

package com.example.hyunsikyoo.retrofit_example_kotlin.retrofit

import com.example.hyunsikyoo.retrofit_example_kotlin.BuildConfig
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory

class RetrofitCreator {

    companion object {
        val API_BASE_URL = "https://api.github.com"

        private fun defaultRetrofit(): Retrofit {
            return Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .client(createOkHttpClient())
                    .build()
        }

        fun <T> create(service: Class<T>): T {
            return defaultRetrofit().create(service)
        }

        private fun createOkHttpClient(): OkHttpClient {
            val interceptor = HttpLoggingInterceptor()
            if (BuildConfig.DEBUG) {
                interceptor.level = HttpLoggingInterceptor.Level.BODY
            } else {
                interceptor.level = HttpLoggingInterceptor.Level.NONE
            }

            return OkHttpClient.Builder()
                    .addNetworkInterceptor(interceptor)
                    .build()
        }
    }
}

API 호출

이제 모든 조건은 갖춰졌으니 API를 호출해봅시다.

MainActivity.kt

package com.example.hyunsikyoo.retrofit_example_kotlin

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.example.hyunsikyoo.retrofit_example_kotlin.model.GithubResponseModel
import com.example.hyunsikyoo.retrofit_example_kotlin.retrofit.GithubApi
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers

class MainActivity : AppCompatActivity() {
    lateinit var compositeDisposable: CompositeDisposable

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        compositeDisposable = CompositeDisposable()

        compositeDisposable.add(GithubApi.getRepoList("test")
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.newThread())
                .subscribe({ response: GithubResponseModel ->
                    for (item in response.items) {
                        Log.d("MainActivity", item.name)
                    }
                }, { error: Throwable ->
                    Log.d("MainActivity", error.localizedMessage)
                    Toast.makeText(this, "Error ${error.localizedMessage}", Toast.LENGTH_SHORT).show()
                }))
    }

    override fun onDestroy() {
        super.onDestroy()
        compositeDisposable.dispose()
    }
}

 

전체 샘플 데모 프로젝트는 아래 링크에 있습니다.
https://github.com/Hyunsik-Yoo/retrofit-example-kotlin