Tutorial: IntelliJ를 사용한 Kotlin Flow 디버깅

페이지 편집

원문

이 튜토리얼은 IntelliJ IDEA를 사용해 Kotlin Flow를 생성하고 디버깅 하는 방법에 대해 설명한다.

이 튜토리얼에서는 독자들이 Coroutine 개념에 대한 사전 지식이 있다고 가정한다.

Kotlin Flow 생성하기

느린 방출기와 느린 수집기를 가진 Kotlin flow를 생성한다:

1. Intellij IDEA에서 Kotlin 프로젝트를 연다. 만약 프로젝트가 없다면 하나를 새로 만든다.

2. kotlinx.coroutines 라이브러리를 Gradle 프로젝트에서 사용하기 위해서 다음 종속성을 build.gradle(.kts)에 추가한다.

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
}
dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
}

다른 빌드 시스템을 위해서는 kotlinx.coroutines README 의 지침을 참고하라.

3. src/main/kotlin 속의 Main.kt 파일을 연다.

src 디렉토리에는 Kotlin 소스 파일과 리소스가 포함되어 있다. Main.kt 파일에서는 Hello World! 를 출력하는 샘플 코드가 들어 있다.

4. 세 개 숫자를 반환하는 simple() 함수를 생성한다.

  • delay() 함수를 사용해 CPU 리소스를 소모하는 블로킹 코드를 모방한다. 이는 스레드를 블로킹하지 않고 Coroutine을 100ms 동안 일시중단 한다.
  • for 루프에서 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)
    }
}

5. main() 함수에서 코드를 바꾼다.

  • runBlocking() 블록을 사용해 Coroutine을 감싼다.
  • 방출된 값들을 collect() 함수를 사용해 수집한다.
  • delay() 함수를 사용해 CPU 리소스를 소모하는 블로킹 코드를 모방한다. 이는 스레드를 블로킹하지 않고 Coroutine을 300ms 동안 일시중단 한다.
  • Flow로부터 수집된 값을 println() 함수를 사용해 출력한다.
fun main() = runBlocking {
    simple()
        .collect { value ->
            delay(300)
            println(value)
        }
}

6. Build Project를 눌러서 코드를 빌드한다.

Coroutine 디버그하기

1. emit() 함수가 호출되는 곳에 브레이크포인트를 설정한다:

2. 화면 상단의 Run 구성 옆의 Debug를 클릭해서 코드를 디버그모드에서 실행한다.

디버그 툴 윈도우는 다음과 같이 나타난다:

  • Frames 탭은 콜스택을 포함한다.
  • Variable 탭은 현재 Context 속의 변수를 포함한다. 이는 Flow가 첫 값을 방출한다는 것을 보여준다.
  • Coroutines 탭은 실행중이거나 일시중단된 Coroutine에 대한 정보를 포함한다.

3. 디버그 툴 윈도우의 Resume Program을 눌러서 Debugger 세션을 재개한다. 프로그램은 같은 브레이크포인트에서 중단된다.

이제 Flow는 두번째 값을 방출한다.

Optimized-out variables

만약 디버거에서 suspend 함수를 사용한다면, 변수 이름 뒤에 "was optimized out" 문자가 붙은 것을 볼 수 있을 것이다.

이 문자는 변수가 더이상 메모리 상에 없다는 것을 뜻한다. 이 변수에 대한 값을 볼 수 없기 때문에 optimized 변수들을 포함한 코드를 디버그 하는 것은 어렵다. 컴파일러 옵션에 -Xdebug를 추가함으로써 이러한 동작을 비활성화 할 수 있다.

⚠ 이 플래그를 제품에 절대로 사용하지 마세요. -Xdebug는 메모리 누수를 일으킬 수 있습니다.

동시에 실행되는 Coroutine 추가하기

1. src/main/kotlin 속의 Main.kt 파일을 연다.

2. 코드에서 방출기와 수집기가 동시에 실행되도록 개선한다.

  • buffer() 함수 호출을 추가해 방출기와 수집기가 동시에 실행되도록 한다. buffer()은 방출된 값을 저장하고 Flow 수집기를 분리된 Coroutine에서 실행한다.
fun main() = runBlocking<Unit> {
    simple()
        .buffer()
        .collect { value ->
            delay(300)
            println(value)
        }
}

3. Build Project를 클릭해 코드를 빌드한다.

두 개의 Coroutine을 가진 Kotlin Flow를 디버깅하기

1. println(value)에 새로운 브레이크포인트를 설정한다.

2. 화면 상단의 Run 옆에 있는 Debug를 클릭해서 디버그 모드로 코드를 실행한다.

디버그 툴 윈도우는 다음과 같이 나타난다.

Coroutines 탭에는, 동시에 실행중인 두개의 Coroutine을 볼 수 있다. Flow 수집기와 방출기는 buffer() 함수 때문에 분리된 Coroutine에서 실행된다. buffer() 함수는 flow에서 방출된 값들을 버퍼에 저장한다. 방출기 Coroutine은 RUNNING 상태를 가지고, 수집기 Coroutine은 SUSPENDED 상태를 가진다.

3. 디버그 툴 윈도우의 Resume Program을 눌러서 디버거 세션을 재개한다.

이제 수집기 Coroutine은 RUNNING 상태를 가지는 반면 방출기 Coroutine은 SUSPENDED 상태를 가진다.

각 Coroutine을 더욱 깊게 파고들어 코드를 디버깅 할 수 있다.

results matching ""

    No results matching ""