Jetpack Compose에서 flow를 안전하게 사용하기

hongbeom
hongbeomi dev
Published in
9 min readAug 14, 2022

--

Consuming flows safely in Jetpack Compose를 번역한 글입니다.

Photo by Maksym Tymchyk 🇺🇦 on Unsplash

이 글은 Manuel Vivo님의 Consuming flows safely in Jetpack Compose글을 한국어로 번역한 글입니다.

라이프사이클을 인지하는 방법으로 flow를 수집하는 것이 Android에서 flow를 수집하는 권장 방법입니다. 만약 Jetpack Compose로 앱을 구축하는 경우, 우리는 collectAsStateWithLifecycle API를 사용하여 UI에서 라이프사이클을 인지하는 방식으로 flow를 수집할 수 있습니다.

collectAsStateWithLifecycle을 사용하면 앱이 백그라운드에 있을 때처럼 필요하지 않을 때 앱의 리소스를 절약할 수 있습니다. 리소스를 불필요하게 유지하면 사용자의 기기 상태에 영향을 끼칠 수 있습니다. 파이어베이스 쿼리, 위치 또는 네트워크 업데이트 작업 및 데이터베이스 연결 같은 작업들이 이런 리소스 작업에 해당합니다.

이 API에 대해 자세히 알아보고 라이프사이클 인식 방식으로 수집해야 하는 이유, collectAsState API와 비교하는 방식에 대해 알아보겠습니다.

collectAsStateWithLifecycle

collectAsStateWithLifecycle은 flow에서 값을 수집하고 최신 값을 Compose state로 나타내는 라이프사이클 인식 방식의 composable 함수입니다. 새로운 flow 방출이 발생할 때마다 이 State 객체의 값이 업데이트 됩니다. 이렇게 하면 Composition의 모든 State.value가 사용되는 부분에서 recomposition이 일어납니다.

기본적으로 collectAsStateWithLifecycleLifecycle.State.STARTED를 사용하여 flow에서 값 수집을 시작하거나 중지합니다. 하지만 collectAsState는 라이프사이클이 타겟 상태가 바깥으로 변경될 경우 문제가 발생합니다. 라이프사이클의 상태 범위 조절은 minActiveState 파라미터를 통해 다르게 구성할 수도 있습니다.

collectAsStateWithLifecycle은 기본적으로 앱이 백그라운드로 진입할 경우 flow 수집을 취소합니다.

아래 코드는 collectAsStateWithLifecycle을 사용하여 composable 함수의 ViewModel이 방출한 StateFlow의 uiState 필드를 수집하는 방법을 보여주고 있습니다.

위 코드에서, AuthorViewModeluiState가 새로운 AuthorScreenUiState 값을 내보낼 때마다, AuthorRoute가 recomposition됩니다. collectAsStateWithLifecycle의 더 많은 사용 사례를 알아보기 위해 Now in Android 앱과 이migration PR을 확인해보세요.

프로젝트에서 collectAsStateWithLifecycle API 사용을 시작하려면 프로젝트에 androidx.lifecycle.lifecycle-runtime-compose 아티팩트를 추가하면 됩니다.

⚠️ 주의 : 이는 현재 알파 단계의 API입니다. 또한 ExperimentalLifecycleComposeApi 어노테이션을 추가해야 합니다.

내부 동작

collectAsStateWithLifecycle은 View 시스템을 사용하여 Android에서 flow를 수집할 때의 권장 방법인 repeatOnLifecycle API를 사용하고 있습니다.

collectAsStateWithLifecycle을 사용하면 아래와 같은 보일러 플레이트 코드를 작성해줄 필요가 없습니다. 아래 코드는 Composable 함수에서 라이프사이클 인식 방식으로 flow를 수집합니다.

아키텍처에서 Flow 수집

앱 아키텍처에서, 특정 타입은 다른 타입의 세부 구현 정보를 알면 안 됩니다. UI는 ViewModel이 UI 상태를 생성하는 방법을 알면 안 됩니다. UI가 화면에 표시되지 않으면, flow 수집을 중지하여 앱의 리소스를 확보해야 합니다.

UI는 collectAsStateWithLifecycle을 사용하여 UI 상태를 수집하여 리소스를 확보할 수 있습니다. 또한 ViewModel은 수집하는 곳을 인지하는 방법으로 UI 상태를 생성하여 위 작업을 수행할 수 있습니다. UI가 화면에 표시되지 않는 경우와 같은 수집하는 곳이 없는 경우, 데이터 레이어에서 오는 업스트림 flow를 중지합니다. 이 작업을 우리는 UI 상태를 생성할 때 .stateIn(WhileSubscribed) flow API를 사용하여 수행할 수 있습니다. 이에 대한 자세한 내용은 여기를 참고해보세요. 또한 이 방식으로 ViewModel의 UI 상태를 테스트해보려면 테스트 가이드를 참고해보세요.

UI 계층에서 collectAsStateWithLifecycle을 사용하여 UI 상태를 사용하고 데이터 레이어에서 반응형 스트림을 노출할 때 .stateIn(WhileSubscribed)을 사용하여 UI 상태를 생성합니다. 이렇게 하면 필요하지 않을 때 리소스를 확보할 수 있습니다.

flow의 소비자와 생산자는 서로가 어떻게 구현되는지 알 필요가 없습니다. 여러 환경, 변형, 라이브러리 및 기능이 존재하는 대형 크기의 앱에서 세부 구현 정보를 파악하는 것은 매우 많은 시간이 소비될 수 있습니다. 그리고 더 안 좋은 점은, 세부 구현 사항에 의존하는 코드를 유지보수하기가 매우 어렵다는 것입니다.

백그라운드에서 리소스 활성화를 유지한다는 것

안드로이드 앱은 수많은 안드로이드 기기에서 실행될 수 있습니다. 불행하게도, 모든 장치와 모든 유저가 무한한 리소스를 가지고 있는 것은 아닙니다. 앱은 일반적으로 제한된 환경에서 실행되고, Android 앱이 실행 중일 때 유저 환경 및 기기의 시스템 상태에 영향을 미치는 중요한 요소가 존재합니다.

  • CPU 사용량 : CPU는 모든 기기의 구성 요소에서 배터리 소모량이 가장 높습니다. 배터리 수명은 유저의 지속적인 관심사입니다. 이 부분이 남용될 경우 유저가 앱을 제거할 수 있습니다.
  • Data 사용량 : 와이파이에 연결되지 않았을 때 앱에서 네트워크 트래픽을 줄이면 유저가 비용을 절약할 수 있습니다.
  • Memory 사용량 : 앱이 메모리를 사용하는 방식은 기기의 전반적인 안정성과 성능에 매우 큰 영향을 끼칠 수 있습니다.

수십억 사용자를 위한 앱 빌드나 기기 시스템을 구축하려는 Android 개발자는 시장, 기기 또는 대상 국가에 따라 이러한 다양한 요소들을 최적화해야 합니다. 불필요한 리소스를 계속 유지하면 기기의 타입과 기기가 실행 중인 Android 버전에 따라 부정적인 영향을 미칠 수 있습니다. UI 레이어에서 collectAsStateWithLifecycle을 사용하면 나머지 레이어에서 리소스를 확보할 수 있습니다.

collectAsState와 비교

개발자들은 종종 다음과 같은 질문을 던집니다:

collectAsStateWithLifecycle이 Android에서 Composable 함수로부터 flow를 수집하는 가장 안전한 방법이라면, collectAsState API는 왜 필요한가? 또는 새로운 API를 만드는 대신 collectAsState에 라이프사이클 인식 함수를 추가하는 것이 어떤가?

Composition 단계에서 composable 함수 인스턴스의 라이프사이클

collectAsState API는 Composition의 라이프사이클을 따릅니다. composable이 Composition 단계에 진입할 때 flow를 수집하기 시작하고, Composition에서 나갈 때 수집을 중지합니다. collectAsState는 flow를 수집하는데 사용할 수 있는 플랫폼에 구애받지 않는 API입니다.

그러나 Android 앱에서 Compose를 사용하는 경우 Android의 라이프사이클은 리소스를 관리하는 방법에 대해 중요한 역할을 맡고 있습니다. Compose는 Android 앱이 백그라운드에 있는 동안 recomposition을 중지하더라도 collectAsState는 수집 작업이 활성화된 상태로 유지합니다. 따라서 나머지 레이어에서 리소스를 확보할 수 없습니다.

collectAsStatecollectAsStateWithLifecycle은 모두 Compose에 목적을 두고 있습니다. 후자는 Android 앱을 개발할 때, 전자는 다른 플랫폼을 위해 개발할 때 사용해야 합니다.

collectAsState 에서 collectAsStateWithLifecycle 로 마이그레이션하는 것은 쉬운 일입니다.

라이프사이클을 인지하는 방식으로 flow를 수집하는 것이 Android에서 flow를 수집하여 필요한 경우 앱의 다른 레이어에서 리소스를 확보할 수 있도록 하는 권장되는 방법입니다.

Jetpack Compose로 Android 앱을 빌드하는 경우 collectAsStateWithLifecycle을 Composable 함수에서 사용하여 이 작업을 수행합니다.

참고

--

--