๊ฐœ๋ฐœ ์ผ์ง€ ๐Ÿ’ป/Nutri Capture

Nutri Capture ํ”„๋ก ํŠธ์—”๋“œ - ๋ฌดํ•œ ์Šคํฌ๋กค ๋กœ์ง ๋ฆฌํŒฉํ† ๋ง

interfacer_han 2024. 11. 16. 01:34

#1 ๋ฌธ์ œ

#1-1 ๋ฌธ์ œ ์ƒํ™ฉ

์ •๋ ฌ๋œ ์•„์ดํ…œ์ด๊ธฐ์—, ๊ฐ ์•„์ดํ…œ์˜ ์‹œ๊ฐ์€ ์œ„์—์„œ ์•„๋ž˜๋กœ ๊ฐˆ์ˆ˜๋ก ์ž‘์•„์ ธ์•ผํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, mealId 41์ธ ์•„์ดํ…œ๊ณผ mealId 50์ธ ์•„์ดํ…œ์„ ๋ณด๋ฉด ์ด์ƒํ•œ ์ ์ด ๋ณด์ธ๋‹ค. ๋ฐ”๋กœ mealId 50์ธ ์•„์ดํ…œ์˜ ์‹œ๊ฐ์€ 18:01:14...๋กœ, 17:09:03...์ธ mealId 50๋ณด๋‹ค ํฐ ๊ฒƒ์ด๋‹ค.

 

#1-2 ๋ฌธ์ œ ์ฝ”๋“œ

...

@Composable
fun NutrientScreen(
    ...
) {
    LaunchedEffect(key1 = true) {
        ...
    }

    LaunchedEffect(key1 = true) {
        ...
    }

    LaunchedEffect(key1 = viewModel.isInitialized.value) {
        if (viewModel.isInitialized.value) {
            // ๋ฌดํ•œ ์Šคํฌ๋กค
            snapshotFlow { listState.layoutInfo.visibleItemsInfo }.collect { visibleItemsInfo ->
                val totalMaxIndex = listState.layoutInfo.totalItemsCount - 1
                val firstVisibleItemIndex = listState.firstVisibleItemIndex
                val visibleItemCount = visibleItemsInfo.size

                if (totalMaxIndex <= firstVisibleItemIndex + visibleItemCount) {
                    viewModel.onEvent(NutrientViewModelEvent.LoadMoreItemsAfterLastDayMeal)
                }
            }
        }
    }

    LazyColumn(
        ...
    ) {
        ...
    }
}

์‹œํ–‰์ฐฉ์˜ค๋ฅผ ๊ฒช์œผ๋ฉฐ ์•Œ๊ฒŒ๋œ ๋ฌธ์ œ์˜ ์›์ธ์€, viewModel.onEvent()๊ฐ€ ๋™์‹œ๋‹ค๋ฐœ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. viewModel.onEvent()๋Š”, NutrientViewModelEvent.LoadMoreItemsAfterLastDayMeal๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์œผ๋ฉด ์ฝ”๋ฃจํ‹ด(viewModelScope)์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•ด Item์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค. ์ฝ”๋ฃจํ‹ด์€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๊ธฐ์—, ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ๋˜ ๋กœ๋“œ ์š”์ฒญ์ด ๋“ค์–ด์˜จ ๊ฒฝ์šฐ ๋จผ์ €๋ฒˆ์˜ ์š”์ฒญ์™€ ์ดํ›„์˜ ์š”์ฒญ ๊ฐ„ (์™„๋ฃŒ์— ๋Œ€ํ•œ) ์ˆœ์ฐจ์„ฑ์ด ๋ณด์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ View๊ฐ€ ์˜๋„์™€ ๋‹ค๋ฅธ ์ด์ƒํ•œ ํ™”๋ฉด์„ ์ถœ๋ ฅํ–ˆ๋˜ ๊ฒƒ์ด๋‹ค.

 

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ํฌ๊ฒŒ 2๊ฐ€์ง€ ๋– ์˜ค๋ฅธ๋‹ค. ๋จผ์ €, ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ์— ๋™๊ธฐ์„ฑ์„ ๋ถ€์—ฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋˜ ํ•˜๋‚˜๋Š” viewModel.onEvent(NutrientViewModelEvent.LoadMoreItemsAfterLastDayMeal) ์ดํ•˜ '๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'์ด ์—ฌ๋Ÿฌ ๋ฒˆ ์ˆ˜ํ–‰๋˜์ง€ ์•Š๊ฒŒ ๋ง‰๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

 

์ „์ž์˜ ๋ฐฉ๋ฒ•์ด ๊ฐ€์žฅ ํ™•์‹คํ•ด๋ณด์ด์ง€๋งŒ, ๊ตฌํ˜„์ด ์‰ฝ์ง€ ์•Š์•„ ๋ณด์ธ๋‹ค. (๋‚ด ์ƒ๊ฐ์ผ ๋ฟ์ด์ง€๋งŒ) ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๋„ ๋‚˜๋น ์งˆ ๊ฒƒ ๊ฐ™๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ์„  ํ›„์ž์˜ ๋ฐฉ๋ฒ•์„ ์‹œ๋„ํ•ด๋ณธ๋‹ค. ๋‹ค๋งŒ, ๋ฌด์Šจ ์ง“์„ ํ•ด๋„ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ „์ž์˜ ๋ฐฉ๋ฒ•์„ ์‹œ๋„ํ•ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.

 

#2 ํ•ด๊ฒฐ

#2-1 ์›์ธ: ๋„ˆ๋ฌด ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š” State

'๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'์ด ์—ฐ๋‹ฌ์•„ ์œ ๋ฐœ๋˜๋Š” ์›์ธ์€, snapshotFlow { ... }์— listState.layoutInfo.visibleItemsInfo๋ฅผ ๋„ฃ์€ ๋ฐ์— ์žˆ๋‹ค. listState.layoutInfo.visibleItemsInfo๋Š” LazyColumn์— ๋ณด์—ฌ์ง€๊ณ  ์žˆ๋Š” ์•„์ดํ…œ ์ •๋ณด๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กคํ•  ๋•Œ๋งˆ๋‹ค ๋Š์ž„์—†์ด ๋ณ€ํ•œ๋‹ค. ์‹ค์ œ๋กœ snapshotFlow { listState.layoutInfo.visibleItemsInfo }.collect { visibleItemsInfo -> ... }์˜ ... ๋ถ€๋ถ„์— Log.i( ... )๋ฅผ ๋„ฃ์œผ๋ฉด ์–ด๋งˆ์–ด๋งˆํ•œ ์–‘์˜ Log ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

#2-2 snapshotFlow๋ฅผ ์ ์šฉํ•  State ๋ณ€๊ฒฝ

// ๋ฌดํ•œ ์Šคํฌ๋กค
snapshotFlow {
    val totalMaxIndex = listState.layoutInfo.totalItemsCount - 1
    val firstVisibleItemIndex = listState.firstVisibleItemIndex
    val visibleItemCount = listState.layoutInfo.visibleItemsInfo.size
    totalMaxIndex <= firstVisibleItemIndex + visibleItemCount
}.collect { shouldLoadMoreData ->
    if (shouldLoadMoreData) {
        viewModel.onEvent(NutrientViewModelEvent.LoadMoreItemsAfterLastDayMeal)
    }
}

์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค. ์ด์ œ, collect()๊ฐ€ ์ด์ „๋ณด๋‹ค ํ›จ์”ฌ ๋œ ๋นˆ๋ฒˆํžˆ ํ˜ธ์ถœ๋  ๊ฒƒ์ด๋‹ค.

 

#2-3 ์ฐ์ฐํ•จ

#2-2์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•˜๋ฉด, #1-1์—์„œ ๋ฐœ์ƒํ–ˆ๋˜ ๋ฌธ์ œ ์ƒํ™ฉ์ด ๋ง๋”ํžˆ ํ•ด๊ฒฐ๋œ๋‹ค. ํ•˜์ง€๋งŒ, ์•ฝ๊ฐ„์˜ ์ฐ์ฐํ•จ์ด ๋‚จ๋Š”๋‹ค. ๋งŒ์•ฝ, shouldLoadMoreData๊ฐ€ ์—ฐ๋‹ฌ์•„ true์ธ ๊ฒฝ์šฐ๊ฐ€ ์กด์žฌํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ๊ทธ ๊ฒฝ์šฐ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” '๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'์ด (๋ณธ ๊ฒŒ์‹œ๊ธ€์˜ ๋ชฉ์ ๊ณผ๋Š” ๋ฐ˜๋Œ€๋กœ) ์—ฐ๋‹ฌ์•„ ์‹คํ–‰๋˜๊ธฐ๋ฅผ ๋ฐ”๋ผ๊ณ  ์žˆ์„ ํ…Œ๋‹ค. ํ•˜์ง€๋งŒ, shouldLoadMoreData๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๊ธฐ์— ๊ทธ๋Ÿฐ ์ผ์€ ์ผ์–ด๋‚˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ค. ๋‚˜๋Š” ์ด๊ฒŒ ๋งˆ์Œ์— ๋“ค์ง€ ์•Š๋Š”๋‹ค.

 

#2-4 Flow.combine() ํ™œ์šฉ

// ๋ฌดํ•œ ์Šคํฌ๋กค
val shouldLoadMoreData = snapshotFlow {
    val totalMaxIndex = listState.layoutInfo.totalItemsCount - 1
    val firstVisibleItemIndex = listState.firstVisibleItemIndex
    val visibleItemCount = listState.layoutInfo.visibleItemsInfo.size
    totalMaxIndex <= firstVisibleItemIndex + visibleItemCount
}

val totalItemCount = snapshotFlow {
    listState.layoutInfo.totalItemsCount
}

val loadMoreData = combine(
    shouldLoadMoreData,
    totalItemCount
) { shouldLoadMoreDataValue, totalItemCountValue ->
    Pair(shouldLoadMoreDataValue, totalItemCountValue)
}

loadMoreData.collect { (shouldLoadMoreData, _) ->
    if (shouldLoadMoreData) {
        viewModel.onEvent(NutrientViewModelEvent.LoadMoreItemsAfterLastDayMeal)
    }
}

Flow.combine()์„ ์ด์šฉํ•ด, #2-3์˜ ์ฐ์ฐํ•จ์„ ํ•ด์†Œํ•œ๋‹ค. ๊ธฐ์กด์—๋Š” ์œ„ ์ฝ”๋“œ์—์„œ shouldLoadMoreData์˜ ๋ณ€ํ™”๋งŒ์„ ๊ฐ์ง€ํ•ด collect()ํ–ˆ์ง€๋งŒ, ์ด์ œ๋Š” totalItemCount์˜ ๋ณ€ํ™”๊นŒ์ง€ ๊ฐ์ง€ํ•œ๋‹ค. shouldLoadMoreData๊ฐ€ true๋กœ ์ผ์ •ํ•˜๊ฒŒ ์œ ์ง€๋˜์–ด๋„, totalItemCount๊ฐ€ ๋ณ€ํ•˜๋ฉด '๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'์ด ์œ ๋ฐœ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

'๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'์ด ์ด์ƒ์ ์ธ ๋ฐฉํ–ฅ์œผ๋กœ ์—ฐ๋‹ฌ์•„ ์‹คํ–‰๋˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋Š”, ๋จผ์ € ๋ฒˆ์˜ '๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'์ด ์™„๋ฃŒ๋˜์–ด View์— Loadํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌด์‚ฌํžˆ ๋ถ™์ธ ์งํ›„ ๋‹ค์‹œ ์ดํ›„์˜  '๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'์ด ์‹คํ–‰๋˜๋Š” ์ƒํ™ฉ์ด๋‹ค. ์œ„ ์ฝ”๋“œ๋Œ€๋กœ๋ผ๋ฉด, totalItemCount๋Š” '๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'์ด ์™„๋ฃŒ๋จ๊ณผ ๋™์‹œ์— ๋ณ€ํ™”ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด์ „ '๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ'๊ณผ ์ดํ›„ '๋ฐ์ดํ„ฐ ๋กœ๋“œ ์š”์ฒญ' ๊ฐ„ ์™„๋ฃŒ์— ๋Œ€ํ•œ ์ˆœ์ฐจ์„ฑ์ด ๋ณด์žฅ๋œ๋‹ค.

 

#3 ์š”์•ฝ

๋ฌดํ•œ ์Šคํฌ๋กค ๋กœ์ง์ด ๊ผฌ์ด์ง€ ์•Š๊ฒŒ ๋ฆฌํŒฉํ† ๋งํ–ˆ๋‹ค.

 

#4 ์™„์„ฑ๋œ ์•ฑ

#4-1 ์Šคํฌ๋ฆฐ์ƒท

์œ„ ํ™”๋ฉด ์†์˜ ์•„์ดํ…œ๋“ค ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ชจ๋“  ์•„์ดํ…œ์— ๋Œ€ํ•ด ์ •๋ ฌ์ด ์ž˜ ์œ ์ง€๋œ๋‹ค.

 

#4-2 ์ด ๊ฒŒ์‹œ๊ธ€ ์‹œ์ ์˜ Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com

 

#4-3 ๋ณธ ํ”„๋กœ์ ํŠธ์˜ ๊ฐ€์žฅ ์ตœ์‹  Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com