코루틴의 개념과 사용 이유 - swkim0128/PARA GitHub Wiki

1. 코루틴(Coroutine)이란?

코루틴(Coroutine)은 코틀린에서 제공하는 경량 스레드로, 비동기 작업을 효율적으로 처리할 수 있는 기능입니다.

비동기 프로그래밍을 간결하게 구현하고, 스레드보다 더 가벼운 실행 단위를 제공합니다.

코루틴의 핵심 개념

  • ✅ 비동기(Asynchronous) 프로그래밍을 쉽게 구현 가능.
  • ✅ 경량 스레드 (Lightweight thread): 수천 개의 코루틴을 실행할 수 있음.
  • ✅ 메모리 효율적: 스레드보다 가볍고, 필요할 때만 실행되며, 블로킹(blocking) 없이 동작.
  • ✅ 구조적 동시성(Structured Concurrency) 제공.
  • ✅ 스레드 대신 디스패처(Dispatcher)를 사용하여 실행 컨텍스트 관리.

2. 코루틴을 사용하는 이유

(1) 스레드보다 가벼움

  • 코루틴은 기존 스레드보다 훨씬 적은 리소스를 사용합니다.
  • 스레드 생성 비용이 없으며, 필요할 때만 실행됨.

(2) 비동기 프로그래밍을 간단하게 구현

  • 콜백(callback) 지옥을 피할 수 있음.
  • 비동기 코드를 순차적 코드처럼 작성할 수 있어 가독성이 향상됨.

(3) 중단(Suspending)과 재개(Resuming) 가능

  • suspend 키워드를 사용하여 특정 지점에서 작업을 중단(suspend)하고 나중에 다시 재개(resume)할 수 있음.

(4) 구조적 동시성 제공

  • 코루틴 스코프(CoroutineScope)를 사용하여 작업을 관리하고, 자동으로 취소할 수 있음.

3. 코루틴의 기본 사용법

(1) runBlocking을 사용한 간단한 예제

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000L)
        println("코루틴 실행!")
    }
    println("메인 함수 실행")
}

출력 결과:

메인 함수 실행
(1초 후)
코루틴 실행!

설명:

  • runBlocking → 메인 스레드를 블로킹하며 코루틴을 실행.
  • launch → 새로운 코루틴을 시작.
  • delay(1000L) → 1초 동안 코루틴을 일시 중단(suspend).

4. suspend 키워드 (일시 중단 함수)

  • suspend 키워드를 사용하면 코루틴 내에서 실행할 수 있는 비동기 함수를 만들 수 있음.

예제

import kotlinx.coroutines.*

suspend fun doWork() {
    delay(2000L) // 2초 동안 중단
    println("작업 완료!")
}

fun main() = runBlocking {
    doWork() // suspend 함수 호출
    println("메인 함수 종료")
}

✅ 출력 결과:

(2초 후)
작업 완료!
메인 함수 종료

설명:

  • suspend fun → 일시 중단 가능한 함수.
  • delay(2000L) → 2초 동안 중단되었다가 재개.

5. launch vs async

  • launch {} → 작업을 실행하고 결과를 반환하지 않음 (Job 반환).
  • async {} → 결과를 반환하는 작업을 실행 (Deferred<T> 반환, .await() 필요).

(1) launch 예제

fun main() = runBlocking {
    val job = launch {
        delay(1000L)
        println("launch 실행 완료!")
    }
    println("메인 실행 중...")
    job.join() // job이 끝날 때까지 기다림
}

✅ 출력 결과:

메인 실행 중...
(1초 후)
launch 실행 완료!

(2) async 예제

fun main() = runBlocking {
    val deferred = async {
        delay(1000L)
        "async 실행 완료!"
    }
    println("메인 실행 중...")
    println(deferred.await()) // 결과를 기다린 후 출력
}

✅ 출력 결과:

메인 실행 중...
(1초 후)
async 실행 완료!

설명:

  • async는 값을 반환할 수 있으며, .await()를 사용해 값을 가져옴.

6. 코루틴 스코프 (CoroutineScope)

  • 코루틴을 적절한 컨텍스트에서 실행하고 관리하기 위해 CoroutineScope를 사용함.

(1) GlobalScope

import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch {
        delay(1000L)
        println("GlobalScope 실행!")
    }
    println("메인 함수 종료") // 실행될 수도 있고 안 될 수도 있음 (메인 종료 시 코루틴 종료)
    Thread.sleep(2000L) // 프로그램이 종료되지 않도록 유지
}

✅ 주의: GlobalScope는 전역적으로 실행되므로 관리가 어려움. 가능하면 CoroutineScope를 사용하는 것이 좋음.

(2) runBlocking과 coroutineScope 차이점

fun main() = runBlocking {
    println("runBlocking 시작")

    coroutineScope {
        launch {
            delay(1000L)
            println("coroutineScope 내부 실행")
        }
    }

    println("runBlocking 종료")
}

✅ runBlocking은 전체 프로그램을 블로킹하지만, coroutineScope는 블로킹하지 않음.

7. 디스패처 (Dispatchers)

코루틴을 실행할 스레드를 결정할 때 사용됨.

디스패처 설명
Dispatchers.Default CPU 작업(연산 집중)
Dispatchers.IO I/O 작업(파일, 네트워크)
Dispatchers.Main UI 작업(Android)
Dispatchers.Unconfined 현재 스레드에서 실행

예제

fun main() = runBlocking {
    launch(Dispatchers.Default) { println("Default 실행") }
    launch(Dispatchers.IO) { println("IO 실행") }
}

8. 코루틴 취소

코루틴을 취소하려면 job.cancel()을 사용합니다.

예제

fun main() = runBlocking {
    val job = launch {
        repeat(1000) { i ->
            println("작업 실행 중... $i")
            delay(500L)
        }
    }

    delay(2000L)
    job.cancel() // 코루틴 취소
    println("작업 취소됨")
}

✅ 취소 가능 작업(delay)이 없으면 취소되지 않으므로 isActive를 사용해야 함.

9. 결론

✅ 코루틴은 비동기 작업을 쉽게 구현하고 스레드보다 가볍게 동작함. ✅ 스레드 블로킹 없이도 중단(suspend)과 재개(resume)이 가능함. ✅ launch는 값을 반환하지 않고, async는 값을 반환하며 await()로 결과를 가져옴. ✅ Dispatchers를 사용하여 적절한 실행 환경에서 실행 가능. ✅ 구조적 동시성을 지원하여 코루틴의 생명주기를 안전하게 관리할 수 있음.

💡 코루틴을 활용하면 효율적이고 가독성 높은 비동기 프로그래밍을 할 수 있습니다! 🚀

⚠️ **GitHub.com Fallback** ⚠️