Android 공부

Retrofit을 통한 Naver 영화 API 사용하기


0. 서론


아무 의미 없이 코드를 작성할 땐, 단순하게 통신이 됐으면 그저 내 것이라고 생각을 했다.

RxJava를 공부하면서 느낀 것이지만 기존에 알고 있던 코드를 분석해보고, 이야기를 해 볼 필요가 있다는 것을 알게 됐다.


1. Retrofit


1-1. Retrofit


각종 클라이언트 통신 라이브러리가 있다면, 해당 라이브러리를 맵핑해서 REST 방식의 호출을 사용할 수

있게 하는 유용한 라이브러리이다.


1-2. OkHttp


OkHttp 라이브러리는 HTTP 호출시에 각종 값들을 셋팅할 수 있게 도와주는 라이브러리이다.

Retrofit과 같이 사용되는 이유는 OkHttp를 통해서 쿠키, timeout, log등 각종 HTTP와 관련된 통신을 도와주기 때문이다.

Retrofit은 이처럼 클라이언트를 통해서 얻어온 정보를 Gson, Json, RxJava등 유연하게 랩핑할 수 있게 도와주기 때문이다.


2. Naver Movie API 분석


2-1. 사이트 :


 https://developers.naver.com/docs/search/movie/



2-2. json 분석:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2018-12-12 19:28:01.335 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "lastBuildDate": "Wed, 12 Dec 2018 19:28:02 +0900",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "total": 3,
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "start": 1,
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "display": 3,
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "items": [
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: {
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "title": "더 자이언트",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "link": "https://movie.naver.com/movie/bi/mi/basic.nhn?code=100994",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "image": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1009/100994_P07_152556.jpg",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "subtitle": "YAK : The Giant King",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "pubDate": "2012",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "director": "프라파스 콜사라논|",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "actor": "",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "userRating": "7.88"
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: },
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: {
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "title": "안아줘요 <b>무무</b> ! - 몽글몽글 도레미",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "link": "https://movie.naver.com/movie/bi/mi/basic.nhn?code=97160",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "image": "https://ssl.pstatic.net/imgmovie/mdi/mit110/0971/97160_P01_112216.jpg",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "subtitle": "MuMuHug-&quot;Bubble Bubble Do-Re-Mi&quot;",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "pubDate": "2012",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "director": "스텔라 후앙|",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "actor": "",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "userRating": "10.00"
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: },
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: {
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "title": "안아줘요 <b>무무</b> - 굿바이 복어",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "link": "https://movie.naver.com/movie/bi/mi/basic.nhn?code=70874",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "image": "",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "subtitle": "MuMuHug - Good-Bye Balloon Fish",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "pubDate": "2008",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "director": "빅 왕|",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "actor": "",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "userRating": "10.00"
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: }
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: ]
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: }
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: <-- END HTTP (1061-byte body)
cs


2-3. 코드:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Movie(
    @SerializedName("lastBuildDate") var lastBuildDate: String,
    @SerializedName("total") var total: Int,
    @SerializedName("start") var start: Int,
    @SerializedName("display") var display: Int,
    @SerializedName("items") var items: Array<Items>
) {
    data class Items(
        @SerializedName("title")
        val title: String,
        @SerializedName("link")
        val link: String,
        @SerializedName("image")
        val image: String,
        @SerializedName("subtitle")
        val subtitle: String,
        @SerializedName("pubdate")
        val pubDate: String,
        @SerializedName("director")
        val director: String,
        @SerializedName("actor")
        val actor: String,
        @SerializedName("userRating")
        val userRating: Double
    )
}
cs

2-4. 해석:

Movie라는 class를 만듭니다.

@SerializedName(name)을 통해서 api에서 가져올 변수 명을 맞춰줍니다.

Items라는 data class를 만들어주고 해당 형태는 여러개를 뜻하기 때문에 Array 형태로 가져옵니다.


3. Interface


3-1. 코드:


1
2
3
4
5
6
7
8
9
10
11
12
13
interface MovieService {
    //GET 방식으로 url을 맵핑할 수 있음.
    @GET("search/{type}")
    //Header를 통해서 각종 값을 전송할 수 있음
    //Path를 통해서 구체적인 API URI로 이동할 수 있음
    //Query를 통해서 웹에 쿼리를 
    fun getSearch(
        @Header("X-Naver-Client-Id") clientId: String,
        @Header("X-Naver-Client-Secret") clientPw: String,
        @Path("type") type: String,
        @Query("query") query: String
    ): Observable<Movie>
}
cs


3-2. 해석:


MovieSearch라는 interface를 만들어서 추후에 레트로핏을 통해서 각종 값을 전달한다.

Naver Movie API에는 client Id, client Secret을 전달해주고

해당 Service를 다른 api에도 재사용할 수 있을 것이라고 생각했기 때문에 

Path 변수를 통해서 uri에 접근한다.

Query를 통해서 찾으려는 word를 검색한다.


4. SearchService


4-1. 코드:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 
import hbs.com.boostcampmoviesearch.Interface.MovieService
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
 
class SearchService{
    private val CONNECT_TIMEOUT:Long = 15
 
    //log를 볼 수 있도록 httpLogginInterceptor를 만듦
    private val okHttpClient : OkHttpClient
    get()
    {
        val httpLoggingInterceptor = HttpLoggingInterceptor()
        httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        return OkHttpClient().newBuilder()
            .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
            .addInterceptor(httpLoggingInterceptor)
            .build()
    }
 
    //retrofit에 각종 값을 setting
    val movieRetrofit:MovieService
    get() {
        return Retrofit.Builder().baseUrl(API_URI.MOVIE_SEARCH.uri)//movieUri
            .client(okHttpClient)//httpClient연결을 통해 log 확인
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//RxJava2를 사용하도록 Factory생성
            .addConverterFactory(GsonConverterFactory.create())//Gson을 쓸 수 있도록 Factory생성
            .build().create(MovieService::class.java)//MovieService Interface사용
    }
}
cs

4-2. 해석:


14~23번째 줄을 통해서 okHttpClient를 생성한다.

해당 okHttpClient를 통해서 Log를 트랙킹하는 httpLoggingInterceptor를 만들고

timeout등을 지정해줄 수 있다.


5. findMovie 매소드

5-1. 코드:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import android.annotation.SuppressLint
import android.arch.lifecycle.MutableLiveData
import android.content.Context
import android.util.Log
import com.bumptech.glide.RequestManager
import hbs.com.boostcampmoviesearch.Adapter.SearchMovieAdapter
import hbs.com.boostcampmoviesearch.Model.Movie
import hbs.com.boostcampmoviesearch.Utils.SearchService
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.util.*
 
class MainVM(val context: Context, var searchDatas: ArrayList<Movie.Items>, var requestManager: RequestManager) {
    var searchWord: MutableLiveData<String>= MutableLiveData() //검색할 영화 제목
    var searchMovieAdapter = SearchMovieAdapter(context, searchDatas, requestManager) //어댑터 생성
 
 
    @SuppressLint("CheckResult")
    fun findMovie(searchWord: MutableLiveData<String>) {
        clearDatas() //찾기 전에 recyclerView와 data 초기화
        val movieRetrofit = SearchService().movieRetrofit//retrofit을 가져옴
        if (searchWord.value.isNullOrEmpty()) {//searchWord의 값이 비었다면 return
            return
        }
        movieRetrofit.getSearch(
            clientId = getResourceString(R.string.naver_client_id),
            clientPw = getResourceString(R.string.naver_client_secret),
            type = "movie.json",
            query = searchWord.value!!
        ).subscribeOn(Schedulers.io())//subscribeOn, subscribe시에 사용할 스레드 :  Schedulers.io를 통해 비동기화 시킵니다.
            .observeOn(AndroidSchedulers.mainThread())//observer시에 사용할 스레드 : Android ui thread에서 동작
            .subscribe({ movie ->
                movie!!.items.iterator().forEach {
                    searchDatas.add(it)
                    searchMovieAdapter.notifyItemInserted(searchDatas.size - 1)
                }
            }, { error -> Log.d("error", error.toString()) }
                , {  Log.d("onComplete""onComplete") }
            )
    }
 
    private fun clearDatas() {
        searchMovieAdapter!!.notifyItemRangeRemoved(0, searchDatas.size)
        searchDatas.clear() //이전 데이터를 클리어
    }
 
    private fun getResourceString(resourceId: Int): String {
        return context.resources.getString(resourceId)
    }
}
cs


5-2. 해석:


movieRetrofit을 만든 후에 searchWord의 값이 비지 않았다면 retrofit을 실행

하지만, clear하는 것만으로도 빈 값이 들어오더라도 UI를 초기화 한다.

movirRetrofit.getSearch(~)를 통해서 각종 값들을 넣어준다ㅏ.

subscribeOn은 subscribe가 실행될 쓰레드를 선택하고, observeOn은 observe가 실행될 쓰레드를 선택한다.


subscribe({},{},{}) 를 통해서는 next, error, complete를 람다식 형태로 편하게 다룰 수 있고

movie의 결과를 통해서 movie.items의 iterator를 만들어서 forEach문 형태로 돌려줍니다.

recyclerView에 사용할 것이기 때문에 list에 add를 시켜줬습니다.


6. 결과


{
2018-12-12 19:28:01.335 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "lastBuildDate": "Wed, 12 Dec 2018 19:28:02 +0900",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "total": 3,
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "start": 1,
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "display": 3,
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "items": [
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: {
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "title": "더 자이언트",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "link": "https://movie.naver.com/movie/bi/mi/basic.nhn?code=100994",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "image": "https://ssl.pstatic.net/imgmovie/mdi/mit110/1009/100994_P07_152556.jpg",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "subtitle": "YAK : The Giant King",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "pubDate": "2012",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "director": "프라파스 콜사라논|",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "actor": "",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "userRating": "7.88"
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: },
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: {
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "title": "안아줘요 <b>무무</b> ! - 몽글몽글 도레미",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "link": "https://movie.naver.com/movie/bi/mi/basic.nhn?code=97160",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "image": "https://ssl.pstatic.net/imgmovie/mdi/mit110/0971/97160_P01_112216.jpg",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "subtitle": "MuMuHug-&quot;Bubble Bubble Do-Re-Mi&quot;",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "pubDate": "2012",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "director": "스텔라 후앙|",
2018-12-12 19:28:01.336 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "actor": "",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "userRating": "10.00"
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: },
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: {
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "title": "안아줘요 <b>무무</b> - 굿바이 복어",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "link": "https://movie.naver.com/movie/bi/mi/basic.nhn?code=70874",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "image": "",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "subtitle": "MuMuHug - Good-Bye Balloon Fish",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "pubDate": "2008",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "director": "빅 왕|",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "actor": "",
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: "userRating": "10.00"
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: }
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: ]
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: }
2018-12-12 19:28:01.337 28221-30728/hbs.com.boostcampmoviesearch D/OkHttp: <-- END HTTP (1061-byte body)



과거에 아무것도 모르고 레트로핏과 rxJava를 썼을 때와는 다르게 사용했던 라이브러리도 제대로 공부하면서 글을 쓰다보니 글을 쓰는데

많은 시간이 소요된 것 같다. 하지만, 좋은 경험이었다고 생각한다.



'Android 공부' 카테고리의 다른 글

Firebase Crashlytics 시작하기  (0) 2018.12.21
안드로이드 Room 고군분투  (0) 2018.12.21
Android 2way databiding - bindingAdapter(2)  (0) 2018.12.12
안드로이드 2way DataBinding  (0) 2018.12.11
LiveData의 간단한 예제  (0) 2018.11.30