포스트맨 이라는 걸로 api 테스트해볼 수 있다. 쿼리 보내면 결과 받음.
https://app.quicktype.io/
여기에 쿼리 결과 JSON 집어넣으면 코틀린 등 여러 타입으로 변환해줌.
// 매니페스트
<uses-permission android:name="android.permission.INTERNET" />
// 그래들 dependencies
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
API 키를 local.properties 에 숨긴다. 자세한 내용은 포스팅 참고.
https://velog.io/@jericho3/API-key-hiding-local.properties
응답에 맞춰서 DTO를 작성해준다.
( https://app.quicktype.io/ )
(json to kotlin 으로 검색하면 여러 사이트가 나온다)
// KakaoDTO.kt
import com.google.gson.annotations.SerializedName
data class KakaoData(
val documents: List<Document>,
val meta: Meta
)
data class Document(
val collection: Collection,
val datetime: String,
@SerializedName("display_sitename")
val displaySitename: String,
@SerializedName("doc_url")
val docURL: String,
val height: Long,
@SerializedName("image_url")
val imageURL: String,
@SerializedName("thumbnail_url")
val thumbnailURL: String,
val width: Long
)
enum class Collection(val value: String) {
@SerializedName("blog")
Blog("blog"),
@SerializedName("cafe")
Cafe("cafe"),
@SerializedName("etc")
Etc("etc"),
@SerializedName("news")
News("news");
}
data class Meta(
@SerializedName("is_end")
val isEnd: Boolean,
@SerializedName("pageable_count")
val pageableCount: Long,
@SerializedName("total_count")
val totalCount: Long
)
api 네트워크 인터페이스를 만든다.
//
interface KakaoImageApiService {
@Headers("Authorization: KakaoAK ${BuildConfig.KAKAO_API_KEY}")
@GET("/v2/search/image")
suspend fun searchImage(
@Query("query") query: String,
@Query("sort") sort: String? = null,
@Query("page") page: Int? = null,
@Query("size") size: Int? = null
): Response<KakaoData>
}
NetworkClient 만든다
//
object NetworkClient {
private const val KAKAO_BASE_URL = "https://dapi.kakao.com"
private fun createOkHttpClient(): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
if (BuildConfig.DEBUG)
interceptor.level = HttpLoggingInterceptor.Level.BODY
else
interceptor.level = HttpLoggingInterceptor.Level.NONE
return OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addNetworkInterceptor(interceptor)
.build()
}
private val kakaoRetrofit = Retrofit.Builder()
.baseUrl(KAKAO_BASE_URL).addConverterFactory(GsonConverterFactory.create()).client(
createOkHttpClient()
).build()
val kakaoNetwork: KakaoImageApiService = kakaoRetrofit.create(KakaoImageApiService::class.java)
}
뷰모델에서 api 날리고 받아서 응답 저장
// ImageSearchViewModel
private var _kakaoData: MutableLiveData<Response<KakaoData>?> =
MutableLiveData()
val kakaoData: LiveData<Response<KakaoData>?> get() = _kakaoData
suspend fun communicateNetwork(
query: String,
sort: String? = null,
page: Int? = null,
size: Int? = null
) {
_kakaoData.value = NetworkClient.kakaoNetwork.searchImage(query, sort, page, size)
Log.d("kakaoData ::", kakaoData.value.toString())
}
프래그먼트에서 뷰모델 api 메서드 호출하고 옵저빙.
// 프래그먼트
private fun communicateNetwork(
query: String,
sort: String? = null,
page: Int? = null,
size: Int? = null
) = lifecycleScope.launch() {
viewModel.communicateNetwork(query, sort, page, size)
}
// 버튼클릭
communicateNetwork(b.etSearchInput.text.toString())
// (일단 잘 들어오는지 텍스트로 확인)
viewModel.kakaoData.observe(viewLifecycleOwner) {
b.tvSearchDebug.text = it.toString() + "\n\n" +
it?.body()?.meta + "\n\n" +
it?.body()?.documents?.fold("") { s: String, document: Document ->
s + document + "\n\n"
}
}