안드로이드 개발자로서 코루틴은 뗄레야 뗄 수 없는 사이가 되었습니다. 당연하게도 모든 앱에서 코루틴이 들어가고 있죠. 하지만 간만에 봤던 전화면접에서 코루틴에 대해서 설명하라고 하는데... 참 멍청하게도 예전에도 지금에도 설명할 수 있는게 많이 없더라구요. 그런데 하나의 글에 전부 정리할 자신은 없어서 여러개의 코루틴 관련 글을 작성해보려고 합니다. 이 글을 쓰는게 도움이 되면 좋겠습니다.
Coroutine이란!
저는 안드로이드 개발을 통해 코틀린을 처음 접해서 당연하게도 안드로이드의 AsyncTask와 같이 라이브러리에서 제공해주는 간편한 비동기 클래스라고 생각했습니다. 그리고 꽤 오랫동안 CoroutineScope.launch 로만 작업해야 하는 줄 알았고, 그렇게 작성을 했었죠. 뭐 할 말은 없지만, 처음 취직하고 나서 코틀린으로 개발을 하던 당시에는 개발에 사용하는 시간 대비 효율이 좋지 않아 사용할 줄만 알면 그냥 써보고 되면 무지성으로 사용하던 시기였습니다. 추후 친구의 코드를 통해 viewModelScope와 같은 다른 scope들을 발견할 수 있었는데요. 그 친구가 아니었다면...
여튼 코틀린에서 지원하는 코루틴은 굉장히 사용하기 쉽고, 간편한 지식만 알고 있더라도 다채롭게 활용할 수 있도록 잘 만들어져있기 때문에 저처럼 쓰는데 급급해서 공부를 뒷전으로 했거나 호기심이 부족해서 더 긴밀하게 찾아보지 않은 분들은 아마 코루틴이 단순 라이브러리라고 생각했을 수도 있을 것 같습니다. 그러나 코루틴은 자그마치 1958년에도 존재하는 개념이었다고 하네요.
Co + Routine이라는 이름에서 확인할 수 있듯 함께하는 반복적인 업무를 의미하는데요. 하나의 프로그램 내에서 여러 동작(루틴)들이 서로 협력해 실행을 주고받는 실행 모델을 의미한다고 볼 수 있겠습니다. 그래서 코루틴은 협력적 멀티태스킹의 대표적인 예로 표현됩니다.
협력적 멀티태스킹이란 운영체제가 강제로 프로세스를 중단시키고 제어권을 가져와 다른 작업을 실행시키는 방식을 가진 선점형 멀티태스킹과 반대되는 개념으로 비선점적 멀티태스킹이라고도 표현합니다. 협력형은 프로세스가 스스로 CPU를 양보하지 않으면 다른 프로세스가 CPU를 사용할 수 없어 현재 CPU를 사용중인 프로세스가 독점하고 있으면 시스템이 정상적으로 동작하지 않는 단점이 있지만, 각 스레드나 태스크가 스스로 CPU 사용을 종료하거나 다음 작업으로 넘어가겠다고 명시적으로 협력할 때 전환되기 때문에 전환 시점에 대해서 좀 더 명확하게 판단할 수 있습니다.
Coroutine의 발생 목적
코루틴 개념이 등장하게 된 원인을 설명하려면 ‘서브루틴(subroutine)’부터 짚고 넘어가야 할 것 같습니다. 서브루틴은 전통적인 함수 호출 모델을 의미하고, 코틀린 기준으로는 우리가 보통 fun으로 정의하는 함수/메소드처럼 “호출하면 끝까지 실행한 뒤 return으로 돌아오는” 형태를 떠올리면 됩니다.
이런 서브루틴은 기본적으로 단방향 제어 흐름을 갖습니다. 특정 메소드를 호출하면 그 메소드의 내부 코드가 처음부터 끝까지 실행되고, return을 통해 호출자에게 결과가 반환됩니다. 즉, 한 번 들어가면(호출되면) 원칙적으로 끝날 때까지 계속 실행되는 모델입니다. 이런 서브루틴은 작업을 동기적(sync)으로 처리한다고 보면 되겠습니다.
하지만 현실의 프로그램은 서버 통신처럼 대기 시간이 발생하기도 하고, UI처럼 실행 흐름 중간에도 이벤트가 계속 들어오는 환경이 많습니다. 한 번에 여러가지 일이 동시에 몰아치는 거죠. 이때 동기적으로 동작하는 “서브루틴만”으로 이런 흐름을 표현하려고 하면, 콜백을 여러 단계로 연결하거나(flag/상태값을 두고) 직접 상태 머신을 구성하는 식으로 코드가 쪼개지기 쉽습니다. 결국 콜백 지옥, 전역 상태 증가, 의도치 않은 타이밍 버그 같은 문제로 이어질 수 있죠.
이런 한계를 해결하기 위해 여러 아이디어가 제안되었고, 그중 하나가 코루틴입니다. 코루틴은 실행 도중에 스스로 실행권을 양보(yield)했다가, 필요한 시점에 중단된 지점부터 다시 재개(resume)할 수 있습니다. 즉, 로직상으로는 하나의 흐름인데도 콜백/명시적 상태 머신 때문에 코드가 여러 곳으로 흩어져 가독성이 떨어지고, 플래그/클로저 등을 통해 중간 상태값을 따로 저장해야 하는 문제가 생기곤 합니다. 코루틴은 이런 문제를 줄이면서, 지역 상태를 보존한 채로 멈췄다가 다시 이어서 실행할 수 있게 하려는 목적을 가지고 있습니다.
이를 잘 이해하기 위해선 “언제 멈추는가?”를 같이 보면 좋을 것 같습니다. 코루틴은 OS 스레드처럼 강제로 끊기는 모델이 아니라, 기본적으로 정해진 지점에서만(서스펜션 포인트에서만) 스스로 양보합니다. 코틀린에서는 delay(), withContext(), await() 같은 대표적인 suspend 함수들이 그 지점을 제공합니다. 예를 들어 suspend 함수 A 안에서 withContext(Dispatchers.IO)를 만나면, A의 실행은 잠깐 중단되었다가, 블록이 IO 컨텍스트에서 실행된 뒤 완료 시점에 다시 A로 돌아와 재개됩니다. (A 실행 → withContext에서 A 중단 → IO 블록 실행 → 끝나면 A 재개) 이러한 코루틴의 특성은 동시성이라는 개념으로 표현할 수 있습니다.
동시성(Concurrency)과 병렬성(Parallelism)

위 그림은 동시성과 병렬성에 대해 표현한 것입니다(ChatGPT에게 그려달라고 했는데 괜찮은 것 같습니다). 동시성이란 위 사진처럼 실제로는 작업이 매우 빠른 속도로 스위칭되며 반복되고 있지만, 이를 사람이 인지하지 못할 정도로 빠르게 반복해 동시에 보이게 하는 개념입니다. 반면에 병렬성은 실제로 여러개의 작업이 동시에 실행되는 것을 의미합니다. 물리적으로 여러개의 코어를 가지고 각각의 코어가 하나씩 작업을 맡아 동시에 코어의 개수만큼 일을 병렬적으로 진행할 수 있습니다.
동시성의 경우 여러 가지 작업을 동시에 수행하는 것처럼 보이기 위해 작업 A → B → C로 번갈아 실행되는 스위칭이 발생하게 됩니다. 이 전환에는 비용이 들 수 있는데요. 그럼에도 동시성의 개념을 차용하는 이유 중 하나는 A가 I/O 작업일 때 대기 시간 동안 프로그램이 멈춰 있는 것처럼 보이지 않게, 다른 작업(B/C)을 진행할 수 있기 때문입니다. 즉, 유휴 시간을 활용해 응답성을 높이고 전체 처리 흐름을 매끄럽게 만들 수 있습니다. 또한 단일 코어 환경에서도 여러가지가 동시에 돌아가고 있어 유저에게는 여러가지 작업이 동시에 진행되는 것처럼 보여지게 됩니다.
병렬성의 경우 여러 코어가 동시에 일을 나눠 처리할 수 있기 때문에 처리량이 증가하고, 하드웨어 성능을 더 적극적으로 끌어낼 수 있습니다. 다만 병렬성을 만족시키려면 기본적으로 코어가 여러 개인 하드웨어가 필요합니다(물리적 제약). 또한 병렬로 실행되는 여러 작업이 동시에 데이터를 읽고 쓰는 과정에서 동기화가 되지 않으면 원자성 및 일관성이 깨질 수 있어(race condition), 이를 막기 위한 락/원자 연산 같은 장치가 필요합니다.
다행히도 이를 해결하기 위한 방법들은 이미 충분히 제안되어 있고, 커널/하드웨어/런타임 레벨에서 제공하는 다양한 동기화 원시 기능들이 존재합니다. 또한 동시성과 병렬성은 공존할 수 있기 때문에, 여러 코어에서 병렬로 실행하면서 각 코어 내부에서는 동시성을 통해 작업이 번갈아 진행되는 형태도 흔합니다. (현대 시스템에서는 사실상 이 조합이 기본에 가깝습니다.)
코루틴에 대해서 정리해보려고 글을 쓰고 있는데, 한 글에 모든 내용을 담기에는 정리도 안되고 글 쓰다가 날아가면 의욕도 다 날아갈 것 같아서 시리즈 형태로 나눠서 올려보려고 합니다. 현재 글에서는 코루틴을 소개하는 내용 정도인데요. 코루틴을 이번에 소개했으니 아마 다음에는 코틀린에서 이 코루틴을 어떻게 구현했는지, 그리고 안드로이드(제가 안드로이드 개발자이기 때문에 ㅎㅎ...)에서는 어떤 방식으로 사용하면 되는지 등을 정리해볼까 합니다.
그러고 나면 Thread vs Coroutine을 정리하면... 되지 않을까... 근데 예전에 Thread 관련 내용을 적어뒀던 것 같은데 블로그에는 없네요.
'안드로이드 > 기타' 카테고리의 다른 글
| Coroutine(2) - 코틀린에서 코루틴 사용하기 (0) | 2026.02.04 |
|---|---|
| ImageView의 ColorFilter 다중 적용 문제 (0) | 2025.12.08 |
| Preview Driven Development(프리뷰 주도 개발?) (0) | 2024.11.23 |
| Android Clean Architecture 와 Android App Architecture - 3 (1) | 2024.07.26 |
| Android Clean Architecture 와 Android App Architecture - 2 (0) | 2024.06.26 |
댓글