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

I/O Extended 인천 2023 - Compose 디버깅

I/O Extended 인천 2023 - Compose 디버깅

Suhyeon Kim

August 26, 2023
Tweet

More Decks by Suhyeon Kim

Other Decks in Programming

Transcript

  1. - 우아한형제들 Android Dev/Educator - NEXTSTEP Kotlin/Android Code Reviewer -

    DroidKnights Conference Organizer 김수현 | Suhyeon Kim # 발표자 소개
  2. Table of Contents 01 02 03 04 05 디버깅이 필요한

    순간 디버깅 마인드셋 디버깅 접근 방법 최적화가 필요한 순간 결론
  3. @Composable fun DirectRecomposition() { var count by remember { mutableStateOf(0)

    } Text("$count") Button(onClick = { count++ }) { Text("Increment") } }
  4. @Composable fun UnstableRecomposition() { var count by remember { mutableStateOf(0)

    } Text("$count") val list by remember { mutableStateOf(listOf(1, 2, 3)) } MyList(list) Button(onClick = { count++ }) { Text("Increment") } } @Composable fun MyList(list: List<Int>) { ... }
  5. @Composable fun UnstableRecomposition() { var count by remember { mutableStateOf(0)

    } Text("$count") val list by remember { mutableStateOf(listOf(1, 2, 3)) } MyList(list) Button(onClick = { count++ }) { Text("Increment") } } @Composable fun MyList(list: List<Int>) { ... } Recomposition 된다 vs 안 된다 Quiz 1
  6. @Composable fun UnstableRecomposition() { var count by remember { mutableStateOf(0)

    } Text("$count") val list by remember { mutableStateOf(listOf(1, 2, 3)) } MyList(list) Button(onClick = { count++ }) { Text("Increment") } } @Composable fun MyList(list: List<Int>) { ... } Recomposition 된다 vs 안 된다 Quiz 1
  7. Unchanged - 값이 변경되지 않았다 Changed - 값이 변경되었다 Uncertain

    - 값이 변경되었는지 여부를 Compose가 판단하고 있다 Static - Compose가 값이 절대 변하지 않는다고 판단했다 Unstable - 값이 불안정하다 변경 상태 #1 디버깅이 필요한 순간
  8. Composable 함수의 어떤 매개 변수가 변경되어 Recomposition이 발생하는지 확인할 수

    있다. 또한 각 Stack을 단계적으로 추적하여 콜 스택에서 매개 변수값이 어떻게 변경되는지 확인할 수 있다. Android Studio Hedgehog Debugger #1 디버깅이 필요한 순간
  9. @Composable fun ScrollingList() { val listState = rememberLazyListState() LazyColumn(state =

    listState) { ... } MyComposable(offset = listState.firstVisibleItemIndex) } @Composable fun MyComposable(offset: Int) { ... }
  10. @Composable fun ScrollingList() { val listState = rememberLazyListState() LazyColumn(state =

    listState) { ... } MyComposable(offset = listState.firstVisibleItemIndex) } @Composable fun MyComposable(offset: Int) { ... } Recomposition을 트래킹하기 위해 디버거를 사용한다?
  11. @Composable fun ScrollingList() { val listState = rememberLazyListState() LazyColumn(state =

    listState) { ... } Log.d(TAG, "List recompose ${listState.firstVisibleItemIndex}") MyComposable(offset = listState.firstVisibleItemIndex) } @Composable fun MyComposable(offset: Int) { ... }
  12. @Composable fun ScrollingList() ( val listState = rememberLazyListState() LazyColumn(state =

    listState) { ... } Log.d(TAG, "List recompose ${listState.firstVisibleItemIndex}") MyComposable(offset = listState.firstVisibleItemIndex) } @Composable fun MyComposable(offset: Int) { ... } Quiz 2 무엇이 문제가 될까?
  13. @Composable fun ScrollingList() ( val listState = rememberLazyListState() LazyColumn(state =

    listState) { ... } Log.d(TAG, "List recompose ${listState.firstVisibleItemIndex}") MyComposable(offset = listState.firstVisibleItemIndex) } @Composable fun MyComposable(offset: Int) { ... } Quiz 2
  14. @Composable fun ScrollingList() ( val listState = rememberLazyListState() LazyColumn(state =

    listState) { ... } SideEffect { Log.d(TAG, "... ${listState.firstVisibleItemIndex}") } MyComposable(offset = listState.firstVisibleItemIndex) } @Composable fun MyComposable(offset: Int) { ... } Quiz 2 SideEffect: 성공적으로 Composition이 완료되었을 때 발행하는 Effect
  15. 짧은 시간 내에 자주 발생하는 Recomposition을 상대해야 할 경우, 로깅이

    좋은 디버깅 방법이 될 수 있다. 단, 오히려 로깅으로 인해 Recomposition이 발생할 수 있는 경우를 조심해야 한다. Logs #1 디버깅이 필요한 순간
  16. 문제 정의 테스트 / 검증 재현 픽스 어떤 상황이 문제가

    되는지, 무엇을 해결하려는지 정의하는 것부터 시작하자. - 현재는 어떤 상황이 재현되는가? - 기대되는 동작은 무엇인가? - 왜 그렇게 생각했는가? - 문제 발생 경위를 어떻게 추정하고 있는가? 어떤 문제가 있는가? #2 디버깅 마인드셋
  17. 문제 정의 테스트 / 검증 재현 픽스 - 재현 가능한

    경로 예시를 만든다. - 결과물을 검증하기 위한 테스트를 작성한다. 문제를 재현해보자 #2 디버깅 마인드셋
  18. 문제 정의 테스트 / 검증 재현 픽스 문제를 재현할 수

    있으니 적절한 도구를 사용해서 가설을 검증한다. 어떤 문제인지에 따라 사용할 수 있는 도구가 다르다. 가설을 검증한다 #2 디버깅 마인드셋
  19. 문제 정의 테스트 / 검증 재현 픽스 채택된 가설을 이용하여

    문제를 해결한다. 테스트를 활용해 결과를 검증한다. 문제를 해결한다 #2 디버깅 마인드셋
  20. - 백그라운드 컬러 애니메이션은 Recomposition을 발생시키지 않아야 한다. - 데이터가

    변경되지 않는 경우 Recomposition이 스킵되어야 한다. 추정 #3 디버깅 접근 방법
  21. Unchanged - 값이 변경되지 않았다 Changed - 값이 변경되었다 Uncertain

    - 값이 변경되었는지 여부를 Compose가 판단하고 있다 Static - Compose가 값이 절대 변하지 않는다고 판단했다 Unstable - 값이 불안정하다 변경 상태 #3 디버깅 접근 방법
  22. - 데이터가 변경되지 않는 경우도 Recomposition이 스킵되지 않았다 - goo.gle/jetcaster-pager

    - goo.gle/compose-stability-explained 틀린 추정 #3 디버깅 접근 방법
  23. 시각적으로 컴포넌트별 Recomposition 발생 원인을 트래킹할 때 사용할 수 있다.

    테스트를 작성하여 해결 방법이 실제로 문제를 해결하고 있는지 확인한다. Layout Inspector Microbenchmark 어떤 매개 변수가 Recomposition의 원인이 되는지 디버깅한다. Debugger #3 디버깅 접근 방법 중간 정리
  24. 트래이싱(Tracing) #4 최적화가 필요한 순간 오버헤드가 낮음 어떤 이벤트가 얼만큼

    시간을 사용하는지 판단 단, 마킹된 이벤트만 트래이싱 가능 System trace Method trace 오버헤드가 높음 모든 함수 호출을 보여줌 모든 이벤트 실행 시간이 실제 이벤트 실행 시간을 보장하진 않음
  25. 트래이싱(Tracing) #4 최적화가 필요한 순간 오버헤드가 낮음 어떤 이벤트가 얼만큼

    시간을 사용하는지 판단 단, 마킹된 이벤트만 트래이싱 가능 System trace Method trace 오버헤드가 높음 모든 함수 호출을 보여줌 모든 이벤트 실행 시간이 실제 이벤트 실행 시간을 보장하진 않음
  26. 트래이싱(Tracing) #4 최적화가 필요한 순간 오버헤드가 낮음 어떤 이벤트가 얼만큼

    시간을 사용하는지 판단 단, 마킹된 이벤트만 트래이싱 가능 System trace Method trace 오버헤드가 높음 모든 함수 호출을 보여줌 모든 이벤트 실행 시간이 실제 이벤트 실행 시간을 보장하진 않음 System Trace Composable Functions +
  27. 시각적으로 컴포넌트별 Recomposition 발생 원인을 트래킹할 때 사용할 수 있다.

    테스트를 작성하여 해결 방법이 실제로 문제를 해결하고 있는지 확인한다. Layout Inspector Microbenchmark 어떤 매개 변수가 Recomposition의 원인이 되는지 디버깅한다. Debugger Wrap Up! 짧은 시간 내에 자주 발생하는 Recomposition을 상대해야 할 경우 유용하다. Logs System trace에서 Composable 함수 이벤트를 확인할 수 있다. Tracing Debug Tools
  28. - 버그를 고쳐준다 X - 버그를 잘 찾을 수 있는

    가이드를 제시한다 O - 성능을 개선한다 X - 어떤 영역에서 성능이 저하되고 있는지 찾게 도와준다 O - 정량적인 지표로 개선점을 확인할 수 있다 O 결론: 디버깅을 통해 얻을 수 있는 것 Wrap Up!