Mendable을 적용하여 Compose를 더 Stable하게 만들기

Team return·2024년 8월 28일
3
post-thumbnail

안녕하세요. 자비스 2기 부장을 맡고 있는 안드로이드 개발자 박의엘입니다. 자비스 안드로이드에서 Compose로 개발되어 있는 코드들을 최적화한 내용에 대해 소개합니다.

Stable이란?

우선 Compose로 만들어진 코드에서 Stable이 얼마나 중요한지 알아야합니다.

Recomposition이 발생했을 때 Parameter가 Stable 하다면 Compose Runtime에서 Recomposition을 건너뛰는 것(Skippable)이 가능한 상태.

Stable이 Compose에서 중요하게 작용하는 이유는 Smart Recomposition 때문입니다.

@Composable 어노테이션이 달린 함수는 Compose 컴파일러 플러그인에 의해 일반 함수와 다르게 Compose 생명주기에 따라 재실행 합니다. 상태가 바뀔때마다 실행되는 불필요한 recompostion을 막기 위해 Smart Recomposition을 사용합니다.
Smart Recomposition은 변경된 상태가 이전상태와 동일한지 비교하여 변경된 부분만 다시 그리고 나머지는 재사용하여 효율적으로 UI를 업데이트 합니다.
Smart Recomposition을 만들기 위해서는 객체를 Stable로 만들어야합니다.

What is Mendable??

Mendable은 Jetpack Compose compiler metrics의 지표를 HTML로 볼 수 있게 해주는 CLI 툴입니다.

적용하기

1.gradle 설정하기

allprojects {
    tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile::class.java).configureEach {
        kotlinOptions {
            // Trigger this with:
            // ./gradlew assembleRelease -PenableMultiModuleComposeReports=true --rerun-tasks
            if (project.findProperty("enableMultiModuleComposeReports") == "true") {
                freeCompilerArgs += listOf("-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + rootProject.buildDir.absolutePath + "/compose_metrics/")
                freeCompilerArgs += listOf("-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + rootProject.buildDir.absolutePath + "/compose_metrics/")
            }
        }
    }
}

루트 모듈의 build.gradle 파일에 컴파일 옵션을 추가합니다.

2.터미널에서 명령어 실행하기

./gradlew assembleRelease -PenableMultiModuleComposeReports=true --rerun-tasks


저는 11분동안 돌아가서 노트북 터지는 줄 알았습니다.

3. mendable.jar 저장

rootProject/build/compose_metrics 경로에 mendable.jar를 저장합니다.
mendable.jar여기에서 다운로드 받습니다.

4. mendable 실행

java -jar mendable.jar

mendable.jar가 저장된 경로에서 gradle 명령어를 실행합니다.
실행이 완료되면 index.html 파일이 생성됩니다.

Stable하게 만들기

자비스 프로젝트에 적용을 했는데 70% stable 이라는 결과가 나왔습니다.
아래 결과를 보고 수정하는 작업을 하면 됩니다.

1.List

companyInfo: List<CompanyInfoData> // Not stable

list는 MutableList를 넣을 수 있기 때문에 Unstable한 함수로 확인이 됩니다. immutable collection을 사용해서 ImmutableList로 변경하였습니다.

companyInfo: ImmutableList<CompanyInfoData>
companyInfo = companyInfo.toPersistentList()

2. Data Class

state: SetProfileState // Not stable

StableMaker의 @Immutable 어노테이션을 사용하여 stable하게 수정하였습니다.

@Immutable
internal data class SetProfileState(
    val image: File?,
    val imageUrl: String,
    val uri: Uri?,
    val buttonEnabled: Boolean,
)

하지만 여기서 DataClass가 compose와 의존성이 없는 모듈에 있다면 문제가 발생합니다. 자비스에서는 domain 모듈에 있는 entity를 참조하는 과정에서 문제가 발생했습니다. 찾아본 결과 skydoves님이 만드신 stableMarker 라이브러리를 사용하여 해결할 수 있었습니다.

3.Painter

painter: Painter // Not stable

Painter 클래스는 공식문서에 따르면 이미지 처리의 복잡성으로 인해 stable 클래스로 명시하지 않다고 나와있습니다. 따라서 painter 클래스 대신 Int 타입의 리소스 아이디를 매개변수로 전달하는 새로운 컴포저블을 만들면 문제를 해결할 수 있습니다.

@DrawableRes drawableResId: Int

4.Uri

uri: Uri? // Not stable

이미지를 사용하는곳에서 uri를 참조하는 부분이 있어 stable한 String으로 수정하였습니다.

uri: String?

5. NavController

navController: NavController // Not stable

navcontroller에 default값을 추가하여 stable로 변경하였습니다.

navController: NavController = rememberNavController()

최종 결과

100%를 달성하였습니다!!

후기

Mendable을 적용하고 수정하면서 recomposition에 대해 더 많이 알게 되었습니다. Stable로 변경하고 찾아보면서 좋은 경험이 되었습니다. 앞으로도 100%를 유지할 수 있도록 하겠습니다.

profile
Team return 기술 블로그입니다.

0개의 댓글