๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Kotlin

[Kotlin] Coroutines - ๊ธฐ์ดˆ

interfacer_han 2024. 2. 7. 16:58

๋ณธ ๊ฒŒ์‹œ๊ธ€์˜ Coroutine ๊ฐœ๋…์€ Android ๋‚ด์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์„ ์ „์ œ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋‹ค.

 

#1 ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ ๊ตฌํ˜„

#1-1 ์ฝ”๋ฃจํ‹ด

 

Android์˜ Kotlin ์ฝ”๋ฃจํ‹ด  |  Android Developers

Android์˜ Kotlin ์ฝ”๋ฃจํ‹ด ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ์ฝ”๋ฃจํ‹ด์€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋ฅผ ๊ฐ„์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด Android์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋™

developer.android.com

์ปดํ“จํ„ฐ ๊ณผํ•™์—์„œ์˜ ๋ฉ€ํ‹ฐํƒœ์Šคํ‚น(Multitasking)์—๋Š” ํฌ๊ฒŒ 2๊ฐ€์ง€ ์ข…๋ฅ˜์ด ์žˆ๋Š”๋ฐ, ์ฒซ ๋ฒˆ์งธ ์ข…๋ฅ˜๋Š” ์šด์˜์ฒด์ œ๊ฐ€ ์•Œ์•„์„œ ํ”„๋กœ์„ธ์Šค๋“ค์„ ์ „ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๊ณ , ๋‘ ๋ฒˆ์งธ ์ข…๋ฅ˜๋Š” ํ”„๋กœ์„ธ์Šค ์ž์ฒด์—์„œ ์Šค์Šค๋กœ์˜ ๋™์ž‘์„ ์ œ์–ดํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ์ „์ž๋Š” ์šด์˜์ฒด์ œ์˜ ์Šค์ผ€์ฅด๋Ÿฌ๊ฐ€ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ด€์—ฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฐ˜๋ฉด, ํ›„์ž๋Š” ํ”„๋กœ์„ธ์Šค์˜ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์œ„ํ•ด ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•„์—ฐ์ ์œผ๋กœ ๊ด€์—ฌํ•ด์•ผ ํ•œ๋‹ค.
 
ํ”„๋กœ์„ธ์Šค๋ฅผ ์ชผ๊ฐœ๋ฉด, ํ”„๋กœ์„ธ์Šค์˜ ์ตœ์†Œ ๋‹จ์œ„์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋œ๋‹ค. ๋‘ ๋ฒˆ์งธ ์ข…๋ฅ˜๋Š” ์–ด๋–ค ์ž‘์—…์ด ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๋ฅผ ์˜ค๊ฐ€๋ฉฐ ์ˆ˜ํ–‰(๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ)๋  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ ๋‹ค. ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐœ๋ฐœ์ž๋Š” ๊ทธ ํ๋ฆ„์„ ๋ช…์‹œ์ ์ธ ์ฝ”๋“œ๋กœ ์งœ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
 
๋‘ ๋ฒˆ์งธ ์ข…๋ฅ˜์˜ ๊ตฌํ˜„์„ ์œ„ํ•œ RxJava, AsyncTask, Executors, HandlerThreads, IntentServices ๋“ฑ ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ์žˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์€ ๊ฝค ์˜ค๋ž˜๋˜์—ˆ๋‹ค๊ณ  ํ•œ๋‹ค. Kotlin์˜ Coroutines(์ฝ”๋ฃจํ‹ด)์€ ๋˜ํ•œ ๋‘ ๋ฒˆ์งธ ์ข…๋ฅ˜๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ๋ฐ, ์˜ˆ์ „์— ์“ฐ์˜€๋˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์— ๋น„ํ•ด ์‰ฝ๊ณ  ํšจ์œจ์ ์ด๋‹ค.
 

#1-2 ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ ๊ตฌํ˜„์˜ ํ•„์š”์„ฑ

๋Œ€๋ถ€๋ถ„์˜ ์Šค๋งˆํŠธํฐ์€ 1์ดˆ(1000๋ฐ€๋ฆฌ์ดˆ)์— ์ ์–ด๋„ 60๋ฒˆ ํ™”๋ฉด์„ ๊ฐฑ์‹ (Refresh)ํ•œ๋‹ค. ์ฆ‰ (1000 / 60) = 16.666...๋ฐ€๋ฆฌ์ดˆ์— ์ ์–ด๋„ ํ•œ ๋ฒˆ ์ด์ƒ ํ™”๋ฉด์„ Refreshํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ์•ˆ๋“œ๋กœ์ด๋“œ ์šด์˜์ฒด์ œ์˜ Main Thread๋Š” XML ํŒŒ์ผ์„ ์ธ์Šคํ„ด์Šคํ™”ํ•˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋“ฑ ์ด๋ฏธ ํ•  ๊ฒŒ ๋„ˆ๋ฌด๋‚˜๋„ ๋งŽ๋‹ค. ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ํ™”๋ฉด ๊ฐฑ์‹ ๋งˆ์ € Main Thread์—์„œ ์‹คํ–‰๋œ๋‹ค๋ฉด, ์Šค๋งˆํŠธํฐ์€ ๊ต‰์žฅํžˆ ๋Š๋ ค์งˆ ๊ฒƒ์ด๋‹ค. ๋Š๋ฆฐ ์Šค๋งˆํŠธํฐ์„ ๋ˆ„๊ฐ€ ์‚ด๊นŒ? ๋‹ค์ค‘ ์Šค๋ ˆ๋”ฉ ๊ตฌํ˜„(๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ)์€ ์‚ฌ์‹ค ์ƒ์˜ ์˜๋ฌด๋ผ๋Š” ์ด์•ผ๊ธฐ๋‹ค.
 

#2 ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ์˜ ํ•„์š”์„ฑ ์ฒด๊ฐํ•˜๊ธฐ

์ƒ˜ํ”Œ ์•ฑ์„ ํ†ตํ•ด ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ์˜ ํ•„์š”์„ฑ์„ ์ฒด๊ฐํ•ด๋ณธ๋‹ค.
 

#2-1 ์™ธ๊ด€

Count++ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ค‘์•™์˜ ์ˆซ์ž๊ฐ€ 1์”ฉ ์ปค์ง„๋‹ค. ์ƒ๋‹จ์˜ Download Data ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ฐ์ดํ„ฐ ๋‹ค์šด๋กœ๋“œ๋ฅผ ํ‰๋‚ด๋‚ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
 

#2-2 MainActivity.kt

// package com.example.coroutinesbasics

import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    private var count = 0
    private lateinit var tvCount: TextView
    private lateinit var btnCount: Button
    private lateinit var btnDw: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        tvCount = findViewById(R.id.tvCount)
        btnCount = findViewById(R.id.btnCount)
        btnDw = findViewById(R.id.btnDw)

        btnCount.setOnClickListener {
            tvCount.text = count++.toString()
        }

        btnDw.setOnClickListener {
            downloadData()
        }
    }

    private fun downloadData() {
        for (i in 1..100000) {
            Log.i("interfacer_han", "Downloading data $i in ${Thread.currentThread().name}")
        }
    }
}

Downlad Button ํด๋ฆญ ๋ฆฌ์Šค๋„ˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๋™์ž‘์„ '๊ทธ๋Ÿด ๋“ฏ ํ•˜๊ฒŒ ํ‰๋‚ด๋‚ด์–ด' ๊ตฌํ˜„ํ•œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ง ๋‹ค์šด๋กœ๋“œ ํ•˜๋Š” ๋Œ€์‹ , 10๋งŒ๊ฐœ์˜ Log๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค. ์–ด๋Š ์ชฝ์ด๋“  ์˜ค๋žœ ์‹œ๊ฐ„๋™์•ˆ ์‹คํ–‰๋˜๋Š” ์ž‘์—…์ด๋ผ๋Š” ์ ์—์„œ ์œ ์‚ฌํ•˜๋‹ค.
 

#2-3 ๋™์ž‘ ํ™•์ธ

์›๋ž˜๋ผ๋ฉด Count++ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๊ทธ ์œ„์— ์žˆ๋Š” ์ˆซ์ž๊ฐ€ ์ฆ‰์‹œ ์ฆ๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ, Download Data ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ ์ฆ‰์‹œ ๋ฐ”๋กœ Count++ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, ๋ช‡ ์ดˆ ์žˆ๋‹ค๊ฐ€ Count๊ฐ€ ์ฆ๊ฐ€๋œ๋‹ค. ์‹ฌ์ง€์–ด ์ค‘๊ฐ„์— ์•ˆ๋“œ๋กœ์ด๋“œ ์‹œ์Šคํ…œ์ด ์•ฑ์˜ ๋ฒ„๋ฒ…์ž„์„ ๊ฐ์ง€ํ•˜๊ณ , ์•ฑ์„ ์ข…๋ฃŒ์‹œํ‚ฌ ์ง€ ๋Œ€๊ธฐํ•  ์ง€๋ฅผ ๊ณ ๋ฅด๋Š” ๋ฉ”์‹œ์ง€๊นŒ์ง€ ๋œฌ๋‹ค.
 
์ด์œ ๋Š” ์ด๋ ‡๋‹ค. Download Data ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์‹คํ–‰๋˜๋Š” 10๋งŒ๊ฐœ์˜ Log๋Š” "main" ์Šค๋ ˆ๋“œ์—์„œ ๋ฐœ์ƒ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ™์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์ธ Count++ ๋ฒ„ํŠผ์˜ ๋™์ž‘์ด ์šฐ์„ ์ˆœ์œ„์—์„œ ๋ฐ€๋ ค๋‚œ ๊ฒƒ์ด๋‹ค.

์ด๋Ÿฐ ์•ฑ์„ PlayStore์— ์˜ฌ๋ฆฐ๋‹ค๋ฉด ์ข‹์€ ํ‰๊ฐ€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์—†์„ ๊ฒƒ์ด๋‹ค.
 

#3 Coroutines ์‚ฌ์šฉํ•˜๊ธฐ

#2๋ฅผ ์ˆ˜์ •ํ•ด Coroutines์œผ๋กœ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ๊ตฌํ˜„ํ•˜๊ณ , ๊ฒฐ๊ณผ์ ์œผ๋กœ ์•ฑ์˜ ๋ฒ„๋ฒ…์ž„์„ ํ•ด์†Œํ•ด๋ณธ๋‹ค.
 

#3-1 build.gradle.kts (Module)์—์„œ Coroutines ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‹ค์šด๋กœ๋“œ

plugins {
    ...
}

android {
    ...
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0-RC2") // ์ฝ”๋ฃจํ‹ด
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0-RC2") // ์ฝ”๋ฃจํ‹ด

    ...
}

์—ฌ๊ธฐ์— ์žˆ๋Š” implementation ๊ตฌ๋ฌธ์„ ์ฐธ์กฐํ•˜์—ฌ, ์ฝ”ํ‹€๋ฆฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ชจ๋“ˆ ์ˆ˜์ค€ build.gradle.kts์—์„œ ๋‹ค์šด๋กœ๋“œํ•œ๋‹ค. ์ด์ œ๋ถ€ํ„ฐ ์ฝ”๋ฃจํ‹ด API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.
 

#3-2 MainActivity.kt

...

class MainActivity : AppCompatActivity() {
    ...
    private lateinit var btnDwCoroutine: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        btnDwCoroutine = findViewById(R.id.btnDwCoroutine)

        ...
        
        btnDwCoroutine.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                downloadData()
            }
        }
    }

    ...
}

Download Data ๋ฒ„ํŠผ ์™ธ์— Download With Coroutine ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค. CoroutineScope๋Š” ์ฝ”๋ฃจํ‹ด์˜ ์ฝ”๋“œ๊ฐ€ ๋‹ด๊ธธ ์˜์—ญ(Scope)๋ฅผ ์ •ํ•˜๊ธฐ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋‹ค. ์ฆ‰, ์ฝ”๋ฃจํ‹ด์˜ ํฌ์žฅ์ง€๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๋งค์šฐ ๊ธฐ์ดˆ์ ์ธ Coroutines ๊ตฌ๋ฌธ์ด๋‹ค. ๋” ์ž์„ธํ•œ ์„ค๋ช… ํ˜น์€ ๋ฌธ๋ฒ•์€ ์—ฌ๊ธฐ์™€ ์—ฌ๊ธฐ์„œ ํ™•์ธํ•œ๋‹ค. ๋ณธ ๊ฒŒ์‹œ๊ธ€์—์„  ๋ฌธ๋ฒ•๋ณด๋‹จ ์ฝ”๋ฃจํ‹ด์ด ์žˆ๊ณ  ์—†๊ณ ์˜ ์ฐจ์ด๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•œ๋‹ค.
 

#3-3 ๋™์ž‘ ํ™•์ธ

#2-3์™€ ๋™์ผํ•œ ์ƒํ™ฉ์—์„œ ์ด๋ฒˆ์—” Download Data ๋ฒ„ํŠผ ๋Œ€์‹  Download With Coroutine ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋ณด์•˜๋‹ค. ๋ฒ„๋ฒ…์ž„์ด ์ „ํ˜€ ์—†์ด ๋ฐ”๋กœ ์ˆซ์ž๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.
 

#4 ์š”์•ฝ

์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ํ™œ์šฉ์€ ํ•„์ˆ˜๋‹ค. ์ฝ”๋ฃจํ‹ด์€ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
 

#5 ์™„์„ฑ๋œ ์•ฑ

 

android-practice/coroutines/CoroutinesBasics at master ยท Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com