LINE Android CI에 App Bundle 적용기

LINE DEVELOPER DAY 2020에서 차영호 님이 발표하신 Sharing experience of adapting AppBundle on LINE Android CI 세션 내용을 옮긴 글입니다.

안녕하세요. LINE Plus 차영호입니다. LINE Android 팀에서는 작년 7월부터 Android App Bundle을 도입해서 앱 개발을 진행하고 있습니다. 이제 1년 조금 넘게 사용했는데요. LINE Android CI에 App Bundle을 적용하면서 겪었던 여러 가지 어려움을 공유하려고 합니다.

먼저 App Bundle과 유니버설 APK에 대해 간단히 소개하고 LINE Android CI 시스템에서 App Bundle에 대응하기 위해 어떠한 작업을 했는지 살펴보겠습니다. 그다음 CI 시스템에 적용했을 때 발견한 몇 가지 오류와 이를 어떻게 해결했는지 공유하려고 합니다.

 

App Bundle이란

App Bundle은 Google Play 스토어에 배포할 앱을 업로드하기 위한 형식입니다. App Bundle 자체는 Android 기기에 설치되지 않고 Google Play를 거쳐서 분할(split) APK라는 형식으로 변환되어 Android 기기에 설치됩니다. 분할 APK는 말 그대로 잘게 나뉜 APK의 집합이며 단말기의 구성 즉, CPU의 종류나 화면 밀도 혹은 개발자가 임의로 나눈 다이내믹 피처(dynamic feature) 등의 기준에 따라 나뉩니다. 이런 기능들을 사용하려면 최소한 Android Gradle 플러그인의 버전이 3.2 이상이어야 하며 되도록 최신 버전을 사용하는 것이 좋습니다. App Bundle은 기본적으로 Google Play를 거쳐야만 실제 기기에 설치할 수 있습니다. 번들 툴을 이용하면 Google Play 스토어를 거치지 않아도 분할 APK로 변환할 수 있지만 사용하기가 무척 번거롭습니다. 이런 이유로 App Bundle 자체는 사내 배포에 그다지 적합하지 않으며 LINE Android 팀에서는 사내 배포할 때 App Bundle 대신 유니버설 APK라는 형식으로 배포하고 있습니다.

 

유니버설 APK

유니버설 APK는 App Bundle로 생성할 수 있으며 분할 APK와는 다르게 모든 기능이 하나의 APK 바이너리에 포함됩니다. 따라서 Google Play를 거치지 않아도 간편하게 바이너리를 공유할 수 있습니다. 다만 실제로 배포될 앱의 크기를 짐작할 수 없고 몇 가지 자잘한 설정이 실제 배포와 달라질 수 있습니다.

Android 개발자 문서에서는 유니버설 APK를 만들 때 번들 툴이라는 도구를 이용해서 생성 가능하다고 기술되어 있습니다만, Android Gradle 플러그인에서 직접 유니버설 APK를 생성할 수도 있습니다. 아래 화면과 같이 패키지와 빌드 변형(variants), 유니버설 APK라는 Gradle 태스크를 수행하면 빌드 결과물로 유니버설 APK가 생성되는 것을 확인할 수 있습니다.

 

App Bundle 도입

LINE Android 팀에서는 여러 가지 CI 시스템을 운영하고 있는데요. 그중 다음 두 가지 CI 시스템이 있습니다. 하나는 개발자가 소스 코드를 변경했을 때 변경 사항을 적용해도 배포하는 데에 문제가 없는지 확인하기 위한 PR(Pull Request) 체크 시스템이고, 다른 하나는 소스 코드를 이용해 앱을 빌드해서 바이너리를 배포하는 릴리스 파이프라인 시스템입니다.

PR 체크에서는 소스 코드로 앱을 생성하고, 앱의 각 기능 동작을 확인하기 위한 단위 테스트를 수행하며, 코드 스타일을 확인하고, 개발자의 실수를 방지하기 위한 린트 검사를 수행합니다. 릴리스 파이프라인에서는 앱 바이너리 빌드 후 사내 또는 Google Play 스토어에 바이너리를 배포할 때 유니버설 APK 혹은 App Bundle을 동시에 생성해서 사내 배포 저장소와 Google Play에 업로드하는 역할을 수행합니다.

 

App Bundle 도입 전후 비교

각 CI 시스템에서 사용하는 Gradle 작업이 App Bundle을 도입하면서 어떻게 바뀌었는지 전과 후를 비교해 보았습니다. 

App Bundle를 도입하기 전에는 APK 파일을 생성하기 위해 어셈블(assenble) 작업을 수행했고 단위 테스트 및 린트(lint)를 앱 모듈에 대해서 수행했습니다. App Bundle을 도입한 후에는 유니버설 APK와 App Bundle 파일을 생성하기 위한 번들 작업 및 유니버설 APK 패키징 작업을 동시에 수행하도록 바꾸었으며, 단위 테스트는 앱 모듈 외에 추가된 다이내믹 피처 모듈에 대한 단위 테스트도 포함해서 수행하도록 변경했습니다. 

린트의 경우 Gradle 플러그인 3.6 버전까지는 각 모듈별로 명시적으로 수행해야 했는데요. 4.0 버전부터는 앱 모듈에 대해 린트를 수행하면 앱 모듈에 포함될 다이내믹 피처에 대한 린트 작업까지 모두 포함돼 진행됩니다. App Bundle 도입 후 CI 시스템을 변경할 때 참고하시면 좋을 것 같습니다.

 

App Bundle을 적용하면서 겪었던 문제들

App Bundle을 적용하면서 겪었던 여러 가지 문제 중 몇 가지를 공유하겠습니다.

 

유니버설 앱 관련 이슈

아래 보이는 오류는 유니버설 APK를 배포하려고 할 때 발생한 문제입니다. 

유니버설 APK를 생성할 때 다이내믹 피처와 베이스 앱 모듈에 포함된 덱스 바이트 코드를 통합하는 도중 위와 같은 오류가 발생했습니다. 에러는 덱스 바이트 코드 중 각 모듈에 메서드의 이름이 중복되는 경우가 있어서 발생한 것입니다. 해당 심벌은 앱 개발자가 작성한 것이 아니라 R8 컴파일러에서 임의로 넣어둔 부분이기 때문에 소스 코드를 수정하는 것으로는 해결할 수 없었습니다. 이에 Google에서 운영하는 이슈 트래커에 문제를 등록한 후 왜 이러한 일이 나타나게 되었는지 간략하게 설명을 추가해 두었습니다. 해당 오류의 원인은 Gradle 플러그인에 내장된 번들 툴에서 해당 심벌을 처리하지 못하는 것이었고, 원인이 밝혀진 후 번들 툴의 버그가 수정되었습니다.

다만 번들 툴이 수정됐다고 해도 버그가 수정된 Gradle 플러그인이 바로 출시되지는 않기 때문에 Gradle 플러그인에 내장된 번들 툴 대신 최신 버전의 번들 툴을 사용하도록 빌드 스크립트를 수정해야 하는데요. Gradle 스크립트 중 Gradle 플러그인이 적재하는 classpath를 선언한 부분 바로 아래에 최신 버전의 번들 툴을 적재하는 스크립트를 추가하면 됩니다.

 

단위 테스트 관련 이슈

다음은 단위 테스트와 관련된 오류입니다. 다이내믹 피처의 단위 테스트를 수행하면 ClassNotFoundException과 함께 다음과 같은 오류가 발생했습니다.

원래대로라면 Android Gradle 플러그인 내부에서 다이내믹 피처가 참조하는 앱의 클래스를 적재할 수 있도록 환경을 꾸며줘야 하는데요. 해당 기능이 정상적으로 동작하지 않고 있었습니다. 이에 강제로 모든 다이내믹 피처의 프로젝트에 대해 해당 클래스를 적재할 수 있도록 루트 프로젝트의 빌드 스크립트에서 워크어라운드를 추가했습니다.

해당 오류는 Gradle 플러그인 4.1 버전에서 수정되었기 때문에 더 이상 이런 워크어라운드가 필요하지 않습니다만, 혹시 Gradle 플러그인 버전을 예전 버전으로 유지해야 한다면 참고하시기 바랍니다.

 

마치며

App Bundle 도입과 그와 관련된 여러 가지 이슈를 살펴보았습니다. App Bundle을 도입하면서 예상치 못한 다양한 오류를 만났는데요. 아마도 Google에서 각각의 CI 시스템과 관련해 예상하지 못한 부분이 있었고, 다양한 요구 사항을 수용하지 못해서 오류가 발생했던 것 같습니다. 대부분의 오류는 Google이나 다른 개발자의 도움으로 해결할 수 있었습니다. 이 글을 읽고 계신 분들도 혹시 오류를 발견한다면 이슈 트래커에 공유해 다른 개발자들도 확인할 수 있게 한다면 많은 개발자가 동일한 오류를 해결하는 데 큰 도움이 될 것입니다. 재현 가능한 샘플 소스를 공유한다면 더욱 도움이 되겠죠. Google 플러그인은 대부분 오픈 소스이기 때문에 문제의 원인을 함께 찾으며 해결해 나갈 수도 있습니다. App Bundle과 관련해서 LINE DEVELOPER DAY 2020의 Best practices for a modularized app with dynamic features 세션도 참고하시면 좋을 것 같습니다. 또한 Google에서 제공하는 다양한 문서와 이슈 트래커도 도움이 될 것입니다.

이상으로 글을 마치겠습니다. 아래에서 발표 영상도 확인하실 수 있습니다.