[ํฐ๋] ํฐ๋ Android Kotlin Coroutine์ ์จ์ผ ํ๋ ์ด์ ์ ์ฌ์ฉ๋ฒ
์๋
ํ์ธ์ ํฐํ์ ์๋๋ก์ด๋ ๊ฐ๋ฐ์ ์ ์ฑํ์
๋๋ค๐๐๐๐
์ด๋ฒ์๋ ๋ฉ๊ฒ๋ง ๋๊ปด์ง๊ณ ์ฒ์ ๋ณด๋ฉด ์ด๋ ต๊ฒ ๋๊ปด์ง๋ ๊ฐ๋ ์ ์ ๋ ์ ๋ฆฌํ๋ฉด์ ํ์ธํ๋ ์๊ฐ์ ๊ฐ์ ธ๋ณด๋ ค๊ณ ํฉ๋๋ค!
์๋๋ก์ด๋๋ฅผ ๊ฐ๋ฐํ๋ ์ฌ๋์ด๋ผ๋ฉด Coroutine์ ๋ค์ด๋ณด์ จ์ ๊ฑฐ๋ผ ์๊ฐํฉ๋๋ค.
Coroutine์ ๋ฌด์์ด๊ณ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ๋ฌด์์ธ์ง ์์๋ณด๋ฌ ๊ฐ๋ด ์๋ค.๐จ
๐ฉ๐ป Coroutine? ์ฝ๋ฃจํด์ด ๋ญ์ผ?
์ฝ๋ฃจํด์ ์์ถํด์ ์ค๋ช ํ์๋ฉด ์ฌ์ด ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ก ์ฝ๋๋ฅผ ์ฝ๊ณ ๊ฐ๋ ์ฑ ๋๊ฒ ์์ฑํ ์ ์๋ ํน์ง์ ๊ฐ์ง ๋๊ตฌ์ ๋๋ค.
์ฝ๋ฃจํด(Coroutines)์ Kotlin์์ ๋์์ฑ์ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์๊ฒ ํ๋ ๊ธฐ๋ฅ ์ค ํ๋์ ๋๋ค.
์ฝ๋ฃจํด์ ๋น๋๊ธฐ ์ฝ๋๋ฅผ ๋๊ธฐ์ ์ผ๋ก ์์ฑํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ฒ ํ์ฌ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ๋์ด๊ณ , ๋ณต์กํ ์ฝ๋ฐฑ์ด๋ ๋ค๋ฅธ ๋์์ฑ ํจํด์ ๋นํด ์ฝ๊ฒ ์์ฑํ ์ ์๊ฒ ํฉ๋๋ค.
์ฝ๋ฃจํด์ ์ํคํผ๋์์์ ์๋์ ๊ฐ์ด ์ ์ํ๊ณ ์์ต๋๋ค.
์คํ์ ์ง์ฐ๊ณผ ์ฌ๊ฐ๋ฅผ ํ์ฉํจ์ผ๋ก์จ, ๋น์ ์ ์ ๋ฉํฐํ์คํน์ ์ํ
์๋ธ ๋ฃจํด์ ์ผ๋ฐํํ ์ปดํจํฐ ํ๋ก๊ทธ๋จ ๊ตฌ์ฑ์์
*๋น์ ์ ์ ๋ฉํฐํ์คํน
- ์ ์ ํ : task๊ฐ cpu๋ฅผ ์ฌ์ฉํ๊ณ ์๋๋ผ๋ ์ด๊ฑธ ๋บ์ด์ ์ค๋จ์ํฌ ์ ์์ต๋๋ค.
- ๋น์ ์ ํ : task๊ฐ cpu ์ฌ์ฉ๊ถ์ ํ ๋น๋ฐ์์ ๋ ์ค์ผ์ค๋ฌ๊ฐ ๊ฐ์ ๋ก cpu์ฌ์ฉ๊ถ์ ๋บ์ ์ ์์ต๋๋ค.
์ฝ๋ฃจํด์ ๋ณํ์ฑ์ ์ ๊ณตํ์ง๋ง, ๋ณ๋ ฌ์ฑ์ ์ ๊ณตํ์ง ์์ต๋๋ค.
- ๋ณํ์ฑ(Concurrency) : ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋ณ๋ ฌ๋ก ์์ ์ด ์คํ๋๋ ๊ฒ์ ์๋์ง๋ง ๊ทธ๋ ๊ฒ ๋ณด์ด๋ ๊ฒ (๋ ผ๋ฆฌ์ ์ผ๋ก ๋ณ๋ ฌ๋ก ์์ ์ด ์คํ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๋ ์ฒ๋ฆฌ = ๋์์ฑ)
- ๋ณ๋ ฌ์ฑ(Parallelism) : ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋ณ๋ ฌ๋ก ์์ ์ด ์คํ๋๋ ๊ฒ (์ค์ ๋ก ๋์์ ์์ ์ด ์ฒ๋ฆฌ)
๐ฉ๐ป ์ฝ๋ฃจํด์ ์ ์จ์ผ ํ๋๋ฐ?
Task1, Task2, Task3 ์ด ์๋ฃ๋์ด์ผ ์ฌ์ฉ์๊ฐ ๊ฐ์ ๋ณด๋ ค๋ฉด ์ด 28์ด๊ฐ ๊ฑธ๋ฆฌ๊ฒ ๋ฉ๋๋ค.(11+14+3 = 28)
์๋ฒ์์ ํฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒฝ์ฐ๋ฅผ ์๊ฐํด๋ณด๋ฉด ์ฌ์ฉ์ฑ์ด ๊ต์ฅํ ๋จ์ด์ง๊ฒ ๋๊ฒ ์ฃ ?
๋๋ฌด ๋ง์ด ๊ธฐ๋ค๋ฆฌ๊ฒ ๋๋ค๋ฉด ์ผ๋ถ ์ฌ์ฉ์๋ ํ๋ฅผ ๋ด๋ฉฐ ์ฑ์ ์ ๊ฑฐํ ์๋ ์์ต๋๋ค ๐คฏ
์ด ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ณ๋ ฌ๋ก ๋ค์ด๋ก๋ํ ์ ์๋ค๋ฉด ์ด๋จ๊น์? ์ด 14์ด๋ฉด ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
ํ์ง๋ง ๋น ๋ฅด๋ค๊ณ ๋ชจ๋ ๊ฒ ์ข์ ๊ฑด ์๋๋๋ค!
๊ตฌ์กฐํ๋์ง ์์ ๋์์ฑ์ ๋ชจ๋ ์์ ์ ์๋ฃ๋ฅผ ๋ณด์ฅํ์ง ์๊ธฐ ๋๋ฌธ์ ์ค์ ๋ก ์์ ์ฝ๋ฃจํด์ด ์๋ฃ๋ ํ์๋
ํ์ ์ฝ๋ฃจํด์ ๊ณ์ ์คํ๋ ์ ์์ต๋๋ค.
์ด๋ฐ ๊ฒฝ์ฐ๋ ์์ธกํ์ง ๋ชปํ ๊ฐ ๋๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ฝ๋ฃจํด์ ๋น๋๊ธฐ ๋น๋๋ฅผ ์ฌ์ฉํ๊ณ ๋ฐํ๊ฐ์ wait ํจ์ ํธ์ถ์ ์ฌ์ฉํ๋ ๋ฑ์ ๋ฐฉ์์ผ๋ก ํด๊ฒฐํ ์ ์์ต๋๋ค.
์ค๋ช ์ ์ํด ์ฝ๋ฃจํด์ ๋ํ ๋ ์์ธํ ๋ด์ฉ์ผ๋ก ์ด์ด๊ฐ๊ฒ ์ต๋๋ค.
๐ฉ๐ป ์ฝ๋ฃจํด์ ์ด๋ป๊ฒ ์ฌ์ฉํ์ง?
์์กด์ฑ ์ฃผ์ build.gradle
coroutine_version = "1.6.0"
//coroutine
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
์ฝ๋ฃจํด์ ์ฃผ์ ์์:
- suspend : suspend fun์ผ๋ก ์ ์ธํ ํจ์๋ ์ฝ๋ฃจํด ๋ด์์๋ง ํธ์ถ๋ ์ ์์ต๋๋ค. ์ด ํจ์๋ ์ค๊ฐ์ ์ผ์ ์ค์ง๋ ์ ์๊ณ ๋์ค์ ๋ค์ ์์๋ ์ ์์ต๋๋ค.
- launch: ์ฝ๋ฃจํด์ ์์ํ๊ธฐ ์ํ ํจ์์ ๋๋ค. ์ฃผ๋ก ๋น๋๊ธฐ ์์ ์ ์ฌ์ฉ๋ฉ๋๋ค.
- async/await: ๋น๋๊ธฐ ์์ ์ ์์ํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ธฐ ์ํ ํจํด์ ๋๋ค.
- runBlocking: ์ฃผ๋ก ํ ์คํธ๋ ๋ฉ์ธ ํจ์์์ ์ฌ์ฉ๋๋ ๋ธ๋กํน ํจ์์ ๋๋ค.
์ฝ๋ฃจํด Dispatchers:
์ฝ๋ฃจํด์ ํน์ CoroutineDispatcher์์ ์คํ๋ฉ๋๋ค. ์ฉ๋์ ๋ง๋ Dispatcher๋ฅผ ์ฌ์ฉํด์ ์์ ํ๋ฉด ๋ฉ๋๋ค.
- Dispatchers.Main: ์ฃผ UI ์ค๋ ๋์์ ์์ ์ ์คํํ๊ธฐ ์ํ ๋์คํจ์ฒ์ ๋๋ค. UI ์ ๋ฐ์ดํธ๋ ์ ๋๋ฉ์ด์ ๋ฑ์ ์์ ์ ์ ํฉํฉ๋๋ค. Android์์๋ Dispatchers.Main์ด ๋ฉ์ธ ์ค๋ ๋์ ์ฐ๊ฒฐ๋ฉ๋๋ค. => UI ์์
- Dispatchers.IO :์ ์ถ๋ ฅ ์์ (๋คํธ์ํฌ ํธ์ถ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ๋ฑ)์ ์ต์ ํ๋ ๋์คํจ์ฒ์ ๋๋ค. => ๋คํธ์ํฌ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
- Dispatchers.Default: CPU ์ง์ค์ ์ธ ์์ ์ ์ต์ ํ๋ ๋์คํจ์ฒ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ํฐ ๋ฆฌ์คํธ์ ์ ๋ ฌ์ด๋ ๊ณ์ฐ ์์ ๋ฑ์ ์ฌ์ฉ๋ฉ๋๋ค. => CPU ์ง์ค์ ์ธ ์์
- Dispatchers.Unconfined: ํน์ ์ค๋ ๋์ ์ข ์๋์ง ์๋ ๋์คํจ์ฒ์ ๋๋ค. ์ฃผ๋ก ํ ์คํธ ์ฉ๋๋ก ์ฌ์ฉ๋ฉ๋๋ค. => ํ ์คํธ
ex. launch๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๋ฃจํด์ ์์ํ๊ณ , Dispatchers.IO์์ ๋คํธ์ํฌ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ, Dispatchers.Main์์ UI๋ฅผ ์ ๋ฐ์ดํธํ๋ ๊ฒฝ์ฐ
suspend fun fetchDataFromNetwork(): String { //suspend fun -> ์ฝ๋ฃจํด๋ด์์๋ง ํธ์ถ
// ๋คํธ์ํฌ ๋๋ DB์ ๊ฐ์ ๋น๋๊ธฐ ์์
์ ๊ฐ์
delay(1000) // ๋น๋๊ธฐ ์ง์ฐ ํจ์๋ก 1์ด ์ง์ฐ์ ๋ํ๋
๋๋ค.
return "๋ฐ์ดํฐ ๋ฐ์์ค๊ธฐ ์๋ฃ"
}
fun updateUI(data: String) {
println("UI ์
๋ฐ์ดํธ: $data")
}
fun main() {
runBlocking { // ํ
์คํธ ๋ฐ ์์ ๋ชฉ์ ์ผ๋ก runBlocking ์ฌ์ฉ
launch(Dispatchers.IO) {
// IO ์์
์ํ
val data = fetchDataFromNetwork()
withContext(Dispatchers.Main) {
// UI ์
๋ฐ์ดํธ
updateUI(data)
}
}
}
}
๐ฉ๐ป ์ฝ๋ฃจํด ์ ์ฉํ๊ธฐ
์ฝ๋ฃจํด์ ๋ํ ๊ธฐ๋ณธ ์ดํด๊ฐ ๋์๋ค๋ฉด ์ค์ ๋ก ์ฐ๋ฆฌ ์ฝ๋์์ ์ฝ๋ฃจํด์ ์์ฑํ๊ธฐ ์ํด ๋ ์์๋ณด๊ฒ ์ต๋๋ค.
(๋ด์ฉ์ด ๊น๋๋ค.. ์๋์ ์์ฝ๋ณธ ์์ต๋๋ค.)
- withContext: ์ฃผ๋ก ํ์ฌ ์ฝ๋ฃจํด์ ์คํ ์ปจํ ์คํธ๋ฅผ ์์๋ก ๋ณ๊ฒฝํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- CoroutineScope: ์ฝ๋ฃจํด์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๋ช ์์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ์ ์ดํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
- ViewModelScope: Android ViewModel์ ๋ด์ฅ๋ ์ค์ฝํ๋ก, ViewModel์ ์๋ช ์ฃผ๊ธฐ์ ์๋์ผ๋ก ์ฐ๊ฒฐ๋ฉ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก withContext๋ ์์ ์ ์คํ ์ปจํ ์คํธ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐ, CoroutineScope์ ViewModelScope๋ ์ฝ๋ฃจํด์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐ ์ด์ ์ ๋ก๋๋ค.
1. withContext
- ์ค๋ช : withContext๋ ์ฝ๋ฃจํด์ ์คํ ์ปจํ ์คํธ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ์ฃผ๋ก ๋ค๋ฅธ Dispatcher๋ก ์ฝ๋ฃจํด์ ์ด๋ํ์ฌ ์์ ์ ์คํํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
suspend fun fetchDataFromDatabase(): Data {
return withContext(Dispatchers.IO) {
// DB๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด
database.getData()
}
}
2. CoroutineScope
- ์ค๋ช : CoroutineScope๋ ์ฝ๋ฃจํด์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ์ ์ดํ๊ณ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ํน์ ์ค์ฝํ ๋ด์์ ์์๋ ์ฝ๋ฃจํด์ ํด๋น ์ค์ฝํ์ ์๋ช ์ฃผ๊ธฐ์ ์ข ์๋ฉ๋๋ค.
- ์์: Activity๋ Fragment์ ์๋ช ์ฃผ๊ธฐ์ ์ฝ๋ฃจํด์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๋๊ธฐํํ๊ณ ์ ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
class MyActivity : AppCompatActivity(), CoroutineScope by CoroutineScope(Dispatchers.Main) {
override fun onDestroy() {
super.onDestroy()
cancel() // ๋ชจ๋ ์ฝ๋ฃจํด ์ทจ์
}
fun loadData() {
launch {
// ๋ฐ์ดํฐ ๋ก๋ฉ ๋ก์ง
}
}
}
3. ViewModelScope
- ์ค๋ช : ViewModelScope๋ Android์ ViewModel๊ณผ ์ฐ๊ฒฐ๋ ์ฝ๋ฃจํด ์ค์ฝํ์ ๋๋ค. ViewModel์ด ํด๋ฆฌ์ด๋ ๋ ํด๋น ์ค์ฝํ ๋ด์ ๋ชจ๋ ์ฝ๋ฃจํด๋ ์๋์ผ๋ก ์ทจ์๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ฉ๋ชจ๋ฆฌ ๋์๋ ํ์ ์๋ ์์ ์ ์คํ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
- ์์: ViewModel ๋ด์์ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค.
- kotlinCopy code class MyViewModel : ViewModel() { fun loadData() { viewModelScope.launch { // ๋ฐ์ดํฐ ๋ก๋ฉ ๋ก์ง } } }
class MyViewModel : ViewModel() {
fun loadData() {
viewModelScope.launch {
// ๋ฐ์ดํฐ ๋ก๋ฉ ๋ก์ง
}
}
}
async await
async์ await๋ ์ฝ๋ฃจํด์์ ๋น๋๊ธฐ ์์ ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์ฌ์ฉ๋๋ ํจํด์ ๋๋ค. ์ฃผ๋ก ์ฌ๋ฌ ๋น๋๊ธฐ ์์ ์ ๋์์ ์์ํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์์ ์ฒ๋ฆฌํ ๋ ์ ์ฉํฉ๋๋ค.
- async: ์ด ํจ์๋ ์ฃผ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๋น๋๊ธฐ ์์ ์ ์์ํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค. async๋ก ์์๋ ์ฝ๋ฃจํด์ Deferred ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ฉฐ, ์ด ๊ฐ์ฒด๋ฅผ ํตํด ๋์ค์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
- await: Deferred ๊ฐ์ฒด์์ ์ค์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์ฌ์ฉ๋๋ ํจ์์ ๋๋ค. ์ด ํจ์๋ ํด๋น ๋น๋๊ธฐ ์์ ์ด ์๋ฃ๋ ๋๊น์ง ํ์ฌ์ ์ฝ๋ฃจํด ์คํ์ ์ผ์ ์ค๋จํฉ๋๋ค.
์ฌ์ฉ ์:
suspend fun fetchData(): Deferred<String> = coroutineScope {
async {
delay(1000) // 1์ด ๋์ ์ง์ฐ
"Data Fetched"
}
}
suspend fun main() {
val deferredData: Deferred<String> = fetchData()
println("Doing some other work...")
val data: String = deferredData.await() // ์ฌ๊ธฐ์์ ์ฝ๋ฃจํด์ fetchData์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆฝ๋๋ค.
println(data) // "Data Fetched"๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
}
OnCleared
ViewModel ๋ด๋ถ์์ ์ฝ๋ฃจํด์ ์ฌ์ฉํ ๋ onCleared๋ฅผ ์ค๋ฒ๋ผ์ด๋ํด์ผ ํฉ๋๋ค.
- ์์ ํด์ : ViewModel์ด ๋ ์ด์ ํ์ํ์ง ์์ ๋, ์ฆ ๊ด๋ จ๋ UI ์ปดํฌ๋ํธ(์: ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ)๊ฐ ํ๊ดด๋์์ ๋ onCleared๋ ํธ์ถ๋ฉ๋๋ค. ์ด๋ ์คํ ์ค์ธ ์ฝ๋ฃจํด์ ์ทจ์ํ์ฌ ๋ถํ์ํ ์์ ์ ์ค๋จํ๊ณ ๊ด๋ จ ์์์ ํด์ ํ๊ธฐ ์ํด onCleared๋ฅผ ์ค๋ฒ๋ผ์ด๋ํฉ๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง: ์ฝ๋ฃจํด์ด ๊ณ์ ์คํ๋๋ฉด์ ViewModel์ด ํ๊ดด๋ ํ์๋ ์ฐธ์กฐ๊ฐ ์ ์ง๋ ๊ฒฝ์ฐ ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ฝ๋ฃจํด์ ์ ์ ํ ์ทจ์ํจ์ผ๋ก์จ ์ด๋ฌํ ๋์๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
⇒ ViewModel์์ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ViewModelScope๋ฅผ ์ฌ์ฉํฉ๋๋ค.
Structured Concurrency
์ฝ๋ฃจํด์ Structured Concurrency, Unstructured Concurrency ๋ ๊ฐ์ง๋ก ์ด๋ฃจ์ด์ ธ ์์ต๋๋ค.
๊ทธ ์ค Unstructured Concurrency๋ฅผ ์ฐ๋ฉด ์์ธ๊ฐ ์ผ์ด๋๋ ๊ฒฝ์ฐ๊ฐ ์์ด Structured Concurrency๋ฅผ ์จ์ผํฉ๋๋ค.์ด ๋ฌธ์ ๋ CoroutineScope๋ฅผ ์ฌ์ฉํ๋ฉด ํด๊ฒฐํ ์ ์์ต๋๋ค.
Structured Concurrency๋ฅผ ์ฌ์ฉํ๋ฉด ์ฝ๋ฃจํด์ด ์๋์ผ๋ก ์ทจ์๋์ด ์์ธ ์ฒ๋ฆฌ๊ฐ ๋์ฑ ์์ ํ๊ฒ ์ด๋ฃจ์ด์ง๋๋ค. CoroutineScope๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ ํ CoroutineContext๋ฅผ ์ค์ ํ๊ณ , launch๋ async๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๋ฃจํด์ ์คํํ๋ ๊ฒ์ด ์ข์ต๋๋ค. Unstructured Concurrency๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ํ ์ถ๊ฐ์ ์ธ ์ฝ๋๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ ์ฃผ์ํด์ผ ํฉ๋๋ค.
๐ฉ๐ป ์ฝ๋ฃจํด ์์ฝ!
์ง์ ํ๋ ํ๋ ํผ๊ทธ๋ง๋ก ์์ฝํ ๋ด์ฉ์ ๋๋ค! ๐ซ
๐ฉ๐ป ๋ง์น๋ฉฐ
์ด๋ฒ ๊ธ์ ์ด์ผ๊ธฐ๊ฐ ๋ฐฉ๋ํด์ ๋ฐฉ์ ๋์์ต๋๋ค...๐ชซ
์ญ์ ์ง์์ ๊น์ด์ง์๋ก ์ด๋ ค์ด ๊ฑฐ ๊ฐ์ต๋๋ค...!
์ค๋ ํ๋ฃจ ๋ชจ๋ ํ๋ณตํ ์๊ฐ ๋ณด๋ด์ จ์ผ๋ฉด ์ข๊ฒ ์ต๋๋ค ๐
๋ค์ ๊ธ๋ ๊ธฐ๋ ๋ถํ๋๋ฆฌ๊ฒ ์ต๋๋ค:)๐ค
๊ฐ์ฌํฉ๋๋ค!!
โฌ๏ธ ์ง๊ธ ๊ตฌ๊ธ ํ๋ ์ด ์คํ ์ด์์ ํฐ๋ ๋ค์ด ๋ฐ๊ธฐ
https://play.google.com/store/apps/details?id=com.team7.tikkle&hl=ko-KR
๐ฉ Contact : wjdcogus6@gmail.com
๐ฒ SNS : https://www.instagram.com/chaeehyuny/