코루틴이란?
- 코루틴은 코틀린에서 많이 사용되어 코틀린에서 사용하는 기술인 것 처럼 느껴지지만, 사실 파이썬, C#, Go, JS 등 여러 언어에서 지원하고 있는 개념이다.
- JS를 사용하는 개발자라면 async, await을 자주 사용하기 때문에 이미 코루틴을 사용해본 경험이 있다고 말할 수 있다.
- 즉 코루틴은 새로운 기술이 아닌 프로그래밍이 세상이 나온 초창기부터 존재하는 개념이다.
그래서 코루틴이란?
코루틴은 3가지 키워드 정도로 알아 볼 수 있다.
- 협력형 멀티 태스킹
- 동시성 프로그래밍 지원
- 비동기 처리를 쉽게 도와준다.
[협력형 멀티 태스킹]
- 협력형 멀티태스킹을 프로그래밍 언어로 표현하자면 Co + routine이다. Co라는 접두어는 “협력”, “함께”라는 의미를 지니고 있다. Routine은 하나의 태스크, 함수 정도로 생각하면 된다.
- 즉 협력하는 함수.
- Routine에는 우리가 흔히 알고있는 메인 루틴과 서브 루틴이 존재한다.
// 메인 루틴 public static void main(String[] args) { int addedValue = plusOne(10); } // 서브 루틴 int plusOne(int value) { return value + 1; }
- 오른쪽 그림을 보면 서브 루틴의 경우 진입점과 빠져나오는 지점이 명확하다.
- 즉 메인 루틴이 서브 루틴을 호출하면, 서브 루틴의 맨 처음 부분에 진입하여 return 문을 만나거나 서브루틴의 닫는 괄호를 만나면 해당 루틴을 빠져나오게 된다.

오른쪽 코틀린 코드를 보면 메인 스레드가 plusOne이라는 서브 루틴에 진입하게 된다.
당연히 코드는 처음부터 진입이 되어 맨 윗줄 부터 실행이 될 것이고, 아래 코드를 실행하면서 return을 만나면 서브루틴 호출했던 부분으로 탈출하게 된다.
진입점과 탈출점 사이에 스레드는 블락되어 있다. 우리가 늘 짜는 코드라고 생가가면 된다.

하지만 코루틴은 조금 다르다 !
코루틴을 사용하지 않았다면 혼란스러울 수 있지만 아래 그림을 살펴본다.
코루틴도 “루틴”이기 때문에 하나의 함수로 생각하자.
그런데 이 함수는 진입할 수 있는 진입점도 여러개고, 함수를 빠져나갈 수 있는 탈출점도 여러개다. 즉 코루틴 함수는 꼭 return 문이나 마지막 닫는 괄호를 만나지 않더라도 언제든지 중간에 나갈 수 있고 언제든지 다시 나갔던 지점으로 들어올 수 있다.


drawPerson()
이라는 함수가 있다. 이 함수 안에는 startCoroutine
이라는 코루틴 빌더가 있다(실제로 startCoroutine 이라는 빌더는 존재하지 않는다. 실제 코루틴 라이브러리에서는 다른 방식으로 코루틴을 만들지만 이해를 쉽게 하기 위함)startCoroutine
이라는 코루틴을 만나게 되면 해당 함수는 코루틴으로 작동할 수 있다.따라서 언제든 함수 실행 중간에 나갈 수도 있고, 다시 들어올 수도 있는 자격이 부여된다. 언제 코루틴을 중간에 나갈 수 있을까? suspend로 선언된 함수를 만나면 코루틴 밖으로 잠시 나갈 수 있다.
- 스레드의 메인 함수가
drawPerson()
을 호출하면startcoroutine
블럭을 만나 코루틴이 된다.(정확하게는 하나의 코루틴을 만들어 시작한다.) 위에도 말했듯이 이제drawPerson()
은 진입점과 탈출점이 여러개가 되는 자격이 주어진다.
- 코루틴이 실행이 되었지만, suspend를 만나기 전까지는 그다지 특별한 힘이 없다. suspend로 정의된 함수가 없다면 그냥 마지막 괄호를 만날 때 까지 계속 실행된다. 그러나 darwHead()는 suspend 키워드로 정의되어진 함수다. 따라서 drawHead() 부분에서 더 이상 아래 코드를 실행하지 않고 drawPerson()이라는 코루틴 함수를 (잠시) 탈출한다.
- 메인 스레드가 해당 코루틴을 탈출했다. 그렇다고 스레드가 놀고 있을리는 없다. 우리가 짜 놓은 다른 코드들을 실행할 수도 있고, 안드로이드라면 UI 애니메이션을 처리 할 수도 있다. 그러나 Head는 어디선가 계속 그려지고 있다.
drawHead()
는 2초가 걸리는 sespend 함수였음을 기억해보자. drawHead()라는 suspend를 만나 코루틴을 탈출했지만 drawHead() 함수의 기능은 메인쓰레드에서 동시서 프로그래밍으로 작동하고 있을 수도 있고, 다른 쓰레드에서 돌아가고 있을 수도 있다. 그것은 개발자가 자유롭게 선택할 수 있다.
위의 과정에서 보았듯이 코루틴 함수는 언제든지 나왓다가 다시 들어올 수 있다. 이 대목에서 이미 눈치를 챈 분들도 있을 것 같은데, 코루틴의 이런 성향은 동시성 프로그래밍과 밀접한 관계가 있다.
[동시성 프로그래밍]
함수를 중간에 빠져나왔다가, 다른 함수에 진입하고, 다시 원점으로 돌아와 맘추었던 부분부터 다시 시작하는 이특성은 동시성 프로그래밍을 가능하게 한다.
동시성 프로그래밍의 개념을 잡고가자면, 병렬성 프로그래밍과 완전히 다른 개념이다. 예를 들어 양쪽에 놓여진 두 개의 도화지에 사람 그림을 각자 그린다고 가정해보자.

동시성 프로그래밍이란 오른쪽 손에만 펜을 쥐고서 왼쪽 도화지에 사람 일부를 조금 그리고 오른쪽 도화지에 가서 잠시 또 사람을 그리고, 다시 왼쪽 도화지에 사람을 찔끔 그리고.. 이 행위를 아주 빨리 반복하는 것이다.
사실 내가 쥔 펜은 한 순간에 하나의 도화지에만 닿는다. 그러나 이 행위를 멀리서 본다면 마치 동시에 그림이 그려지고 있는 것 처럼 보일 것이다. 이것이 동시성 프로그래밍이다.
병렬성 프로그래밍은 이 것과 다르다. 병렬성은 실제로 양쪽 손에 펜을 하나씩 들고서 왼쪽과 오른쪽에 실제로 동시에 그리는 것이다. 같은 시간동안 두 개의 그림을 그리는 것이다.
코루틴은 개념자체로만 보면 병렬성이 아닌 동시성을 지원하는 개념이다. 위의 설명을 코드로 다시살펴본다.

코루틴도 루틴이다. 즉 스레드가 아니라 일반 서브루틴과 비슷한 루틴이기 때문에 하나의 스레드에 여러개가 존재할 수 있다.
위의 코드에서는 메인 스레드에 코루틴이 두개가 있다. 하나는 왼쪽 도화지에 그림을 그리는 코드고 다른 하나는 오른쪽 도화지에 그림을 그리는 코드다. 메인 쓰레드가 실행되면서 먼저 왼쪽 코루틴인 drawPersonToPaperA() 라는 함수를 만났다고 가정해보자. 해당 함수는 가상 코루틴 빌더인 startCoroutine() 블럭으로 인해 코루틴이 되고, 함수를 중간에 나갔다가 다시 들어올 수 있는 자격을 얻게 된다.
drawPersonToPaperA()가 호출되어 suspend 함수인 drawHead()를 만나게 되면 이 코루틴을 잠시 빠져나간다.
왼쪽 코루틴을 빠져 나갔지만 그렇다고 메인 쓰레드가 놀고있진 않다. 다른 suspend 함수를 찾거나 resume 되어지는 다른 코드들을 찾는다.
왼쪽 코루틴의 경우 2초 동안 drawHead() 작업을 하게 된다. 그러나 delay(2000)은 스레드를 블락시키지 않으므로 다른 일들을 할 수가 있다. 뿐만 아니라 drawHead() 함수 안에서 다른 쓰레드를 실행시킨다면 병행적으로도 실행이 가능한다.
왼쪽 코루틴을 빠져나온 스레드가 오른쪽 코루틴을 만나게 되어 또 한번 suspend 함수를 만나게 되면 아까 도화지 그림에서 설명한 것과 같은 현상이 일어난다.
아까 오른손에 펜을 쥐고 왼쪽과 오른쪽 도화지를 아주빠르게 왔다 갔다 하면서 그림을 그리는 것 같은 셈이다. 이렇게 코루틴을 사용하여 쓰레드 하나에서 동시성 프로그래밍이 가능하다
코루틴을 생성해서 동시성 프로그래밍을 하는 것은, 쓰레드를 사용해서 동시성 프로그래밍을 하는 것과 차원이 다른 효율성을 제공한다. 위에서 말한 동작을 쓰레드 두 개를 만들어 실행한다고 가정해보자.

비동기 처리가 이렇게 편해지다니
위에서 설명한 코틀린의 능력으로 비동기 처리가 굉장히 쉬워진다. 심지어는 이게 비동기 처리인가? 싶을 정도로 읽기 쉽고 짜기도 쉬운 코드를 볼 수 있다.
한 가지 예시를 들어보겠다. 필자의 아침 기상으로부터 회사에 도착하기 까지의 과정이다.
- 8시 기상
- 샤워하기
- 옷입기
- 출발하기
- 회사도착하기
- 일하기
위의 시나리오는 꼭 순서대로 이뤄져야하는 작업이다. 기상하지도 않았는데 옷을 입을수 없고, 옷을 입지 않았는데 출근을 할 수도 없다. 또한 각 과정은 시간이 오래 걸리는 작업이라고 가정하자. 코드로 치면 네트워크 처리쯤?

