코루틴이란?
코루틴은 Kotlin 을 사용해 안드로이드 앱을 개발할 때 아마 가장 처음 접하게 되는 키워드 일것이다. 보통 네트워크 통신, 혹은 내부 DB에서 데이터를 불러오거나 저장할 때 사용하게 되며, 기존에 학습곡선이 높은 RxJava 와는 다르게 쉽게 비동기 처리를 하도록 도와준다.
코루틴을 간단하게 설명하면 다음과 같다.
- 협동 멀티태스킹
- 쉽게 비동기 처리를 도와줌
- 동시성 프로그래밍
협동 멀티태스킹
Coroutine 은 Co + Routine 으로 이루어져 있다. 여기서 Co 라는 접두어는 "협력", "함께" 라는 의미를 뜻한다. Routine 이라는 단어가 조금 생소한데, 함수라고 생각하자.
우리는 평소에 다음과 같이 코드를 작성한다. 간단한 Kotlin 코드를 살펴보자.
fun main() {
print(add(3, 5))
}
fun add(a: Int, b: Int) = a + b
여기서 메인함수를 보통 메인 루틴, 그리고 메인 루틴에서 호출하는 함수들을 서브 루틴이라고 명명한다. 즉 우리의 프로그램은 하나의 메인 루틴과 여러 개의 서브 루틴으로 이루어져 있는 것이다.
서브 루틴
위 코드에서 add 라는 함수를 서브 루틴이라고 칭한다고 하였다. 서브 루틴들에는 특징이 있는데, 바로 시작점과 종료 지점이 명확하다는 점이다. 서브 루틴은 항상 서브 루틴의 맨 처음부터 시작하여, return 문을 만나거나 해당 코드 블럭이 끝날 때 빠져나오게 된다.
위 그림이 이 때까지 알던 방식이다.
코루틴
코루틴도 루틴의 일종이다. 그러나 다른 루틴과 다른 점이 있다.
- 코루틴은 진입점과 탈출점이 여러개 존재한다.
- 언제든지 코루틴 블럭을 빠져나갈 수 있고, 다시 진입할 때 빠져나간 지점부터 다시 코드를 실행한다.
진입점과 탈출점이 여러개 존재하고, 다시 코드를 실행할 때 중단한 지점부터 실행하기 때문에, 이를 비동기 처리에 사용할 수 있는 것이다. 비동기적으로 실행되는 네트워크 통신, 혹은 IO 작업을 실행하면, 해당 작업의 결과를 기다리는 동안 코루틴을 탈출해 다른 코드를 실행할 수 있다.
Kotlin 의 코루틴
Kotlin 에선, 다양한 Coroutine 빌더를 사용해 Coroutine 을 생성할 수 있다. Coroutine 빌더를 생성할 때 코드 블럭을 함께 넘겨받고, suspendable 하게 해당 코드 블럭을 수행한다.
fun main() {
runBlocking {
launch {
delay(1000)
println("World")
}
println("Hello!")
}
}
위 함수의 실행 결과는 "Hello! World"가 된다. 우리가 일반적으로 코루틴을 모를 때 생각하는 결과인 "World Hello!" 과는 정반대의 결과가 나오는 것을 확인할 수 있다. 위 코드를 한 번 분석해보자
- runBlocking - Coroutine Builder 이자, 쉽게 설명하면 non-coroutine인 main 함수와 코루틴으로 실행되는 블럭 안에 있는 코드를 연결시켜주는 역할을 한다. runBlocking 블럭 안은 IDE 에 의해 CoroutineScope 라고 명시되며, 여기서 다른 코루틴 빌더들을 사용해 코루틴을 생성할 수 있다.
- launch - Coroutine Builder 이자, 프로그램의 나머지 코드와 동시적으로 실행될 코루틴을 생성한다. 중요한 것은 CoroutineScope 에서만 호출될 수 있기 때문에, 만약 runBlocking 함수가 사라지면 컴파일 에러가 발생한다.
- delay - suspend 함수로, 전달된 시간만큼 코루틴 실행을 잠시 대기시킨다
여기서 중요한 것은, delay 가 suspend 함수라는 것이다. 코틀린에서 suspend 함수는 Coroutine 안에서 실행될 수 있으며, 코루틴이 suspend 함수를 만나면 해당 함수가 종료될 때 까지 코드 실행을 멈춘다. 이 때 스레드 자체가 멈추는 것이 아니고, 스레드는 코루틴을 빠져나가 나머지 코드를 실행한다. suspend 함수가 종료되면 다시 중단했던 지점부터 코루틴의 실행이 시작된다.
즉 launch 로 생성된 Coroutine 에서 suspend 함수인 delay 를 만나 해당 코루틴을 빠져나온다. 그리고 코드는 다른 코루틴에 있는 코드인 Hello 를 먼저 출력하고, 1초 뒤에 다시 launch 로 생성된 Coroutine 에 진입해 World 를 출력하는 것이다.
Coroutine 을 활용한 비동기 처리
네트워크 통신이나, IO 작업같은 비동기 작업은 보통 시간이 많이 소요된다. 따라서 Android의 메인 쓰레드에서 해당 비동기 작업을 시작할 경우, UI 를 담당하는 메인 쓰레드가 멈추게 되어 사용자가 UI와 상호작용할 수 없다 (ANR).
이런 현상을 방지하기 위해 우리는 주로 RxJava 같은 비동기 라이브러리를 사용하거나, 새로운 쓰레드를 생성해 비동기 작업을 수행했다. RxJava 도 훌륭한 비동기 처리 라이브러리이지만, 그 특유의 학습곡선으로 인해 RxJava 를 잘 모르는 개발자가 있을 경우 소통에 어려움을 겪었다.
그러나 코루틴을 활용하면 이런 비동기 작업을 쉽게 처리할 수 있고, 코드 자체도 거의 동기적으로 짜는 수준과 다름없어 이해도 훨씬 간편해진다.
startCoroutine {
val user = UserRepository.getUser(id,pw) <-- 네트워크 통신
Toast.makeText(context,"로그인 성공! $user",Toast.Length.short())
}
'Kotlin' 카테고리의 다른 글
Coroutine - 3 타임아웃, 취소 (0) | 2022.01.31 |
---|---|
Coroutine - 2 기본 사용법 (0) | 2022.01.30 |
Comment