⇒ 따라서 필요한 경우 백그라운드 스레드로 이동
| 상황 | 필요한 Dispatcher |
|---|---|
| UI 업데이트 | Dispatchers.Main |
| 네트워크 요청 / DB 작업 | Dispatchers.IO |
| 무거운 연산 (JSON 파싱, 데이터 변환) | Dispatchers.Default |
| UI 상태 변경을 즉시 실행 | Dispatchers.Main.immediate |
⇒ 불필요한 스레드 변경은 성능을 저하시킬 수 있기 때문에 최소화하는 게 중요
| Dispatcher | 스레드 개수 | 설명 |
|---|---|---|
| Dispatchers.Main | 1개 (메인 스레드) | UI 작업 전용 (새로운 스레드 X) |
| Dispatchers.IO | 여러 개 (CPU 코어수 * 64개) | I/O 작업 전용 스레드 풀 사용 |
| Dispatchers.Default | CPU 코어 개수만큼 | CPU 연산 최적화 |
| Dispatchers.Main.immediate | 1개 (메인 스레드) | 메인 스레드에서 바로 실행 가능 |
Dispatchers.IO 내에서 withContext(Dispatchers.IO) {}를 호출해도 추가적인 스위칭 없이 실행Dispatchers.IO와 Dispatchers.Default는 기존 스레드를 재사용하는 방식, 오랜 시간 사용하지 않으면 자동으로 줄어드는 방식⇒ 이런 경우 컨텍스트 스위칭 오버헤드만 증가하고 성능이 나빠질 수 있음
UI 상태를 변경할 때, 이미 메인 스레드라면 불필요한 컨텍스트 스위칭 없이 바로 실행
launch(Dispatchers.Main.immediate) { updateUi() }
Dispatchers.Main.immediate 란?
⇒ 메인 스레드에서 실행 중일 때 추가적인 스케줄링을 피하고 즉시 실행하는 최적화된 버전의 디스패처
LaunchedEffect(Unit) {
val data = withContext(Dispatchers.IO) { fetchData() }
textState = data // 자동으로 메인 스레드에서 실행됨
}
LaunchedEffect(Unit) {
val result = withContext(Dispatchers.Default) { processHeavyData() }
textState = result
}
LaunchedEffect는 Composable이 재구성될 때마다 실행될 수 있음
즉, 화면이 리컴포지션될 때 의도치 않게 fetchData()가 여러 번 호출될 수 있음
var textState by remember { mutableStateOf("Initial") }
LaunchedEffect(Unit) {
textState = fetchData() // 네트워크 요청
}
UI 이벤트에서 코루틴을 실행할 때는 rememberCoroutineScope()을 사용하는 것이 좋음
@Composable
fun MyScreen() {
val scope = rememberCoroutineScope()
var textState by remember { mutableStateOf("Initial") }
Button(onClick = {
scope.launch(Dispatchers.IO) {
val result = fetchData()
textState = result // UI 업데이트 (자동으로 Main 스레드에서 실행됨)
}
}) {
Text("Load Data")
}
}
rememberCoroutineScope()를 사용하면 Composable의 생명주기에 맞춰 CoroutineScope가 관리됨
onClick 이벤트가 발생할 때만 코루틴이 실행되므로 불필요한 호출 방지
네트워크 요청이나 무거운 연산을 수행할 때 이전 작업이 완료되지 않았는데 다시 실행되면 불필요한 리소스 낭비
불필요한 작업을 최소화하고, 새로운 요청이 들어오면 이전 요청을 취소하는 방식
var job: Job? = null
Button(onClick = {
job?.cancel() // 이전 작업 취소
job = scope.launch(Dispatchers.IO) { fetchData() }
}) {
Text("Reload Data")
}
스레드를 바꿀 때는 신중하게!
필요한 경우에만 적절한 Dispatcher를 사용해야 함