모듈화로 안드로이드 빌드 시간 줄이기

Sunghoon Kang
Banksalad Tech
Published in
8 min readFeb 21, 2017

안녕하세요? 레이니스트에서 소프트웨어 엔지니어로 근무하고있는 강성훈입니다. 레이니스트의 안드로이드 개발팀은 Android Weekly를 구독하면서 팀원들끼리 Android에 관련된 정보를 공유하고, 기술 트렌드를 파악하고 있습니다.

여느때와 같이 Android Weekly를 팀원들과 함께 보고있는데, 모듈화가 안드로이드 앱 빌드시간에 어떻게 영향을 끼치는가 라는 글이 눈에 띄었습니다. 개발중인 앱의 프로젝트 크기가 늘어나면서, 빌드시간도 그에 비례해서 증가하게 되었는데, 이번 기회에 위 글을 참고하면서 모듈화를 도입해 빌드시간을 줄여서 비효율을 줄여보기로 결정했습니다.

tl;dr

  • Gradle은 바뀐(수정된) 모듈만 다시 빌드합니다.
  • 따라서 프로젝트를 모듈화하면 빌드속도가 빨라집니다.

시작하기에 앞서, 현재 뱅크샐러드 코드베이스의 구조에 대해서 소개드리면 아래와 같이 총 6개의 패키지로 나뉘어져 있습니다.

  • busEventBus 관련 패키지
  • dataData source로부터 Data를 불러오는 Data Layer 관련 패키지 (e.g. Realm, API에 접근)
  • domainDomain Layer 관련 패키지 (e.g. Repository, UseCase)
  • entity Domain EntityValueObject를 정의한 패키지
  • presentation UI (View) 관련 패키지
  • utilityView, Resource 관련 Extension이 담긴 패키지

빌드 속도를 줄이는것도 중요하지만, 지금의 패키지 구조가 저희가 추구하는 아키텍쳐에 가장 적합한 구성이기에 최대한 기존의 패키지 구조를 유지하며 모듈화를 진행하기로 결정했습니다.

빌드 시간에 영향을 미칠 수 있는 다른 프로그램들은 모두 종료하고 테스트했습니다.

모듈화 전

gradle 커맨드에 --profile 옵션을 주게 되면 빌드 폴더 내에 Gradle Build Report가 다음과 같이 생성되는데요, 각각의 Task에 소요된 시간을 확인할 수 있습니다.

먼저, 모듈을 나누지 않고 빌드 시간을 총 5번 측정했을 때 다음과 같은 결과가 나왔습니다.

기존 빌드 소요시간

모듈화 시작

기존의 패키지 구조 그대로

bus / data / domain / entity / presentation / utility

총 6개의 모듈로 나누기를 시도를 했고, 안에서부터 바깥(entity layer -> presentation layer)으로 모듈화를 진행해 보았습니다.

가장 먼저 다른 패키지에 의존성이 없는 entity 패키지를 모듈로 추출하기로 결정했습니다. Android Studio에서 모듈을 생성하고, 기존의 entity 패키지를 생성한 모듈로 이동시켰는데, 빌드에 실패했습니다. 그 이유는 바로 Proguard 때문이었는데요, Gradle은 각 모듈별로 난독화를 하는데, Entity는 모듈 내에서 자체적으로 사용되지 않았기 때문에 사용하지 않는 Class들이 빌드 결과물에 포함되지 않아 IOException이 발생하여 app모듈 빌드에 실패했습니다.

어차피 app 모듈에서 전사적인 Proguard 설정이 되었기 때문에 모듈에서 Proguard를 비활성 처리 후 빌드하도록 Gradle 파일을 수정했습니다.

현재 나눠진 모듈은 총 2개입니다. (app / entity)

모듈을 두개로만 나누었음에도 불구하고, 속도가 약 8~9초 정도 개선된 것을 확인할 수 있었습니다.

다음으로, Domain 패키지 모듈화를 진행했습니다. 현재 나눠진 모듈은 총 3개입니다. (app / entity / domain)

대체로 빌드 시간이 감소하긴 했으나 체감이 될 정도로 줄어들지는 않았습니다.

EntityDomain Layer를 모듈로 나눈 후에 다른 패키지도 모듈화를 진행하려 했으나, 결국에는 남은 패키지들은 모듈화 하지 않는것으로 결정했습니다.

그 이유는 다음과 같습니다.

Resource 관리의 불편함

남은 4개의 패키지 중, 3개의 패키지 (data, presentation, utility)에서 (Android) Resource를 참조하고 있습니다. 이 문제를 해결하기 위해 resource라는 패키지를 만들어 한 모듈에서 전부 리소스를 관리하려 했으나, 리소스를 모듈화하게 되면 현재 사용하고 있는 kotlin-android-extension을 사용할 수 없습니다. 또한, 현재 프로젝트에서는 Anko를 사용하고 있기 때문에 `find`를 사용하는 방법도 생각해보았으나, 불필요한 코드가 너무 많아지게 되어 모듈화 하지 않기로 결정했습니다.

kotlin-android-extension을 사용하다 Anko의 find를 사용한다면…

Presentation Layer를 쪼개지 않는 이상 빌드시간 감소를 체감하긴 힘들다

DomainEntity를 모듈화한 결과, Presentation Layer를 쪼개서 모듈화 하지 않는 이상 빌드 시간 감소를 체감하긴 힘들것 같다는 생각이 들었습니다. Android Studio에서 기본적으로 제공하는 APK Analyzer를 통해 그 이유를 알 수 있었는데요,

패키지별 Method 현황

대부분의 Method가 presentaion, data 패키지에 집중되어있는것을 확인할 수 있었습니다.(전체 Method 중 84%)

하지만 Presentation Layer를 모듈화 하려고 하니 재사용 가능한 Resource들에 대해 앞서 말씀드린 것과 같은 문제가 발생하기도 하고, 또한, Presentation Layer의 경우 특정 기능을 기준으로 나누기도 애매해져 저희가 추구하는 코드베이스의 방향과는 다르다고 판단되어 Presentation Layer를 모듈화 하지 않기로 결정했습니다.

결정적으로, 저희가 만든 busutility패키지가 data 패키지, presentation 패키지 둘 다에서 쓰이고 있었습니다. bus의 경우 RxRelay를 기반으로 설계된 Event Bus이기에 모듈로 분리하는데 큰 지장이 없었지만, utility의 경우 Resource를 참조하고 있어 모듈화에 어려움이 있었습니다.

현재의 패키지 구조

비록 원하던 수준의 빌드 시간 감소를 이뤄내지는 못했지만, 부분적인 모듈화를 통해 얻을 수 있는 장점도 분명 있었습니다. 바로 data domain을 확실하게 나눌 수 있다는 점이었습니다. 이번 모듈화를 진행하며 기존 코드를 다시 살펴볼 기회가 있었는데, domaindata의 분리가 명확히 이뤄지지 않았던 부분들을 발견했습니다. 모듈화 후에는 컴파일 시점에 두 모듈간의 독립성을 보장할 수 있으니, 코드 리뷰 시에 놓칠 수 있는 부분이 줄어들게 되었습니다.

모듈화는 죄가 없다

추가로 모듈화 후 재미있었던 일화에 대해서 소개해드리고자 합니다. 모듈화 이후 배포를 위해 APK를 빌드한 후, 앱을 검수하는데 디버그 빌드에서는 정상적으로 작동하던 앱이 특정 화면에 들어가기만 하면 작동하지 않는 문제가 발생했습니다. Proguard를 적용 하면 작동을 안하고, Proguard를 적용 안하면 작동 하는데, 모듈화 이전에는 이런 문제가 전혀 없었기에 모두가 모듈화가 범인이라고 생각하고 여러 모듈화 예제들을 살펴봤습니다. 하지만 공개되어있는 거의 모든 프로젝트들에서 마땅한 해결책을 찾을 수 없었습니다. 그렇게 도대체 뭐가 문젤까라는 생각에 머리를 쥐어뜯다 범인을 알게 되었습니다. 범인은 모듈화가 아니라 저희였습니다.

범인은 우리였다!

Fragment를 교체할 때 findFragmentByTag를 이용하는데, 저희는 Fragment Tag를 Class의 simpleName으로 사용했었습니다. 바로 이 부분이 문제였는데요, simpleName의 경우 패키지 경로를 포함하지 않는 class name을 리턴합니다. 반면에 name의 경우에는 패키지 경로까지 포함하여 리턴하기 때문에 Class마다 Unique한 값을 갖게 됩니다. 따라서 코드를 name으로 수정하니 릴리즈 빌드에서도 정상적으로 작동하는것을 확인할 수 있었습니다. 따라서 모듈화를 진행하는 도중 발생하지 않던 문제가 생긴다면 다른곳에서 문제 가 발생한것이니 안심하고 모듈화 하셔도 괜찮습니다!

simpleName이 아닌 name을 사용해야합니다

글을 맺으며

이번에 진행한 모듈화는 결과만 놓고 보자면 빌드시간이 줄긴 줄었지만, 눈에 띄게 빨라지지 않았기 때문에 성공적이지는 않았다고 생각합니다. 하지만 이 과정을 통해 기존 코드의 문제점과 제품 출시 후 벌어졌을법한 문제를 사전에 발견하고, 해결했다는 점에서 개발팀의 생산성에 기여했다고 생각합니다.

읽어주셔서 감사합니다 :D

--

--

Sunghoon Kang
Banksalad Tech

Software engineer who is interested in developer productivity and happiness