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

W01 Jetpack Compose - Lifecycle & Side Effects

Eunice
February 09, 2025

W01 Jetpack Compose - Lifecycle & Side Effects

Eunice

February 09, 2025
Tweet

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