Jetpack Compose의 목표는 처음부터 우수한 성능을 제공하는 것입니다. 이 페이지에서는 최고의 성능을 위해 앱을 작성하고 구성하는 방법과 피해야 할 패턴에 관해 설명합니다.
이 내용을 읽기 전에 Compose 이해에서 핵심 Compose 개념을 숙지하는 것이 좋습니다.
적절하게 앱 구성
앱의 성능이 좋지 않으면 구성 문제가 있을 수 있습니다. 먼저 다음 구성 옵션을 확인하는 것이 좋습니다.
출시 모드에서 빌드 및 R8 사용
성능 문제를 발견하면 앱을 출시 모드로 실행해 보세요. 디버그 모드는 여러 문제를 발견하는 데 유용하지만 상당한 성능 비용이 발생하며 성능을 저하할 수 있는 다른 코드 문제를 발견하기가 어려워질 수 있습니다. 또한 R8 컴파일러를 사용하여 앱에서 불필요한 코드를 삭제해야 합니다. 기본적으로 출시 모드에서 빌드하면 자동으로 R8 컴파일러가 사용됩니다.
기준 프로필 사용
Compose는 Android 플랫폼의 일부가 아닌 라이브러리로 배포됩니다. 이 접근 방식을 사용하면 Compose를 자주 업데이트하고 이전 Android 버전을 지원할 수 있습니다. 그러나 Compose를 라이브러리로 배포하면 비용이 발생합니다. Android 플랫폼 코드는 이미 컴파일되어 기기에 설치되어 있습니다. 반면 라이브러리는 앱이 실행될 때 로드되고 기능이 필요할 때 just-in-time 방식으로 해석되어야 합니다. 이로 인해 시작 시 그리고 라이브러리 기능을 처음 사용할 때마다 앱의 속도가 느려질 수 있습니다.
기준 프로필을 정의하여 성능을 개선할 수 있습니다. 이러한 프로필은 중요한 사용자 여정에 필요한 클래스와 메서드를 정의하고 앱의 APK와 함께 배포됩니다. 앱 설치 중에 ART는 중요한 코드를 ahead-of-time 방식으로 컴파일하므로 앱이 실행될 때 사용할 수 있습니다.
적절한 기준 프로필을 정의하는 것이 어려울 수도 있으므로 기본적으로 Compose는 기준 프로필과 함께 제공됩니다. 이러한 이점을 위해 별도의 작업을 하지 않아도 될 수도 있습니다. 그러나 직접 프로필을 정의하려는 경우 앱의 성능을 실제로 향상하지 않는 프로필을 생성할 수도 있습니다. 프로필을 테스트하여 도움이 되는지 확인해야 합니다. 이를 위한 좋은 방법은 앱의 Macrobenchmark 테스트를 작성하여 기준 프로필을 작성하고 수정할 때 테스트 결과를 확인하는 것입니다. Compose UI의 Macrobenchmark 테스트를 작성하는 방법에 관한 예는 Macrobenchmark Compose 샘플을 참고하세요.
출시 모드, R8 및 기준 프로필의 효과에 관한 자세한 내용은 블로그 게시물 Compose 성능을 항상 출시에서 테스트해야 하는 이유는 무엇인가요?를 참고하세요.
3가지 Compose 단계가 성능에 미치는 영향
Jetpack Compose 단계에서 설명한 대로 Compose에서 프레임을 업데이트하면 다음 세 단계를 거칩니다.
- 컴포지션: Compose가 표시할 항목을 결정합니다. 구성 가능한 함수를 실행하고 UI 트리를 빌드합니다.
- 레이아웃: Compose가 UI 트리에 있는 각 요소의 크기와 배치를 결정합니다.
- 그리기: Compose가 실제로 개별 UI 요소를 렌더링합니다.
Compose는 필요하지 않으면 이러한 단계를 지능적으로 건너뛸 수 있습니다. 예를 들어 단일 그래픽 요소가 크기가 같은 두 아이콘 간에 전환된다고 가정해 보겠습니다. 이 요소는 크기가 변경되지 않고 UI 트리의 요소가 추가되거나 삭제되지 않으므로 Compose는 컴포지션 단계와 레이아웃 단계를 건너뛰고 이 요소 하나만 다시 그릴 수 있습니다.
그러나 일부 코딩 실수로 인해 Compose가 안전하게 건너뛸 수 있는 단계를 파악하기 어려울 수 있습니다. 확실하지 않은 경우 Compose는 세 단계를 모두 실행하게 되며 이로 인해 UI가 필요 이상으로 느려질 수 있습니다. 따라서 성능 권장사항은 대부분 Compose가 필요하지 않은 단계를 건너뛰도록 돕는 데 중점을 둡니다.
몇 가지 광범위한 원칙을 따르면 일반적으로 성능을 개선할 수 있습니다.
첫째, 가능하면 구성 가능한 함수 외부로 계산을 이동합니다. 구성 가능한 함수는 UI가 변경될 때마다 다시 실행해야 할 수 있습니다. 컴포저블에 넣은 모든 코드는 잠재적으로 애니메이션의 모든 프레임에서 다시 실행됩니다. 따라서 컴포저블의 코드를 UI를 빌드하는 데 실제로 필요한 것으로만 제한해야 합니다.
둘째, 최대한 오랫동안 상태 읽기를 연기합니다. 상태 읽기를 하위 컴포저블 또는 이후 단계로 이동하면 재구성을 최소화하거나 컴포지션 단계를 완전히 건너뛸 수 있습니다. 자주 변경되는 상태의 상태 값 대신 람다 함수를 전달하고, 자주 변경되는 상태를 전달할 때 람다 기반 수정자를 기본으로 선택하여 이를 실행할 수 있습니다. 이 기법의 예는 최대한 읽기 연기 섹션을 참고하세요.