Android발행일 2025. 1. 4.원본 https://blog.naver.com/jword_/223714875787 ↗

Jetpack Compose로 무한 스크롤 구현하기

Jetpack Compose로 무한 스크롤 구현하기 — #개발자의도구들 #jetpackcompose #무한스크롤 🚨📝 📖 📒✏️💡🔍 AI스쿨 msa기반 java 백...

#Android Studio#Naver Blog

#개발자의도구들 #jetpackcompose #무한스크롤

🚨📝 📖 📒✏️💡🔍

​

AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 작성하였습니다

\* 현재 Spring x 프로젝트를 진행하고 있습니다. 전체 목차를 보시려면 여기를 눌러주세요.

\* 해당 프로젝트 이전에 공부한 내용을 보시려면 여기를 눌러주세요.

​

이번에는 Compose UI에 대해서 다루겠습니다. 자주 사용되는 기능인 만큼 깔끔하게 정리했으니 어디서든 응용할 수 있으면 좋을 것 같습니다!

​

요구사항

javascript 코드 예제
                                    🤵
1. 재사용 가능한 컴포넌트 형태로 만들 것
2. 양방향으로 스크롤링이 가능할 것
3. 현재 보이는 화면 끝으로 이동하면 api를 호출할 것

LazyColumn에 대한 이해

무한 스크롤링에는 LazyCoumn이 사용됩니다. 메모리 관점에서도 훌륭한 모듈인데, 지연 로딩 방식을 사용하여 화면에 보이는 요소만 메모리에 할당하고 보이지 않는 요소는 자동으로 해제합니다.

​

기본적으로 아래의 형태를 취합니다.

javascript 코드 예제
                                    LazyColumn(
    state: rememberLazyListState(), // 스크롤을 할거면 필수이다.
) {
    items(List<T>) // 다중 item에 대한 scroll 정보를 state에서 추적한다.
}

간단한 구현

​

javascript 코드 예제
                                    @Composable
fun InfiniteTimelineScrollBox(
    modifier: Modifier = Modifier,
    items: List<@Composable () -> Unit>? = null,
    loadMore: () -> Unit
) {
    val lazyListState = rememberLazyListState()
    var tests = remember { MutableList(20) { "Item #${it + 1}" } }

    Column(modifier = Modifier.fillMaxSize()) {
        LazyColumn(
            state = lazyListState, // lazyListState...
            modifier = Modifier.fillMaxSize()
        ) {
            items(tests.size, key = {index -> index}) { index ->
                ListItemView(item = tests[index])
            }
        }
    }
}

#

LazyColumn을 사용하면 자동으로 items를 추적하여 스크롤을 만들어줍니다.

​

여기서는 우선 사용법에 익숙해집시다.

무한 스크롤링을 사용하려면

모델(데이터) 구조와 UI 구조를 간략하게 나누었습니다.

🏗️ model structure​1. viewModel로부터 list(items)를 추적한다. ​2. bottom위치에 도달하면 vieModel에서 부터 fetch를 진행한다. ( -- fetchprev는 더 최신 정보. -- fetchnext는 이전 정보를 의미.​ 3. fetch된 데이터는 list에 붙인다.
✒️ drawing​1. lazyColumn의 가장 처음 값부터 시작한다. ​2. lazycolum의 끝으로 이동시 -> 다음 데이터를 fetch한다.​ 3. fetch된 데이터는 itmes에 붙고, ui가 recomposition된다.

버튼을 통해 동작을 이해하자

javascript 코드 예제
                                    @Composable
fun InfiniteTimelineScrollBox(
    modifier: Modifier = Modifier,
    items: List<@Composable () -> Unit>? = null,
    loadMore: () -> Unit
) {
    val lazyListState = rememberLazyListState()
    val coroutineScope = rememberCoroutineScope()

    var tests = remember { MutableList(40) { "Item #${it + 1}" } }

    Column(modifier = Modifier.fillMaxSize()) {
        // 너무 상단이면 클릭이 안됨
        Spacer(modifier = Modifier.height(25.dp))

        // 버튼 추가 - 특정 위치로 스크롤 이동
        Button(
            onClick = {
                coroutineScope.launch {
                    // 10번째 아이템으로 스크롤
                    lazyListState.animateScrollToItem(index = 10)
                }
            },
            modifier = Modifier.padding(8.dp)
        ) {
            Text("Scroll to Item 10")
        }

        Button(
            onClick = {
                tests = tests.toMutableList().apply { set(12, "Updated Item #13") }
            },
            modifier = Modifier.padding(8.dp)
        ) {
            Text("update Item 12")
        }

        LazyColumn(
            state = lazyListState, // lazyListState...
            modifier = Modifier.fillMaxSize()
        ) {
            items(tests.size, key = {index -> index}) { index ->
                ListItemView(item = tests[index])
            }
        }
    }
}

#

  • LazyColumn은 상태 정보를 가지고 있습니다.
  • 이를 통해서 여러가지 동작을 수 행할 수 잇습니다.
  • 몇 번째 아이템으로 이동하거나, 아이템의 내용을 변경할 수 있습니다.

​

\* LazyColumn에 들어가는 데이터 정보를 items, 개별 데이터를 item으로 부릅시다.

바닥을 확인하자

무한 스크롤링을 하기 위해서는 LaztColumn이 맨 아래로 내려가는 순간 다음 데이터를 fetch해야합니다. 이를 구현하기 위해서 다음 두가지 동작이 필요합니다.

​

  1. 바닥을 확인
  2. api를 부르기

​

LazyListState를 통해서 LazyColumn에 들어있는 items 중에 현재 보이는 가장 마지막 item을 추적할 수 있습니다. 바닥지점은 마지막 item의 index가 실제 items의 마지막 인덱스 지정입니다.

javascript 코드 예제
                                    val reachedBottom: Boolean by remember {
        derivedStateOf {
            val lastVisibleItem = lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()
            lastVisibleItem?.index != 0 && lastVisibleItem?.index ==
                    lazyListState.layoutInfo.totalItemsCount - 1 // 전체 수 - 1: last idx
        }
    }

derivedStateOf는 계속해서 layListState.layoutInfo.visibleItemsInfo를 추적하고, 값이 변경 될때마다 boolean을 계산합니다.

데이터를 추가하자.

Comose는 관리중인 데이터의 변화가 생기면 Recomposition을 수행합니다. 현재 Screen에서 관리중인 데이터는 tests이기 때문에 이 tests가 변하면 새로운 화면이 그려집니다.

​

스크롤이 맨 마지막에 도달하면 새로운 item을 10개 넣는 로직을 구현해보았습니다.

javascript 코드 예제
                                    LaunchedEffect(reachedBottom) {
        Log.i("scroll", "LaunchedEffect starts!\nreachedBottom: ${reachedBottom}")
        Log.i("scroll", "test : ${tests}")
        if (reachedBottom) {
            tests = tests.apply {
                addAll((tests.size until tests.size + 10).map { "Item #${it + 1}" })
            }
            Log.i("scroll", "added test : ${tests}")
        }
    }

LanchedEffect는 reachedBottonm 상태를 추적하고, 맨 아래상태에 도달하면 데이터를 10개 추가합니다.

#

​

  • 데이터가 계속해서 추가되는 것을 볼 수 잇습니다.
  • 이 원리가 무한 스크롤의 기본 동작 원리입니다.

앞으로의 과제

현재는 데이터를 Screen이 관리하고 있습니다. 하지만 실제 환경에서는 viewModel이 데이터를 관리합니다

​

새로운 데이터 또한 api를 통해서 추가됩니다. 다음 글에서는 이를 구현하는 방법에 대해 다뤄보겠습니다.