[Android 앱 개발 심화] 과제 - 이미지 검색 앱 (2) 카카오 이미지 검색 API

0
post-thumbnail
post-custom-banner

🍥구현 기능

  • 카카오 이미지 정보 API로 이미지 검색 결과 받아오기

🍥구현하기

카카오 이미지 검색 API

build.gradle(:app)

  • 필요한 dependency 추가하기
dependencies {
	//...
    implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'
    implementation 'com.squareup.okhttp3:okhttp:4.9.0'
    implementation 'com.squareup.okhttp3:okhttp-urlconnection:4.4.1'

    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
    implementation 'com.google.code.gson:gson:2.6.2'

    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
    //...
}

AndroidManifest.xml

  • 인터넷 사용 권한 추가하기
<uses-permission android:name="android.permission.INTERNET"/>

ImageDTO.kt

  • 카카오 이미지 검색 API의 응답으로 받는 JSON 구조와 동일하게 data class 설계하기
  • 응답 예시
    HTTP/1.1 200 OK
    Content-Type: application/json;charset=UTF-8
    {
    "meta": {
      "total_count": 422583,
      "pageable_count": 3854,
      "is_end": false
    },
    "documents": [
      {
        "collection": "news",
        "thumbnail_url": "https://search2.kakaocdn.net/argon/130x130_85_c/36hQpoTrVZp",
        "image_url": "http://t1.daumcdn.net/news/201706/21/kedtv/20170621155930292vyyx.jpg",
        "width": 540,
        "height": 457,
        "display_sitename": "한국경제TV",
        "doc_url": "http://v.media.daum.net/v/20170621155930002",
        "datetime": "2017-06-21T15:59:30.000+09:00"
      },
      ...
    ]
    }
  • JSON 응답 중 Datetime 타입은, Kotlin String 타입으로 받아오면 된다!
    (Java LocalDate/LocalDateTime/Date 타입으로 받아오려다가 삽질을 좀 했다 ㅠ.ㅠ)
data class ImageResponse(
    val meta: Meta,
    val documents: MutableList<Document>?
)

data class Meta(
    val total_count:Int,
    val pageable_count:Int,
    val is_end:Boolean
)

data class  Document(
    val collection:String,
    val thumbnail_url:String,
    val image_url:String,
    val width:Int,
    val height:Int,
    val display_sitename:String,
    val doc_url:String,
    val datetime: String
)

SearchClient.kt

object SearchClient {
    private const val SEARCH_BASE_URL = "https://dapi.kakao.com/v2/search/"

    private val searchRetrofit = Retrofit.Builder()
        .baseUrl(SEARCH_BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .client(createOkHttpClient())
        .build()
    val searchNetWork: SearchNetWorkInterface = searchRetrofit.create(SearchNetWorkInterface::class.java)

    private fun createOkHttpClient(): OkHttpClient {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY

        return OkHttpClient.Builder()
            .connectTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .addNetworkInterceptor(interceptor)
            .build()
    }
}

SearchNetWorkInterface.kt

  • REST API 키 헤더에 담아 GET으로 요청하기
    (REST API 키 발급 방법: kakao developers > 내 애플리케이션 > 애플리케이션 추가하기)
  • 검색 결과 최대 80개까지만 표시되도록 size 파라미터에 80 전달
const val REST_API_KEY = "..." 

interface SearchNetWorkInterface {
    @Headers("Authorization: KakaoAK ${REST_API_KEY}")
    @GET("image")
    suspend fun getImageResponse(
        @Query("query") query:String,
        @Query("size") size:Int = 80
    ) : ImageResponse
}

SearchFragment.kt

  • 키워드를 입력하고 '검색' 버튼을 클릭했을 때, 카카오 이미지 검색 API 응답 받아오기
    • 메인 쓰레드 내부가 아닌, Coroutine 내에서 실행하기
  • '검색' 버튼을 클릭했을 때, 키보드 숨기기

📌참고자료:코틀린 안드로이드 키보드 내리기, 숨기기

import android.content.Context
import android.view.inputmethod.InputMethodManager
val inputMethodManager = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
private fun hideKeyboard(inputMethodManager: InputMethodManager, view: View) {
  inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0);
  view.clearFocus()
}
	override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        initImageRecyclerView()
        setSearchButtonOnClickListener()
    }
    
    //...
    
	private fun setSearchButtonOnClickListener() {
        binding.btnSearch.setOnClickListener {
            hideKeyboard(it)

            val keyword = binding.etSearch.text.toString()
            lifecycleScope.launch { communicateImageSearchNetwork(keyword) }
        }
    }
    
    private fun hideKeyboard(view:View){
        val inputMethodManager = activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
        view.clearFocus()
    }

    private suspend fun communicateImageSearchNetwork(query: String) {
        var imageResponse: ImageResponse? = null
        val job = lifecycleScope.launch {
            try {
                imageResponse = SearchClient.searchNetWork.getImageResponse(query)
            } catch (e: Exception) {
                e.printStackTrace()
                cancel()
            }
        }
        job.join()

        val newDataset = imageResponseToDataset(imageResponse)
        updateImageRecyclerView(newDataset)
    }

    private fun updateImageRecyclerView(newDataset: MutableList<Image>) {
        imageAdapter.changeDataset(newDataset)
    }
profile
Be able to be vulnerable, in search of truth
post-custom-banner

0개의 댓글