코루틴
비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴입니다.
비동기 vs 동기
예를 들면,
요리를 할 때
동생 같은 요리 초보는 프라이팬을 하나만 사용하면서 요리를 한다.
이러한 방식을 동기
라고 한다.
나같은 요리 고수는 프라이팬을 여러 개 사용하면서, 칼질도 하는 등 여러 작업을 동시에 한다.
이러한 방식을 비동기
라고 한다.
그렇다면 비동기가 왜 필요할까?
만약 내가 버튼을 누름과 동시에 데이터베이스에서 접근해서 데이터베이스를 읽어 들이는데,
그 데이터베이스를 기다리는 동안 화면 UI는 데이터베이스에서 정보가 들어오지 않았기에, 계속 기다려야 하고, 그렇게 되면 사용자 입장에서는 텅 빈 화면을 바라봐야 한다.
이러한 상황이 발생되면 사용자도 불편하고, 앱도 별로라는 평가를 많이 받기에,
우리는 프라이팬의 용도를 정해서 그 용도마다 작업을 해야 한다.
이 프라이팬을 안드로이드의 스레드라고 생각하면 된다.
코루틴은 코루틴 스코프 내에서 실행되며, 코루틴은 항상 자신이 속한 스코프를 참조해야 한다.
코루틴 스코프
- GlobalScope : 앱의 생명주기와 함께 동작하기 때문에 실행 도중에 별도 생명 주기 관리가 필요 없다.
시작~종료까지 긴 기간 실행되는 코루틴의 경우에 적합하다. - CoroutineScope : 버튼을 눌러 다운로드하거나 서버에서 이미지를 열 때 등 필요할 때만 열고 완료되면 닫아주는 코루틴 스코프를 사용할 수 있다.
- CoroutineScope는 GlobalScope와 달리 디스패처를 지정할 수 있는데, 이는 코루틴이 실행될 스레드를 지정해 주는 의미.
- ViewModelScope : jetpack 아키텍처의 뷰모델 컴포넌트 사용 시 ViewModel 인스턴스에서 사용하기 위해 제공되는 스코프이다. 해당 스코프로 실행되는 코루틴은 뷰모델 인스턴스가 소멸될 때 자동으로 취소된다.
CoroutineContext의 구성요소
- 코루틴이 실행 중인 취소 가능한 작업을 표현하는 job
- 코루틴과 스레드의 연관을 제어하는 dispatcher
코루틴 디스패처의 종류는 3가지가 있고, 각 디스패처는 코루틴을 적당한 스레드에 할당해 준다.
코틀린 코루틴의 디스패처
- Main : 사용자 입력이 처리되는 UI 작업.
- IO : 이미지 다운로드, 파일 입출력 등 입출력에 최적화되어있는 디스패쳐(네트워크, DB 등 백그라운드에서 필요한 작업을 하는데 적합)
- Default : CPU를 많이 쓰는 작업에 최적화(정렬이나 무거운 계산 작업 등에 적합)
코루틴의 상태 관리 메서드
Cancel
- 코루틴의 동작을 멈추는 상태관리 메서드로, 하나의 스코프 안에 여러 코루틴의 존재하는 경우 하위 코루틴 또한 모두 멈추게 한다.
suspend fun main(){
val printer = GlobalScope.launch(Dispatchers.Default) {
var i = 1
while(isActive) {
println(i++)
}
}
delay(100) // job이 어느정도 실행될 시간을 준다.
Printer.cancel() //실행 취소
}
여기서 만약 while(isActive)가 들어간 이유는 코루틴이 취소됐는지 검사를 해야 하기 때문이다.
suspend fun main(){
val printer = GlobalScope.launch(Dispatchers.Default) {
var i = 1
while(1) { //이 부분이 달라짐.
println(i++)
}
}
delay(100) // job이 어느정도 실행될 시간을 준다.
Printer.cancel() //실행 취소
}
위 코드와 같은 코드지만 해당 코드는 실행해 보면, 계속 실행되는 것을 알 수 있다.
왜냐하면 cancel을 했지만, 취소됐는지에 대한 검사가 이루어지지 않아서, 계속 printer가 실행되는 것이다.
Join
- 코루틴 내부에서 여러 lanch 블록이 있는 경우 순서를 정해주는 용도.
- join을 통해서 작업을 순차적으로 실행할 수 있다.
CoroutineScope(Dispatchers.Default).launch{
launch {
for( i in 0 ..5){
delay(500)
prinln(i)
}
}
}.join()
launch{
for( in 6 .. 10){
delay(500)
println(i)
}
}
}
이렇게 잡에 join을 부여해 주면 작업을 순차적으로 진행할 수 있다.
Suspend
- 코루틴 안에서 사용되면 suspend 함수가 호출될 경우 이전까지의 코드의 실행이 멈추며 suspend 함수가 처리가 완료된 후 멈춰있던 원래 스코프의 다음 코드가 실행된다.
- Suspend 함수가 호출되는 순간 이전까지의 실행은 멈추고, Suspend의 작업이 모두 완료된 이후의 멈췄던 실행이 다시 시작된다.
- 코드가 잠시 멈추지만, 스레드의 중단은 없다.
코루틴 빌더
launch()
- 코루틴을 시작하고, 실행 중인 작업의 상태를 추적하고 변경할 수 있는 job 객체를 반환함.
- 결과를 반환하지 않기에, 동시성 작업이 결과를 만들어내지 않는 경우 적합하다.
async()
- 결과나 예외를 반환함.
- 실행 결과는 Deferred의 인스턴스를 통해서 반환됨.
- await() 메서드는 계산이 완료되거나, 계산작업이 취소될 때까지 코루틴을 중지시킴.
Job
- 동시성 작업의 생명 주기를 표현하는 객체.
- 잡을 사용 하면 작업 상태를 추적하고 필요할 때 작업을 취소할 수 있다.
Job의 상태
- isActive()
- isCompleted()
- isCancelled()
일단은 코루틴에 대해서 아주 겉핥기식으로 공부했습니다.
너무 어렵네요..
차차 공부하고 살을 붙여서 글을 마무리할 예정입니다.. ㅠㅠㅠㅠㅠ
느낀 점
- 비동기라는 개념을 정확하게는 알지 못했는데, 코루틴을 공부하면서 알게 되었고, 비동기 방법이 효율적인 스레드의 사용이 가능해진다는 점을 알았다.
- 네트워크 작업, DB를 읽는 등 시간이 오래 걸리는 작업을 무작정 기다리지 않고, 코루틴을 사용해서 효과적으로 작업을 처리할 수 있다.
- 이때까지 계속 데이터를 받아 오기 전에 화면이 먼저 노출되어서, 불편하게 코드를 짰었는데, 코루틴을 사용하면 될 것 같다.
- 정리한 코루틴은 진짜 코루틴의 손톱..? 코루틴은 다 좋지만, 코드 복잡도가 엄청 올라가서, 사용자의 실력이 중요한 것 같다.
- 비동기를 처리하는 방법 중 rxjava도 있다고 하는데, 뭐가 더 좋다기보다는 안드로이드 쪽에서 코루틴을 밀어주는 것 같다..? ㅎ
'Skils > Kotlin' 카테고리의 다른 글
[Kotlin] - Scope function(apply, run, with, also, let) (0) | 2023.09.09 |
---|---|
[Kotlin]-정규표현식 (0) | 2023.08.10 |
[Kotlin] 고차 함수와 함수타입 (0) | 2022.09.14 |
[Kotlin] 객체(Object) (0) | 2022.09.12 |
[Kotlin] 프로퍼티(Property) (0) | 2022.09.02 |