[Kotlin] Retrofit 사용법

Hand·2022년 8월 9일
0

Android

목록 보기
9/17

💡 TMDB를 사용하여 영화 목록을 가져오도록 하겠습니다.
api_key를 발급받아 사용해야하니, 따라해보고 싶은 분들은 검색을 통해 발급 받고 사용해보시면 좋을 것 같습니다.

TMDB

tmdb는 영화의 정보를 가져올 수 있는 api를 제공하는 사이트입니다.

1. build.gradle (app)

제일 처음 우리는 Retrofit을 사용해주기 위해서 gradle에 라이브러리를 추가해 줄 필요가 있습니다.

gradle의 하단의 dependencies 를 확인하면 초기 임에도 불구하고 많은 항목들을 implementation하고 있는 모습을 확인할 수 있습니다.

여기서 우리는 retrofit과 Okhttp3를 추가해줄 것입니다.
Okhttp3의 경우 api를 사용할 때, 어떤 데이터를 주고 받는지, 어떤 에러가 발생하는지 확인할 수 있도록 interceptor를 사용해주기 위해서 입니다.
(간단한 작업이라 필요없다고 생각하실 경우 생략해도 무관합니다.)

//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
implementation 'com.google.code.gson:gson:2.9.0'

//Okhttp3
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.10.0'
implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.10.0'

버전의 경우
https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit
여기서 확인하실 수 있습니다.

그리고 parcelize를 사용해주기 위해서 plugins에 코드를 추가해줍시다.
id 'kotlin-parcelize'

2. AndroidManifext.xml

http와 https의 차이점은 보안을 담당하는 계층이 있냐 하나의 차이지만, 우리가 사용할 땐 말이 달라집니다.

사이드프로젝트를 진행하면서, 백엔드를 담당하시는 분이 http로 시작하는 base_url을 제공해줄 경우, Manifest에 코드를 추가해 줄 필요가 있습니다. 안드로이드는 기본적으로 http를 통한 접근을 제한하기 때문입니다.
(tmdb의 base_url은 https를 사용합니다.)

AndroidManifest.xml의 application의 바로 아래에 코드를 추가해 줄 필요가 있습니다.
android: useCleartextTraffic="true"

그리고 위의 경우와 상관없이, 인터넷을 통해서 데이터를 주고받는 작업을 행하기에, permission을 추가해줄 필요가 있습니다.

AndroidManifext.xml의 manifext 단에 코드를 추가해줍니다.
<uses-permission android:name="android.permission.INTERNET" />

3. RetrofitClient

retrofit client의 경우 싱글톤으로 만들어서 중복해서 생성하는 일을 방지할 수 있도록 합니다.

object RetrofitClient {
	// private 로 instance를 만들어 줍니다. 없으면 생성해서 반환하고, 있으면 이 객체를 반환해줄 것입니다.
    private var instance: Retrofit? = null
    private val gson = GsonBuilder().setLenient().create()
	
    private const val BASE_URL = "https://api.themoviedb.org/3/"
    // 타임아웃 시간을 설정하는 것입니다. (ms 기준)
    private const val CONNECT_TIMEOUT_SEC = 20000L

    fun getInstance() : Retrofit {
    	// 없으면 생성
        if(instance == null) {
        	// interceptor를 설정해줍니다.
            val interceptor = HttpLoggingInterceptor()
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)

			
            val client = OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .connectTimeout(CONNECT_TIMEOUT_SEC, TimeUnit.SECONDS)
                .build()

            instance = Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }
		
        // 반환
        return instance!!
    }
}

4. interface, Resquest, Response

api를 사용할 땐, Query, Request와 내가 받을 Response가 필요합니다.
그리고 이들을 어떤 HTTP method를 사용할지 정해줄 interface가 필요합니다.
이 들은 api 명세서를 통해 확인할 수 있습니다.

Request
POST나 PUT, PATCH의 경우 Request Body에 담아서 보내 줄 필요가 있습니다.
다음에 기회가 된다면 쓰도록 하겠습니다.

GET의 경우 Query String에 데이터를 담아서 보내주기 때문에 Query 정보가 필요합니다.

Query의 경우 api_key, language, page, region이 필요하다고 나옵니다.
이는 아래의 interface에서 어떻게 구현하는지 적혀있습니다.

Response

Response의 경우 담아줄 공간이 정말 많았습니다.

@Parcelize
data class NowPlayingResponse(
    var page: Int,
    @SerializedName("results")
    var movies: ArrayList<Movie>,
    var dates: Date,
    var total_pages: Int,
    var total_result: Int
) : Parcelable

@Parcelize
data class Movie(
    var poster_path: String,
    var adult: Boolean,
    var overview: String,
    var release_data: String,
    var genre_ids: List<Int>,
    var id: Int,
    var original_title: String,
    var original_language: String,
    var title: String,
    var backdrop_path: String,
    var popularity: Number,
    var vote_count: Int,
    var video: Boolean,
    var vote_average: Number
): Parcelable

@Parcelize
data class Date (
    var maximum: String,
    var minimum: String
) : Parcelable

여기서 @SerializedName와 @Parcelize라는 annotation이 보이실 겁니다.

SerializedName은 간단히 말해서 받은 객체의 이름은 A지만, 나는 B로 사용하고 싶다 할 때 사용합니다.
Parcelize는 간단히 말해서 데이터를 주고받기 편하도록 할 때 사용합니다.

interface
interface에는 POST, GET, DELETE, PUT, PATCH 등의 HTTP Method를 사용할 지 구현해 둘 필요가 있습니다.

interface MovieService {
    @Headers("content-type: application/json")
    @GET("movie/now_playing")
    fun getNowPlaying(
        @Query("api_key") api_key: String,
        @Query("language") language: String,
        @Query("page") page: Int
    ): Call<NowPlayingResponse>
}

5. Activity에서 사용해보기

일단 retrofit의 구현이 필요합니다.

private fun initRetrofit() {
	retrofit = RetrofitClient.getInstance()
	movieService = retrofit.create(MovieService::class.java)
}

그 후, api를 사용해주면 완료됩니다.

private fun getNowPlayingMovie() {
	var nowPlayingService = movieService.getNowPlaying("${BuildConfig.TMDB_API_KEY}", "ko, en-US", 1)
	
    nowPlayingService.enqueue(object: Callback<NowPlayingResponse> {
		override fun onResponse(call: Call<NowPlayingResponse>, response: Response<NowPlayingResponse> ) {
			if(response.isSuccessful) {
				val movies = response.body()!!.movies
				Log.e("Movies: ", "$movies")
			}
		}

		override fun onFailure(call: Call<NowPlayingResponse>, t: Throwable) {
			Log.e("MoviesError", "$t")
		}
	})
}

onResponse 함수와 onFailure 함수가 보일 것입니다.
이 둘은 각각 성공을 했을 때 어떤 명령을 처리할 지를 나눠놓은 것입니다.

저는 성공을 했을 경우 Movies를 출력하고, 실패했을 경우 Error Log를 출력하도록 해놨습니다.

로그를 확인하면, 정상적으로 데이터를 받는 모습을 볼 수 있습니다.

이 데이터를 어떻게 가공하여 보여줄 지는 개발자의 몫이라고 생각합니다!

Code

https://github.com/tkdwns0301/cookie

profile
화이팅!

0개의 댓글