(5분 읽기) Kotlin 코루틴을 활용한 비동기 처리 완벽 정리🚀 (초보 개발자용)
안드로이드 앱을 개발하다 보면 네트워크 요청, 데이터베이스 조회, UI 이벤트 처리 등 비동기 작업을 자주 수행해야 해.
비동기 처리를 잘못하면 **콜백 지옥(Callback Hell)**에 빠지고, 앱이 느려지거나 튕기는 문제가 생길 수도 있어.
이때 **Kotlin 코루틴(Coroutines)**을 사용하면 비동기 작업을 쉽고, 가독성 높게 처리할 수 있어!
오늘은 초보 개발자도 쉽게 이해할 수 있도록 코루틴을 활용한 비동기 처리에 대해 정리해볼게. 💡
🎯 비동기 처리란? (동기 vs 비동기 이해하기)
비동기(Asynchronous)는 말 그대로 **"동시에 실행하지 않는 것"**이야.
비동기 처리를 이해하려면 동기(Synchronous) 처리와 비교하면 쉬워.
✅ 동기 처리(Synchronous)란?
동기 처리는 한 작업이 끝날 때까지 다음 작업이 대기하는 방식이야.
💡 예제: 동기 코드
fun main() {
println("1. 데이터를 불러오는 중...")
Thread.sleep(3000) // 3초 동안 대기 (네트워크 요청 가정)
println("2. 데이터 로드 완료!")
}
📌 결과
1. 데이터를 불러오는 중...
(3초 대기)
2. 데이터 로드 완료!
문제점: 첫 번째 작업이 끝날 때까지 다음 코드가 실행되지 않아 앱이 멈춘 것처럼 느껴질 수 있어. 😰
✅ 비동기 처리(Asynchronous)란?
비동기 처리는 한 작업이 끝나길 기다리지 않고, 다른 작업을 동시에 진행하는 방식이야.
💡 예제: 기본적인 비동기 처리 (콜백)
fun fetchData(callback: (String) -> Unit) {
Thread {
Thread.sleep(3000) // 3초 후 데이터 반환
callback("2. 데이터 로드 완료!")
}.start()
}
fun main() {
println("1. 데이터를 불러오는 중...")
fetchData { result -> println(result) }
println("3. 다른 작업 실행 중...")
}
📌 결과
1. 데이터를 불러오는 중...
3. 다른 작업 실행 중... // fetchData()가 실행되는 동안 다른 코드가 먼저 실행됨
(3초 후)
2. 데이터 로드 완료!
- 장점: 앱이 멈추지 않고, UI가 자연스럽게 동작해!
- 단점: 콜백이 중첩될수록 가독성이 떨어지고 **콜백 지옥(Callback Hell)**이 발생할 수 있음.
🔥 코루틴을 활용한 비동기 처리 방법
✅ 1️⃣ suspend 함수로 비동기 코드 작성하기
suspend 키워드를 사용하면 함수 내에서 비동기 작업을 일시 중단했다가 재개할 수 있어.
💡 예제: suspend 함수를 사용한 비동기 처리
import kotlinx.coroutines.*
suspend fun fetchData(): String {
delay(3000) // 3초 후 데이터 반환
return "2. 데이터 로드 완료!"
}
fun main() = runBlocking {
println("1. 데이터를 불러오는 중...")
val result = fetchData() // 비동기 함수 실행 (delay 동안 다른 코드 실행 가능)
println(result)
}
📌 결과
1. 데이터를 불러오는 중...
(3초 후)
2. 데이터 로드 완료!
✔ delay(3000)을 사용하면 Thread.sleep()처럼 스레드를 멈추지 않고 비동기 대기 가능!
✔ suspend 함수는 일반 함수에서 직접 호출할 수 없고, 코루틴 블록(runBlocking) 내에서 실행해야 함
✅ 2️⃣ launch로 여러 비동기 작업 동시에 실행하기
💡 예제: launch를 이용한 병렬 실행
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000)
println("2. 비동기 작업 1 완료!")
}
launch {
delay(2000)
println("3. 비동기 작업 2 완료!")
}
println("1. 메인 스레드 실행 중...")
}
📌 결과
1. 메인 스레드 실행 중...
(1초 후)
2. 비동기 작업 1 완료!
(2초 후)
3. 비동기 작업 2 완료!
✔ launch를 사용하면 여러 개의 비동기 작업을 동시에 실행할 수 있어!
✅ 3️⃣ async와 await을 이용한 결과 반환
💡 예제: async를 활용한 비동기 데이터 처리
import kotlinx.coroutines.*
fun main() = runBlocking {
val deferred1 = async {
delay(1000)
"2. 데이터 1 로드 완료!"
}
val deferred2 = async {
delay(2000)
"3. 데이터 2 로드 완료!"
}
println("1. 데이터 로딩 시작...")
println(deferred1.await()) // 1초 후 결과 출력
println(deferred2.await()) // 2초 후 결과 출력
}
📌 결과
1. 데이터 로딩 시작...
(1초 후)
2. 데이터 1 로드 완료!
(2초 후)
3. 데이터 2 로드 완료!
✔ async는 launch와 다르게 결과를 반환할 수 있음!
✔ await()을 사용하면 비동기 작업이 완료될 때까지 기다렸다가 결과를 받아옴
🚀 코루틴 + Flow: 실시간 데이터 스트림 처리
코루틴은 단일 작업을 처리하는 데 강력하지만, 스트림 형태의 데이터(예: 실시간 센서, WebSocket, Room DB)를 처리하려면 Flow가 필요해!
💡 예제: Flow를 이용한 데이터 스트림 처리
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun fetchData(): Flow<String> = flow {
for (i in 1..3) {
delay(1000)
emit("데이터 $i 로드 완료!")
}
}
fun main() = runBlocking {
fetchData().collect { println(it) } // Flow 데이터 수집
}
📌 결과
(1초 후) 데이터 1 로드 완료!
(2초 후) 데이터 2 로드 완료!
(3초 후) 데이터 3 로드 완료!
✔ emit()을 사용해 데이터를 계속 전달할 수 있음
✔ collect()를 사용해 Flow 데이터를 실시간으로 수집 가능
🎯 결론: 코루틴을 사용하면 비동기 처리가 쉬워진다!
✅ 동기 코드처럼 읽히면서도 비동기 실행 가능
✅ launch, async를 활용하면 여러 개의 비동기 작업을 쉽게 관리 가능
✅ Flow를 이용하면 실시간 데이터 스트림 처리까지 가능
이제 코루틴을 활용해서 더 가독성 높고, 효율적인 비동기 코드를 작성해보자! 🚀