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. 사이트 :
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-"Bubble Bubble Do-Re-Mi"", 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등을 지정해줄 수 있다.
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-"Bubble Bubble Do-Re-Mi"",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 |