Retrofit 예제

jericho·2024년 1월 28일

Android

목록 보기
8/15

포스트맨 이라는 걸로 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"
            }
}

0개의 댓글