λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸš€ Development/Android

[Android] λ„€νŠΈμ›Œν¬ μž‘μ—… μ‹œ 비동기 μž‘μ—…μ΄ ν•„μš”ν•œ 이유 + 코루틴(coroutine)

by Jay Din 2024. 11. 25.
728x90
λ°˜μ‘ν˜•

1. UI 차단(Block) ν˜„μƒμ˜ 원리와 문제점

  1. Android의 μŠ€λ ˆλ“œ λͺ¨λΈ
    • Android 앱은 기본적으둜 메인 μŠ€λ ˆλ“œ(Main Thread)μ—μ„œ μ‚¬μš©μž μΈν„°νŽ˜μ΄μŠ€(UI)와 κ΄€λ ¨λœ μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
      • 예: 화면에 λ²„νŠΌ ν‘œμ‹œ, μ‚¬μš©μžμ˜ 클릭 이벀트 처리.
    • λ„€νŠΈμ›Œν¬ μž‘μ—…μ€ 주둜 였래 걸릴 수 μžˆλŠ” μž‘μ—…μ— μ†ν•˜λ©°, 메인 μŠ€λ ˆλ“œμ—μ„œ μ‹€ν–‰ν•˜λ©΄ λ‹€λ₯Έ μž‘μ—…(특히 UI κ΄€λ ¨ μž‘μ—…)이 λŒ€κΈ° μƒνƒœμ— λ“€μ–΄κ°‘λ‹ˆλ‹€.
  2. UI 차단(Blocking)의 원인
    • λ„€νŠΈμ›Œν¬ μž‘μ—…μ€ 데이터λ₯Ό μ†‘μˆ˜μ‹ ν•˜λŠ” λ™μ•ˆ μ™ΈλΆ€ μ„œλ²„μ™€μ˜ 톡신을 κΈ°λ‹€λ €μ•Ό ν•©λ‹ˆλ‹€.
      • 예) μ„œλ²„μ—μ„œ 데이터λ₯Ό μ²˜λ¦¬ν•˜κ±°λ‚˜, λ„€νŠΈμ›Œν¬ 지연(Latency)이 λ°œμƒν•˜λŠ” 경우.
    • 이 λ™μ•ˆ 앱은 κ²°κ³Όκ°€ λ°˜ν™˜λ˜κΈ°λ₯Ό 기닀리며 아무것도 ν•˜μ§€ λͺ»ν•˜λŠ” μƒνƒœκ°€ λ©λ‹ˆλ‹€.
    • 메인 μŠ€λ ˆλ“œκ°€ 이 λŒ€κΈ° μž‘μ—…μ— 묢이게 되면 UI μ—…λ°μ΄νŠΈ(ν™”λ©΄ 그리기, ν„°μΉ˜ 응닡 λ“±)κ°€ μ€‘λ‹¨λ©λ‹ˆλ‹€.
  3. μ‚¬μš©μžκ°€ λŠλΌλŠ” 문제
    • 화면이 멈좀 κ²ƒμ²˜λŸΌ 보이며, 앱이 "μ‘λ‹΅ν•˜μ§€ μ•ŠμŒ"으둜 인식될 수 μžˆμŠ΅λ‹ˆλ‹€.
    • 앱은 λ‹€μŒκ³Ό 같은 증상을 보일 수 μžˆμŠ΅λ‹ˆλ‹€:
      • λ²„νŠΌ 클릭 μ‹œ μ•„λ¬΄λŸ° λ°˜μ‘μ΄ μ—†μŒ.
      • ν™”λ©΄μ˜ ν…μŠ€νŠΈλ‚˜ 이미지λ₯Ό μƒˆλ‘œκ³ μΉ¨ν•˜μ§€ μ•ŠμŒ.
    • 이둜 인해 μ‚¬μš©μž κ²½ν—˜(UX)이 크게 μ €ν•˜λ©λ‹ˆλ‹€.
  4. ANR(Application Not Responding)
    • AndroidλŠ” 메인 μŠ€λ ˆλ“œμ—μ„œ μž‘μ—…μ΄ 5초 이상 μ°¨λ‹¨λ˜λ©΄ ANR 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚΅λ‹ˆλ‹€.
      • ANR μ°½: "앱이 μ‘λ‹΅ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 앱을 μ’…λ£Œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?" λΌλŠ” λ©”μ‹œμ§€κ°€ ν‘œμ‹œλ¨.
    • ANR은 μ‚¬μš©μžκ°€ 앱을 μ’…λ£Œν•˜κ²Œ λ§Œλ“€λ©°, μ•± ν‰νŒμ— μ•…μ˜ν–₯을 λ―ΈμΉ  수 μžˆμŠ΅λ‹ˆλ‹€.

 

2. λ„€νŠΈμ›Œν¬ μž‘μ—…μ—μ„œ λΉ„μš©κΈ° μž‘μ—…μ˜ ν•„μš”μ„±

  1. 비동기 처리의 κ°œλ…
    • 비동기 μž‘μ—…μ€ λ„€νŠΈμ›Œν¬ μš”μ²­κ³Ό 같은 κΈ΄ μž‘μ—…μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬λŠ” λŒ€μ‹ , μž‘μ—…μ„ λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ²˜λ¦¬ν•˜λ©° λ‹€λ₯Έ μž‘μ—…μ€ 계속 μ§„ν–‰ν•©λ‹ˆλ‹€.
    • μž‘μ—… μ™„λ£Œ ν›„ 결과만 전달받아 UIλ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.
  2. 비동기 μž‘μ—…μ˜ μž₯점
    • UI 응닡성 μœ μ§€:
      • λ„€νŠΈμ›Œν¬ μž‘μ—…μ΄ μ§„ν–‰λ˜λŠ” λ™μ•ˆμ—λ„ UIλŠ” μ •μƒμ μœΌλ‘œ λ™μž‘.
      • μ‚¬μš©μžλŠ” 앱이 μ€‘λ‹¨λ˜μ§€ μ•Šκ³  "λ°˜μ‘μ΄ λΉ λ₯΄λ‹€:"κ³  λŠλ‚Œ.
    • 효율적인 λ¦¬μ†ŒμŠ€ μ‚¬μš©:
      • 메인 μŠ€λ ˆλ“œμ—μ„œ μž‘μ—…μ΄ μ€‘λ‹¨λ˜μ§€ μ•ŠμœΌλ―€λ‘œ CPU와 λ©”λͺ¨λ¦¬λ₯Ό 효율적으둜 ν™œμš©.
    • μ€‘μ²©λœ 호좜 관리:
      • μ—¬λŸ¬ λ„€νŠΈμ›Œν¬ μž‘μ—…μ„ λ™μ‹œμ— μ²˜λ¦¬ν•  수 μžˆμ–΄ μž‘μ—… 효율이 높아짐.

 

3. μ½”λ£¨ν‹΄μ˜ μž‘λ™ 원리와 κΈ°λŠ₯

1. κ²½λŸ‰ μŠ€λ ˆλ“œ

  • 코루틴은 운영체제의 μŠ€λ ˆλ“œ λŒ€μ‹ , κ°€λ²Όμš΄ ꡬ쑰체 ν˜•νƒœλ‘œ λ™μž‘ν•˜μ—¬ ν•˜λ‚˜μ˜ μŠ€λ ˆλ“œμ—μ„œ 수천 개의 코루틴을 μ‹€ν–‰ κ°€λŠ₯ν•©λ‹ˆλ‹€.
  • μŠ€λ ˆλ“œ ν’€(Thread Pool)을 직접 관리할 ν•„μš” 없이, ν•„μš”ν•œ 만큼 비동기 μž‘μ—…μ„ 생성 κ°€λŠ₯ν•©λ‹ˆλ‹€.

2. μˆœμ°¨μ μ΄λ©΄μ„œ 비동기적인 μ½”λ“œ μž‘μ„±

  • 코루틴을 μ‚¬μš©ν•˜λ©΄ λ„€νŠΈμ›Œν¬ ν˜ΈμΆœμ„ 마치 동기 μž‘μ—…μ²˜λŸΌ 순차적으둜 μž‘μ„±ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • μ•„λž˜ μ½”λ“œλŠ” λ™κΈ°μ μœΌλ‘œ λ³΄μ΄μ§€λ§Œ, μ‹€μ œλ‘œλŠ” λ„€νŠΈμ›Œν¬ 호좜이 λ°±κ·ΈλΌμš΄λ“œμ—μ„œ λΉ„λ™κΈ°λ‘œ μ²˜λ¦¬λ©λ‹ˆλ‹€.
suspend fun fetchData() {
    val data = apiService.getData() // 비동기 호좜
    updateUI(data) // UI μ—…λ°μ΄νŠΈ
}

 

3. Dispatchers의 ν™œμš©

  • Dispatchers.IO: λ„€νŠΈμ›Œν¬ μž‘μ—…κ³Ό 같은 I/O 집쀑 μž‘μ—…μ— μ΅œμ ν™”.
  • Dispatchers.Main: UI와 κ΄€λ ¨λœ μž‘μ—…μ€ λ°˜λ“œμ‹œ 메인 μŠ€λ ˆλ“œμ—μ„œ μ‹€ν–‰.
  • μ½”λ£¨ν‹΄μ˜ withContextλ₯Ό μ‚¬μš©ν•˜λ©΄ μ•ˆμ „ν•˜κ²Œ μž‘μ—…μ„ μ μ ˆν•œ μŠ€λ ˆλ“œλ‘œ μ „ν™˜ κ°€λŠ₯.
CoroutineScope(Dispatchers.IO).launch {
    val result = fetchNetworkData()
    withContext(Dispatchers.Main) {
        textView.text = result
    }
}

 

4. 였λ₯˜ κ΄€λ¦¬μ˜ λ‹¨μˆœν™”

  • μ½”λ£¨ν‹΄μ—μ„œ λ„€νŠΈμ›Œν¬ μž‘μ—…μ˜ 였λ₯˜λ₯Ό μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ try-catchλ₯Ό μ‚¬μš©.
  • λ„€νŠΈμ›Œν¬ 호좜 μ‹€νŒ¨λ‚˜ μ˜ˆμ™Έ λ°œμƒ μ‹œ UI에 μ—λŸ¬ λ©”μ‹œμ§€λ₯Ό μ•ˆμ „ν•˜κ²Œ ν‘œμ‹œ κ°€λŠ₯.
try {
    val response = apiService.getData()
    // 성곡 처리
} catch (e: Exception) {
    // 였λ₯˜ 처리
}

 

4. 코루틴 ν™œμš© μ‹œμ˜ λ„€νŠΈμ›Œν¬ μž‘μ—… 흐름

  • μž‘μ—… 뢄리
    • 메인 μŠ€λ ˆλ“œ: UI κ΄€λ ¨ μž‘μ—… 및 μ‚¬μš©μž μž…λ ₯ 처리.
    • I/O μŠ€λ ˆλ“œ: λ„€νŠΈμ›Œν¬ μž‘μ—…, 데이터 처리 λ“± CPU λ˜λŠ” I/O 집쀑 μž‘μ—….
  • μž‘μ—… μ™„λ£Œ ν›„ UI μ—…λ°μ΄νŠΈ
    • λ„€νŠΈμ›Œν¬ μž‘μ—…μ΄ μ™„λ£Œλ˜λ©΄ Dispatchers.Main둜 μ „ν™˜ν•˜μ—¬ μ•ˆμ „ν•˜κ²Œ UIλ₯Ό κ°±μ‹ .
    • λ°±κ·ΈλΌμš΄λ“œ μž‘μ—…κ³Ό UI μž‘μ—… κ°„μ˜ λͺ…ν™•ν•œ μ—­ν•  λΆ„λ‹΄μœΌλ‘œ μ•ˆμ •μ„± 확보.
  • 닀쀑 λ„€νŠΈμ›Œν¬ 호좜 처리
    • μ—¬λŸ¬ λ„€νŠΈμ›Œν¬ μš”μ²­μ„ λ™μ‹œμ— μ‹€ν–‰ν•˜κ³ , κ²°κ³Όλ₯Ό μ‘°ν•©ν•˜μ—¬ 처리 κ°€λŠ₯.
CoroutineScope(Dispatchers.IO).launch {
    val data1 = async { fetchData1() }
    val data2 = async { fetchData2() }
    val combined = data1.await() + data2.await()
    withContext(Dispatchers.Main) {
        updateUI(combined)
    }
}

 

 

μ •λ¦¬ν•˜λ©΄

  1. 비동기 μž‘μ—…μ΄ ν•„μš”ν•œ μ΄μœ λŠ” λ„€νŠΈμ›Œν¬ μž‘μ—…μ˜ νŠΉμ„±κ³Ό Android의 μŠ€λ ˆλ“œ μ •μ±… λ•Œλ¬Έμž…λ‹ˆλ‹€. UI차단은 μ‚¬μš©μžκ°€ 앱을 λŠλ¦¬κ±°λ‚˜ λΆˆμ•ˆμ •ν•˜λ‹€κ³  μΈμ‹ν•˜κ²Œ λ§Œλ“€λ©°, ANR둜 μ΄μ–΄μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
  2. 코루틴은 μ΄λŸ¬ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μž‘μ—…μ„ μ‹€ν–‰ν•˜λ©΄μ„œλ„, UI μ—…λ°μ΄νŠΈ μ‹œ 메인 μŠ€λ ˆλ“œλ₯Ό μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλŠ” κ°•λ ₯ν•œ 도ꡬλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.
  3. λ„€νŠΈμ›Œν¬ μž‘μ—…μ—μ„œ 코루틴을 ν™œμš©ν•˜λ©΄ μ½”λ“œμ˜ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ΄ ν–₯μƒλ˜λ©°, μ•ˆμ •μ μ΄κ³  응닡성 쒋은 앱을 λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

728x90
λ°˜μ‘ν˜•