코루틴 개요
코루틴 개요
애플리케이션이 잘 돌아가려면 보통 한 번에 여러 일을 처리해야 해요. 사용자 입력에 응답하면서 데이터를 불러오고, 동시에 화면도 갱신해야 하는 식이죠. 이런 작업은 서로를 막지 않고 독립적으로 실행될 수 있어야 하는데, 이걸 **동시성(concurrency)**이라고 불러요.
가장 흔한 방법은 운영체제가 관리하는 스레드를 쓰는 거예요. 그런데 스레드는 상대적으로 무겁고, 많이 만들수록 성능 문제가 생기기 쉬워요. Kotlin은 이런 문제를 **코루틴(coroutine)**으로 해결해요. 코루틴은 스레드의 가벼운 대안으로, 시스템 자원을 막지 않고 **일시 중단(suspend)**될 수 있어서 세밀한 동시성에 훨씬 잘 어울려요.
대부분의 코루틴 기능은 kotlinx.coroutines 라이브러리가 제공해요. 코루틴을 실행하고, 동시성을 다루고, 비동기 스트림을 만드는 도구들이 여기 모여 있어요. 이 페이지에서는 이 라이브러리가 어떤 핵심 개념들을 제공하는지 큰 그림부터 살펴볼게요.
일시 중단 함수와 코루틴 빌더
Kotlin의 코루틴은 일시 중단 함수(suspending function) 위에 세워져 있어요. suspend 키워드를 붙이면 스레드를 막지 않고 코드를 잠시 멈췄다가 다시 이어갈 수 있어요. 이 키워드는 오래 걸리는 작업을 비동기로 처리하는 함수를 표시해요.
새 코루틴을 띄우려면 .launch()나 .async() 같은 코루틴 빌더를 써요. 이 둘은 CoroutineScope의 확장 함수예요. CoroutineScope가 코루틴의 수명주기와 컨텍스트(맥락)를 정의해 주죠.
코루틴 컨텍스트와 행동
CoroutineScope에서 코루틴을 실행하면 그 실행 방식을 정하는 컨텍스트가 만들어져요. .launch()나 .async() 같은 빌더는 코루틴이 어떻게 행동할지 정의하는 요소들을 자동으로 만들어요.
Job인터페이스는 코루틴의 수명주기를 추적하고, **구조적 동시성(structured concurrency)**을 가능하게 해요.CoroutineDispatcher는 코루틴이 어디에서 돌지를 정해요. UI 애플리케이션에서는 메인 스레드나 백그라운드 스레드 중 어디에서 실행할지를 결정하죠.CoroutineExceptionHandler는 잡히지 않은 예외를 처리해요.
이 요소들이 모여 코루틴 컨텍스트를 구성하고, 기본적으로 부모 코루틴에게 물려받아요. 이 컨텍스트가 계층을 이루면서 구조적 동시성이 성립하는데, 덕분에 관련된 코루틴들은 함께 취소되거나 그룹으로 예외를 처리할 수 있어요.
코루틴끼리 값을 주고받을 땐 값을 어떻게 공유하고 싶은지에 따라 도구가 달라져요.
Flow는 코루틴이 **능동적으로 수집(collect)**할 때만 값을 만들어요.Channel은 여러 코루틴이 값을 보내고 받게 해주며, 각 값은 정확히 하나의 코루틴에게 전달돼요.SharedFlow는 모든 값을 모든 수집 중인 코루틴에게 계속 공유해요.
여러 코루틴이 같은 가변 상태를 읽거나 갱신할 때는 주의가 필요해요. 아무 조율 없이 건드리면 연산이 서로 예측할 수 없게 간섭하는 **경쟁 상태(race condition)**가 생길 수 있어요. 공유 상태를 안전하게 관리하려면 StateFlow로 그 데이터를 감싸고, 한 코루틴에서 갱신하고 다른 코루틴에서 최신 값을 수집하면 돼요.
자세한 내용은 Flows, Channels, 그리고 Coroutines and channels 문서를 함께 보면 좋아요.
더 알아보기
- 코루틴의 기초, 일시 중단 함수, 빌더는 Coroutine basics에서 다뤄요.
- 일시 중단 함수를 조합하고 코루틴 파이프라인을 만드는 방법은 Composing suspending functions를 참고하세요.
- IntelliJ IDEA의 내장 도구로 코루틴을 디버깅하는 방법은 debugging coroutines에서 확인할 수 있어요.
- 전체
kotlinx.coroutinesAPI 레퍼런스도 둘러보시면 도움이 돼요.