http://theluxebeautyblog.wordpress.com/2012/02/03/diet-coke-and-benefit-get-glam/

APK 파일 다이어트 하기

marojun
marojun’s Android
12 min readSep 4, 2014

--

개발을 하다보면 야금야금 늘어나는 APK의 사이즈를 보게 됩니다. 지금은 별로 신경쓰지 않는 분위기 일지 모르겠지만 초기 안드로이드 버전에서는 간단한 앱의 경우에도 2MB로 생성되어 개발자들을 매우 괴롭게 했습니다. 아 모토로이..

문득 본인이 개발한 앱의 초기 버전과 현재 버전의 APK파일의 사이즈를 비교해 보신적이 있나요?

대부분은 ‘엇 언제 이렇게 용량이 커졌지?’ 라는 생각이 드실겁니다. 보통 사용자의 기대 부응과 개발 욕심(?)에 의해 일어나는데 좀 더 구체적으로 살펴보면 아래 항목들이 주 요인이라 볼 수 있습니다.

  • 다양한 해상도를 위한 카테고리 ([l|m|tv|h|x|xx|xxx]dpi)
  • 안드로이드 플랫폼의 발전, 개발툴과 라이브러리

안드로이드 OS가 판올림되면 기존의 로직을 걷어내고 업그레이드에 따른 대체코드를 통해 좀 더 심플하게 변경하는 경우도 있지만 새로운 기능을 추가하는 경우가 더 많죠. 라이브러리도 본인이 원하는 기능 이상의 것들이 포함되어있는 것을 많이 사용하실 겁니다.

  • 유저들의 기대를 충족시킬 높은 퀄리티의 UI들
  • 기타 등등

스토어에 적은 용량으로 앱을 퍼블리싱하는 것은 개발자들에게 필수적인 요소입니다.

먼저 ‘용량을 최적화 하자’ 라는 생각을 가지고 개발하면 코드 자체가 심플해져서 구조 및 가독성이 향상되어 유지보수 및 추후 개발에 용이하게 됩니다.

둘째로 개발자들이 요즘들어 APK에 용량을 신경쓰지 않는다고 했지만 사실 마지노선이 있습니다. 앱이 50MB를 넘어가게 되면 번거로운 extensions file 작업을 통해 사용자들이 다운받도록 해야되기 때문입니다.

마지막 이유는 바로 제한된 환경 입니다. 사용자 대부분이 무제한 데이터 요금제를 사용하지 않고, 빵빵터지는 양질의 통신지역이 아닐것이며 최고 사양의 폰이 아니라는 것은 너무도 당연한 이야기 입니다. 이러한 환경에서 작은 사이즈의 APK는 다운로드와 설치를 빠르게 하고 결국엔 사용자들의 좋은 평가로 이어지게 됩니다.

물론 용량의 증가는 유저들의 요구사항과 기대를 만족시키지 위한 어쩔수 없는 결과이기도 합니다만 확신하건데 이러한 기대 대비 APK 용량은 비례하지 않고 훨씬 더 많은 용량을 차지하고 있을 겁니다!!

그래서 여기서는 몇 가지 기술과 규칙을 통해 모두가 행복해지도록 APK의 용량을 줄이는 방법을 알려드리려고 합니다.

The APK file format

용량을 줄이는 방법을 알아보기 전에 먼저 APK 의 포맷에 대해 알아봅시다. APK 는 여러가지 파일들이 압축되어 있는 압축파일로 이 파일을 풀어보면 아래와 같은 내용을 볼 수 있습니다.

unzip <your_apk_name>.apk 처럼 커멘드를 통해서도 가능하지만 ZIP 유틸로 간단하게 풀수도 있습니다.

APK 압축을 풀었을 때의 파일 구조

위 그림처럼 압축을 푼 내용의 폴더와 파일은 개발자에겐 익숙합니다. 프로젝트 구조를 반영한 것이죠. 특별할 것이 있다면 classes.dex 와 resources.arsc 가 보인다는 것인데 전자는 달빅 가상 머신에 의해 자바 코드가 dex 파일 형식으로 컴파일 된 영역이며 후자는 이진 XML로 컴파일 리소스(values, XML drawables, etc.) 를 포함하는 파일 입니다.

위 내용을 보면 아시겠지만 APK는 압축파일 입니다. 즉, 압축 되었을때와 압축되어 있지 않는 때의 사이즈는 다릅니다. 여기서 중점적으로 볼것은 압축되어 있는 사이즈입니다만 결국 압축되지 않은 파일의 사이즈는 일반적으로 압춘된 파일의 사이즈와 비례하므로 APK가 작을수록 압축되지 않은 버전도 작아집니다.

Reducing APK file size

APK 파일의 사이즈를 줄이는데에는 몇 가지 기술이 있습니다. 물론 앱 하나하나는 제각각이기 때문에 완벽한 방법을 딱 꼬집어서 이야기 할 수 는 없지만 보통 3가지 요소를 통해 적용해 볼 수 있습니다.

  • Java source code
  • resources/assets
  • native code

Have a good coding hygiene

클린코드. 좋은 말입니다. 이러한 작업이야 말로 용량을 줄이는 첫번째 방법입니다. 코드를 정확하게 파악하여 사용하지 않는 모든 요소들을 지워버립시다. 이렇게 꾸준히 청소해나가면 결국엔 정말 필요한 요소들만 남게 되겠죠?

깔끔한 구조의 코드 베이스를 갖고 있으면 프로젝트를 새롭게 시작할 때 매우 편리합니다. 혹시 현재 진행하고 있는 프로젝트가 매우 오래되었다면 사용하지 않는 코드들을 정리해야 할 필요성을 느끼실 겁니다. 다행히 요즘엔 개발 툴을 통해 이러한 작업을 쉽게 진행할 수 있습니다.

Run Proguard

Proguard 는 난독화와 최적화 그리고 컴파일 시간을 단축하는데 매우 유용한 툴입니다. 하지만 이것의 주요기능 중 하나는 APK의 용량을 줄여준다는 것입니다. 프로가드는 기본적으로 코드 전체를 스캔하여 사용하지 않는 코드를 찾아내 APK에 포함하지 않게하는 기능이 있습니다. 또한 클래스와 인터페이스, 필드, 메소드 등의 이름을 a, b, c 형태로 재정의해서 앱을 가볍게 만듭니다.

필요성이 조금은 느껴지시나요? 보통 프로가드 적용시 빌드를 하게 되면 라이브러리 디펜던시와 같은 이유로 예외 클래스를 정의 해야돼서 귀찮아 하시는 분들이 많은데 실보다는 득이 많다는 것을 명심해 주셨으면 합니다.

Use Lint extensively

프로가드는 자바코드에 대해서만 동작합니다. 즉, res/drawable 에서 사용하는 실제 이미지는 처리할 수 없고 R class에 있는 레퍼런스들에만 적용이 됩니다.

너무 실망하지는 마세요! Lint 를 이용하면 사용하지 않는 리소스에 대해 분석할 수 있습니다. Lint 는 ADT16 버전부터 추가된 정적분석 툴로 여러가지 유용한 기능을 가지고 있습니다.

사용하지 않는 리소스를 분석하려면 이클립스 메뉴의 Window — Preferences — Android — Lint Error Checking 항목으로 들어가 Ignore All 버튼을 눌러 필요한 필터옵션이외의 나머지는 비활성화 시킨 후 Performance 항목중 UnusedResources 항목을 알아보기 쉽도록 warning 혹은 error로 변경시키면 끝입니다! 분석이 끝나면 정리된 결과표를 보실 수 있습니다.

그러나 Lint 도 만능은 아닙니다. Assets 폴더 내의 리소스들은 커버하지 못하므로 이 부분에 대해서는 개발자 스스로 정리해야 합니다.

Be opinionated about resources

안드로이드는 다양한 해상도를 지원합니다. 요즘은 좀 괜찮아진 편이지만 예전에서는 각각의 제조사에서 본인들만의 스타일을 주장하며 이상한 크기의 화면과 변태(?) 해상도 를 지원하는 디바이스가 종종 발표되었습니다. 물론 지금 OS 4.4에서 지원하는 density 들을 보면 ( ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi 심지어 xxxhdpi ) 개발 시작하기 전에 맥이 딱 풀려버릴것 같지만 너무 무서워하지는 마세요. 특이 해상도를 사용하는 사람들은 극히 일부분이고 보통은 hdpi, xhdpi, xxhdpi 만 지원하면 안드로이드가 알아서 스케일링 등을 해주기 때문에 별 문제가 없습니다. 사실 열심히 저해상도에 대응한다고 해도 막상 결과를 보면 실망하시게 될 겁니다.

이렇게 말씀드리는건 요즘 앱 사용자들의 성향을 보면 80% 이상이 hdpi/xhdpi/xxhdpi 을 사용하고 있고 xxxhdpi 의 경우에는 아직까진 본격적으로 사용되지 않고 있으며 마지막으로 mdpi 또는 ldpi 와 같은 저 해상도는 더 이상 신경쓰지 않아도 않아도 될만큼 적게 사용되고 있기 때문입니다.

스케일링 필요없거나 많이 쓰이지 않는 이미지의 경우는 drawable-nodpi 를 사용하도록 합니다.

Minimize resources configurations

안드로이드 개발에는 필요에 따라서 Android Support Library, Google Play Services, Facebook SDK 등등의 라이브러리들을 사용합니다. 이런 라이브러리들은 원하는 기능구현을 쉽게 하도록 도와주지만 필요하지 않는 부분도 포함되어 있는것이 사실입니다. 예를들면 구글 플레이서비스는 개발자가 필요로 하지 않는 여러 다국어와 mdpi 와 같은 저해상도의 리소스를 포함합니다.

Gradle Plugin 0.7 부터는 빌드 시스템에 사용할 정보에 대해 정의할 수 있습니다. resConfig 와 resConfigs 를 통해 여기에 정의 되어 있지 않는 리소스는 포함하지 않도록 합니다. 그 결과 APK 의 용량은 작아지게 됩니다. 적용방법은 간단합니다. 아래와 같이 정의할 경우 4가지 언어와 5가지 화면에 대해서만 사용하도록 제한을 걸게 됩니다.

Compress images

안드로이드에서 이미지는 일반적으로 aapt 에 의해 자동 최적화가 이루어집니다. 예를들면 256 컬러 이상을 사용하지 않는 트루컬러 PNG 가 있다면 빌드 시 8 비트의 PNG 로 변환되는 것이죠.

이것이 의미하는 바는 위 조건의 경우라면 애초에 8 비트의 PNG 로 이미지를 생성하여 용량을 줄이자는 것입니다. 디자이너의 요구사항에 맞춰 pngquant, ImageAlpha, ImageOptim 와 같은 툴 중 하나를 선택해서 사용하시면 됩니다.

또 하나는 나인패치의 사용입니다. 이건 다들 너무나도 잘 아실테니 별도의 설명은 하지 않겠습니다 : )

Limit the number of architectures

보통의 안드로이드 앱은 자바로 이루어져 있지만 특별한 앱들은 네이티브 코드를 사용하는 경우가 있습니다. 이러한 네이티브 코드 또한 다이어트가 필요한데요. 이러한 기법은 excellent article 을 참고하여 적용하도록 합시다.

Reuse whenever possible

모든 자원은 재활용해야 됩니다! 굳이 그대로 사용하거나 조금만 변경해도 되는것을 다시 만드는 건 큰 낭비죠. 대표적으로 안드로이드에서는 리스트 뷰나 리사이클러 뷰를 통해 반복되는 포맷을 재활용하여 화면의 부드러운 스크롤링에 혁혁한 공헌을 하고 있습니다.

이러한 재활용 방식은 단순히 퍼포먼스 측면 뿐만 아니라 APK 의 사이즈를 줄이는데도 한 몫하는데 예를들면 안드로이드에서는 컬러를 다시 입히는 유틸을 제공합니다.

안드로이드 L 에서는 android:tint 와 android:tintMode 를 all 버전에서는 ColorFilter 를 사용.

또 다른 예를 들면 단순히 회전을 통해서 똑같이 표현되는 이미지들이 있을 수 있습니다. 여기서는 ic_arrow_expand 와 ic_arrow_collapse 이란 이름으로 사용되는 이미지겠죠.

이 둘중에 하나를 삭제하고 이미지를 회전시키는 속성을 통해서 불필요한 이미지의 생산을 제외시킵니다. 이러한 작업은 디자이너 입장에서도 좋고 파일이 적어지니 유지보수 측면에서도 도움이 될 것입니다.

Render in code when appropriate

애니메이션 적용 시 대부분 이미지를 반복적으로 사용해서 많은 용량을 차지하게 됩니다. 안드로이드 웨어도 마찬가지인데요. 단순히 헬로 월드 만 보여주는 APK 를 만들고 용량을 확인해 보면 1.5 MB 라는 어마무시한 용량을 볼 수 있습니다. 왜 이럴까요? Android wearable support library 를 살펴보면 기본적으로 3개의 다른 해상도 용으로 ‘성공’ (31 frames)과 ‘폰에서 확인하기’ (54 frames)라는 2개의 애니메이션이 만들어져 있는것을 볼 수 있었습니다.

‘성공’용 애니메이션을 위한 이미지들

‘성공' 애니메이션을 동작하게 하기 위한 XML 파일은 다음과 같습니다.

AnimationDrawable XML 파일

여기서의 포인트는 해당 애니메이션을 30 프레임으로 돌리기 위해 각각의 이미지를 33ms 단위로 변경한다는 것입니다. 만약 동일 조건에서 16ms 으로 변경한다면 2배의 이미지가 필요하므로 약 2배의 용량이 더 필요할 것입니다. 이를 해결하기 위해 위에 보이는 XML 의 15 라인에 있는 generic_confirmation_00175 을 살펴보면 해당 프레임은 333ms 인것을 볼 수 있습니다. 비슷한 9개의 이미지를 삭제하고 해당 시간 만큼을 더한 값인거죠. 이것은 훌륭한 최적화 방법입니다.

만약 60 프레임의 애니메이션이 필요하다면 위 기법을 적절히 사용하면 많은 혜택을 받을 수 있을 것입니다. 아직까지 이러한 기법을 위해 구글에서 제공하는 툴은 없으므로 ‘Adobe After Effect to VectorDrawable’ 이라는 툴을 이용하면 쉽게 적용할 수 있을 것입니다.

많은 분들이 프로젝트가 완료된 뒤에 수동으로 사용하지 않는 코드와 이미지들을 삭제하고 있다는 것을 알게되어 이렇게 정리해봤습니다. 저도 나름 툴을 이용해서 많이 정리한다고 생각했는데요. 그래들을 통한 리소스 관리는 처음 접해보는 방식이라 아직 부족하다는 것을 느꼈습니다. 이 글이 APK 용량을 줄이고자 하는 분들께 조금이나마 도움이 되었으면 하네요. 문의사항은 언제든지 댓글이나 @_marojun 으로 트윗 해주세요.

원문은 아래 링크를 통해 확인해 보실 수 있습니다. 감사합니다.

--

--

marojun
marojun’s Android

전슬마로. KTH, SK Planet, NCSOFT 에서 iOS와 Android를 개발하고 있다. — 안드로이드 개발 그룹 https://www.facebook.com/groups/junsle/