Kotlin 코루틴의 기본 개념부터 실전 사용법, 그리고 자주 쓰이는 패턴까지 초보자도 이해할 수 있도록 쉽게 설명합니다.

코루틴을 활용한 비동기 프로그래밍의 핵심을 예제와 함께 익혀보세요.

코루틴이란?

코루틴(coroutine)은 Kotlin에서 제공하는 비동기 프로그래밍을 위한 기능으로, 기존의 스레드 기반 처리보다 가볍고 효율적으로 동시성을 구현할 수 있게 해줍니다. 코루틴은 일시 중단(suspend)과 재개(resume)가 가능한 함수 단위의 실행 블록입니다.

코루틴의 기본 구조

코루틴을 사용하기 위해서는 CoroutineScopelaunch, async와 같은 빌더를 활용해야 합니다. 가장 기본적인 코루틴 실행 방법은 다음과 같습니다.

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
}

위 예제에서 runBlocking은 메인 스레드를 블록하여 코루틴이 완료될 때까지 기다립니다. launch는 새로운 코루틴을 생성하고, delay는 코루틴을 일시 중단(suspend)시킵니다.

suspend 함수와 일시 중단

코루틴 내부에서 suspend 키워드를 사용하면 함수 실행을 일시 중단할 수 있습니다. 예를 들어, 네트워크 요청이나 파일 IO와 같이 시간이 오래 걸리는 작업을 suspend 함수로 처리하면 코드가 간결해지고, UI가 멈추지 않습니다.

suspend fun fetchData(): String {
    delay(2000L) // 네트워크 요청 시뮬레이션
    return "데이터 수신 완료"
}

fun main() = runBlocking {
    println("요청 시작")
    val result = fetchData()
    println(result)
}

launch vs async

  • launch: 결과값이 필요 없는 작업에 사용. Job 객체를 반환.
  • async: 결과값이 필요한 작업에 사용. Deferred 객체를 반환하며, await()로 결과를 받을 수 있습니다.
fun main() = runBlocking {
    val job = launch {
        println("launch: 결과 필요 없음")
    }
    val deferred = async {
        println("async: 결과 반환")
        42
    }
    job.join() // launch 완료 대기
    println("async 결과: ${deferred.await()}")
}

코루틴 컨텍스트와 디스패처

코루틴은 어떤 스레드에서 실행할지 지정할 수 있습니다. 대표적으로 Dispatchers.Default, Dispatchers.IO, Dispatchers.Main 등이 있습니다.

launch(Dispatchers.IO) {
    // IO 작업에 최적화
}
launch(Dispatchers.Default) {
    // CPU 연산에 최적화
}

예외 처리

코루틴 내 예외 처리는 try-catch로 할 수 있으며, CoroutineExceptionHandler를 활용할 수도 있습니다.

val handler = CoroutineExceptionHandler { _, exception ->
    println("예외 발생: $exception")
}

launch(handler) {
    throw RuntimeException("테스트 예외")
}

실전 예제: 여러 API 동시 호출하기

suspend fun fetchUser(): String {
    delay(1000L)
    return "User"
}
suspend fun fetchPosts(): String {
    delay(1500L)
    return "Posts"
}

fun main() = runBlocking {
    val userDeferred = async { fetchUser() }
    val postsDeferred = async { fetchPosts() }
    println("user: ${userDeferred.await()}, posts: ${postsDeferred.await()}")
}

참고할 만한 레퍼런스

코루틴을 활용하면 복잡한 비동기 로직도 간결하고 효율적으로 작성할 수 있습니다. 다양한 실전 예제를 직접 따라해보며 코루틴을 마스터해보세요!