Kotlin의 코루틴은 OS 스레드를 차단하지 않고 계산을 일시 중지하고 다시 시작할 수 있는 경량 스레드입니다. launch, async와 같은 함수를 통해 부모 CoroutineScope가 있을 때 시작됩니다.
분산 처리는 코루틴이 실행되는 스레드/실행기(Dispatchers.Main, Dispatchers.IO, Dispatchers.Default)를 정의하는 CoroutineDispatcher 객체를 통해 이루어집니다.
예제:
fun main() = runBlocking { launch(Dispatchers.IO) { val data = getDataFromNetwork() withContext(Dispatchers.Main) { updateUI(data) } } }
취소: 코루틴은 작업/스코프를 취소하여 취소되며, 이는 내부 예외인 CancellationException을 발생시킵니다. 코루틴은 정기적으로 취소 플래그를 확인하거나 일시 중지 함수 호출을 통해 올바르게 종료되어야 합니다.
오류 처리: 코루틴의 예외는 "사라질" 수 있습니다. 예를 들어, 자식 코루틴 내에서 오류를 처리하지 않으면 그 코루틴은 작업을 종료하더라도 부모는 문제를 알지 못합니다. 이를 위해 SupervisorJob 및 CoroutineExceptionHandler 메커니즘이 존재합니다.
컨텍스트 전달의 세부 사항:
부모 코루틴 내에서 자식 중 하나가 오류로 종료되면 다른 자식들은 계속 작업을 진행할까요? 모든 자식이 취소됩니까?
많은 사람들이 오류가 "자식에 남아있다"고 잘못 생각합니다.
정답: 일반 Job(또는 launch)을 사용하는 경우, 자식 중 하나에서 오류가 발생하면 모든 자식 코루틴이 자동으로 취소됩니다. 자식 코루틴이 취소되지 않도록 하려면 SupervisorJob 또는 supervisorScope를 사용해야 합니다:
supervisorScope { launch { error("fail") } launch { println("이 코드는 작동합니다") } }
이야기
잘못된 오류 처리 — 흐름 중단: 프로젝트에서 데이터를 로드하기 위해 코루틴을 사용했습니다. 예외가 자식 코루틴 내에서 "사라져" 재시도/복구 수준이 작동하지 않았고, 메인 화면은 빈 상태로 아무런 오류 메시지 없이 사용자에게 남아 있었습니다.
이야기
Dispatchers.Main에서의 UI 동결: 한 초보 Android 개발자가 Dispatchers.Main에서 무거운 계산 작업을 실행하여 UI가 "멈췄습니다". 모든 무거운 계산이 Dispatchers.Default 또는 Dispatchers.IO에서 실행되어야 한다는 이해가 부족했습니다.
이야기
부모 스코프 취소 시 AbortError: 한 개발자가 부모 스코프를 취소할 때 모든 자식 코루틴이 취소된다는 것을 고려하지 않아 중요한 작업(데이터 저장)이 완료되지 않았습니다.