Upgrade to PRO for Only $50/Yearโ€”Limited-Time Offer! ๐Ÿ”ฅ

W01 Jetpack Compose - Lifecycle & Side Effects

Avatar for Eunice Eunice
February 09, 2025

W01 Jetpack Compose - Lifecycle & Sideย Effects

Avatar for Eunice

Eunice

February 09, 2025
Tweet

More Decks by Eunice

Other Decks in Education

Transcript

  1. ์ƒ์•  ์ฃผ๊ธฐ ๊ฐœ์š” Jetpack Compose๋Š” UI๋ฅผ ์ปดํฌ์ง€์…˜(Composition) ์œผ๋กœ ๊ตฌ์„ฑํ•˜๊ณ , ์•ฑ์˜

    ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ๋ฆฌ์ปดํฌ์ง€์…˜(Recomposition) ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. 2
  2. ์ปดํฌ์ง€์…˜์˜ ํ๋ฆ„ 1. ์ดˆ๊ธฐ ์ปดํฌ์ง€์…˜: UI๊ฐ€ ์ฒ˜์Œ ์ƒ์„ฑ๋  ๋•Œ ์‹คํ–‰๋จ

    2. ๋ฆฌ์ปดํฌ์ง€์…˜: ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ, ํ•„์š”ํ•œ UI ์š”์†Œ๋งŒ ์—…๋ฐ์ดํŠธ๋จ 3. ์ปดํฌ์ง€์…˜ ์ข…๋ฃŒ: ๋” ์ด์ƒ ํ•„์š” ์—†๋Š” UI ์š”์†Œ๊ฐ€ ์ œ๊ฑฐ๋จ @Composable fun MyComposable() { Column { Text("Hello") Text("World") } } 4
  3. ๋ฆฌ์ŠคํŠธ์—์„œ์˜ ๋ฆฌ์ปดํฌ์ง€์…˜ ์ปดํฌ์ €๋ธ”์ด ๋ฆฌ์ŠคํŠธ ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ๋˜๋ฉด ๊ฐ ํ˜ธ์ถœ์€

    ๊ณ ์œ ํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. @Composable fun MoviesScreen(movies: List<Movie>) { Column { for (movie in movies) { MovieOverview(movie) } } } 6
  4. key() ๋ฅผ ํ™œ์šฉํ•œ ์ตœ์ ํ™” ๋ฆฌ์ŠคํŠธ ๋‚ด ์ปดํฌ์ €๋ธ”์„ key() ๋กœ ๊ตฌ๋ถ„ํ•˜๋ฉด

    ๋ถˆํ•„์š”ํ•œ ๋ฆฌ์ปดํฌ์ง€์…˜์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. @Composable fun MoviesScreenWithKey(movies: List<Movie>) { Column { for (movie in movies) { key(movie.id) { MovieOverview(movie) } } } } 7
  5. Compose ๋ Œ๋”๋ง 3 ๋‹จ๊ณ„ (Phases) 1. ์ปดํฌ์ง€์…˜(Composition): UI๋ฅผ ์„ ์–ธํ•˜๊ณ  ๊ตฌ์„ฑ

    2. ๋ ˆ์ด์•„์›ƒ(Layout): UI ์š”์†Œ์˜ ์œ„์น˜์™€ ํฌ๊ธฐ ๊ฒฐ์ • 3. ๊ทธ๋ฆฌ๊ธฐ(Draw): ํ™”๋ฉด์— UI๋ฅผ ์‹ค์ œ๋กœ ๋ Œ๋”๋ง 9
  6. 1. ์ปดํฌ์ง€์…˜ ๋‹จ๊ณ„ (Composition) UI์˜ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•˜๋Š” ๋‹จ๊ณ„ @Composable ํ•จ์ˆ˜๊ฐ€

    ์‹คํ–‰๋˜๋ฉฐ, UI ํŠธ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋จ val padding by remember { mutableStateOf(8.dp) } Text( text = "Hello", // The `padding` state is read in the composition phase // when the modifier is constructed. // Changes in `padding` will invoke recomposition. modifier = Modifier.padding(padding) ) 10
  7. 2. ๋ ˆ์ด์•„์›ƒ ๋‹จ๊ณ„ (Layout) UI ์š”์†Œ์˜ ํฌ๊ธฐ์™€ ์œ„์น˜๋ฅผ ๊ฒฐ์ • UI

    ํŠธ๋ฆฌ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋ ˆ์ด์•„์›ƒ์ด ๊ณ„์‚ฐ๋จ var offsetX by remember { mutableStateOf(8.dp) } Text( text = "Hello", modifier = Modifier.offset { // The `offsetX` state is read in the placement step // of the layout phase when the offset is calculated. // Changes in `offsetX` restart the layout. IntOffset(offsetX.roundToPx(), 0) } ) 11
  8. 3. ๊ทธ๋ฆฌ๊ธฐ ๋‹จ๊ณ„ (Draw) ์‹ค์ œ ํ™”๋ฉด์— UI๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋‹จ๊ณ„ Canvas

    ๋˜๋Š” Modifier.drawBehind ๋“ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ทธ๋ž˜ํ”ฝ์„ ์ง์ ‘ ๊ทธ๋ฆผ var color by remember { mutableStateOf(Color.Red) } Canvas(modifier = modifier) { // The `color` state is read in the drawing phase // when the canvas is rendered. // Changes in `color` restart the drawing. drawRect(color) } 12
  9. ๋ถ€์ˆ˜ ํšจ๊ณผ (Side Effects) ์ปดํฌ์ €๋ธ”์€ ๋ถ€์ˆ˜ ํšจ๊ณผ(Side Effects) ์—†์ด ๋™์ž‘ํ•˜๋Š”

    ๊ฒƒ์ด ์ด์ƒ์ ์ด์ง€๋งŒ, API ์š”์ฒญ์ด๋‚˜ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ Effect API ๋ฅผ ํ™œ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 14
  10. LaunchedEffect LaunchedEffect๋Š” ํŠน์ • ํ‚ค ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋Š”

    ์ •์ง€ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. LaunchedEffect(pulseRateMs) { while (isActive) { delay(pulseRateMs) alpha.animateTo(0f) alpha.animateTo(1f) } } 15
  11. DisposableEffect DisposableEffect๋Š” ์ปดํฌ์ €๋ธ”์ด ์‚ฌ๋ผ์งˆ ๋•Œ ์ •๋ฆฌํ•ด์•ผ ํ•˜๋Š” ๋ฆฌ์†Œ์Šค๊ฐ€ ์žˆ์„ ๋•Œ

    ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_START) { onStart() } else if (event == Lifecycle.Event.ON_STOP) { onStop() } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } 16
  12. SideEffect SideEffect๋Š” Compose ์ƒํƒœ๋ฅผ Compose ์™ธ๋ถ€์˜ ์ฝ”๋“œ์™€ ๋™๊ธฐํ™”ํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

    Compose ๋‚ด๋ถ€์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. @Composable fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics { val analytics: FirebaseAnalytics = remember { FirebaseAnalytics() } SideEffect { analytics.setUserProperty("userType", user.userType) } return analytics } 17
  13. rememberCoroutineScope ์ปดํฌ์ €๋ธ” ์™ธ๋ถ€์—์„œ ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„๋ฅผ ์ œ๊ณตํ•˜๋Š” API์ž…๋‹ˆ๋‹ค.

    @Composable fun MoviesScreen(snackbarHostState: SnackbarHostState) { val scope = rememberCoroutineScope() Scaffold( snackbarHost = { SnackbarHost(hostState = snackbarHostState) } ) { contentPadding -> Column(Modifier.padding(contentPadding)) { Button( onClick = { scope.launch { snackbarHostState.showSnackbar("Something happened!") } } ) { Text("Press me") } } } 18
  14. rememberUpdatedState ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ ๋‹ค์‹œ ์‹œ์ž‘๋˜์ง€ ์•Š๋„๋ก ๊ฐ’์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

    @Composable fun LandingScreen(onTimeout: () -> Unit) { val currentOnTimeout by rememberUpdatedState(onTimeout) LaunchedEffect(true) { delay(3000) currentOnTimeout() } } 19
  15. produceState Compose ์™ธ๋ถ€์˜ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ Compose ์ƒํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” API์ž…๋‹ˆ๋‹ค. @Composable

    fun loadNetworkImage( url: String, imageRepository: ImageRepository = ImageRepository() ): State<Result<Image>> { return produceState<Result<Image>>(initialValue = Result.Loading, url, imageR val image = imageRepository.load(url) value = if (image == null) Result.Error else Result.Success(image) } } 20
  16. derivedStateOf ์ƒํƒœ ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฅธ ์ƒํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š”

    ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๋งŒ๋“ค ๋•Œ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. @Composable fun MessageList(messages: List<Message>) { val listState = rememberLazyListState() LazyColumn(state = listState) { items(messages) { message -> MessageItem(message) } } val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } AnimatedVisibility(visible = showButton) { ScrollToTopButton() } 21
  17. ์ตœ์ ํ™” ์ „๋žต 1. ยญ ๋ฆฌ์ปดํฌ์ง€์…˜ ์ตœ์†Œํ™” key()๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ ์„ฑ๋Šฅ

    ์ตœ์ ํ™” ๋ชจ๋“  ์ƒํƒœ๋ฅผ ์ตœ๋Œ€ํ•œ ๋‚ฎ์€ ๋‹จ๊ณ„์—์„œ ์ฝ๊ธฐ 22
  18. ์ตœ์ ํ™” ์ „๋žต 1. ยญ ๋ฆฌ์ปดํฌ์ง€์…˜ ์ตœ์†Œํ™” key()๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ ์„ฑ๋Šฅ

    ์ตœ์ ํ™” ๋ชจ๋“  ์ƒํƒœ๋ฅผ ์ตœ๋Œ€ํ•œ ๋‚ฎ์€ ๋‹จ๊ณ„์—์„œ ์ฝ๊ธฐ 2. ยญ ๋ถ€์ˆ˜ ํšจ๊ณผ ์ตœ์†Œํ™” rememberCoroutineScope() ํ™œ์šฉํ•˜์—ฌ ํ•„์š”ํ•  ๋•Œ๋งŒ ์ฝ”๋ฃจํ‹ด ์‹คํ–‰ SideEffect๋ฅผ ์™ธ๋ถ€ API ์—ฐ๋™์—๋งŒ ์‚ฌ์šฉ DisposableEffect๋ฅผ ์‚ฌ์šฉํ•ด ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ ํ•„์ˆ˜ 22