🍥구현 기능
- 카카오 이미지 정보 API로 이미지 검색 결과 받아오기
🍥구현하기
카카오 이미지 검색 API
build.gradle(:app)
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)
}