코루틴에 대하여

김용현·2025년 1월 19일

CS

목록 보기
9/12

이번엔 코루틴에 대해 알아보겠습니다!

코루틴(CoRoutine)이란?

코루틴은 Co + Routine 의 합성어로 그대로 해석하면 루틴(Routine)이 서로 협력(Co) 하는 형태의 프로그래밍을 말하는데요!

좀 더 사전적인 정의는 다음과 같습니다.

실행의 지연과 재개를 허용함으로써, 비선점적 멀티태스킹을 위한 서브 루틴을 일반화한 컴퓨터 프로그램 구성요소 -Wiki-

다시 말하면, 어떤 작업을 여러 개의 서브 루틴으로 나누고 이 루틴들이 서로를 호출하며 작업을 수행하는 것을 말해요!

흔히 우리가 프로그래밍을 할 때 함수가 중간에 멈출 수 있다는 생각은 하지 않는데요! 코루틴에서는 함수를 중간에 빠져나올 수 있는 곳이 여러 곳 존재하고, 위 이미지처럼 중간에 실행을 멈췄다 재개하는 것을 반복하며 동시성 프로그래밍을 가능케 합니다.

지금까지 비동기 = 멀티 쓰레드 라고 생각했었는데, 코루틴을 활용해서도 비동기적인 처리가 가능하다는 점이 인상 깊었어요!

그렇다면 멀티 쓰레드와 코루틴은 어떤 차이가 있는 것일까요?

사실 두 기술 모두 비동기적인 처리가 가능하다는 점에서는 같은 목적을 갖고 있지만, 내부적으로 코루틴은 하나의 쓰레드 위에 여러 루틴을 나누는 개념입니다.

즉 멀티 쓰레드는 스케줄러에 의해 CPU가 자원을 할당하고 해제하는 컨텍스트 스위칭 비용이 들지만 코루틴은 쓰레드가 block되지 않기 때문에 컨텍스트 스위칭 비용이 발생하지 않아요! 그만큼 자원을 효율적으로 사용할 수 있는 것입니다.

코루틴의 장점

이러한 코루틴을 쓰면 장점은 무엇이 있을까요?

가독성 좋은 비동기 코드

다음 이미지는 CallBack Hell이라 불리는 코드의 형태 입니다. 비동기 처리의 결과로 콜백 함수를 등록하는 과정에서 생기는 현상인데, 딱 봐도 가독성이 굉장히 안 좋은 걸 알 수 있죠!

이에 반해 코틀린 코루틴의 경우 함수처럼 작성할 수 있기 때문에 가독성 좋은 코드를 만들어 낼 수 있습니다!

효율성과 가벼움

스레드를 생성하는 것보다 코루틴 하나를 생성하는 것이 훨씬 가볍다고 합니다. 또한 멀티 쓰레드의 경우 컨텍스트 스위칭 비용이 멀티 프로세스 보단 훨씬 적긴 하지만, 어찌 됐건 스위칭 과정에서 쓰레드의 상태 정보를 저장하는 과정은 필요합니다. 그러나 코루틴은 쓰레드를 block 시키지 않고 컨텍스트 스위칭도 일어나지 않기 때문에 굉장히 효율적입니다.

위에서 봤던 이미지를 다시 보면 더 이해가 잘 될 것 같네요!

코틀린에서의 코루틴

그렇다면 코틀린에서는 코루틴을 어떻게 지원하고 있을까요?

코루틴의 동작 원리를 알기 위해선 CPS(Continuation Passing Style)와 State Machine 개념을 이해 해야 합니다!

CPS와 State Machine이란?

Continuation을 Passing 하는 Style입니다..!
좀 더 구체적으로 설명해보면 코틀린에서 suspend를 붙인 함수는 코루틴에서 실행될 수 있는 함수가 되는데 이 때 컴파일 과정에서 해당 함수의 인자로 Continuation이라는 객체를 함께 넘겨주게 됩니다. 이 Continuation 객체를 통해 상태를 추적하고 현재 상태에 맞는 다음 작업을 수행하는 개념인데요!

fun getGingerBrave(api: CookieService, completion: Continuation<Any?>) {
    when (label) {
        0 -> // Label 0 -> first execution !
            val dough = api.makeDough() // suspend

        1 -> // Label 1 -> resume from makeDough
            val magicDough = api.addMagicPowder(dough) // suspend

        2 -> // Label 2 -> resume from addMagicPowder
            val cookie = api.escapeOven(magicDough) // suspend

        3 -> // Label 3 -> resume from escapeOven
            val gingerBrave = api.fetchGingerBrave(cookie) // suspend

        4 -> { // Label 4 -> resume from fetchGingerBrave
            Log.d("You can't catch me! I'm the Gingerbre.. I'm Ginger Brave!")
            completion.resume(gingerBrave)
        }
    }
}

가령 예를 들면 이런 식으로 suspend 함수를 실행하는 main 함수에서는 label 값을 이용해 현재 함수에서 어디까지 실행이 되었는지를 구분합니다. 이를 이용해 함수를 나갔다가 다시 돌아왔을 때 어디서부터 실행하면 되는지를 알 수 있는 것이죠! 이 label을 이용해 상태를 구분하는 것이 state Machine 개념을 적용한 것입니다.

마무리하며

추후 코틀린의 코루틴 내부 동작 과정에 대해 자세한 내용을 더 추가하고자 합니다.
지금까지 동시성 프로그래밍 하면 멀티 쓰레드만 떠올릴 수 있었는데, 이번 코루틴 개념에 대해 학습하며 동시성을 달성하기 위한 또 다른 방법을 하나 터득한 것 같아요!

📚참고 자료

https://dev.gmarket.com/82
https://curiousjinan.tistory.com/entry/kotlin-coroutines-explained

profile
평생 여행 다니는게 꿈 💭 👊 😁 🏋️‍♀️ 🦦 🔥

0개의 댓글