---
id: mobile-app-size-optimization
title: App Size 최적화 — 100MB 이하로
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [mobile, app-size, optimization, vibe-coding]
tech_stack: { language: "Swift / Kotlin / RN", applicable_to: ["iOS", "Android"] }
applied_in: []
aliases: [app size, IPA, AAB, App Bundle, app thinning, bitcode, R8]
---

# App Size 최적화

> 200MB 앱 = 다운로드 안 함. **iOS App Thinning + Android App Bundle (AAB) 자동 분할**. 그래도 base 가 큼 = 비트맵 / 폰트 / 라이브러리 정리. iOS 앱 200MB 셀룰러 다운로드 한계.

## 📖 핵심 개념
- iOS App Thinning: 디바이스별 slice 만 다운로드.
- Android App Bundle: ABI / density / language 별 분할.
- Resource: image / font / 라이브러리.
- Code: 미사용 / dead code 제거.

## 💻 코드 패턴

### Android App Bundle (AAB)
```bash
./gradlew bundleRelease
# .aab — Play 가 디바이스별 .apk 생성
```

```groovy
// build.gradle
android {
    bundle {
        density { enableSplit = true }
        abi { enableSplit = true }
        language { enableSplit = true }
    }
}
```

### Android R8 / Proguard (코드 축소)
```groovy
buildTypes {
    release {
        minifyEnabled true       // R8 활성
        shrinkResources true     // 미사용 리소스 제거
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
```

```
# proguard-rules.pro
-keep class com.acme.api.dto.** { *; }   # JSON 모델 keep
-keep class * extends android.app.Activity
-keepclassmembers,allowoptimization enum * { *; }
```

### iOS Bitcode (deprecated 2022 — Xcode 14+ 자동 처리)
- 옛 thin: bitcode → Apple 이 device 별 컴파일.
- 현재: App Thinning 만 (slicing).

### Swift code size
```swift
// ❌ Generic 깊음 → bloat
struct GenericContainer<T: Equatable & Comparable & Hashable & Codable> { ... }

// 단순화 가능하면 단순화
```

```bash
# Build setting
SWIFT_OPTIMIZATION_LEVEL = -O
DEAD_CODE_STRIPPING = YES
LINK_DEAD_CODE_STRIPPING = YES (-dead_strip)
```

### 이미지 / asset
```bash
# WebP / HEIF 로 변환 (PNG 보다 60% 작음)
cwebp -q 80 in.png -o out.webp

# Asset catalog 사용 (iOS): @1x/@2x/@3x 자동 thinning
```

```kotlin
// Android: drawable-nodpi 대신 vector
// SVG → VectorDrawable
```

### Font subset
```bash
# 사용하는 글자만
fonttools subset NanumGothic.ttf --unicodes="U+0020-007E,AC00-D7AF" --output-file=NanumGothic.subset.ttf
```

### 라이브러리 정리
```bash
# Bundle analysis
# iOS
xcrun bitcode_strip -r App.app/App -o App.app/App.stripped # bitcode 제거
du -h App.app/Frameworks/*

# Android
./gradlew assembleRelease
# build/outputs/apk/release/app-release.apk → APK Analyzer 로 분석
```

미사용 dependency 제거:
```bash
# Knip / depcheck (RN)
npx knip
```

### React Native Hermes + bytecode
```js
// Hermes 활성 — JS bytecode 가 작음
// android/app/build.gradle
project.ext.react = [enableHermes: true]
```

### Dynamic feature (Android)
```groovy
// 옵션 기능 = 별도 모듈, 필요 시 다운로드
android.dynamicFeatures = [':feature_premium']
```

```kotlin
val request = SplitInstallRequest.newBuilder().addModule('feature_premium').build()
splitInstallManager.startInstall(request)
```

### iOS On-Demand Resources
```swift
let req = NSBundleResourceRequest(tags: ["level10"])
req.beginAccessingResources { error in ... }
```

특정 자산 (큰 비디오 / 게임 레벨) 사용 시 다운로드.

### 측정 / 모니터
- Android: APK Analyzer (Android Studio).
- iOS: Xcode Organizer → App Size.
- App Store Connect: install size 보고.

## 🤔 의사결정 기준
| 항목 | 액션 |
|---|---|
| 큰 이미지 | WebP / AVIF / asset catalog |
| 큰 폰트 | Subset |
| 미사용 코드 | R8 / dead_strip |
| 큰 라이브러리 | 가벼운 alternative / 자체 구현 |
| 옵션 기능 | Dynamic feature / On-demand |
| Cellular install 200MB+ | App Thinning 강력 |

## ❌ 안티패턴
- **모든 해상도 PNG**: drawable-xxxhdpi 만 + vector.
- **APK 배포 (AAB X)**: 사용자 폰 별 분할 못 함.
- **R8 안 켬**: 30-50% 절약 손실.
- **3rd party SDK 무절제**: 한 SDK = 5MB+.
- **Asset 큰 폰트 모두**: subset.
- **bitcode 옛 가정**: 현재 영향 X.
- **Universal binary 없는 thin 안 함**: 디바이스별 slice 안 됨.
- **Image lossless 만**: 80% 압축이면 시각 동일.

## 🤖 LLM 활용 힌트
- AAB + R8 + WebP + 폰트 subset 4종.
- iOS = App Thinning + dead_strip + Hermes.
- Dynamic feature / On-demand 큰 자원.

## 🔗 관련 문서
- [[Frontend_Image_Optimization]]
- [[RN_Hermes_Optimization]]
- [[Native_Memory_Profiling]]
