| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- designPattern
- PrototypePattern
- 디자인패턴 #
- 추상 팩토리
- 빌터패턴
- builderPattern
- compose
- Coroutines
- android designsystem
- Functional Programming
- kmp
- 안드로이드 디자인시스템
- 프로토타입 패턴
- Design Pattern
- Abstract Factory
- ㅋㅁ
- 코틀린멀티플랫폼
- 코틀린
- material3
- 추상팩토리패턴
- 함수형프로그래밍
- factory method
- 팩토리 메소드
- Kotlin
- 옵저버 패턴
- 코루틴
- 디자인패턴
- Observer Pattern
- kotlin multiplatform
- define
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
6장 코루틴 빌더 본문
중단함수는 Continuation을 다른 중단함수로 전달해야 한다.
중단함수는 일반함수 호출가능, 중단함수를 호출가능
일반함수는 일반함수 호출가능, 중단함수를 호출 불가능
중단함수를 "연속적"으로 호출하다보면 시"작되는 지점"이 존재한다. 그것이 코루틴 빌더이다.
launch 빌더
코루틴을 시작하면 불꽃놀이를 할 때 불꽃이 하늘 위로 각자 펴지는 것처럼 별개로 실행된다.
CoroutineScope 인터페이스의 확장함수
=> 부모 코루틴과 자식 코루틴사이의 관계를 정립하기 위한 목적으로 사용되는 구조화된 동시성의 핵심
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
fun main() {
GlobalScope.launch {
delay(1000L)
println("World!")
}
GlobalScope.launch {
delay(1000L)
println("World!")
}
GlobalScope.launch {
delay(1000L)
println("World!")
}
println("hello, ")
Thread.sleep(2000L)
}
// hello,
// (1초 후)
// World!
// World!
// World!
Thread.sleep(2000L)
=> 스레드를 블로킹 하지 않으면 시작과 후 코루틴이 작업하기전에 main함수가 종료된다.
delay()
=> 스레드가 아닌 코루틴을 중단시킨다.
*블로킹한 스레드를 유지하는 비용보단 중단된 코루틴을 유지하는건 상대적으로 적다.
runBlocking 빌더
main 함수의 경우 프로그램을 너무 빨리 끝내지 않기 위해 스레드를 블로킹 해야할때, 사용한다.
코루틴이 중단되었을때, 시작한 스레드를 중단 시킨다.
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
fun main() {
runBlocking {
delay(1000L)
println("World!")
}
runBlocking {
delay(1000L)
println("World!")
}
runBlocking {
delay(1000L)
println("World!")
}
println("hello, ")
}
// (1초 후)
// World!
// (1초 후)
// World!
// (1초 후)
// World!
// hello,
첫번째, 프로그램이 끝나는것을 방지
두번째 유닛테스트 - runTest로 대체됨
async 빌더
launch와 비슷하지만 값을 생성하도록 설계되어 있다.
Deferred<T> 타입의 객체를 반환.
작업이 끝나면 값을 반환하는 중단 메서드인 await가 존재한다.
import kotlinx.coroutines.*
fun main() = runBlocking {
val resultDeferred: Deferred<Int> = GlobalScope.async {
delay(1000L)
42
}
// 다른 작업
val result: Int = resultDeferred.await() // 1초 뒤에 result에 값이 대입됨
println(result) // 42
// 간단하게 작성할 경우
println(resultDeferred.await())
}
import kotlinx.coroutines.*
fun main() = runBlocking {
val res1 = GlobalScope.async {
delay(1000L)
"Text 1"
}
val res2 = GlobalScope.async {
delay(3000L)
"Text 2"
}
val res3 = GlobalScope.async {
delay(2000L)
"Text 3"
}
println(res1.await())
println(res2.await())
println(res3.await())
}
몇 개의 작업을 한번에 시작하고 모든결과를 한꺼번에 기다릴 때 사용한다
값이 필요없을때는 launch를 사용한다
fun main() = runBlocking {
// 이렇게 사용하면 안된다.
GlobalScope.async {
delay(1000L)
println("World!")
}
println("Hello,")
delay(2000L)
}
async 빌더는 두 가지 다른곳에서 데이터를 얻어와 합치는 경우 처럼, 두 작업을 병렬로 실행할 때 주로 사용한다.
scope.launch {
val news = async {
newsRepo.getNews()
.sortedByDescending{ it.date }
}
val newsSummary = newsRepo.getNewsSummary()
view.showNews(
newsSummary,
news.await()
)
}
구조화된 동시성
코루틴이 GlobalScope에서 시작하였다면, 코루틴은 어떤 스레드도 블록하지 않기에 아래 코드에서 반드시 delay(3000L)이 필요하다. 아니면, 프로그램은 종료한다.
import kotlinx.coroutines.*
fun main() = runBlocking {
GlobalScope.async {
delay(1000L)
"Text 1"
}
GlobalScope.async {
delay(3000L)
"Text 2"
}
GlobalScope.async {
delay(2000L)
"Text 3"
}
println("Hello, ")
delay(3000L)
}
launch와 async는 CoroutineScope의 확장 함수이고 runBloacking의 block의 파라미터가 리시버 타입이 CoroutineScope인 함수형 타입이다.
public actual fun <T> runBlocking(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
즉, 아래와 같이 호출 가능하며, launch는 runBlocking의 자식이되며, 부모 코루틴은 자식 코루틴이 모든 작업을 완료할때 까지 중단된다.
import kotlinx.coroutines.*
fun main() = runBlocking {
this.launch {
delay(1000L)
println("World!")
}
launch {
delay(2000L)
println("World!")
}
println("Hello, ")
}
* runBlocking은 CoroutineScope의 확장 함수가 아니기 때문에, 특정 코루틴의 자식이 될 수 없다.
부모는 자식들을 위한 scope를 제공하고, 자식들은 그 scope 내에서 호출된어 부모-자식 관계를 형성하며 구조화된 동시성이 관계가 성립된다.
1. 자식은 부모로부터 컨텍스트를 상속받는다.(자식이 재정의 할수도 있음)
2. 부모는 모든 자식이 작업을 마칠때 까지 기다립니다(중단된다)
3. 부모 코루틴이 취소되면 자식 코루틴도 취소된다.
4. 자식 코루틴에서 에러가 발생하면, 부모 코루틴 또한 에러로 소멸한다.
모든 코루틴 빌더는 CoroutineScope (runBlocking은 제외)에서 시작되어야 한다.
첫 번째 빌더가 CoroutineScope에서 시작되면, 첫번째 빌더의 스코프에서 또 다른 빌더가 시작되고.. 이것이 구조화 되는 본질이다.
coroutineScope 사용하기
* 중단함수에선 scope를 어떻게 처리할까?
- 스코프를 인자로 넘기는것은 좋지 않은 방법이다.
- 코루틴 빌더가 사용할 스코프를 만들어주는 coroutineScope 함수를 사용하는것이 바람직하다.
예) 레포짓토리에서 비동기적으로 2개의 자원(사용자 데이터, 글 목록)을 가지고 와서 사용자가 볼 수 있는 데이터만 반환하고 싶다고 생각해보자.
suspend fun getArticlesForUser(
userToken: String?
): List<ArticleJson> = coroutineScope {
val articles = async { articleRepository.getArticles() }
val user = userService.getUser(userToken)
articles.await()
.filter { canSeeOnList(user, it) }
.map { toArticleJson(it) }
}
coroutineScope는 람다 표현식이 필요로 하는 스코프를 만들어 주는 중단함수이다.
coroutineScope는 중단함수 내에서 스코프가 필요할 때 일반적으로 사용하는 함수입니다.

'Coroutine > 코틀린 코루틴' 카테고리의 다른 글
| 5장 코루틴 언어차원에서의 지원 vs 라이브러리 (0) | 2025.12.22 |
|---|---|
| 3장 중단은 어떻게 작동할까? (0) | 2025.11.27 |
| 2장 시퀀스 빌더 (0) | 2025.11.17 |
| 1장 코틀린 코루틴을 배워야 하는 이유 (0) | 2025.11.09 |