이번 포스팅에서는 클라이언트가 네트워크 통신을 하는데 비동기 처리를 하기 위해 주로 사용되는 코루틴에 대해서 알아보자.
동기와 비동기 (sync vs async)
동기 (Synchronous : 동시적으로 발생하는)
작업을 수행하고 그 작업이 완료될 때까지 다른 작업을 하지 못하고 기다리는 방식이다.
이렇게 동기적으로 프로그램이 돌아가면 먼저 들어온 요청에 의한 로직이 돌고 있는 동안은 다른 작업은 대기하는, 즉 block 상태가 된다.
비동기 (Asynchronous : 동시에 일어나지 않는)
동기는 의식의 흐름대로 코드를 작성할 수 있어 구현하기는 쉽지만,
특정 요청에 의한 코드 블록이 실행되는 동안 다른 코드는 대기를 하는 block 상태에 빠지는 단점이 있다.
비동기 방식은 어떤 작업을 수행하지만 완료와 상관없이 계속해서 작업을 할 수 있는 방식이다.
비동시 방식을 이용하면 요청을 보냈을 때 응답 상태와 상관없이 다음 동작을 수행 할 수 있다.
네트워크 통신을 필수적으로 비동기로 처리해야하는 이유
안드로이드 클라이언트는 1초에 120번 화면을 그려내고 있다. 즉, 앱을 켤 때 스와이프 같은 것으로 어떤 값을 조절할 때 UI에 보여지는 것들은 모든 수치로 연속적으로 그려내지 않고 1초에 120번 화면에 그려내서 연속적으로 화면이 드려지고 있는 것처럼 묘사된다.
그런데 여기서 네트워크 통신 때문에 이런 작업들이 멈추고 데이터를 받아오고 다시 UI에 그려지게 된다면 어떻게 될까?
채팅을 할 때 매 순간 데이터를 받아오고 있는데 매 순간 화면이 멈춰있다면 사용자가 채팅 기능을 사용할 수 있을까?
이런 문제점 때문에 안드로이드에서는 5초이상 화면을 그리는 작업이 멈추면(=메인(UI)스레드가 블락되면)
ANR(Application Not Responding)이라는 에러를 터뜨리게 된다.
네트워크 통신은 항상 5초 이내로 응답이 오지 않을 수 있기 때문에 클라이언트에서 이런 작업들을 비동기로 처리해야 화면을 그려내면서 데이터를 받아오면 화면에 그려낼 수 있게 해야 한다.
만약 메인 스레드는 그대로 UI를 그리는 작업만을 진행하고, 네트워크 연결은 별도의 스레드를 만들어서 따로 작업을 진행하면 어떨까?
앱은 멈춘 것처럼 보이지 않고( 적어도 로딩 중이라는 화면을 보여줄 수 있으므로) 네트워크는 네트워크대로 연결 작업을 진행하게 될 것이다.
이처럼 여러 작업을 각자의 스레드(또는 프로세스 등등)가 나누어 가져가서 여러 작업을 block없이 진행할 수 있게하기위해 비동기로 처리해야 한다.
코루틴(coroutine)이란?
구글에서는 비동기 처리에 코루틴 사용을 추천한다.
coroutine의 co는 with 또는 together 의미한다.
코루틴은 메인이 되는 루틴과 별도로 진행이 가능한 루틴으로 개발자가 루틴의 실행, 종료를 마음대로 제어할 수 있는 단위이다.
또한 코루틴은 제어범위 및 실행 범위를 지정할 수 있다.
코루틴은 협력 작업, 예외, 이벤트 루프, 반복자, 무한목록 및 파이프와 같은 친숙한 프로그램 구성 요소를 구현하는 데 적합하다.
코루틴 활용하면 좋은 점
- 비동기 처리를 간단한 코드로 구현할 수 있게 해준다. (비동기시 해야할 콜백, 캔슬, 리소스 관리 등 간단하게 작성 가능)
- 메인스레드가 블로킹되는 상태를 관리할 수 있게 도움을 준다.
- 비동기 콜백 처리(async callback)하는 것을 순차적인 코드로 바꿔준다.
코드로 코틀린 코루틴을 사용해야 하는 이유를 자세히 알아보자.
bloking.kt
fun loadUser(){
val user = api.fetchUser() // 서버를 한번 호출하고 서버에서 온 유저 데이터를
show(user) // ui에 업데이트
}
안타깝게도 이 코드는 동작이 되지 않는다. 메인 스레드에서 서버 api를 호출하면 UI가 블로킹되면서 화면에 그릴 수 없게 되기 때문이다.
그리고 이 앱은 죽게 된다.
다음 코드 방식을 보자.
async.kt
fun loadUser() {
api.fetchUser { user -> //새로 api를 하나 만들어서 스레드 신청해서
show(user) // 다시 ui를 업데이트하는 콜백 방식
}
}
결과적으로 성공하고 좋은 코드이지만 우리가 원하는 드림코드에는 가깝지 않다.
그렇다면 어떻게 해야할까? → 코틀린의 코루틴 이용하는 예시를 보자.
coroutines.kt
**suspend** fun loadUser() {
val user = api.fetchUser() // 서버 호출하는 코드
show(user) // ui를 업데이트 하는 코드 -> 비동기를 순차적인 코드로 만듦
}
이렇게 하면 콜백도 없고 별도의 스레드로 갔다가 온 처리도 없지만 실제 실행은 새로운 스레드가 생성되고 네트워크가 완료된 다음 다시 ui 업데이트가 된다. 즉 콜백을 했을때와 같은 형태로 운영한다. 하지만 작성된 코드 입장에서는 그런 처리가 없다. 그런 처리는 코틀린 코루틴이 해준다.
'Kotlin' 카테고리의 다른 글
[Kotlin] lateinit와 by lazy 비교 (2) | 2023.11.20 |
---|---|
[Kotlin] 코루틴(coroutine)(2) - 코루틴 사용 방법 (0) | 2023.11.15 |
[Kotlin] 익명객체와 옵저버 패턴 (0) | 2023.09.05 |
[Kotlin] 고차함수와 람다함수 (0) | 2023.08.16 |
[Kotlin] 스코프 함수 (0) | 2023.08.16 |