IntelliJ IDEA로 Kotlin Flow 디버깅하기
IntelliJ IDEA로 Kotlin Flow 디버깅하기 (Debug Kotlin Flow using IntelliJ IDEA – tutorial)
Kotlin Flow를 만들고 IntelliJ IDEA로 디버깅하는 방법을 알아보는 튜토리얼이에요.
이 튜토리얼은 coroutines과 flow 개념을 미리 알고 있다는 전제로 진행해요.
본문
Kotlin flow 만들기 (Create a Kotlin flow)
느린 emitter와 느린 collector를 가진 Kotlin flow를 만들어 볼게요.
- IntelliJ IDEA에서 Kotlin 프로젝트를 열어요. 프로젝트가 없다면 하나 만들면 돼요.
- Gradle 프로젝트에서
kotlinx.coroutines라이브러리를 사용하려면build.gradle(.kts)에 다음 의존성을 추가해요.
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0")
}
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0'
}
다른 빌드 시스템은 kotlinx.coroutines의 kotlinx.coroutines README에 나온 안내를 참고하면 돼요.
src/main/kotlin에 있는Main.kt파일을 열어요.src디렉터리에는 Kotlin 소스 파일과 리소스가 들어 있어요.Main.kt파일에는Hello World!를 출력하는 샘플 코드가 담겨 있어요.- 세 개의 숫자를 흘려보내는(emit) flow를 반환하는
simple()함수를 만들어요. - CPU를 많이 쓰는 차단 코드를 흉내 내기 위해
delay()delay() 함수를 사용해요. 이 함수는 스레드를 블록하지 않고 코루틴을 100ms 동안 중단시켜요. for루프에서emit()emit() 함수로 값을 만들어 내요.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlin.system.*
fun simple(): Flow<Int> = flow {
for (i in 1..3) {
delay(100)
emit(i)
}
}
main()함수의 코드를 다음과 같이 바꿔요.- 코루틴을 감싸려면
runBlocking()runBlocking() 블록을 사용해요. collect()collect() 함수로 만들어 낸 값을 수집해요.- CPU를 많이 쓰는 코드를 흉내 내기 위해
delay()delay() 함수를 사용해요. 이 함수는 스레드를 블록하지 않고 코루틴을 300ms 동안 중단시켜요. println()println() 함수로 flow에서 수집한 값을 출력해요.
fun main() = runBlocking {
simple()
.collect { value ->
delay(300)
println(value)
}
}
- Build Project를 클릭해서 코드를 빌드해요.
코루틴 디버깅하기 (Debug the coroutine)
emit()함수가 호출되는 줄에 중단점(breakpoint)을 걸어요.- 화면 위쪽 실행 구성 옆의 Debug를 클릭해서 코드를 디버그 모드로 실행해요.
그러면 Debug 도구 창이 나타나요.
- Frames 탭에는 호출 스택이 들어 있어요.
- Variables 탭에는 현재 컨텍스트의 변수들이 들어 있어요. 여기서 flow가 첫 번째 값을 만들고 있음을 알 수 있어요.
- Coroutines 탭에는 실행 중이거나 중단된 코루틴에 대한 정보가 들어 있어요.
- Debug 도구 창에서 Resume Program을 클릭해서 디버거 세션을 재개해요. 프로그램은 같은 중단점에서 다시 멈춰요. 이제 flow가 두 번째 값을 만들어 내요.
최적화되어 없어진 변수 (Optimized-out variables)
suspend 함수를 사용하면 디버거에서 변수 이름 옆에 "was optimized out"이라는 텍스트가 보일 수 있어요.
이 텍스트는 변수의 수명이 줄어들어 변수가 더 이상 존재하지 않는다는 뜻이에요. 최적화된 변수들은 값을 볼 수 없어서 디버깅하기 어렵죠. 이 동작은 -Xdebug 컴파일러 옵션으로 끌 수 있어요.
WARNING: 이 플래그는 절대 프로덕션에서 쓰지 마세요.
-Xdebug는 메모리 누수를 일으킬 수 있거든요.
동시에 실행되는 코루틴 추가하기 (Add a concurrently running coroutine)
src/main/kotlin에 있는Main.kt파일을 열어요.- emitter와 collector가 동시에 실행되도록 코드를 보강해요.
- emitter와 collector를 동시에 실행하려면
buffer()buffer() 함수 호출을 추가해요.buffer()는 만들어 낸 값을 저장하고 flow collector를 별도의 코루틴에서 실행시켜요.
fun main() = runBlocking<Unit> {
simple()
.buffer()
.collect { value ->
delay(300)
println(value)
}
}
- Build Project를 클릭해서 코드를 빌드해요.
두 개의 코루틴으로 Kotlin flow 디버깅하기 (Debug a Kotlin flow with two coroutines)
println(value)에 새 중단점을 걸어요.- 화면 위쪽 실행 구성 옆의 Debug를 클릭해서 코드를 디버그 모드로 실행해요.
그러면 Debug 도구 창이 나타나요.
Coroutines 탭에서 두 개의 코루틴이 동시에 실행되고 있음을 확인할 수 있어요.
buffer()함수 덕분에 flow collector와 emitter가 별도의 코루틴에서 실행되거든요.buffer()함수는 flow에서 만들어 낸 값을 버퍼링해요. emitter 코루틴은 RUNNING 상태이고, collector 코루틴은 SUSPENDED 상태예요. - Debug 도구 창에서 Resume Program을 클릭해서 디버거 세션을 재개해요. 이제 collector 코루틴이 RUNNING 상태가 되고, emitter 코루틴이 SUSPENDED 상태가 돼요. 각 코루틴을 더 깊이 들여다보며 코드를 디버깅할 수 있어요.