0Pricing
Kotlin Academy · 강의

SupervisorJob과 Job 비교: 실패 격리

SupervisorJob을 사용해 한 자식의 실패가 형제 코루틴을 취소하지 않도록 해 보세요.

SupervisorJob과 Job 비교: 실패 격리은(는) CoddyKit의 무료 Kotlin Academy 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Kotlin Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Kotlin Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

기본 Job 실패 전파

일반적인 Job에서는 자식의 실패가 부모를 취소하고, 부모가 모든 형제 코루틴을 취소합니다. 하나의 실패가 전체 계층을 중단시킵니다.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {
            launch { delay(50); throw RuntimeException("child 1 failed") }
            launch { delay(1000); println("child 2 — never prints") }
        }
    } catch (e: RuntimeException) {
        println("Caught: ${e.message}")
    }
}

SupervisorJob 개요

SupervisorJob은 규칙을 바꿉니다. 자식의 실패는 형제 코루틴이나 부모를 취소하지 않습니다(NOT). 각 자식은 독립적으로 실패합니다.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val supervisor = SupervisorJob()
    val scope = CoroutineScope(coroutineContext + supervisor)
    scope.launch { throw RuntimeException("child 1 failed") }
    scope.launch { delay(100); println("child 2 still runs") }
    delay(200)
    supervisor.cancel()
}

supervisorScope 빌더

supervisorScope { }는 부모로 SupervisorJob을 사용하는 범위를 생성합니다. 독립적인 자식 코루틴을 실행하는 관용적인 방법입니다.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val job1 = launch {
            throw RuntimeException("job1 failed")
        }
        val job2 = launch {
            delay(100)
            println("job2 succeeded")
        }
        job1.join()  // wait for job1 (it failed)
        job2.join()  // job2 is unaffected
    }
}

자식 실패 처리하기

supervisorScope에서는 각 자식이 자체 예외를 처리합니다. 자식에서 처리되지 않은 예외는 각 자식별로 잡아야 합니다.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val result = async {
            delay(50)
            throw RuntimeException("async failed")
        }
        try {
            result.await()
        } catch (e: RuntimeException) {
            println("Caught from async: ${e.message}")
        }
        println("Scope continues")
    }
}

범위 생성 시 Job과 SupervisorJob 비교

개별 자식의 실패 이후에도 유지되어야 하는 서비스나 ViewModel용 CoroutineScope를 생성할 때 SupervisorJob()을 전달합니다.

import kotlinx.coroutines.*
class MyService {
    private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
    fun startTask(name: String) {
        scope.launch {
            if (name == "fail") throw RuntimeException("$name failed")
            println("$name done")
        }
    }
    fun stop() = scope.cancel()
}
fun main() = runBlocking {
    val svc = MyService()
    svc.startTask("fail")
    svc.startTask("ok")
    delay(100)
    svc.stop()
}

viewModelScope는 SupervisorJob을 사용합니다

Android의 viewModelScope는 SupervisorJob을 기반으로 합니다. 하나의 launch가 실패해도 ViewModel의 다른 코루틴은 취소되지 않습니다.

import kotlinx.coroutines.*
// Android ViewModel internals:
// val viewModelScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
// Each launch is independent — one failure doesn't crash the ViewModel
fun main() = runBlocking {
    println("viewModelScope = SupervisorJob + Main")
}

supervisorScope를 사용한 병렬 분해

일부 작업이 실패하더라도 서로 영향을 주지 않아야 하는 병렬 작업에는 supervisorScope + async를 사용합니다.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val results = supervisorScope {
        val a = async { delay(50); "result-A" }
        val b = async { throw RuntimeException("B failed") }
        val c = async { delay(30); "result-C" }
        listOf(
            runCatching { a.await() },
            runCatching { b.await() },
            runCatching { c.await() }
        )
    }
    results.forEach { println(it) }
}

일반 작업을 사용할 때

모든 자식 작업이 함께 성공해야 할 때는 일반 Job (coroutineScope)을 사용합니다. 하나가 실패하면 그룹 전체를 중단해야 합니다. 트랜잭션처럼 전부 성공하거나 전부 실패하는 방식입니다.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {  // regular Job
            val a = async { delay(50); "A" }
            val b = async { throw RuntimeException("B failed") }
            println(a.await())
            println(b.await())  // throws — cancels a too
        }
    } catch (e: RuntimeException) {
        println("Transaction failed: ${e.message}")
    }
}

supervisorScope에서의 예외 전파

supervisorScope에서는 자식 작업의 최상위 수준에서 예외가 빠져나갈 때만 범위로 예외가 전파됩니다. 즉, 자식 작업에 try-catch가 없어야 합니다. 부모는 이 예외를 전달받지 않습니다.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        // Exception caught inside child — scope unaffected
        launch {
            try { throw RuntimeException("handled") }
            catch (e: Exception) { println("Child caught: ${e.message}") }
        }
        delay(100)
        println("Scope survived")
    }
}

SupervisorJob과 CoroutineExceptionHandler

감독자 범위에 CoroutineExceptionHandler를 설치하면 처리되지 않은 자식 작업의 실패를 범위 전체를 중단하지 않고 기록하거나 대응할 수 있습니다.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { _, e ->
        println("Uncaught: ${e.message}")
    }
    val scope = CoroutineScope(SupervisorJob() + handler)
    scope.launch { throw RuntimeException("unhandled failure") }
    scope.launch { delay(100); println("still running") }
    delay(200)
    scope.cancel()
}

실패 격리 요약

작업: 자식 하나가 실패하면 모두 실패합니다. SupervisorJob: 자식 작업들이 서로 독립적으로 실패합니다. 서비스와 ViewModels에는 감독자를 사용하고, 트랜잭션 그룹에는 일반 작업을 사용합니다.

import kotlinx.coroutines.*
// Decision table:
// coroutineScope { }   -> Job: all-or-nothing
// supervisorScope { }  -> SupervisorJob: independent failures
// CoroutineScope(SupervisorJob()) -> long-lived service
fun main() = runBlocking { println("Pick the right job for the right scope") }

빠른 확인

자식 작업의 실패로 형제 작업이 취소되지 않게 하는 범위는 무엇입니까?

복습

작업: 실패가 형제 작업으로 연쇄됩니다. SupervisorJob/supervisorScope: 실패가 자식 작업별로 격리됩니다. 서로 독립적인 병렬 작업에는 supervisorScope를 사용하고, 트랜잭션 그룹에는 일반 coroutineScope을 사용합니다.

자주 묻는 질문

“SupervisorJob과 Job 비교: 실패 격리” 강의는 무료인가요?

네 — “SupervisorJob과 Job 비교: 실패 격리” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Kotlin Academy 강의 전체를 잠금 해제할 수 있습니다. Kotlin Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“SupervisorJob과 Job 비교: 실패 격리”에서 뭘 배우나요?

SupervisorJob을 사용해 한 자식의 실패가 형제 코루틴을 취소하지 않도록 해 보세요. 브라우저에서 직접 실행하는 실습 코드로 Kotlin Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Kotlin Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Kotlin Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“SupervisorJob과 Job 비교: 실패 격리” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Kotlin Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Kotlin Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. SupervisorJob과 Job 비교: 실패 격리
  2. CoroutineExceptionHandler: 전역 미처리 예외 처리기
  3. async/await 예외 전파
  4. 복원력 있는 코루틴 아키텍처 설계
← Kotlin Academy(으)로 돌아가기