--- id: native-battery-network-profiling title: Battery / Network Profiling — 사용량 줄이기 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [native, battery, network, profiling, vibe-coding] tech_stack: { language: "Swift / Kotlin", applicable_to: ["iOS", "Android"] } applied_in: [] aliases: [battery drain, network usage, doze, background fetch, WorkManager] --- # Battery / Network Profiling > 모바일 앱 = 배터리 + 데이터 모두 한정. 하루 5% 이상 소모면 사용자 삭제. **wake lock / 백그라운드 polling / GPS / sync** 4종이 주범. 측정 → 줄이기. ## 📖 핵심 개념 - Doze (Android): screen off + 정지 → background 제한. - Background app refresh (iOS): OS 가 사용 패턴 학습 → 자유로 결정. - Wake lock: CPU 끄기 막음. 마지막 수단. - Network bytes: cellular/wifi 별 측정. ## 💻 코드 패턴 ### Android — Battery Historian ```bash adb bugreport bugreport.zip # https://bathist.ef.lc/ 에 업로드 또는 로컬 docker docker run -p 9999:9999 bhaavan/battery-historian ``` ### iOS — Energy Log ``` Xcode → Open Developer Tool → Instruments → Energy Log # 또는 디바이스: Settings → Privacy → Analytics → Logs ``` ### Network — 사용량 측정 ```kotlin // Android: TrafficStats / NetworkStatsManager val rx = TrafficStats.getUidRxBytes(android.os.Process.myUid()) val tx = TrafficStats.getUidTxBytes(android.os.Process.myUid()) ``` ```swift // iOS: URLSessionTaskMetrics 로 본인 트래픽 측정 class M: NSObject, URLSessionTaskDelegate { func urlSession(_ s: URLSession, task: URLSessionTask, didFinishCollecting m: URLSessionTaskMetrics) { for tx in m.transactionMetrics { print("\(tx.request.url!): \(tx.countOfRequestBodyBytesSent) tx, \(tx.countOfResponseBodyBytesReceived) rx") } } } ``` ### 백그라운드 작업 — WorkManager (Android) ```kotlin val req = PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES) .setConstraints( Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) // wifi 만 .setRequiresCharging(false) .setRequiresBatteryNotLow(true) .build() ) .build() WorkManager.getInstance(ctx).enqueueUniquePeriodicWork("sync", KEEP, req) ``` ### iOS — BGTaskScheduler ```swift BGTaskScheduler.shared.register( forTaskWithIdentifier: "com.app.refresh", using: nil ) { task in handleRefresh(task as! BGAppRefreshTask) } let r = BGAppRefreshTaskRequest(identifier: "com.app.refresh") r.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) try? BGTaskScheduler.shared.submit(r) ``` ### Network 압축 / batch ```kotlin // OkHttp 자동 gzip // 또는 brotli interceptor 추가 // Batch: 5초 마다 모아서 한 번 POST 보다 좋은 게 있나? 응 — 즉시 + jitter 분산 ``` ### 위치 (가장 비싼 자원) ```kotlin val req = LocationRequest.Builder(BALANCED_POWER_ACCURACY, 60_000) // 1분 + 협력적 .setMinUpdateDistanceMeters(50f) // 50m 변화만 .build() ``` ```swift let m = CLLocationManager() m.desiredAccuracy = kCLLocationAccuracyHundredMeters m.distanceFilter = 100 // significant change service m.startMonitoringSignificantLocationChanges() ``` ## 🤔 의사결정 기준 | 작업 | 권장 | |---|---| | Background sync | WorkManager (Android) / BGTaskScheduler (iOS) | | Push 보다 polling | 항상 push 가 우월 (FCM/APNs) | | 위치 추적 | Significant change / geofence (지속 추적 X) | | 큰 다운로드 | Background URLSession / WorkManager unmetered | | Wake lock | 단기 + 명시적 release | ## ❌ 안티패턴 - **Background 5초 polling**: 배터리 폭발. - **GPS continuous high accuracy**: 분당 5%. - **Wake lock 잡고 release 안 함**: 영구 켜짐. - **Cellular 에서 큰 동기화**: 데이터 폭발. unmetered 조건. - **Image 원본 다운로드**: thumbnail / WebP / AVIF. - **WebSocket 평생 유지**: foreground 만, background 는 push. - **JSON 비압축**: gzip + minify. ## 🤖 LLM 활용 힌트 - WorkManager / BGTaskScheduler 항상 사용. - 위치는 significant / geofence. - 측정 → push, batch, 압축 3종. ## 🔗 관련 문서 - [[Native_Memory_Profiling]] - [[Native_Perf_Tracing_Systrace]] - [[Background_Sync_Strategies]]