07
30

๋ฌดํ•œ ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•ด์•ผ๋˜๋Š”๋ฐ, ๊ธฐ์กด์— ๊ฒฝํ—˜ํ•ด๋ดค๋˜ ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ ๋ทฐ ์Šคํฌ๋กค ๊ฐ์ง€๋ฐฉ๋ฒ• ๋ง๊ณ  ํŽ˜์ด์ง• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค. ์ด๋ฒˆ์— ์‚ฌ์šฉํ•œ ๊ฑด Paging3๋‹ค.

https://developer.android.com/topic/libraries/architecture/paging/v3-overview?hl=ko

 

ํŽ˜์ด์ง• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ์š”  |  Android Developers

์ด ํŽ˜์ด์ง€๋Š” Cloud Translation API๋ฅผ ํ†ตํ•ด ๋ฒˆ์—ญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ํŽ˜์ด์ง• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ์š” Android Jetpack์˜ ๊ตฌ์„ฑ์š”์†Œ Paging

developer.android.com

์ดˆ๊ธฐ ์„ธํŒ…๊ฐ™์€ ๊ฑด ๋ฌธ์„œ๋ณด๋ฉด ์ž˜ ๋‚˜์™€์žˆ์œผ๋‹ˆ ๋‚˜๋Š” ๋‚ด๊ฐ€ ์ ์šฉํ•ด๋ณด๋ฉด์„œ ๋ง‰ํ˜”๋˜ ๋ถ€๋ถ„ ์œ„์ฃผ๋กœ ์ ๊ฒ ๋‹ค.\

# Paging ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

ํŽ˜์ด์ง• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Android Jetpack์— ์†ํ•ด์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฉฐ ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž‘์€ ๋ฉ์–ด๋ฆฌ๋กœ ๋‚˜๋ˆ ์„œ ํšจ์œจ์ ์œผ๋กœ ๋กœ๋“œํ•˜๊ณ  ํ‘œ์‹œํ•ด์ค€๋‹ค. ์Šคํฌ๋กค์ด๋ฒคํŠธ๋ฅผ ์•Œ์•„์„œ ๊ฐ€์ ธ๊ฐ€ ๋‹ค์Œ ํŽ˜์ด์ง€๋ฅผ ๋กœ๋“œํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ตœ์ ํ™” ๋˜์–ด์žˆ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. 

 

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๋ฉด์„œ ๋ฌด์กฐ๊ฑด ๋งŒ๋‚˜๊ฒŒ ๋  ์ปดํฌ๋„ŒํŠธ 5๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  • `PagingSource`: ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๊ณณ์ด๋‹ค. ํŽ˜์ด์ง€ ๋ณ„๋กœ api๋‚˜ db๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒŒ ์—ฌ๊ธฐ์— ํ•ด๋‹นํ•œ๋‹ค.
  • `PagingConfig`: ํŽ˜์ด์ง• ๋™์ž‘์„ ๊ตฌ์„ฑํ•œ๋‹ค. ํŽ˜์ด์ง€ ํฌ๊ธฐ๋Š” ์–ผ๋งˆ๋กœ ํ• ๊ฑด์ง€ ์ •ํ•˜๋Š” ๊ณณ์ด ์—ฌ๊ธฐ๋‹ค.
  • `Pager`: PagingSource์™€ PagingConfig๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ PagingData ์ŠคํŠธ๋ฆผ์„ ์ƒ์„ฑํ•ด flow๋กœ ์˜๋Š” ๊ณณ์ด๋‹ค.
  • `PagingData`: ํŽ˜์ด์ง•๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ปจํ…Œ์ด๋„ˆ๋‹ค. PagingData๋ผ๊ณ  ํ•ด์„œ ๋‹จ์ผ๋ฐ์ดํ„ฐ๋กœ ์ฐฉ๊ฐํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
  • `PagingDataAdapter`: RecyclerView Adapter์— ๋‹ฌ์•„์ฃผ๋Š” ๊ฑด๋ฐ, DiffUtil์ด ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ์‚ฌ์‹ค์ƒ ListAdapter์ธ๋ฐ ํŽ˜์ด์ง•์ด ๋‹ฌ๋ฆฐ๊ฑธ๋กœ ์ดํ•ดํ•˜๋ฉด๋œ๋‹ค. paging item์ด nullable ํ•œ ๊ฒƒ๊ณผ, submitList๊ฐ€ ์•„๋‹ˆ๋ผ submitData๋กœ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ”๋€ ๊ฒƒ ๋นผ๊ณ ๋Š” ๊ฑฐ์˜ ๋™์ผํ•˜๋‹ค.

์ฝ”๋“œ๋กœ ๋ณด๊ธฐ์ „์— ์•„ํ‚คํ…์ณ์— ์–ด๋–ป๊ฒŒ ์ ์šฉํ–ˆ๋Š”์ง€๋งŒ ๋งํ•˜๊ณ  ๊ฐ€๊ฒ ๋‹ค.

๊ณต์‹๋ฌธ์„œ์— ์“ฐ์—ฌ์žˆ๋Š” ํŽ˜์ด์ง• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์•„ํ‚คํ…์ณ ์ ์šฉ๋ฒ•์ด๋‹ค. ๋‚˜๋Š” PagingSource๋ฅผ data layer์— ๋‘๊ณ , repository์—์„œ Pager๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ํ–ˆ๋‹ค. ๊ณต์‹๋ฌธ์„œ์™€ ์กฐ๊ธˆ ๋‹ค๋ฅด์ง€๋งŒ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ์•„ํ‚คํ…์ณ๊ฐ€ `API -> DataSource -> Repository -> UseCase -> ViewModel`์ด๋ผ์„œ ๊ธฐ์กด ํŒจํ„ด์„ ์ตœ๋Œ€ํ•œ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด ์„ ํƒํ•œ ๋ฐฉ์‹์ด๋‹ค.

 

๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ PagingSource ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

class FeedPagingSource(private val feedDataSource: FeedDataSource) :
    PagingSource<Int, Feed>() {
    override fun getRefreshKey(state: PagingState<Int, Feed>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
        }
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Feed> {
        val nextPage = params.key ?: 1

        return when (val result = safeApiCall { feedDataSource.getFeed(nextPage, params.loadSize) }) {
            is ApiResult.Success -> {
                val response = result.data
                Timber.tag("pager").d("${response.items.firstOrNull()?.feedId}")

                LoadResult.Page(
                    data = response.items,
                    prevKey = if (nextPage == 1) null else nextPage - 1,
                    nextKey = if (response.items.isEmpty() || nextPage >= response.totalPages) null else nextPage + 1
                )
            }

            is ApiResult.Error -> {
                Timber.e(result.exception, "๋‹ค์Œ ํŽ˜์ด์ง€ ๋ชป ๋ถˆ๋Ÿฌ์™”์Œ $nextPage")
                LoadResult.Error(result.exception)
            }
        }
    }
}

PagingSource๋ฅผ ํ™•์žฅํ•˜๋ฉด, getRefreshKey์™€ load๋Š” ํ•„์ˆ˜๋กœ overrideํ•ด์•ผ๋œ๋‹ค. load๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค. ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ์™€ ๋กœ๋“œํ•  ์•„์ดํ…œ ์ˆ˜๋ฅผ ๋ฐ›์•„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค. getRefreshKey๋Š” ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์‹œ์ž‘ํ•  ํŽ˜์ด์ง€ ํ‚ค๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋‹ค.

 

๋‘ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์ข€ ์„ค๋ช…ํ•ด๋ณด๊ฒ ๋‹ค. load๋จผ์ € ๋ณด์ž.

`params.key`๋กœ ๋กœ๋“œํ•  ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๋จผ์ € ๋ฐ›๋Š”๋‹ค. ๋ฐ์ดํ„ฐ ์†Œ์Šค์—์„œ ํ˜ธ์ถœํ•œ api ๊ฒฐ๊ณผ๊ฐ€ success๋ฉด `LoadResult.Page`๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , Error๋ฉด `LoadResult.Error`๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฑด `LoadResult.Page`๋‹ค. List๋ฅผ data๋กœ ๋ฐ›๊ณ , ์ด์ „ ํŽ˜์ด์ง€ ํ‚ค์™€ ๋‹ค์Œ ํŽ˜์ด์ง€ ํ‚ค๋ฅผ ๋ฐ›๋Š”๋‹ค. ๋‚˜๋Š” ํ˜„์žฌ ํŽ˜์ด์ง€๊ฐ€ ์ „์ฒด ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๋ž‘ ๊ฐ™์„ ๋•Œ๋ฅผ ํŽ˜์ด์ง€ ๋์ด๋ผ๊ณ  ๋ช…์‹œํ•ด๋’€๋‹ค.

 

getRefreshKey๋Š” ์–ด๋–ป๊ฒŒ ๋Œ์•„๊ฐ€๋Š” ์ง€๋งŒ ๋ณด๊ณ  ๋„˜์–ด๊ฐ€๊ฒ ๋‹ค.

 

 

๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์–ด๋Š ํŽ˜์ด์ง€๋ถ€ํ„ฐ ๋‹ค์‹œ ๋กœ๋“œํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋‹ค. ์•ฝ๊ฐ„ savedStateInstance๋Š๋‚Œ์ธ๋ฐ, ๊ตฌ์„ฑ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•ด๋„ ๊ฐ™์€ ์Šคํฌ๋กค ์œ„์น˜๋ฅผ ๋ณด๊ณ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค. `state.anchorPosition`์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์žฌ ํ‘œ์‹œ๋œ ํ•ญ๋ชฉ์˜ ์œ„์น˜๋ฅผ ๊ฐ์ง€ํ•˜๊ณ , `closestPageToPosition`์œผ๋กœ ํ•ด๋‹น ์œ„์น˜์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํŽ˜์ด์ง€๋ฅผ ์ฐพ๋Š”๋‹ค. ์ด์ „ ํŽ˜์ด์ง€์˜ ๋‹ค์Œ ํŽ˜์ด์ง€ ๋˜๋Š” ๋‹ค์Œ ํŽ˜์ด์ง€์˜ ์ด์ „ ํŽ˜์ด์ง€๋ฅผ refreshKey๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. refresh ์‹œ์ ์„ ์ง€์ •ํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค. ๊ทผ๋ฐ ์ด๊ฑฐ๋„ ๋งŒ๋Šฅ์€ ์•„๋‹Œ๊ฒŒ null์„ ๋ฐ˜ํ™˜ํ•ด๋ฒ„๋ฆฌ๋ฉด ์ฒ˜์Œ๋ถ€ํ„ฐ ๋กœ๋“œํ•ด๋ฒ„๋ฆฐ๋‹ค.

 

์ด๋ ‡๊ฒŒ ๋งŒ๋“  PagingSource๋ฅผ Repository์—์„œ Pager๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ๋„ฃ์–ด์ค€๋‹ค. api ํ˜ธ์ถœ์„ Datasource์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ์–ด์„œ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ฝ”๋“œ๋ž‘์€ ์กฐ๊ธˆ ๋‹ค๋ฅด๋‹ค.

class FeedRepositoryImpl @Inject constructor(
    private val feedDataSource: FeedDataSource
) : FeedRepository {
    override fun getPagedFeed(): Flow<PagingData<Feed>> {
        return Pager(
            config = PagingConfig(
                pageSize = PAGE_SIZE,
                enablePlaceholders = false,
                initialLoadSize = PAGE_SIZE
            ),
            pagingSourceFactory = { FeedPagingSource(feedDataSource) },
        ).flow
    }

    companion object {
        private const val PAGE_SIZE = 10
    }
}

Pager๊ฐ€ ๊ตฌํ˜„๋œ ๋ชจ์Šต์ด๋‹ค. ์ฒ˜์Œ์— ๋ถˆ๋Ÿฌ์šธ ํŽ˜์ด์ง€ ์‚ฌ์ด์ฆˆ๋ฅผ ์ •ํ•˜๊ณ , pagingSourceFactory์— ์•„๊นŒ ๋งŒ๋“  PagingSource๋ฅผ ์ „๋‹ฌํ•ด์„œ flow๋กœ ์˜๋ฉด ๋์ด๋‹ค.

 

PagingConfig์ด ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด๋‹ค. ์ž์ฃผ ์“ฐ์ด๋Š” ํ”„๋กœํผํ‹ฐ๋“ค์„ ๊ฐ€์ ธ์™€ ๋ณด๋ฉด

PagingConfig(
    pageSize = PAGE_SIZE,
    enablePlaceholders = false,
    initialLoadSize = PAGE_SIZE * 2,
    prefetchDistance = PAGE_SIZE,
    maxSize = PAGE_SIZE * 5
)

์ด๊ฑธ ๋‹ค ์ •์˜ํ•ด์ค˜์•ผ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

  • `pageSize`:
    ๊ฐ ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ ๊ฐ€์ ธ์˜ฌ ํ•ญ๋ชฉ ์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ์ด๊ฒŒ ์ž‘์œผ๋ฉด ์ž‘์„ ์ˆ˜๋ก ๋„คํŠธ์›Œํฌ ์š”์ฒญ ๋นˆ๋„๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค.
  • `enablePlaceholders`:
    ์•„์ง ๋กœ๋“œ๋˜์ง€ ์•Š์€ ํ•ญ๋ชฉ์— ๋Œ€ํ•œ ํ”Œ๋ ˆ์ด์Šคํ™€๋” ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ด๋‹ค.
  • `initialLoadSize`:
    ์ฒ˜์Œ ๋กœ๋“œ ์‹œ ๊ฐ€์ ธ์˜ฌ ํ•ญ๋ชฉ์˜ ์ˆ˜๋ฅผ ์ •์˜ํ•œ๋‹ค. ๋„ˆ๋ฌด ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ดˆ๊ธฐ ๋กœ๋“œ๋•Œ ๊ฐ€์ ธ์˜ค๋ฉด ๋กœ๋”ฉ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง€์ง€๋งŒ ์ ๋‹นํžˆ ์ดˆ๊ธฐ ๋กœ๋”ฉ์„ ํ•ด๋‘๋ฉด UX ์ธก๋ฉด์—์„œ ์ข‹๋‹ค. ๋ณดํ†ต pageSize์˜ 2๋ฐฐ๋‚˜ 3๋ฐฐ์ •๋„๋กœ ์žก๋Š” ๊ฒƒ ๊ฐ™์•˜๋‹ค.
  • `prefetchDistance`:
    ์Šคํฌ๋กค์ด ๋์— ๋‹ฟ๊ธฐ ์ „์— ๋‹ค์ŒํŽ˜์ด์ง€๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋“œํ•˜๋Š” ๊ฑธ ์˜๋ฏธํ•œ๋‹ค. ์ด๊ฑด pageSize๋ž‘ ๊ฐ™๊ฒŒ ๊ฐ€์ ธ๊ฐ€๋Š” ๊ฒƒ ๊ฐ™๋‹ค.
  • `maxSize`:
    ๋ฉ”๋ชจ๋ฆฌ์— ์œ ์ง€ํ•  ์ตœ๋Œ€ ํ•ญ๋ชฉ์ˆ˜๋ฅผ ์ •์˜ํ•œ๋‹ค. ๋งค๋ฒˆ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ํ•˜์ง€์•Š๊ณ , ๋ฉ”๋ชจ๋ฆฌ ์ตœ๋Œ€์‚ฌ์šฉ๋Ÿ‰์„ ๋”ฑ ์ •ํ•ด๋†“๊ณ  ์“ฐ๋Š” ๋ฐฉ์‹์ด๋ผ ์–ด๋–ป๊ฒŒ ๋ณด๋ฉด ํšจ์œจ์ ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

์ด์ œ ๋ทฐ๋ชจ๋ธ์—์„œ ์ŠคํŠธ๋ฆผ์„ ๋ฐ›์•„์ค˜์•ผํ•œ๋‹ค.

private val _pagedFeed = MutableStateFlow<PagingData<Feed>?>(null)
val pagedFeed: StateFlow<PagingData<Feed>?> = _pagedFeed

fun getFeed() {
    viewModelScope.launch {
        getPagedFeedUseCase().cachedIn(viewModelScope).collectLatest { pagedData ->
            _pagedFeed.value = pagedData
        }
    }
}

PagingData ์ปจํ…Œ์ด๋„ˆ๋กœ ๊ฐ์‹ธ์ค€๋‹ค. ๋‹จ์–ด๊ฐ€ Data๋ผ์„œ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ปจํ…Œ์ด๋„ˆ๋‹ค.

getPagedFeedUseCase๋Š” ์•„๊นŒ ๋งŒ๋“  RepositoryImpl์„ invokeํ•ด์ฃผ๋Š” Usecase๋‹ค.  cachedIn์€ Paging์—์„œ ์ตœ์ ํ™”๋ฅผ ๋„์™€์ฃผ๋Š” ๋„๊ตฌ ์ค‘ ํ•˜๋‚˜๋‹ค. ํŽ˜์ด์ง• ๋ฐ์ดํ„ฐ๋ฅผ viewModelScope๊ฐ€ ์‚ด์•„์žˆ์„ ๋•Œ ์บ์‹ฑํ•ด๋‘๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ์ด์ œ ์ด๊ฑธ recyclerview์˜ PagingDataAdapter์— ๋„ฃ์–ด์ฃผ๋ฉด ๋๋‚œ๋‹ค. 

 

์–ด๋Œ‘ํ„ฐ๋ถ€๋ถ„์€ ๋”ฐ๋กœ ๋ญ˜ ๋ณผ๊ฒŒ ์—†์–ด์„œ ์ƒ๋žตํ•˜๊ฒ ๋‹ค.

 

ํŽ˜์ด์ง•์„ ์‚ฌ์šฉํ•˜๋ฉด ์•„๋ฌด๋ž˜๋„ ๋กœ๋“œํ•˜๋Š” ์‹œ๊ฐ„์ด ์žˆ๋Š”๋ฐ, ์ด๊ฑธ ์ด์šฉํ•ด์„œ ๋กœ๋”ฉ - ๋กœ๋”ฉ์™„๋ฃŒ ํ™”๋ฉด์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

feedAdapter.loadStateFlow.flowWithLifecycle(viewLifecycleOwner.lifecycle)
    .onEach { loadStates ->
        val isLoading = loadStates.source.refresh is LoadState.Loading
        if (!isLoading) loading.dismiss() else loading.show()
	}.launchIn(viewLifecycleOwner.lifecycleScope)

์ด๋ ‡๊ฒŒ ํ•ด์ฃผ๋ฉด, adapter์— ๋“ค์–ด๊ฐ„ LoadState๊ฐ€ Loading์ผ๋•Œ loading dialog๋ฅผ ๋„์›Œ์ฃผ๊ณ ,  ์•„๋‹ˆ๋ผ๋ฉด dialog๋ฅผ ๊บผ์ค€๋‹ค.

์•„๋ž˜๋Š” ๊ตฌํ˜„ํ•œ ๋ชจ์Šต์ด๋‹ค.

 

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT