영주의 개발노트

코루틴 뽀개기 - (1) 본문

STUDY 📖/Kotlin

코루틴 뽀개기 - (1)

0JUUU 2025. 3. 12. 20:11

🤯

최근 프로젝트 진행 중 코루틴 컨텍스트에 대해 잘 알지 못하여 엄청난 삽질을 한 적이 있다. 어영부영 해결법은 알아내서 이슈는 잠재웠지만 제대로 이해하고 싶었다. 그렇기에 지금부터 코루틴을 뽀개보려한다. 여러 글에 걸쳐 차근차근 코루틴부터 코루틴 컨텍스트까지 공부할 것이다. 

 

코루틴이 등장한 이유 💡

기존에 우리는 비동기 프로그래밍을 위해 쓰레드와 콜백을 주로 사용했다. 하지만 이 방식들은 각각의 문제를 가지고 있다. 

1) 쓰레드 방식의 문제

  • 비용이 크다: 쓰레드는 생성과 관리에 많은 리소스를 필요로 한다. 또한, 다량의 쓰레드를 실행하면 시스템 부하가 커진다.
  • 컨텍스트 스위칭 오버헤드: 여러 개의 쓰레드가 실행될 때, CPU가 쓰레드 간 전환을 수행하면서 성능이 저하될 수 있다.
  • 동기화 문제: 여러 쓰레드가 동일한 자원에 접근하면 동기화 문제가 발생할 수 있다. 이를 해결하려면 추가적인 동기화 코드(synchronized, Mutex, ...) 를 작성해야 하므로 복잡성이 증가한다. 

2) 콜백 방식의 문제

  • 콜백 지옥: 중첩된 콜백 호출이 많아질수록 코드의 가독성이 심각하게 저하된다.
  • 오류 처리의 어려움: 예외 처리가 복잡해지고, 디버깅이 어려워진다.
  • 유지보수의 어려움: 비즈니스 로직이 여러 곳에 분산되어 관리하기 어려워진다.

코루틴의 등장 ✨

위 문제들을 해결하기 위해 코루틴이 등장하게 된다. 코루틴은 기존 방식의 한계를 극복하면서 이전보다 더 가볍고, 직관적인 코드를 작성할 수 있게 한다. 

 

코루틴이란? 🤷‍♀️

코루틴(Coroutine)은 경량화된 비동기 프로그래밍 방식으로, 기존의 쓰레드처럼 독립적인 실행 단위를 가지지만, 필요할 때만 실행되고 중단될 수 있다. 이를 통해 자원을 효율적으로 사용하면서도 직관적인 코드 작성을 가능하게 한다.

코루틴은 다음과 같은 장점을 가지고 있다. 

  • 가벼움: 쓰레드보다 훨씬 가볍고, 필요할 때만 실행되므로 리소스를 절약할 수 있다. 
    • 코루틴은 쓰레드와 달리 운영체제(OS)에서 직접 관리하는 것이 아니라 코틀린의 코루틴 스케줄러에 의해 관리된다. 따라서 쓰레드를 생성할 때 필요한 메모리 할당 및 컨텍스트 스위칭 비용이 들지 않아 리소스를 절약할 수 있다. 
  • 동기 코드처럼 읽기 쉬운 비동기 코드: 기존의 콜백 방식과 달리 suspend 키워드를 활용하면 코드가 순차적으로 실행되는 것처럼 보이지만, 실제로는 비동기적으로 동작한다.
  • 자동적인 중단 및 재개: delay() 같은 함수를 사용하면 특정 지점에서 중단하고, 이후 다시 실행할 수 있다.
  • 구조적인 동시성 지원: 코루틴은 계층적으로 관리할 수 있기에 부모 코루틴이 취소되면 자식 코루틴도 자동으로 취소되는 등 안정적인 동시성 제어가 가능하다. 

기본적인 코루틴 사용법 🛠️

  • suspend 함수

코루틴에서 중단 가능한 함수를 만드려면 suspend 키워드를 사용해야 한다. suspend 함수는 일반 함수처럼 동작하지만, 실행을 일시 중지할 수 있다는 점이 다르다.

suspend fun fetchData(): String {
	delay(1000)
    return "fetch data"
}

fun main() = runBlocking {
	println("fetching...")
    val result = fetchData()
    println(result)
}
  • launch와 async

코루틴을 실행하는 방법은 여러 가지가 있지만, 대표적으로 launch와 async가 있다. launch는 결과값을 반환하지 않고 실행하는 코루틴이고, async는 결과값을 반환하는 코루틴이다.

fun main() = runBlocking {
    val job = launch {
        delay(1000)
        println("launch")
    }
    
    val deferred = async {
        delay(1000)
        "async"
    }
    
    job.join()
    println(deferred.await())
}

lauch는 단순히 실행만 하는 경우 사용하고, async는 반환 값을 필요로 할 때 사용한다. async를 사용할 경우, await()을 호출하여 결과 값을 가져올 수 있다.


이처럼 코루틴은 기존의 쓰레드 및 콜백 방식에서 발생하는 문제를 해결하면서도, 가볍고 직관적인 비동기 프로그래밍을 가능하게 해준다. 이번 글에서는 코루틴이 등장한 이유와 기존 방식과의 차이점, 기본적인 사용법에 대해 간략하게 다루어보았다.

다음에는 코루틴의 컨텍스트와 관련된 내용에 대해 더 깊이 알아보고자 한다. 더 깊이 알아보고 내가 겪었던 문제에 대해서도 소개할 수 있는 시간이 되었으면 한다. 

'STUDY 📖 > Kotlin' 카테고리의 다른 글

코틀린스럽다고? 너 누군데? 🤷‍♀️  (2) 2025.01.29