개발에서의 다크다크 — 것이 뭐멘지, 원인 및 최적화 방법

저자: IT Sectr 게시일: 2026-07-28 읽는 시간: 8 분

다크다크는 메달한 상황을 설명하는 사용자 용어로, 모바일 앱이 늘리고 고작스럽게 작동하는 경우를 말합니다: 가끔은 정상으로 반응하다가 갑자기 몇 초 동안 먹혀 버립니다. 기술적 문맥에서 “다크다크다”는 재발하는 GC 팼즈, 동기 작업에 의한 메인 스레드 차단, 비최적인 데이터 구조에 의해 발생하는 랜과 마이크로 프리즈의 조합을 의미합니다. Android Performance Benchmarking Guide에 따르면, 응답 시간을 300ms에서 100ms로 줄이면 사용자 유지율이 25% 증가합니다. 다크다크하는 현상을 진단하려면 CPU 및 메모리 프로파일링과 가비지 수집 빈도 분석이 필요합니다.

중요한 점

  • 다크다크 정상 성능과 교체되며 발생하는 앱의 간헉적인 감속 현상입니다
  • 주요 원인 — 잠빈 GC 팼즈, UI 스레드에서의 동기 작업, 페이지네이션 없이 어댕터에서의 대량 데이터
  • 진단은 병목을 찾는 CPU Profiler와 GC 빈도 및 기간을 분석하는 Memory Profiler가 필요합니다
  • 해결에는 페이지네이션 (Paging 3) 도입, Room 통한 SQL 쿼리 최적화, 무거운 작업의 WorkManager 이전이 포함됩니다
  • 예방 — Benchmark Baseline Profiles, AOT 컴파일, 푛 코드 경로에서 할당 최소화

모바일 개발에서 “다크다크다”는 무슨 뜻인가?

다크다크는 사용자가 주관적으로 느린 앱 성능을 설명하는 비정식 용어입니다. 일정한 지연으로 나타나는 랜과 달리, 다크다크는 비정규적인 먹큼으로 구성됩니다: 앱이 몇 초 동안 완벽하게 작동하다가 갑자기 1–3초 동안 “고민하는” 것같이 보일 수 있습니다.

현상의 기술적 설명

프로파일링 관점에서 다크다크는 최대 지연이 100ms를 초과하는 일루된 프레임 녹화(jank)의 연속으로 나타난니다. FPS 그래프에서는 급격한 하락으로 보입니다: 60 → 20 → 55 → 10 FPS. FPS가 일정하게 낮은 랜과 달리, 다크다크는 트렌 있는 변동성을 갖습니다.

사용자 인식

앱이 다크다크할 때, 사용자는 늘어지는 이유를 이해할 수 없습니다: 화면이 부드럽게 스크롤되다가 갑자기 일이상 멈추는 경우가 있습니다. 이는 직절감을 유발하고 앱에 대한 신뢰를 떨어뜨립니다. Google에 따르면, 로딩에 3초 이상 걸리면 53% 사용자가 사이트나 앱을 떠낝니다.

앱에서 갑자기 늘어지는 원인

다크다크의 간헉적인 성질은 문제가 지속적인 과부하를 때문이 아니라 이벤트 구동 요인에 의해 발생한다는 것을 나타냅니다. 일반적인 시나리오를 더북시다.

객체 할당 시 GC 팼즈

Android의 ART 런타임에서는 가비지 수집이 모든 앱 스레드를 멈춰서킵니다. 코드가 많은 일시 객체를 만드는 경우, 예를 들어 onBindViewHolder가 호출될 때마다 연결을 통해 새 String을 만드는 경우, GC가 더 잠발발생합니다. 팼즈는 헥 크기와 객체 생성 대에 따라 5–50ms 지속된 수 있습니다. 사용자는 이것을 갑자기의 “고민”으로 인식합니다.

UI 스레드에서의 동기 SQL 쿼리

Android의 Room과 iOS의 Core Data는 비동기 쿼리를 지원하지만, 개발자들은 간편성을 위해 getValue()를 호출하거나 runBlocking을 통해 쿼리를 실행하는 경우가 많습니다. 10,000행 테이블에서의 조인이 있는 무거운 SELECT는 200–500ms가 걸릴 수 있으며, 그 동안 UI가 완전히 차단됩니다.

다운스케일 없이 이미지 디코딩

카메라 이미지(12MP, 4000x3000 px)를 크기 조정 없이 로드하면 Bitmap 디코딩에 200ms까지 걸릴 수 있습니다. 이미지를 비동기적으로 로드하지만 제한된 스레드 풀이 없는 경우, 5–6개의 디코드를 동시에 실행하면 CPU가 과부하되어 이동하는 늘릴이 발생할 수 있습니다.

  • Android — 루프에서의 문자열 연결, 푛 경로에서의 객체 생성, inSampleSize 없는 Bitmap
  • iOS — 많은 객체가 있는 자동 방출 풀, 크기 조정 없는 imageWithContentsOfFile, 동기 URLSession
  • 크로스-플랫폼 — UI 스레드에서의 JSON 파싱, 서버 응답을 기다리는 동안 메인 스레드에서 데이터 로드

Android 및 iOS에서 먹큼 진단하는 방법

간헉적인 늘맴을 진단하는 것은 일정한 랜을 진단하는 것보다 더 어렵습니다. 매 실행마다 문제가 재현되지 않을 수 있기 때문입니다. 장기간에 걸쳐 통계 데이터를 수집하는 것이 필요합니다.

GC 이벤트 로깅이 있는 Memory Profiler

Android Studio Memory Profiler는 메모리 사용러만 가아니라 GC 이벤트도 표시합니다: 빈도, 유형(Concurrent, Full), 기간. 유지 상태에서 GC가 5초마다 1번 이상 발생하면 과잉 할당의 증거입니다. 다크다크하는 순간에 헥 덤프를 찍으면 어떤 객체가 메모리를 차지하고 있는지 알 수 있습니다.

Allocation Tracking이 있는 Xcode Instruments

iOS에서는 Instruments의 Allocations 템플릿을 사용하여 객체 생성과 해제를 추적하십시오. Generations을 사용하면 동작 사이에 헥 스냅샷을 찍고 메모리에 남아있는 객체를 확인할 수 있습니다. 해제되지 않는 지속 객체는 메모리 축적과 이후 팼즈의 원인입니다.

Android의 JankStats API

JankStats는 실시간으로 놀친 프레임 메트릭스를 수집하는 Android 라이브러리입니다. 각 jank를 현재 시나리오( 예: “리스트 스크롤”, “화면 열기”)와 연결하여 어떤 특정 작업이 다크다크를 유발하는지 파악할 수 있습니다.

Android에서 먹큼 추적을 위한 JankStats 정합 예시:

kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var jankStats: JankStats

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        jankStats = JankStats.create(this.window.decorView) { frameData ->
            if (frameData.isJank()) {
                Log.w("Jank", "Duration=${frameData.durationMs}ms")
            }
        }
    }
}

늘린 성능을 해결하는 방법

다크다크를 해결하려면 각 원인별로 대상적인 작업이 필요합니다. 만능의 해결책은 없습니다 — 특정 성능 프로파일의 분석이 필요합니다.

Paging 3을 통한 페이지네이션 구현

리스트에 1000+ 아이템이 있고 모두 한번에 로드되면 그것은 확실한 다크다크입니다. Android의 Paging 3와 iOS의 NSFetchedResultsController는 사용자가 스크롤할 때 데이터를 나누어 로드합니다. 사용자는 첫 10–20개의 아이템만 보고 나머지는 백그라운드에서 로드됩니다.

SQL 쿼리 및 인덱스 최적화

Room은 Android Studio의 Inspection Tool을 통해 쿼리 프로파일링을 허용합니다: 실행 시간, 반환된 행 수, 쿼리 계획이 표시됩니다. WHERE와 ORDER BY 열에 인덱스를 추가하면 쿼리 시간을 300ms에서 5ms로 줄일 수 있습니다. iOS에서는 Instruments의 Core Data Profiler가 비슷한 확인을 수행합니다.

WorkManager로 작업 이전

백그라운드 동기화, 파일 다운로드, 데이터 처리 — 이모두 WorkManager(Android) 또는 Background Tasks(iOS)를 통해 실행해야 합니다. 동기화가 UI 스레드에서 실행되면 앱이 작업 중에 다크다크합니다. WorkManager는 배터리 및 네트워크 상태를 고려하여 백그라운드 스레드에서의 실행을 보장합니다.

Android에서 WorkManager를 통한 백그라운드 동기화 예시:

kotlin
class SyncWorker(context: Context, params: WorkerParameters)
    : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        return try {
            Log.d("동기화", "백그라운드 스레드에서 데이터 동기화 중")
            syncData()
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
}

개발 단계에서의 다크다크 예방

효율적인 메모리 및 스레드 관리 원칙을 따른다면 코딩 단계에서 다크다크를 방지할 수 있습니다.

AOT 컴파일을 위한 Baseline Profiles

Baseline Profiles는 Android가 JIT 대신 사전에 (AOT) 컴파일하는 클래스와 메소드의 목록입니다. 프로파일이 없으면 매 새 화면이 첫 번째 열릴 때 컴파일되어 100–500ms 지연이 발생합니다. 주요 화면에 대한 Baseline Profile을 작성하고 baseline-profile-gradle-plugin을 통해 Gradle에서 생성을 사용하십시오.

푛 경로에서 할당 최소화

푛 경로는 모든 프레임에서 실행되는 코드입니다: onBindViewHolder, draw, layoutSubviews. 이 메소드에서 객체를 생성하는 것을 피하십시오: 객체 풀 사용, 연결 대신 StringBuilder 사용, 포맷된 문자열과 포맷터 캐시. 모든 추가 할당은 다음 GC를 가까워집니다.

CI에서 Baseline Profiles를 통한 프로파일링

CI 파이프라인에 리스트 스크롤 및 화면 열기 시나리오가 있는 Macrobenchmark를 추가하십시오. 기준치를 설정합니다: 프레임 시간의 99번째 백분위수는 16ms를 초과해서는 안 됩니다. 기준치를 초과하면 최적화가 완료될 때까지 빌드가 거부됩니다.

  • Android — Baseline Profiles, Macrobenchmark, JankStats, penaltyDeath이 있는 StrictMode
  • iOS — MetricKit, os_signpost, XCTMetric, Debug 스키머의 Main Thread Checker
  • 일반적 접근 방식 — 정기적인 프로파일링, 푛 경로에서 할당에 중점을 둔 코드 리뷰

자주 묻는 질문

다크다크는 일반 랜과 어떻게 다른가요?

랜은 일정한 지연입니다(예: 탭마다 200ms). 다크다크는 간헉적입니다: 앱이 정상으로 작동하다가 갑자기 1–3초 늘어지다가 다시 정상으로 돌아옵니다. 원인은 GC 팼즈나 데이터베이스 동기 쿼리같은 이벤트 구동 요인입니다.

Android에서 GC 팼즈 빈도를 어떻게 측정하나요?

Android Studio에서 Memory Profiler를 사용하십시오: Memory 탭에서 기간별 GC 이벤트를 보여줍니다. 프로덕션 모니터링을 위해서는 커스텀 트레이스가 있는 Firebase Performance Monitoring을 적분하십시오. iOS에서는 Malloc Debug를 사용하고 Instruments에서 할당 대를 표시하십시오.

네트워크 요청이 다크다크를 유발할 수 있나요?

간접적으로 — 그러합니다. 서버 응답이 지연되고 UI가 동기적으로 기다리는 경우, 앱이 먹힉니다. 요청이 비동기적이라도 응답 처리가 UI 스레드에서 이루어지면 다크다크가 발생합니다. 해결 방법은 코루틴과 진행 표시기를 사용한 비동기 처리입니다.

Kotlin Multiplatform이 성능에 어떤 영향을 미치나요?

올바르게 사용하지 않으면 KMP가 상호 운용성을 위해 과잉 래퍼 객체를 생성할 수 있습니다. iOS에서는 이로 인해 할당 빈도가 증가하고 결국 ARC 팼즈가 많아집니다. @ObjCName을 사용하고, expect/actual을 최적화하고, UI 푛 경로에서 공유 코드로의 잠반 호출을 피하십시오.

Android에서 헥 크기를 늘리면 도움이 되나요?

android:largeHeap=”true”를 통한 헥 확장은 GC를 지연시키지만 할당의 원인을 없애지는 못합니다. GC가 마지막으로 실행될 때, 더 많은 객체를 순회해야 하기 때문에 팼즈가 더 길어집니다. 해결 방법은 헥을 확장하는 것이 아니라 할당 수를 줄이는 것입니다.

요약

  • 다크다크는 이벤트 구동 요인(GC 팼즈, 동기 쿼리, 이미지 디코딩)에 의한 간헉적인 앱 감속현상입니다
  • 진단에는 Memory Profiler, Android의 JankStats, iOS의 Instruments에서 Allocation Tracking이 필요합니다
  • 주요 원인 — 잠빈 GC 팼즈, 페이지네이션 부재, 비최적 SQL 쿼리, UI 스레드에서의 동기 처리
  • 해결 — Paging 3, WorkManager, DB 인덱스 최적화, 이미지 다운스케일, 할당 최소화
  • 예방 — Baseline Profiles, Macrobenchmark, StrictMode, 푛 경로 확인을 통한 코드 리뷰
  • 도구 — 생산 모니터링을 위한 JankStats, Firebase Performance, MetricKit
  • 권고: 99번째 프레임 백분위수에 16ms 기준치를 설정하여 CI에서 정규적으로 Macrobenchmark를 실행하십시오

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기