| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- 추상팩토리패턴
- 코틀린멀티플랫폼
- 빌터패턴
- kotlin multiplatform
- android designsystem
- material3
- ㅋㅁ
- designPattern
- 코루틴
- builderPattern
- Abstract Factory
- 함수형프로그래밍
- 코틀린
- PrototypePattern
- kmp
- Functional Programming
- factory method
- compose
- 팩토리 메소드
- 디자인패턴 #
- Kotlin
- define
- Design Pattern
- 안드로이드 디자인시스템
- 프로토타입 패턴
- Observer Pattern
- Coroutines
- 옵저버 패턴
- 디자인패턴
- 추상 팩토리
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
11장 코루틴 스코프 함수 - 2 본문
코루틴 스코프 함수
스코프를 만드는 다양한 함수가 있습니다.
coroutineScope
supervisorScope - Job대신 SupervisorJob사용
withContext - 컨텍스트를 변경
withTimeoutScope - 실행시간 존재
이러한 스코프 함수들은 스코프를 만들기 위해 사용된다.
코루틴 빌더 vs 코루틴 스코프 함수
| 코루틴 빌더 | 코루틴 스코프 함수 |
| launch, async, produce | coroutineScope, supervisorScope, withContext,withTimeoutScope |
| CoroutineScope의 확장 함수 | 중단함수 |
| CoroutineScope의 리시버 코루틴 컨텍스트 사용 | 중단함수의 컨티뉴에이션 객체의 코루틴 컨텍스트사용 |
| 예외는 Job을 통해 부모로 전파됨 | 일반함수와 같은 방식으로 예외를 던짐 |
| 비동기 코루틴을 시작함 | 코루틴 빌더라가 호출된 곳에서 코루틴을 시작함. |
WithContext
스코프의 컨텍스트를 변경가능하다
withContext(EmptyCoroutineContext)는 coroutineScope와 정확히 같은 방식으로 동작한다.
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
fun CoroutineScope.log(text: String) {
val name = this.coroutineContext[CoroutineName]?.name
println("[$name] $text")
}
fun main(): Unit = runBlocking(CoroutineName("Parent")) {
log("Before")
withContext(CoroutineName("Child 1")) {
delay(1000)
log("Hello 1")
}
withContext(CoroutineName("Child 2")) {
delay(1000)
log("Hello 2")
}
log("After")
}
//[Parent] Before
//[Child 1] Hello 1
//[Child 2] Hello 2
//[Parent] After
* coroutineScope의 작동방식이 async{ ... }.await() 처럼 곧바로 호출하는것과 비슷하다.
withContext 또한 컨텍스트만 변경될 뿐 같다.
supervisorScope
자식 코루틴이 예외를 던지더라도 취소되지 않는다.
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
println("Before")
supervisorScope {
launch {
delay(1000)
throw Error()
}
launch {
delay(2000)
println("Done")
}
}
println("After")
}
//Before
//Exception in thread "main" java.lang.Error
//Done
//After
서로 독립적인 작업을 시작하는 함수에서 주로 사용.
suspend fun notifiyAnalytics(actions: List<UserAction>) =
supervisorScope {
actions.forEach { action ->
launch { notify(action) }
}
}
async를 사용할 경우, 추가적인 예외 처리 필요하다. async 코루틴이 예외로 끝나게 되면, await은 예외를 다시 던진다.
async에서 발생하는 예외를 전부 처리하려면, try - catch 블록으로 랩핑해야된다.
class ArticlesRepositoryComposite(
private val articleRepositories: List<ArticleRepository>
) : ArticleRepository {
override suspend fun fetchArticles(): List<Article> =
supervisorScope {
articleRepositories
.map { async { it.fetchArticles() } }
.mapNotNull {
try {
it.await()
} catch (e: Throwable) {
e.printStackTrace()
null
}
}
.flatten()
.sortedByDescending { it.publishedAt }
}
}
* withContext(SupervisorJob())을 사용하면, withContext는 여전히 기존에 가지고 있던 Job을 사용하며, SupervisorJob은 기존 Job()의 부모가 됩니다.
import kotlinx.coroutines.*
fun main(): Unit = runBlocking {
println("Before")
withContext(SupervisorJob()) {
launch {
delay(1000)
throw Error()
}
launch {
delay(2000)
println("Done")
}
}
println("After")
}
Before 출력후 예외가 터지면 전파되어 전부 종료됨.
WithTimeout
실행하는 데 시간이 너무 오래 걸리면, 람다식은 취소(CancellationException의 하위 타입인 TimeoutCancelationException)된다.
import kotlinx.coroutines.*
suspend fun test(): Int = withTimeout(1500) {
delay(1000)
println("Still thinking")
delay(1000)
println("Done")
42
}
suspend fun main(): Unit = coroutineScope {
try {
test()
} catch (e: TimeoutCancellationException) {
println("Cancelled")
}
delay(1000)// test함수가 취소되었기 때문에,
// 타임아웃 시간을 늘려도 아무런 도움이 되지 않습니다.
}
//Still thinking
//Cancelled
특정함수가 시간이 오래걸리는지 테스트할때 유용하게 사용됩니다.
코루틴 빌더 내부에서 TimeoutCancellationException을 던지면 해당 코루틴만 취소되고 부모에게는 영향이 없다.
import kotlinx.coroutines.*
suspend fun main(): Unit = coroutineScope {
launch { // 1
launch { // 2
delay(2000L)
println("Will not be printed")
}
withTimeout(1000){ // 이 코루틴은 취소된다.
delay(1500L)
}
}
launch { // 3
delay(2000L)
println("Done")
}
}
//Done
*withTimeoutOrNull
- 예외를 던지지 않습니다.
- 지정된 시간이 초과되면, 람다식이 취소되고 null이 반환된다.
- 시간이 너무 길때, 무언가 잘못되었음을 알리는 용도로 사용한다.
코루틴 스코프 함수 연결하기
서로 다른 스코프의 기능을 사용하고 싶다면, 중첩하여 사용한다
suspend fun calculateAnswerOrNull(): User? =
withContext(Dispatchers.Default) {
withTimeoutOrNull(1000L) {
calculateAnswer()
}
}
추가적인 연산
작업을 수항하는 도중 추가적인 연산을 수행하는 경우
예) 사용자 프로필을 보여주고, 분석을 위한 목적으로 요청 보내기.
class ShowUserDataUseCase(
private val repo: UserDataRepository,
private val view: UserDataView,
){
suspend fun showUserData() = coroutineScope {
val name = async { repo.getName() }
val friends = async { repo.getFriends() }
val profile = async { repo.getProfile() }
val user = User(
name = name.await(),
friends = friends.await(),
profile = profile.await(),
)
view.show(user)
launch { repo.notifyProfileShown() }
}
}
fun onCreate() {
viewModelScope.launch{
_progressBar.value = true
showUserData()
_progressBar.value = false
}
}
위 방식은 몇가지 문제를 가지고 있다.
첫번째 문제는 뷰업데이트를 위해 프로그레스바를 보여준다면, launch { repo.notifyProfileShown() }이 끝날때 까지 기다려야 한다.
함수의 목적과 관련된 유의미한 작업이 아니다.
두번째 문제는 취소이다.
코루틴은 기본적으로 예외가 발생하면, 다른 연산을 취소하도록 설계되었다.
이름, 친구, 프로필을 가져오다 문제가 생기면, 사용자를 생성할수 없기 때문에 취소가 되어야한다.
하지만, 분석을 위한 호출이 실패했다고, 전체 과정이 취소가되는건 말이 되지 않는다.
핵심동작에 영향을 주지 않는 추가적인 연산이 있다면, 또 다른 스코프에서 시작을 시켜야 한다.
가장 쉬운 방법은 analyticsScope를 만드는것 입니다.
val analyticScope = CoroutineScope(SupervisorJob())
class ShowUserDataUseCase(
private val repo: UserDataRepository,
private val view: UserDataView,
private val analyticScope: CoroutineScope
){
suspend fun showUserData() = coroutineScope {
val name = async { repo.getName() }
val friends = async { repo.getFriends() }
val profile = async { repo.getProfile() }
val user = User(
name = name.await(),
friends = friends.await(),
profile = profile.await(),
)
view.show(user)
analyticScope.launch { repo.notifyProfileShown() }
}
}
주입된 스코프에서 추가적인 연산을 시작하는건 자주 사용되는 방법입니다.
스코프를 전달하면, 전달된 클래스를 통해 독립적인 작업이 실행한다는것을 명확히 알수 있다.
'Coroutine > 코틀린 코루틴' 카테고리의 다른 글
| 11장 코루틴 스코프 함수 - 1 (1) | 2026.02.18 |
|---|---|
| 10장 예외처리 (0) | 2026.02.09 |
| 9장 취소 (0) | 2026.02.01 |
| 8장 Job과 자식 코루틴 기다리기. (0) | 2026.01.26 |
| 7장 코루틴 컨텍스트 (1) | 2026.01.19 |
