Coroutine Builder: 개념, 코루틴 빌더 유형 및 작동 방식

저자: IT Sectr 게시일: 2026-06-21 읽는 시간: 8 분

Coroutine Builder — 코루틴을 생성하고 실행하며 실행 방식을 정의하는 Kotlin Coroutines 함수입니다. launch, async, runBlocking, produce 빌더는 백그라운드 작업부터 결과를 반환하는 병렬 계산까지 다양한 시나리오를 다룹니다. JetBrains, 2024에 따르면 Coroutine Builder는 코루틴 모델의 기초로서 구조적 동시성과 라이프사이클 관리를 제공합니다.

핵심 포인트

  • Coroutine Builder — 특정 CoroutineScope 내에서 코루틴을 생성하는 함수
  • launch — 결과를 반환하지 않고 코루틴을 시작하며 Job 객체를 반환
  • async — Deferred를 반환하며 코루틴을 시작, await()를 통해 결과를 얻을 수 있음
  • runBlocking — 현재 스레드를 차단하며 blocking 코드와 suspending 코드 간의 브리지 역할
  • produce — 데이터 스트리밍을 위한 채널로 코루틴 생성 (Flow 권장으로 비권장)

Coroutine Builder란?

Coroutine Builder는 CoroutineScope와 suspend 블록을 받아 새로운 코루틴을 생성하고 시작하는 Kotlin 확장 함수입니다. 각 빌더는 결과 반환 여부, 스레드 차단 여부, 비동기 방식 등 코루틴의 실행 방식을 정의합니다. 빌더는 언어의 코루틴 모델에 대한 진입점입니다.

모든 빌더는 CoroutineScope를 통해 작동하며, 자식 코루틴의 라이프사이클을 관리합니다. 스코프가 취소되면 해당 스코프를 통해 시작된 모든 코루틴이 자동으로 취소됩니다 — 이것이 구조적 동시성의 원칙입니다. 이 접근 방식은 코루틴 누수를 방지하고 예측 가능한 종료를 보장합니다.

kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    // 빌더는 CoroutineScope 내에서 실행됩니다
    val job = launch {
        delay(1000L)
        println("세계!")
    }
    println("안녕하세요,")
    job.join()
}

코루틴 빌더의 주요 유형

Kotlin은 네 가지 내장 코루틴 빌더를 제공합니다: launch, async, runBlocking, produce. 각각 고유한 반환 유형과 적용 영역이 있습니다. Android 모바일 개발의 주요 빌더는 launch와 async입니다 — 이들은 비차단 방식으로 작동하며 아키텍처 컴포넌트와 통합됩니다.

빌더반환 유형스레드 차단시나리오
launchJob아니오Fire-and-forget 작업
asyncDeferred<T>아니오병렬 계산
runBlockingT테스트, main 함수
produceReceiveChannel<E>아니오스트리밍 (비권장)

각 빌더는 추가 매개변수를 허용합니다: CoroutineStart (시작 전략), CoroutineContext (디스패처, 예외), 명명된 코드 블록. 기본적으로 코루틴은 즉시 시작됩니다 (CoroutineStart.DEFAULT).

launch: fire-and-forget 실행

launch는 Android 개발에서 가장 많이 사용되는 빌더입니다. 결과를 반환하지 않는 코루틴을 시작하고 라이프사이클 관리를 위한 Job 객체를 반환합니다. 데이터베이스 저장, 분석 전송, UI 업데이트와 같이 부수 효과만 필요한 작업에 이상적인 선택입니다.

launch의 구문 및 사용

launch 빌더는 CoroutineScope, 선택적 CoroutineContext 및 suspend 블록을 허용합니다. 반환된 Job은 코루틴을 취소하거나, 완료를 기다리거나, 상태를 확인할 수 있게 합니다.

kotlin
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val job: Job = scope.launch(CoroutineStart.LAZY) {
    val data = fetchFromNetwork()
    saveToDatabase(data)
}
job.start()
job.join()

CoroutineStart.LAZY 매개변수는 start() 또는 join()이 명시적으로 호출될 때까지 실행을 지연시킵니다. 이는 지연 초기화 및 조건부 실행에 유용합니다. 표준 즉시 실행의 경우 CoroutineStart.DEFAULT를 사용하거나 매개변수를 생략합니다.

async: 결과를 포함한 병렬 계산

async는 Deferred<T> — 결과의 비동기 약속을 반환하는 빌더입니다. await()를 호출하면 스레드를 차단하지 않고 결과를 얻을 때까지 코루틴을 일시 중단합니다. 이는 Kotlin 코루틴에서 병렬 작업을 위한 기본 메커니즘입니다.

두 요청을 병렬로 시작

async는 여러 독립적인 작업을 동시에 실행해야 할 때 특히 효과적입니다. suspend 함수의 순차적 호출과 달리 async는 코루틴을 병렬로 시작하여 총 실행 시간을 줄입니다.

kotlin
suspend fun fetchUserData(): UserData {
    val deferred1 = CoroutineScope(Dispatchers.IO).async { api.getProfile() }
    val deferred2 = CoroutineScope(Dispatchers.IO).async { api.getSettings() }
    val deferred3 = CoroutineScope(Dispatchers.IO).async { api.getNotifications() }
    return UserData(
        profile = deferred1.await(),
        settings = deferred2.await(),
        notifications = deferred3.await()
    )
}

Deferred는 Job을 상속하므로 async는 취소, 완료 대기, 예외 처리 등 모든 라이프사이클 작업을 지원합니다. 스코프가 취소되면 자식 Deferred 코루틴이 자동으로 취소됩니다.

runBlocking: blocking 세계로의 브리지

runBlocking은 코루틴이 완료될 때까지 현재 스레드를 차단하는 유일한 빌더입니다. 새로운 CoroutineScope를 생성하고 전달된 코루틴을 시작하여 호출 스레드를 차단합니다. main() 진입점, 테스트 및 blocking 코드와의 통합에 사용됩니다.

runBlocking이 필요한 경우

runBlocking은 세 가지 시나리오에서 정당화됩니다: 애플리케이션 진입점 (main), suspend 함수의 단위 테스트, suspend를 사용할 수 없는 콜백 기반 라이브러리와의 통합. 프로덕션 Android 코드에서 메인 스레드에 runBlocking을 사용하는 것은 강력히 권장되지 않습니다.

kotlin
class CoroutineTest {
    @Test
    fun `test suspend function`() = runBlocking {
        val result = mySuspendFunction()
        assertEquals("expected", result)
    }
}

테스트의 경우 runBlocking 대신 TestCoroutineDispatcher와 함께 kotlinx-coroutines-test를 사용하는 것이 좋습니다 — 이를 통해 시간 제어가 가능하고 테스트 환경에서 차단을 방지할 수 있습니다.

적합한 빌더 선택 방법

Coroutine Builder의 선택은 반환 결과와 실행 시나리오에 따라 다릅니다. 데이터 반환이 필요하지 않은 작업에는 launch를 사용합니다. 비동기 작업의 결과가 필요하면 async를 사용합니다. runBlocking은 브리징에만 사용하고 반응형 스트림에는 produce를 Flow로 대체하세요.

Android를 위한 실용적인 권장 사항

Kotlin Coroutines를 사용하는 Android 프로젝트에서 주요 빌더 쌍은 launch와 async입니다. launch는 ViewModel과 UseCases에서 코루틴을 시작하는 데 사용되고, async는 네트워크 또는 데이터베이스에 대한 병렬 요청에 사용됩니다. 최신 라이브러리 (Ktor, Room)는 이미 suspend 함수를 지원하므로 async의 직접 사용 필요성이 최소화됩니다.

  • launch — fire-and-forget용 (로깅, 분석, 캐싱)
  • async — 결과 결합이 있는 병렬 요청용
  • runBlocking — main() 및 테스트 전용 (프로덕션의 Main 스레드에서는 사용 금지)
  • produce — Flow / SharedFlow / StateFlow로 대체

자주 묻는 질문

Kotlin에서 launch와 async의 차이점은 무엇인가요?

launch는 Job을 반환하고 실행 결과를 반환하지 않는 반면, async는 Deferred<T>를 반환하며 await()를 통해 결과를 얻을 수 있습니다. launch는 fire-and-forget 작업에, async는 데이터를 반환하는 작업에 사용됩니다.

Android Activity에서 runBlocking을 사용할 수 있나요?

권장되지 않습니다. 메인 스레드에서 runBlocking은 ANR을 유발하고 UI를 차단합니다. Activity와 Fragment 내에서 lifecycleScope.launch를 사용하세요 — 차단 없는 내장 솔루션입니다.

launch 빌더는 무엇을 반환하나요?

launch 빌더는 Job 객체를 반환하며, 코루틴의 라이프사이클 제어 (취소, 완료 대기, 상태 확인)를 가능하게 합니다.

Kotlin Coroutines에서 Deferred란 무엇인가요?

Deferred<T>는 async 빌더가 반환하는 결과의 비동기 약속입니다. Job을 상속하며 결과를 얻기 위한 await(), 비차단 접근을 위한 getCompleted(), 예외 확인을 위한 getCompletionExceptionOrNull() 메서드를 추가합니다.

지연을 두고 launch를 시작하려면 어떻게 하나요?

CoroutineStart.LAZY 매개변수를 사용합니다: scope.launch(start = CoroutineStart.LAZY) { ... }. 그런 다음 실제 실행을 위해 job.start() 또는 job.join()을 호출합니다. 이는 지연 초기화 및 조건부 코루틴 실행에 유용합니다.

요약

  • Coroutine Builder — 실행 방식과 반환 유형을 정의하는 코루틴 생성 함수
  • launch — 결과가 없는 작업용 주요 빌더, 코루틴 관리를 위해 Job 반환
  • async — 병렬 계산용 빌더, 결과의 await() 기능과 함께 Deferred 반환
  • runBlocking — main() 및 테스트용 차단 빌더, Android 메인 스레드에서는 권장되지 않음
  • 구조적 동시성은 스코프 취소 시 자식 코루틴의 자동 취소를 보장
  • produce는 비권장 — 반응형 데이터 스트림에는 Flow, SharedFlow 또는 StateFlow 사용
  • 빌더 선택은 시나리오에 따라 다름: fire-and-forget → launch, 병렬 데이터 → async, 브리징 → runBlocking

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기