Slide 1

Slide 1 text

발표자 - 김수현 Compose 디버깅

Slide 2

Slide 2 text

발표자 - 김수현 Compose 디버깅

Slide 3

Slide 3 text

- 우아한형제들 Android Dev/Educator - NEXTSTEP Kotlin/Android Code Reviewer - DroidKnights Conference Organizer 김수현 | Suhyeon Kim # 발표자 소개

Slide 4

Slide 4 text

Table of Contents 01 02 03 04 05 디버깅이 필요한 순간 디버깅 마인드셋 디버깅 접근 방법 최적화가 필요한 순간 결론

Slide 5

Slide 5 text

간단한 퀴즈를 통해 디버깅이 필요한 순간들에 대해 알아본다. 디버깅이 필요한 순간 Section 01

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

@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) { ... }

Slide 8

Slide 8 text

@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) { ... } Recomposition 된다 vs 안 된다 Quiz 1

Slide 9

Slide 9 text

@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) { ... } Recomposition 된다 vs 안 된다 Quiz 1

Slide 10

Slide 10 text

Android Studio Hedgehog

Slide 11

Slide 11 text

Live #1 디버깅이 필요한 순간

Slide 12

Slide 12 text

Unchanged - 값이 변경되지 않았다 Changed - 값이 변경되었다 Uncertain - 값이 변경되었는지 여부를 Compose가 판단하고 있다 Static - Compose가 값이 절대 변하지 않는다고 판단했다 Unstable - 값이 불안정하다 변경 상태 #1 디버깅이 필요한 순간

Slide 13

Slide 13 text

Composable 함수의 어떤 매개 변수가 변경되어 Recomposition이 발생하는지 확인할 수 있다. 또한 각 Stack을 단계적으로 추적하여 콜 스택에서 매개 변수값이 어떻게 변경되는지 확인할 수 있다. Android Studio Hedgehog Debugger #1 디버깅이 필요한 순간

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

@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) { ... }

Slide 17

Slide 17 text

@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 무엇이 문제가 될까?

Slide 18

Slide 18 text

@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

Slide 19

Slide 19 text

@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

Slide 20

Slide 20 text

짧은 시간 내에 자주 발생하는 Recomposition을 상대해야 할 경우, 로깅이 좋은 디버깅 방법이 될 수 있다. 단, 오히려 로깅으로 인해 Recomposition이 발생할 수 있는 경우를 조심해야 한다. Logs #1 디버깅이 필요한 순간

Slide 21

Slide 21 text

디버깅을 시작하기 전 가져야 할 마인드셋을 알아본다. 디버깅 마인드셋 Section 02

Slide 22

Slide 22 text

#2 디버깅 마인드셋 문제 정의 테스트 / 검증 재현 픽스

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

문제 정의 테스트 / 검증 재현 픽스 채택된 가설을 이용하여 문제를 해결한다. 테스트를 활용해 결과를 검증한다. 문제를 해결한다 #2 디버깅 마인드셋

Slide 27

Slide 27 text

디버깅 마인드셋에서 소개한 4단계로 문제를 해결해보자. 디버깅 접근 방법 Section 03

Slide 28

Slide 28 text

문제 정의 테스트 / 검증 재현 픽스 #3 디버깅 접근 방법

Slide 29

Slide 29 text

compose-samples/Jetcaster 스크롤되는 Pager 컴포넌트 프레임 드랍 문제가 발생한다. (영상 첨부) 문제 정의 #3 디버깅 접근 방법

Slide 30

Slide 30 text

- 백그라운드 컬러 애니메이션은 Recomposition을 발생시키지 않아야 한다. - 데이터가 변경되지 않는 경우 Recomposition이 스킵되어야 한다. 추정 #3 디버깅 접근 방법

Slide 31

Slide 31 text

#3 디버깅 접근 방법 Live

Slide 32

Slide 32 text

Unchanged - 값이 변경되지 않았다 Changed - 값이 변경되었다 Uncertain - 값이 변경되었는지 여부를 Compose가 판단하고 있다 Static - Compose가 값이 절대 변하지 않는다고 판단했다 Unstable - 값이 불안정하다 변경 상태 #3 디버깅 접근 방법

Slide 33

Slide 33 text

#3 디버깅 접근 방법

Slide 34

Slide 34 text

#3 디버깅 접근 방법

Slide 35

Slide 35 text

정량적 데이터 확인 #3 디버깅 접근 방법

Slide 36

Slide 36 text

- 데이터가 변경되지 않는 경우도 Recomposition이 스킵되지 않았다 - goo.gle/jetcaster-pager - goo.gle/compose-stability-explained 틀린 추정 #3 디버깅 접근 방법

Slide 37

Slide 37 text

시각적으로 컴포넌트별 Recomposition 발생 원인을 트래킹할 때 사용할 수 있다. 테스트를 작성하여 해결 방법이 실제로 문제를 해결하고 있는지 확인한다. Layout Inspector Microbenchmark 어떤 매개 변수가 Recomposition의 원인이 되는지 디버깅한다. Debugger #3 디버깅 접근 방법 중간 정리

Slide 38

Slide 38 text

Tracing의 개념에 대해서 간단하게 알아본다 최적화가 필요한 순간 Section 04

Slide 39

Slide 39 text

#4 최적화가 필요한 순간 “최적화 최적화… 이야기는 많이 들어봤지만 어디서부터 시작해야 할까요?”

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

#4 최적화가 필요한 순간 System Trace Composable Functions

Slide 44

Slide 44 text

마무리 정리 Wrap Up!

Slide 45

Slide 45 text

Wrap Up! 문제 정의 테스트 / 검증 재현 픽스 Debugging Mindset

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Thank You Suhyeon Kim 김수현 Github @wisemuji 발표자료 Github