작성자: Andrew Flynn, Jon Boekenoogen (Tech leads on Google Play)
이 글의 원문은 여기서 확인하실 수 있으며 블로그 리뷰에는 노현석(GDE)님이 참여해주셨습니다.
Google Play 스토어 엔지니어링 책임자들은 지난 2020년에 전체 홈페이지 기술 스택 개편이라는 중대한 결정을 내렸습니다. 출시 후 10년이 넘은 기존 코드에 수없이 많은 Android 플랫폼 버전 및 기능 업데이트가 누적되면서 막대한 기술 부채가 발생했습니다. 개발자 생산성이나 스토어 자체의 사용자 경험 또는 성능에는 부정적인 영향을 미치지 않으면서, 제품에 대한 작업을 수행하는 수백 명의 엔지니어로 확장되는 새로운 프레임워크가 필요했습니다.
저희는 네트워크 레이어부터 픽셀 렌더링에 이르기까지, 스토어의 모든 것을 업데이트하기 위한 다년간의 로드맵을 마련했습니다. 그 일환으로서 저희는 상호 작용성 및 사용자 만족과 관련된 제품 목표를 충족시킬 현대적이고 선언적인 UI 프레임워크도 도입하고자 했습니다. 전반적인 옵션 분석 후 아직 알파 버전 이전 단계에 있었던 Jetpack Compose에 전념한다는 (당시로서는) 과감한 결정을 내렸습니다.
그 이후, Google의 Google Play 스토어 팀과 Jetpack Compose 팀은 구체적인 요구사항에 부합하는 Jetpack Compose 버전의 출시 및 개선을 위해 매우 긴밀하게 협력했습니다. 이번 게시물에서는 마이그레이션에 대한 접근 방식과 그 과정에서 발견한 해결 과제와 이점을 다루면서 앱에 Compose를 채택하면 어떻게 되는지에 대해 함께 알아보겠습니다.
고려 사항
저희는 새로운 UI 렌더링 레이어로 Jetpack Compose를 고려하면서 다음 두 가지를 우선에 두었습니다.
개발자 생산성: 이 코드에 기여하는 Play 스토어 팀 소속 엔지니어가 매우 많기 때문에, 개발하기 쉽고 재미있어야 합니다.
성능: Play 스토어에서 렌더링하는 미디어 중심 콘텐츠는 지연 시간과 버벅거림에 매우 민감한 비즈니스 측정 항목이 많으므로, 모든 기기, 특히 메모리 용량이 적은 하드웨어와 Android (Go Edition) 기기에서 제대로 작동하도록 해야 했습니다.
개발자 생산성
저희는 현재 1년 이상 Jetpack Compose를 사용해 UI 코드를 작성하고 있으며 Jetpack Compose 덕분에 UI 개발이 더욱 간단해 졌습니다.
UI 작성에 필요한 코드가 때로는 최대 50%나 감소할 정도로 크게 줄어든 점이 무척 마음에 듭니다. 이는 Compose가 선언적 UI 프레임워크라는 점과 Kotlin의 간결함 덕분에 가능한 일입니다. 사용자 설정 그리기 및 레이아웃을 N개의 메서드 재정의를 통한 View 서브클래스 대신 간단한 함수 호출로 사용할 수 있습니다.
Ratings Table을 예로 들어보겠습니다.
Views를 사용할 경우 이 테이블은 다음으로 구성됩니다.
- 모서리가 둥근 사각형과 별표에 대해 사용자 설정 그리기가 필요한 View 클래스 2개를 포함하는, 총 3개의 View 클래스
- Java 코드 최대 350줄, XML 코드 55줄
Compose를 사용할 경우 이 테이블은 다음으로 구성됩니다.
- 모든 @Composable 함수가 같은 언어로 된 같은 파일에 포함됩니다!
- Kotlin 최대 210줄
애니메이션은 그 단순성과 표현력으로 호평받는 Compose 기능입니다. 저희 팀은 Compose를 사용해 그 어느 때보다도 Play 스토어 사용자를 즐겁게 할 모션 기능을 제작 중입니다. Compose의 선언적 특성과 애니메이션 API 덕분에 순차 또는 병렬 애니메이션 작성이 비교할 수 없을 정도로 수월해졌습니다. 취소 및 콜백 체인과 관련된 애니메이션의 특수 사례도 더 이상은 저희 팀에게 어려운 문제가 아닙니다. 인기 애니메이션 라이브러리인 Lottie는 간단히 사용할 수 있는 Compose API를 이미 제공하고 있습니다.
그러면 이렇게 생각하실지도 모르겠습니다. '다 좋은 얘기로 들리지만, View를 제공하는 라이브러리 종속성은 어떨까?' 맞습니다. 특히 처음 마이그레이션할 때는 Compose 기반 API를 구현하지 않은 라이브러리도 있었습니다. 하지만 Compose는 ComposeView 및 AndroidView API와의 손쉬운 View 상호 운용성을 제공합니다. 이러한 방식으로 ExoPlayer 및 YouTube의 Player와 같은 인기 라이브러리와 성공적으로 통합했습니다.
성능
Play 스토어 팀과 Jetpack Compose 팀은 Compose가 View 프레임워크만큼 빠르고 버벅거림 없이 실행될 수 있도록 긴밀하게 협력했습니다. Compose가 Android 프레임워크의 일부가 아닌 앱 내의 번들에 포함되어 제공되기 때문에 이는 힘든 과제였습니다. 화면에 개별 UI 구성 요소를 렌더링하는 속도는 빠르지만 Compose 프레임워크 전체를 앱용 메모리로 로드하는 전체 시간 비용은 높았습니다.
Play 스토어에 Compose를 채택하면서 가장 크게 실현한 성능 개선 사항 중 하나는 Baseline Profile 개발에서 비롯되었습니다. 앱 시작 시간을 개선해 주는 클라우드 프로필은 이미 한동안 제공되었지만, API 28 이상에서만 사용할 수 있으며 (매주마다) 릴리스 주기가 잦은 앱 등에는 효과적이지 않습니다. 이 문제를 해결하고자, Play 스토어 팀과 Android 팀은 앱 소유자가 지정할 수 있는 개발자 정의 번들 프로필인 Baseline Profile을 함께 제작했습니다. Baseline Profile은 앱과 함께 제공되고 클라우드 프로필과 완벽히 호환되며 앱 수준의 특이성과 라이브러리 수준에서 모두 정의할 수 있습니다. 또한, Baseline Profile은 Compose 채택자에게 무료로 제공됩니다! Play 스토어는 Baseline Profile 출시를 통해 검색 결과 페이지에서 40%의 초기 페이지 렌더링 시간 단축 효과를 보았습니다. 엄청난 성과입니다!
특히 스크롤이 사용되는 상황에서, Compose를 렌더링에 적합한 성능을 발휘하도록 만드는 핵심 메커니즘은 바로 UI 구성 요소 재사용입니다. Compose는 건너뛸 수 있는 Composable(예: 불변)의 Recomposition에 있어 생략을 시도하지만, 모든 매개변수가 @Stable 주석 요구 사항을 충족할 경우 개발자가 Composable 생략을 강제 적용할 수도 있습니다. Compose 컴파일러는 특정 기능을 생략할 수 없도록 하는 요소에 대한 편리한 가이드도 제공합니다. 스크롤 상황에서 자주 사용되는 Play 스토어의 재사용 UI 구성 요소를 만드는 동안, 저희는 불필요한 Recomposition이 누락된 프레임 시간을 유발하여 버벅거림을 일으킨다는 사실을 발견했습니다. 저희는 이러한 Recomposition을 디버그 설정에서 쉽게 찾을 수 있도록 Modifier를 만들었습니다. 이러한 기술을 UI 구성 요소에 적용하여 버벅거림을 10~15% 줄일 수 있었습니다.
Recomposition 시각화 Modifier가 작동 중인 모습. 파란색(Recomposition 없음), 녹색(Recomposition 1개).
Play 스토어 앱을 위해 Compose를 최적화하는 데에 있어 또 다른 핵심 구성 요소는 앱 전체에 대한 상세하면서도 포괄적인 마이그레이션 전략이었습니다. 초기 통합 실험 과정에서는 단일 사용자 세션 내에서 Compose 렌더링과 View 렌더링을 모두 실행할 경우, 특히 저사양 기기에서 상당히 메모리 집약적인 작업이 되는 2 스택 문제가 있었습니다. 이 문제는 같은 페이지에서 코드를 롤아웃하는 동안뿐 아니라, 서로 다른 두 페이지(예: Play 스토어 홈 페이지와 검색 결과 페이지)가 각각 다른 스택에 있을 때에도 갑자기 발생했습니다. 이 시작 지연 시간을 개선하기 위해, Compose로 마이그레이션하는 페이지의 순서와 타임라인에 대한 구체적인 계획을 세우는 것이 중요했습니다. 또한 앱이 완전히 마이그레이션될 때까지 임시방편으로 일반 클래스를 단기적으로 변경하는 것이 도움이 된다는 점을 알 수 있었습니다.
Compose를 Android 프레임워크에서 독립시킴으로써 Jetpack Compose에 직접 기여하는 팀의 비용이 줄어 모든 개발자에게 이익이 되는 개선 사항을 빠른 시간 내에 실현할 수 있었습니다. Jetpack Compose 팀과 협력하여 LazyList 항목 유형 캐싱과 같은 기능을 출시하고 추가 객체 할당과 같은 간단한 수정 작업을 빠르게 진행할 수 있었습니다.
전망
Play 스토어의 Compose 채택은 저희 팀의 개발자 만족도에 큰 도움이 되었으며 코드 품질과 상태의 대폭적 향상이 이루어졌습니다. 모든 새로운 Play 스토어 기능은 이 프레임워크를 기반으로 하며, Compose는 앱의 속도 향상과 더욱 매끄러운 안착을 실현하는 데 중요한 역할을 했습니다. Compose 마이그레이션 전략의 특성 때문에 APK 크기 변화 또는 개발 속도와 같은 사항을 면밀히 측정할 수는 없었지만, 확인할 수 있는 모든 부분에서는 매우 긍정적으로 보입니다!
Compose는 Android UI 개발의 미래이며, Play 스토어 팀으로서 매우 만족스럽게 생각하고 있습니다!