24.01.25 API에서 복수 데이터 정렬하기

KSang·2024년 1월 29일
0

TIL

목록 보기
40/101

API에서 복수 데이터 정렬하기

지난 시간에 이미지 데이터를 서버에서 받아 왔는데

이제 동영상 정보도 가져와 줄거다

Model

우선은 데이터를 받을 모델을 하나더 만들어야한다

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
  "meta": {    
    "total_count": 6033,
    "pageable_count" : 800,
    "is_end": false
  },
  "documents": [
    {
      "title": "AOA 지민·김용만, 돼지꼬리 맛에 정신혼미 ‘극찬세례’",
      "play_time": 185,
      "thumbnail": "https://search2.kakaocdn.net/argon/138x78_80_pr/FRkbdWEKr4F",
      "url": "http://tv.kakao.com/channel/2653417/cliplink/304487728?playlistId=87634",
      "datetime": "2017-05-06T00:36:45+09:00",
      "author": "_SBS"
    },
    ...
  ]
}

동영상은 이런식으로 데이터를 받아오는데, 이전에 만든 이미지 뷰의 데이터 클래스가

data class ImageModel(
    val meta: Meta,
    val documents: List<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
)

이렇게 되어있다.

이걸 이용해서 만들순없을까

Generic Class

data class ApiResponse<T>(
    val meta: Meta?,
    val documents: List<T>?
)

data class Meta(
    @SerializedName("total_count")
    val totalCount: Int?,
    @SerializedName("pageable_count")
    val pageableCount: Int?,
    @SerializedName("is_end")
    val isEnd: Boolean?
)

data class ImageDocument(
    val collection: String?,
    @SerializedName("thumbnail_url")
    val thumbnailUrl: String?,
    @SerializedName("image_url")
    val imageUrl: String?,
    val width: Int?,
    val height: Int?,
    @SerializedName("display_sitename")
    val displaySitename: String?,
    @SerializedName("doc_url")
    val docUrl: String?,
    val datetime: String?
)

data class VclipDocument(
    val title: String?,
    val url: String?,
    val datetime: String?,
    @SerializedName("play_time")
    val playTime: Int?,
    val thumbnail: String?,
    val author: String?
)

제네릭 클래스를 이용해서 이런식으로 만들어 줬다

제네릭 클래스는 메소드에서 사용할 데이터 타입을 나중에 확정하는 기법인데

이를 이용해서 다양한 타입의 문서 목록(documents)을 갖을 수 있도록 설계했다.

이렇게 함으로써 이미지 검색 API 응답과 비디오 클립 검색 API 응답을 동일한 클래스로 처리할 수 있다

Repository

이제 데이터를 불러오는 레포지토리를 수정 해보자

interface ApiRepository{
    suspend fun searchData(query: String, sort: String, page: Int): MutableList<SearchModel>
}

일단 저번에 만든 레포지토리를 파사드 패턴으로 바꿔주고

if (imageApi.isSuccessful) {
            imageApi.body()?.documents?.map { document ->
                SearchModel(
                    image = Uri.parse(document.image_url),
                    title = document.collection,
                    site = document.display_sitename,
                    date = dateTimeFormatter(document.datetime),
                    doc_url = document.doc_url
                )
            }?.let { searchList.addAll(it) }
        }

이미지 데이터를 불러올때 만든 이 로직을 수정해 줄거다

이런느낌으로 동영상 부분도 똑같이 만들고 리스트를 정렬 해주면될까 싶었는데

        if (imageApi.isSuccessful) {
            imageApi.body()?.documents?.map { document ->
                SearchModel(
                    thumnail = document.imageUrl.toString(),
                    title = document.collection,
                    siteName = document.collection.toString(),
                    date = dateTimeFormatter(document.datetime),
                    docUrl = document.docUrl.toString()
                )
            }?.let { imageApiResults.addAll(it) }
        }

        if (vclipApi.isSuccessful) {
            vclipApi.body()?.documents?.map { 
                ...
            }?.let { vclipApiResults.addAll(it) }
        }

        val combinedResults = imageApiResults + vclipApiResults
        combinedResults.sortedByDescending { LocalDateTime.parse(it.date, formatter) }.toMutableList()
        searchList = combinedResults
        return searchList
    }

뷰에선 이미지 부분이 먼저 불러와지고 비디오 부분이 불러와지면서 정렬이 안됐다

데이터가 불러와질때 마다 데이터를 처리를 해주고 리턴해줘서 정렬이 안되는거 같은데

예상만할뿐 정확한 이유는 자세히 정의하진 못하겠다. //

return withContext(Dispatchers.IO) {
        val imageResults = searchImage(query, sort, page).documents?.map { convertImageSearchModel(it) } ?: listOf()
        val vclipResults = searchVclip(query, sort, page).documents?.map { convertVideoSearchModel(it) } ?: listOf()

        val combinedResults = imageResults + vclipResults

        combinedResults.sortedByDescending { LocalDateTime.parse(it.date, formatter) }.toMutableList()
    }

이전 코드에서 다른 실수가 있었던것 같다 글 을 쓰면서 비슷하게 새로 만들어 봤는데

정렬이 잘 된다...

하지만 단점으로는 이미지와 동영상 데이터가 순차적으로 처리된다.

이미지 데이터와 동영상데이터를 병렬로 함께 처리하는게 더 효율 적이다

async await

어쨋든 이 상황을 해결 하려면 결국 비동기적인 데이터를 묶어주고 데이터가 전부 불러와질때까지 기다리게 해줘야 하는데

async와 await를 사용해서 구현했다.

override suspend fun searchData(query: String, sort: String, page: Int): MutableList<SearchModel> {
        if (page == 1) searchList.clear()
        searchList.addAll(loadApiData(query,sort,page))
        return searchList
    }

    private suspend fun loadApiData(query: String, sort: String, page: Int): MutableList<SearchModel> {
        return withContext(Dispatchers.IO) {
            val imageResultsDeferred = async { searchImage(query, sort, page)
                .documents?.map { convertImageSearchModel(it) } }

            val vclipResultsDeferred = async { searchVclip(query, sort, page)
                .documents?.map { convertVideoSearchModel(it) } }

            val imageResults = imageResultsDeferred.await() ?: listOf()
            val vclipResults = vclipResultsDeferred.await() ?: listOf()

            val combinedResults = imageResults + vclipResults

            combinedResults.sortedByDescending { LocalDateTime.parse(it.date, formatter) }.toMutableList()
        }
    }

    private fun convertImageSearchModel(document: ImageDocument): SearchModel {
        return SearchModel(
            thumnail = document.imageUrl.toString(),
            title = document.displaySitename.toString(),
            siteName = document.collection.toString(),
            postType = "Image",
            date = dateTimeFormatter(document.datetime.toString()),
            docUrl = document.docUrl.toString()
        )
    }

    private fun convertVideoSearchModel(document: VclipDocument): SearchModel {
        return SearchModel(
            thumnail = document.thumbnail.toString(),
            title = document.title.toString(),
            siteName = document.author.toString(),
            postType = "Video",
            date = dateTimeFormatter(document.datetime.toString()),
            docUrl = document.url.toString()
        )
    }

async로 이미지 데이터와 , 동영상 데이터를 병렬처리해서 받은뒤 await해서 다 받을때까지 기다려준뒤 정렬 해준다.

이렇게 되면 병렬적으로 데이터를 받기 때문에 복수데이터를 처리 할때 훨씬 효율적으로 받을 수 있고, 완전한 데이터가 생기고 난 다음 로직이 수행되기 때문에 불필요한 작업을 줄일수 있다.

0개의 댓글