[Android / Kotlin] 코루틴(Coroutine) 이해하기 (3) - Codelab

문승연·2023년 8월 25일
0

Kotlin 코루틴

목록 보기
3/6

이 포스트는 안드로이드 공식 Codelab을 기반으로 작성되었습니다. [링크]

이전 포스트를 통해서 안드로이드 코루틴에 대해 공부했으니 이제 예제를 통해서 이해해보자.

1. github에서 프로젝트 클론해오기

얀드로이드 스튜디오에서 상단 [File] -> [New] => [Project from Version Control...] 에 들어가서 URL에 https://github.com/android/codelab-kotlin-coroutines.git 을 입력하여 프로젝트를 클로닝한다.

2. corouines-codelab 프로젝트 실행

위 git 프로젝트를 클로닝해오면 하나의 프로젝트에 여러개의 코드랩 프로젝트가 들어있음을 알 수 있다.
우리는 이 중 coroutines-codelab 코드랩을 진행할 것이기 때문에 해당 프로젝트를 선택해서 실행해준다.


그러면 위 이미지와 같이 finished_codestart 2개의 폴더로 나뉜 프로젝트가 실행된다.

3. start 앱 실행해보기

예제를 실행해보면 위처럼 MainActivity가 실행된다.

해당 앱은 화면을 터치하면 일정 시간 딜레이 가 발생한 후 taps 횟수가 증가하고 텍스트가 새로 변경 되는 동작을 수행한다. 이때 새로운 텍스트는 서버에서 네트워크 통신을 통해 받아온다.

프로젝트의 구성은 아래와 같다.

이번 Codelab의 목표는 이 프로젝트의 비동기 처리를 Coroutine을 사용하도록 변경 하는 것이다.

4. 프로젝트에 Coroutine 설정 추가하기

프로젝트에서 코루틴을 사용하기 위해 gradle 파일에 관련 설정을 추가한다.

dependencies {
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
}

5. UI 스레드에서 코루틴 사용하기

UI 관련 동작을 수행하는 코루틴을 구성할때는 UI 스레드(메인 스레드)에서 시작하도록 하는 게 좋다. Dispatchers.Main 으로 설정함으로써 코루틴이 메인 스레드에서 동작하게 할 수 있다.

코루틴이 메인 스레드에서 시간이 오래 걸리는 동작을 수행하더라도 메인 스레드는 멈추지(blocked)않고 중지(suspend) 되어 메인 스레드가 계속 작동하게 만든다. 또한 ViewModel 코루틴 역시 메인 스레드에서 UI를 계속 업데이트 하기 때문에 UI 관련 코루틴은 메인 스레드에서 실행하는 것이 좋다.

또한 메인 스레드에서 시작한 코루틴은 동작 이후 자유롭에 Dispatcher 를 전환할 수 있다. (ex. 코루틴에서 대용량의 JSON 데이터를 파싱할 때 메인 스레드가 아닌 다른 스레드로 전환할 수 있다.)

6. ViewModelScope 관련 dependency 추가

dependencies {
  ...
  // replace x.x.x with latest version
  implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:x.x.x"
}

viewModelScope 를 활용할 예정이기 때문에 해당 dependency 를 추가한다. viewModelScopeDispatchers.Main 에서 실행되면서 ViewModel 이 제거되면 같이 취소되기 때문에 유용하다.

7. MainViewModel.kt 코드 코루틴으로 전환

1. updateTaps()

before

/**
* Wait one second then update the tap count.
*/
private fun updateTaps() {
   // TODO: Convert updateTaps to use coroutines
   tapCount++
   BACKGROUND.submit {
       Thread.sleep(1_000)
       _taps.postValue("$tapCount taps")
   }
}

before 코드는 1초 대기 후 tapCount를 증가시키는 동작을 수행한다. BACKGROUND ExecutorService 를 활용해 백그라운드 스레드를 만들어 수행하고 있다.

after

/**
* Wait one second then display a snackbar.
*/
fun updateTaps() {
   // launch a coroutine in viewModelScope
   viewModelScope.launch {
       tapCount++
       // suspend this coroutine for one second
       delay(1_000)
       // resume in the main dispatcher
       // _snackbar.value can be called directly from main thread
       _taps.postValue("$tapCount taps")
   }
}

after 코드에서 먼저 viewModelScope 에서 코루틴을 launch 메소드를 통해 생성한다. 이 코루틴은 viewModelScope 에서 실행되었기 때문에 메인 스레드에서 동작한다.

또한 viewModelScope 가 만약 취소(cancel)되면 (ex. 액티비티를 중지하는 등 viewModel이 제거되는 경우) viewModelScope 내의 모든 코루틴도 취소(cancel)된다.

tapCount를 증가시킨 뒤 delay 함수를 이용해 1초 딜레이를 줘 작업을 중지(suspend)시킨 뒤 변경된 값을 postValue 함으로써 마무리한다. (delay 함수는 suspend 함수이다.)

8. 코루틴 테스트하기

테스트 코드 작성법에 대해서는 아직 자세히 다뤄보지 않았기 때문에 다음 기회에 좀 더 제대로 알아보기로 하고 여기서는 가볍게 넘어가도록 하자.

MainViewModelTest.kt

@Test
fun whenMainClicked_updatesTaps() {
   subject.onMainViewClicked()
   Truth.assertThat(subject.taps.getValueForTest()).isEqualTo("0 taps")
   coroutineScope.advanceTimeBy(1000)
   Truth.assertThat(subject.taps.getValueForTest()).isEqualTo("1 taps")
}

onMainViewClicked 메소드를 호출함으로써 updateTaps 코루틴이 실행된다. 메소드 호출 직후 텍스트가 "0 taps" 이고 1초가 흐른 뒤 "1 taps"가 되면 코루틴이 정상적으로 작동하고 있는 것을 확인할 수 있다.

profile
"비몽(Bemong)"이라는 앱을 개발 및 운영 중인 안드로이드 개발자입니다.

0개의 댓글