이동식 저장소

MyVoca Stability 검사 본문

프로젝트/MyVoca

MyVoca Stability 검사

해스끼 2022. 7. 27. 14:08
 

Compose의 Stability에 관하여

정말 좋은 글이다. 일독을 권한다. Jetpack Compose Stability Explained Have you ever measured the performance of your composable and discovered it is recomposing more code than you expect? “I thought..

thinking-face.tistory.com

 

모든 Composable이 skippable할 필요는 없지만, 일반적으로 skippable한 Composable이 많을수록 성능에 도움이 된다. 이번 글에서는 MyVoca의 stability를 점검해 본다.

세팅

아래 글의 Compose Compiler Reports 문단을 참고했다. 

 

Jetpack Compose Stability Explained

Have you ever measured the performance of your composable and discovered it is recomposing more code than you expect? “I thought Compose…

medium.com

클래스 리뷰

모든 변수가 ``val``인 ``TodayWordImpl``은 stable하다. Compose에서 stable하다는 말은 설령 값이 바뀌더라도 Compose가 그 사실을 알아챌 수 있다는 뜻이다. Compose가 모르는 변경은 없다는 뜻이다.

stable class TodayWordImpl {
  stable val id: Int
  stable val wordId: Int
  stable val checked: Boolean
  <runtime stability> = Stable
}

그런데 역시 ``val``로만 이루어진 ``VocabularyImpl``은 unstable하다. ``List<MeaningImpl>`` 타입이 unstable하기 때문이다.

unstable class VocabularyImpl {
  stable val id: Int
  stable val eng: String
  unstable val meaning: List<MeaningImpl>
  stable val addedTime: Long
  stable val lastEditedTime: Long
  stable val memo: String?
  <runtime stability> = Unstable
}

``List``가 unstable인 이유는 이 ``List``가 ``MutableList``일 수도 있기 때문이다. 물론 이 코드에서는 그렇지 않지만, Compose 컴파일러 입장에서는 ``List``가 사실 ``MutableList``일 가능성을 배제할 수 없다. 재할당은 불가능하지만 ``MutableList``의 메서드를 통해 리스트의 내용이 변경될 수 있기 때문이다.

 

따라서 모든 ``List``는 unstable하다. 사실 모든 collection이 unstable하다. 

 

이 문제를 해결하는 방법은 크게 두 가지가 있다. 첫 번째는 Compose compiler 1.2.0부터 지원하는 Kotlin immutable collections을 사용하여 근본적으로 값의 변경 여지를 없애버리는 방법이다.

Immutable은 한번 만들어진 객체의 값을 어떤 경우에도 변경할 수 없는 객체를 말한다. 값이 변경될 수 있는 stable보다 더 강력한 조건이다.

두 번째는 ``List``를 감싸는 클래스를 선언하고, 해당 클래스에 ``@Stable`` 또는 ``@Immutable``을 붙이는 방법이다.

@Immutable
data class Meanings(val meaning: List<MeaningImpl>)

Immutable collections가 근본적인 해결 방법이긴 하다.

 

``VocabularyImpl``에 immutable list를 적용하면 클래스를 stable로 만들 수 있다.

stable class VocabularyImpl {
  stable val id: Int
  stable val eng: String
  stable val meaning: ImmutableList<MeaningImpl>
  stable val addedTime: Long
  stable val lastEditedTime: Long
  stable val memo: String?
  <runtime stability> = 
}

 


그런데 같은 이유로 unstable인 ``HomeScreenData``에는 ``@Immutable``을 붙여놨다? 

@Immutable
data class HomeScreenData(
    val loading: Boolean = false,
    val totalWordCount: Int = 0,
    val todayWords: List<HomeTodayWord> = emptyList(),
    val todayWordsLastUpdatedTime: Long = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC),
    val showTodayWordHelp: Boolean = false
)

로그 보니 1년 전에 붙여놨구나. 다른 클래스에도 몇 개 붙어 있던데, 뭣도 모르고 그냥 코드 복붙한듯 ㅋㅋ

 

쓰더라도 알고 쓰자. 

Composable 리뷰

용어 정리부터 하자.

  • Skippable: Recomposition 과정에서 composable의 모든 파라미터가 동일하다면 이 composable을 다시 그리지 않고 건너뛸 수 있다.
  • Restartable - State가 바뀐 후 recomposition이 시작될 수 있는 함수를 말한다.

하나의 함수가 skippable이면서 동시에 restartable일 수도 있다.

 

다음 ``WordTitle``은 skippable이다. 모든 변수가 stable하기 때문이다.

restartable skippable fun WordTitle(
  stable title: String
  stable modifier: Modifier? = @static Companion
)

반대로, ``MeaningsContent``는 skippable이 아니다. ``List``가 unstable이기 때문이다.

restartable fun MeaningsContent(
  unstable meanings: List<MeaningImpl>
  stable onMeaningUpdate: Function2<Int, MeaningImpl, Unit>
  stable onMeaningDelete: Function1<Int, Unit>
)

``List``를 ``ImmutableList``로 바꾸면 함수 전체가 skippable이 된다.

restartable skippable fun MeaningsContent(
  stable meanings: ImmutableList<MeaningImpl>
  stable onMeaningUpdate: Function2<Int, MeaningImpl, Unit>
  stable onMeaningDelete: Function1<Int, Unit>
)

모든 Composable이 skippable일 필요는 없다. 하지만 특별한 이유 없이 UI 성능이 좋지 않다면, 자주 호출되는 Composable이 skippable인지 확인해 보자.

'프로젝트 > MyVoca' 카테고리의 다른 글

MyVoca 2.0 출시  (2) 2021.10.04
MyVoca 1.13.1 출시  (0) 2021.06.19
MyVoca 1.13.0 출시  (0) 2021.06.13
MyVoca 1.12.4 출시  (0) 2021.05.26
MyVoca 1.12.3 출시  (0) 2021.05.10
Comments