Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. 상태 변화와 리컴포지션 리컴포지션은 일반적으로 State<T> 객체가 변경될 때 트리거됩니다.

    @Composable fun LoginScreen(showError: Boolean) { if (showError) { LoginError() } LoginInput() } 5
  4. 리스트에서의 리컴포지션 컴포저블이 리스트 내에서 여러 번 호출되면 각 호출은

    고유한 인스턴스를 생성합니다. @Composable fun MoviesScreen(movies: List<Movie>) { Column { for (movie in movies) { MovieOverview(movie) } } } 6
  5. key() 를 활용한 최적화 리스트 내 컴포저블을 key() 로 구분하면

    불필요한 리컴포지션을 방지할 수 있습니다. @Composable fun MoviesScreenWithKey(movies: List<Movie>) { Column { for (movie in movies) { key(movie.id) { MovieOverview(movie) } } } } 7
  6. Compose 렌더링 3 단계 (Phases) 1. 컴포지션(Composition): UI를 선언하고 구성

    2. 레이아웃(Layout): UI 요소의 위치와 크기 결정 3. 그리기(Draw): 화면에 UI를 실제로 렌더링 9
  7. 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
  8. 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
  9. 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
  10. 부수 효과 (Side Effects) 컴포저블은 부수 효과(Side Effects) 없이 동작하는

    것이 이상적이지만, API 요청이나 네트워크 호출이 필요한 경우 Effect API 를 활용해야 합니다. 14
  11. LaunchedEffect LaunchedEffect는 특정 키 값이 변경될 때 한 번만 실행되는

    정지 함수를 실행합니다. LaunchedEffect(pulseRateMs) { while (isActive) { delay(pulseRateMs) alpha.animateTo(0f) alpha.animateTo(1f) } } 15
  12. 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
  13. 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
  14. 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
  15. rememberUpdatedState 값이 변경되더라도 다시 시작되지 않도록 값을 유지하는 데 사용됩니다.

    @Composable fun LandingScreen(onTimeout: () -> Unit) { val currentOnTimeout by rememberUpdatedState(onTimeout) LaunchedEffect(true) { delay(3000) currentOnTimeout() } } 19
  16. 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
  17. 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
  18. 최적화 전략 1. ­ 리컴포지션 최소화 key()를 활용하여 리스트 성능

    최적화 모든 상태를 최대한 낮은 단계에서 읽기 22
  19. 최적화 전략 1. ­ 리컴포지션 최소화 key()를 활용하여 리스트 성능

    최적화 모든 상태를 최대한 낮은 단계에서 읽기 2. ­ 부수 효과 최소화 rememberCoroutineScope() 활용하여 필요할 때만 코루틴 실행 SideEffect를 외부 API 연동에만 사용 DisposableEffect를 사용해 리소스 정리 필수 22