Android 개발이나 코틀린 백엔드 개발을 하다 보면, 기존의 콜백 기반 비동기 API를 코루틴으로 변환하고 싶은 순간이 자주 옵니다.
이때 자주 사용하는 도구 중 하나가 바로 suspendCancellableCoroutine입니다.
하지만 이 함수는 단순히 suspendCoroutine보다 더 강력하면서도 복잡할 수 있습니다.
이번 글에서는 언제, 왜, 어떻게 써야 하는지를 이해하고, resume, tryResume, completeResume의 차이까지 함께 살펴보겠습니다.
🔍 suspendCancellableCoroutine이란?
기존의 콜백 기반 API를 코루틴의 suspend 함수로 변환하고 싶을 때 사용합니다.
특히 코루틴이 중단(cancel)될 수 있다는 것을 고려해야 한다면, suspendCoroutine이 아닌 suspendCancellableCoroutine을 써야 합니다.
suspendCancellableCoroutine { cont ->
val client = LocationServices.getFusedLocationProviderClient(context)
client.lastLocation
.addOnSuccessListener { cont.resume(it) }
.addOnFailureListener { cont.resumeWithException(it) }
}
이 예시처럼, 콜백 기반 API를 코루틴 방식으로 감싸기만 하면 suspend 함수처럼 사용 가능합니다.
✅ suspendCoroutine vs suspendCancellableCoroutine
기본 suspend 기능 | ✅ | ✅ |
코루틴 취소 감지 | ❌ | ✅ |
invokeOnCancellation 사용 가능 | ❌ | ✅ |
즉, 코루틴이 취소되었을 때 리소스를 안전하게 정리하려면 suspendCancellableCoroutine이 필수입니다.
⚠️ 메인 스레드에서 써도 괜찮을까?
YES, 잘만 쓰면 메인 스레드에서도 안전하게 사용할 수 있습니다.
단, 아래 조건을 지켜야 합니다:
- 코루틴은 suspend 상태일 뿐 메인 스레드를 블로킹하지 않음
- 콜백에서 너무 자주 resume하지 않도록 주의
- invokeOnCancellation을 통해 취소 시 리소스를 정리해야 메모리 누수 방지 가능
someApi.unregisterCallback(callback)
}
🎯 resume vs tryResume vs completeResume
✅ resume(value)
- 즉시 코루틴을 재개
- 이미 resume된 경우 → ❗예외(IllegalStateException) 발생
✅ tryResume(value)
- resume을 시도하고, 성공 시 토큰 반환
- 실패(이미 재개되었거나 취소됨) 시 null 반환 → ❗예외 안 남
if (token != null) {
cont.completeResume(token)
}
✅ completeResume(token)
- tryResume()의 결과로 받은 토큰을 사용해 실제로 코루틴을 재개
- tryResume()과 쌍으로 꼭 사용해야 코루틴이 재개됨
tryResume()만 호출하고 completeResume()을 생략하면 코루틴은 영원히 resume되지 않습니다.
🧠 실전 예제: 콜백 기반 API를 안전하게 감싸기
val callback = object : Callback {
override fun onSuccess(result: String) {
cont.tryResume(result)?.let {
cont.completeResume(it)
}
}
override fun onError(e: Exception) {
cont.tryResumeWithException(e)?.let {
cont.completeResume(it)
}
}
}
api.registerCallback(callback)
cont.invokeOnCancellation {
api.unregisterCallback(callback)
}
}
✅ 정리
resume을 한 번만 호출하는 경우 | resume() |
여러 경로에서 동시에 resume 가능성 있음 | tryResume() + completeResume() |
예외 발생 없이 안전하게 resume 시도 | tryResume() 사용 |
코루틴 취소 시 리소스 정리 필요 | suspendCancellableCoroutine + invokeOnCancellation |
📌 마무리
suspendCancellableCoroutine은 단순히 코루틴을 일시 중단하는 함수가 아닙니다.
콜백 기반 코드를 코루틴 스타일로 안전하게 바꾸는 핵심 도구이며,
취소 처리와 resume 관리에 신중해야 합니다.
코루틴을 좀 더 깊이 이해하고자 한다면, 이 개념을 정확히 알고 사용하는 것이 매우 중요합니다.