๐Ÿ“ฑ์ฝ”๋ฃจํ‹ด๊ณผ ๋ฆฌ์ŠคํŠธ

HBยท2025๋…„ 5์›” 6์ผ

๐Ÿ’ ์ฝ”๋ฃจํ‹ด๊ณผ LaunchedEffect

์ฝ”๋ฃจํ‹ด์ด๋ž€?

  • Kotlin์—์„œ ์ œ๊ณตํ•˜๋Š” ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๋„๊ตฌ๋กœ, ์ „ํ†ต์ ์ธ ์Šค๋ ˆ๋“œ ๋ฐฉ์‹๋ณด๋‹ค ํ›จ์”ฌ ๊ฐ€๋ณ๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋™์‹œ ์ž‘์—… ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ
  • UI ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ๋„คํŠธ์›Œํฌ ์š”์ฒญ, ํŒŒ์ผ ์ž…์ถœ๋ ฅ ๋“ฑ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ

์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ

  • CoroutineScope: ์ฝ”๋ฃจํ‹ด์ด ์‹คํ–‰๋˜๋Š” ๋ฒ”์œ„, ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ์— ์ค‘์š”

  • ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜: ์ค‘๋‹จ ๊ฐ€๋Šฅํ•œ ํ•จ์ˆ˜ -> delay(), withContext() ๋“ฑ์ด ์žˆ์Œ

  • ๋””์ŠคํŒจ์ฒ˜ : ์ฝ”๋ฃจํ‹ด์ด ์‹คํ–‰๋  ์Šค๋ ˆ๋“œ๋ฅผ ์ •์˜ (Main, IO, Default ๋“ฑ)

  • ์ฝ”๋ฃจํ‹ด ๋นŒ๋”: launch, async, withContext ๋“ฑ์ด ์žˆ์œผ๋ฉฐ ์ฝ”๋ฃจํ‹ด์„ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉ

Compose์—์„œ ์ฝ”๋ฃจํ‹ด ์‚ฌ์šฉํ•˜๊ธฐ

  • LaunchedEffect๋ฅผ ํ†ตํ•ด ์ปดํฌ์ €๋ธ” ์•ˆ์—์„œ ์ฝ”๋ฃจํ‹ด ์‹คํ–‰ ๊ฐ€๋Šฅ
  • ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜์— ๋ฐ˜์‘ํ•˜๊ฑฐ๋‚˜, ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋Š” ๋ฐ ์ž์ฃผ ์‚ฌ์šฉ
@Composable
fun CoroutineExample() {
    val scope = rememberCoroutineScope()

    Button(onClick = {
        scope.launch {
            delay(1000)
            println("1์ดˆ ํ›„ ์‹คํ–‰๋จ")
        }
    }) {
        Text("์‹คํ–‰")
    }
}

=>LaunchedEffect๋Š” ์ปดํฌ์ €๋ธ”์ด ์ฒ˜์Œ ์ปดํฌ์ฆˆ๋  ๋•Œ ํŠน์ • ์ž‘์—…์„ ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ

์ฝ”๋ฃจํ‹ด ์ค‘์ง€ ๋ฐ ์žฌ์‹œ์ž‘

  • delay()๋ฅผ ํ†ตํ•ด ์ผ์‹œ ์ค‘๋‹จ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์žฌ์‹œ์ž‘์€ ํ˜ธ์ถœ ์ปจํ…์ŠคํŠธ์— ์˜ํ•ด ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ
  • Job.cancel()์„ ํ†ตํ•ด ์ฝ”๋ฃจํ‹ด์„ ๋ช…์‹œ์ ์œผ๋กœ ์ข…๋ฃŒํ•  ์ˆ˜๋„ ์žˆ์–ด ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ž์›์„ ๊ด€๋ฆฌ ๊ฐ€๋Šฅ

์ฝ”๋ฃจํ‹ด ์ฑ„๋„ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜

  • ์ฝ”๋ฃจํ‹ด ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด Channel์ด๋‚˜ Flow, SharedFlow, StateFlow๋ฅผ ์‚ฌ์šฉ
  • Channel์€ ํ•˜๋‚˜์˜ ์ฝ”๋ฃจํ‹ด์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๊ณ  ๋‹ค๋ฅธ ์ฝ”๋ฃจํ‹ด์ด ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์ผ์ข…์˜ ํŒŒ์ดํ”„ ์—ญํ• 
val channel = Channel<Int>()
scope.launch {
    channel.send(1)
}
scope.launch {
    val received = channel.receive()
    println(received)
}



๐Ÿ’ ๋ฆฌ์ŠคํŠธ์™€ ๊ทธ๋ฆฌ๋“œ

  • Compose์—์„œ๋Š” ๋ฆฌ์ŠคํŠธ๋‚˜ ๊ทธ๋ฆฌ๋“œ UI๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ ์„ฑ๋Šฅ๊ณผ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณ ๋ คํ•˜์—ฌ Lazy ์ปดํฌ์ €๋ธ”์„ ํ™œ์šฉ
  • Lazy ์ปดํฌ์ €๋ธ”์€ ๋ณด์ด๋Š” ํ•ญ๋ชฉ๋งŒ ๋ Œ๋”๋งํ•˜์—ฌ ์Šคํฌ๋กค์ด ๋งŽ์€ UI์—์„œ๋„ ๋›ฐ์–ด๋‚œ ์„ฑ๋Šฅ์„ ์ œ๊ณต

LazyColumn ๊ธฐ๋ณธ ๊ตฌ์กฐ

  • Jetpack Compose์˜ ๋Œ€ํ‘œ์ ์ธ ์„ธ๋กœ ๋ฆฌ์ŠคํŠธ๋กœ, RecyclerView์˜ ๋Œ€์ฒด
LazyColumn {
    items(list) { item ->
        Text(text = item.name)
    }
}

LazyRow & LazyVerticalGrid

  • LazyRow: ๊ฐ€๋กœ ์Šคํฌ๋กค์ด ๊ฐ€๋Šฅํ•œ ๋ฆฌ์ŠคํŠธ

  • LazyVerticalGrid: ๊ฒฉ์žํ˜• ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ, ๊ฐค๋Ÿฌ๋ฆฌ ๋“ฑ์— ์ ํ•ฉ

LazyRow {
    items(items) { item ->
        Card(Modifier.padding(8.dp)) {
            Text(item.title)
        }
    }
}
LazyVerticalGrid(columns = GridCells.Fixed(2)) {
    items(items) { item ->
        GridItem(item)
    }
}

์Šคํฌ๋กค ์ƒํƒœ ๊ฐ์ง€

  • rememberLazyListState()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํฌ๋กค ์œ„์น˜๋ฅผ ์ถ”์ ํ•˜๊ณ  ํŠน์ • ์œ„์น˜๋กœ ์ด๋™ ๊ฐ€๋Šฅ
  • ex) ํŠน์ • ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งจ ์œ„๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅ
val listState = rememberLazyListState()
LazyColumn(state = listState) { ... }

LaunchedEffect(Unit) {
    listState.animateScrollToItem(10)
}

์Šคํ‹ฐํ‚ค ํ—ค๋” ๊ตฌํ˜„

  • ์„น์…˜ ๋‹จ์œ„๋กœ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ณ  ํ˜„์žฌ ์„น์…˜์ด ์ƒ๋‹จ์— ๊ณ ์ •๋˜๋„๋ก ํ•ด์ฃผ๋Š” UI ๊ธฐ๋ฒ•
LazyColumn {
    stickyHeader {
        Text("ํ—ค๋”", modifier = Modifier.background(Color.LightGray))
    }
    items(list) {
        Text(it.title)
    }
}

์Šคํฌ๋กค ์œ„์น˜์— ๋ฐ˜์‘ํ•˜๊ธฐ

  • ์Šคํฌ๋กค์ด ํŠน์ • ์œ„์น˜๋ฅผ ๋„˜์–ด๊ฐ”๋Š”์ง€ ์—ฌ๋ถ€์— ๋”ฐ๋ผ UI๋ฅผ ์ œ์–ด ๊ฐ€๋Šฅ
val isScrolled = remember {
    derivedStateOf {
        listState.firstVisibleItemIndex > 2
    }
}



๐Ÿ’ Row/Column ๋ฆฌ์ŠคํŠธ ํŠœํ† ๋ฆฌ์–ผ

listdemo ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ

  • ์ƒˆ๋กœ์šด Compose ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
  • Column ๊ธฐ๋ฐ˜ ๋ฆฌ์ŠคํŠธ ๊ตฌ์„ฑ์œผ๋กœ ๊ธฐ๋ณธ์ ์ธ ๋ฆฌ์ŠคํŠธ ํ‘œํ˜„ ์—ฐ์Šต

Column ๊ธฐ๋ฐ˜ ๋ฆฌ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ

val items = listOf("์ฒซ ๋ฒˆ์งธ", "๋‘ ๋ฒˆ์งธ", "์„ธ ๋ฒˆ์งธ")
Column {
    items.forEach {
        Text(it, modifier = Modifier.padding(8.dp))
    }
}

์ˆ˜๋™ ์Šคํฌ๋กค ๋ฆฌ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ

  • verticalScroll()์„ ์‚ฌ์šฉํ•˜๋ฉด Lazy ์—†์ด๋„ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ UI๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
val scrollState = rememberScrollState()
Column(modifier = Modifier.verticalScroll(scrollState)) {
    for (i in 1..50) {
        Text("์•„์ดํ…œ $i", modifier = Modifier.padding(8.dp))
    }
}



๐Ÿ’ Lazy ๋ฆฌ์ŠคํŠธ ํŠœํ† ๋ฆฌ์–ผ

lazylistdemo ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ

  • assets/data.xml์— ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ํŒŒ์‹ฑ

  • ๋ชจ๋ธ ํด๋ž˜์Šค ์ƒ์„ฑ: data class Item(val name: String, val imageUrl: String)

์ด๋ฏธ์ง€ ๋กœ๋”ฉ ์ฒ˜๋ฆฌํ•˜๊ธฐ

  • Coil ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•œ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ

๋ฆฌ์ŠคํŠธ ์•„์ดํ…œ ์ปดํฌ์ €๋ธ” ๋””์ž์ธ

@Composable
fun ItemCell(item: Item) {
    Row(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
        Image(...)
        Column(modifier = Modifier.padding(start = 8.dp)) {
            Text(item.name, fontWeight = FontWeight.Bold)
        }
    }
}

Lazy ๋ฆฌ์ŠคํŠธ ๊ตฌํ˜„

LazyColumn {
    items(items) { item ->
        ItemCell(item)
    }
}

๋ฆฌ์ŠคํŠธ ํ…Œ์ŠคํŠธ ๋ฐ ํด๋ฆญ ์ฒ˜๋ฆฌ

ItemCell(item = item, onClick = { navigateToDetail(item) })



๐Ÿ’ ์Šคํ‹ฐํ‚ค ํ—ค๋” & ์Šคํฌ๋กค ์ œ์–ด

  • ์‚ฌ์šฉ์ž ์นœํ™”์ ์ธ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์„น์…˜ ๊ตฌ๋ถ„, ํ—ค๋” ๊ณ ์ •, ์Šคํฌ๋กค ์œ„์น˜ ์ด๋™ ๋“ฑ์˜ ๊ธฐ๋Šฅ์ด ํ•„์š”

๋ฆฌ์ŠคํŠธ ๋ฐ์ดํ„ฐ ๊ทธ๋ฃนํ•‘ & ํ—ค๋” ํ‘œ์‹œ

  • ๋ฆฌ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋ฃนํ•‘ํ•˜๊ณ  ๊ฐ ๊ทธ๋ฃน์— ์Šคํ‹ฐํ‚ค ํ—ค๋”๋ฅผ ์ถ”๊ฐ€
val grouped = items.groupBy { it.category }
LazyColumn {
    grouped.forEach { (category, itemList) ->
        stickyHeader {
            Text(text = category, modifier = Modifier.background(Color.Gray).padding(8.dp))
        }
        items(itemList) {
            ItemCell(it)
        }
    }
}

์Šคํฌ๋กค ๋ฒ„ํŠผ ์ถ”๊ฐ€

val scope = rememberCoroutineScope()
val listState = rememberLazyListState()

Button(onClick = {
    scope.launch {
        listState.animateScrollToItem(0)
    }
}) {
    Text("๋งจ ์œ„๋กœ")
}

0๊ฐœ์˜ ๋Œ“๊ธ€