본문 바로가기
오늘은 뭘 배울까?/Android

[kotlin] 코루틴 공부하기 (비동기 처리, 서버 딜레이 처리)

by Kim Juhwan 2021. 2. 14.

 

빌어먹을 코루틴...

저번에 공부하다가 도저히 못해먹겠어서 포기했다가 오늘 다시 도전했다.

항상 느끼는 거지만 아무것도 모를 땐 그렇게 어렵게 느껴지다가

또 막상 성공하면 아 이걸 왜 이해 못했지 생각이 든다. 쩝 ...

 

우선 빌어먹을 코루틴 부터.

코루틴은 내가 영어로 된 영상까지 찾아봤는데 감이 잡힐 것 같으면서도 너무 헷갈린다.

아무튼 비동기 실행을 위해서 사용한다.

꼭 이 용도를 목적으로 만들어진 건 아니지만 이 용도로 많이 쓰인다고 한다.

다른 기능이 더 있나 본데, 이건 추후에 더 공부를 해봐야겠다.

 

 

 


 

1. 비동기란?

비동기 실행은 예를 들어 이런 거다.

난 머리가 단순해서 전화를 받으면서 메모를 적거나 게임을 하는 등 하나를 하면서 다른 작업을 같이 할 수 없다.

이걸 동기라고 하고.

우리 엄마는 신기하게도 전화도 받고 밥도 먹으면서 메모도 하고 티비도 보시고 이것저것 작업을 동시에 하신다.

이걸 비동기라고 한다.

 

함수 {
    네트워크 작업()
    화면 UI를 변경하는 작업()
}

코드에서는 비동기가 필요할 때가 있다.

가령 네트워크를 통해 값을 가져오는 작업은 메인 스레드에서는 하지 못하도록 막혀있다.

 

예를 들어 위와 같은 상황에서 네트워크 작업이 겁~~~~~~나 오래 걸리는 작업이라고 해보자

그러면 화면 UI를 바꾸기 위해서 겁~~~~~나 기다려야 하고

사용자는 빈 화면을 겁~~~~~나 보고 있다가

"아 뭐야 이거 앱 개 느리네"하고 지워버릴지도 모른다.

그래서 메인 스레드가 아닌 별개의 스레드에서 네트워크 작업을 해야 하는 것이다.

 

여기서 우리는 하나의 사실을 알 수 있다.

"아~ UI 작업은 메인 스레드에서 해야 하는구나"

"네트워크 작업은 다른 스레드에서 해야 하는거고, 아하!"

 

 

콜백 함수(Callback function)란 무엇일까?

오늘은 콜백 함수에 대해 공부하기로 했다. 사실 이번에 토이 프로젝트를 하기 전까지는 콜백 함수가 뭔지 몰랐다. 아니 존재 자체도 몰랐다. 보고 따라 사용하면서도 내가 잘 이해하면서 쓰고

todaycode.tistory.com

만약 스레드가 무엇인지 잘 모르겠거나 이해가 안간다면

위 게시물에 있는 만화를 보고 오자.

 

 

2. 코루틴 사용법

최신 버전이 몇 버전인지 궁금하다면 여기를 참고하자.
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'

 

gradle에 코루틴을 추가했다면 아래와 같은 코드를 작성할 수 있다.

 

CoroutineScope(Dispatchers.쓰레드종류).메서드 {

}

스레드의 종류는 총 3가지가 있다.

  • Main: 메인 스레드. 화면 UI 작업 등을 하는 곳
  • IO: 네트워크, DB 등 백그라운드에서 필요한 작업을 하는 곳
  • Default: 정렬이나 무거운 계산 작업 등을 하는 곳

메서드의 종류는 총 2가지가 있다.

  • lanuch
    • 실행하고 잊어버리는 형태의 코루틴 실행
    • 즉시 실행되고, 실행결과는 반환하지 않는다.
    • 관리를 위한 Job객체를 반환한다.
    • join을 통해서 완료를 대기할 수 있다.
  • async
    • 결과나 예외를 반환한다.
    • 실행결과는 Deferred<T>를 통해서 반환하며 await을 통해서 받을 수 있다.
    • await은 작업이 완료될때까지 기다린다.

 

CoroutineScope(Dispatchers.IO).async {
	// 여기서 무언가 작업을...
}

그럼 예를 들어 위 코드는?

네트워크 작업을 하는 스레드로 비동기 작업을 할건데 결과나 예외를 반환해주세요~ 라는 의미가 된다.

 

CoroutineScope(Dispatchers.Main).launch {
    val html = CoroutineScope(Dispatchers.IO).async {
        getHtml()
    }.await()

    textView.text = html.toString()
}

그럼 예를 들어 위 코드는?

갑자기 await이 튀어나왔다. 이건 작업이 완료될 때까지 기다리겠다는 뜻이다.

작업이 완료되면 html에 값이 들어가고, 그걸 텍스트뷰에 뿌려주는 UI 작업을 하는 것이다.

 

 

 

 

액티비티{
        CoroutineScope(Dispatchers.Main).launch {
            val html = CoroutineScope(Dispatchers.Default).async {
                getHtml()
            }.await()

            textView.text = html.toString() // 이거보다 밑에 토스트가 더 먼저 실행됨
        }
        
        Toast.makeText(this, "이게 먼저 뜨지롱", Toast.LENGTH_SHORT).show() // 다른 UI 작업
}

내가 이거 때문에 좀 많이 헷갈렸는데...

CoroutineScope 괄호 안에 있는 건 별도의 스레드라고 생각하면 된다.

즉, 괄호 안에서 겁~~~~나 늦게 작업이 진행된다고 해도

괄호 밖에서는 별도로 작업을 계속 진행한다는 소리다.

 

위 코드를 보면 코드의 순서에 따라

텍스트뷰가 토스트 메시지보다 먼저 바뀔 거 같지만

getHtml() 작업이 느려서 토스트 메시지가 먼저 뜬다.

 

이게 바로 코루틴을 이용한 비동기 실행이다.

더 자세하게 파고들면 새로운 개념, 새로운 함수들이 있는데

그건 다음 시간에 공부해야겠다..

 

 

 

3. 코루틴이 필요한 또 다른 예시 (딜레이 처리)

내부 DB 데이터를 삭제하는 코드()        // 이게 느려서
recyclerView를 새로고침 하는 코드()        // 이게 먼저 실행될 수 있다.

네트워크 작업과 달리 내부 DB를 이용하는 작업은 메인 스레드에서 할 수 있다.

이런 무거운 작업과 recyclerView를 메인 스레드에서 같이 사용하다 보면

나 같은 초보자는 문제를 직면하게 된다.

 

예를 들어, 내부 DB에 데이터가 많아서 삭제를 하는데 시간이 오래 걸린다고 치자.

코드상으로는 분명히 삭제를 하고 refresh를 했는데

화면에는 변화가 없는 것처럼, 삭제가 이루어지지 않은 것처럼 보이게 될 것이다.

실제로 로그를 찍어보면 새로고침이 먼저 되고 DB 삭제가 이루어진다.

 

내부 DB 데이터를 삭제하는 코드()

val handler = android.os.Handler()
handler.postDelayed({
    recyclerView를 새로고침 하는 코드()
}, 1000)

이때 핸들러를 이용해서 해결하는 방법이 있다.

임의로 몇 초 늦췄다가 명령어를 실행하도록 하는 것이다.

단, 이 방법에는 단점이 있다.

 

  1. 항상 똑같은 시간만큼 기다려야 한다. 데이터량이 적으면 삭제를 빨리 끝내고 새로고침을 빨리 할 수 있는데도 불구하고 지정해둔 시간만큼 기다렸다가 다음 명령어를 실행해야 하는 것이다. 시간을 낭비하는 것이다.
  2. 데이터량이 많아져서 지정해둔 시간보다 더 오랜 시간이 걸리는 경우 다시 똑같은 문제가 발생한다. 핸들러가 의미가 없어지는 것이다. 삭제가 되기도 전에 다음 명령어가 실행돼버리는 그런 상황 말이다.

이러한 이유로 핸들러는 근본적인 해결책이 되지 못한다.

 

CoroutineScope(Dispatchers.Main).launch {
    val temp = CoroutineScope(Dispatchers.Default).async {
        내부 DB 데이터를 삭제하는 코드()
    }.await()

    recyclerView를 새로고침 하는 코드()
}

바로 이럴 때 코루틴을 사용하면 된다.

네트워크 작업이 아니니까 Dispatchers.IO가 아니라 Dispatchers.Default로 해주고..

비동기로 DB 작업을 하고, 기다렸다가 끝나면 바로 recyclerView를 새로고침 해주는 것이다.

 

이렇게 해서 오늘 공부한 내용은 끝!

 

 

 

4. 잡담

rxjava와 coroutine의 난이도 비교

 

코루틴 공부하다가 어느 외국 영상에서 봤었는데 (영상 찾고 싶었는데 다시 못 찾겠음...)

코루틴의 난이도가 이렇다고 한다.

즉, 내가 오늘 공부한 건 졸라리 쉬운 거고 아직 코루틴을 공부할게 많고 갈수록 어려워진다는 것

하... 힘내서 빠샤 해보자...

 

참고로 비동기 처리를 하는 방법에는 여러 가지가 있는데

그중 rxjava라는 것도 많이 쓰이고 이건 처음부터 졸라리 어렵다고 한다.

코루틴아 태어나줘서 고마워

 

 

 


 

 

 

초보자도 이해하기 쉽게 설명해주시는 센치한 개발자님

https://www.youtube.com/watch?v=yIdFRXHawYc

 

2~3시간 분량으로 자세히 알려주시는 새차원님

https://www.youtube.com/watch?v=Vs34wiuJMYk&list=PLbJr8hAHHCP5N6Lsot8SAnC28SoxwAU5A

 

블로그 글 중 가장 도움이 됐던 쾌락코딩님

 

코틀린 코루틴 사용법 맛보기 · 쾌락코딩

코틀린 코루틴 사용법 맛보기 17 Mar 2019 | kotlin coroutine 코루틴이 완전히 처음이라면 코틀린 코루틴 개념익히기 를 읽고 돌아오자! 클라이언트 앱을 만들기 위해서 비동기 처리는 필수적이다. 따

wooooooak.github.io

안드로이드 공식문서

 

Android의 Kotlin 코루틴  |  Android 개발자  |  Android Developers

코루틴은 비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴입니다. 코루틴은 Kotlin 버전 1.3에 추가되었으며 다른 언어에서 확립된 개념을 기반으로

developer.android.com

 

감사합니다!

반응형

댓글