๐ก ์ฝ๋ฃจํด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ๋ kotlin.coroutines์์ ์ฌ์ฉํ๋ ๋ค์ํ ์ฝ๋ฃจํด ๋น๋์ ๋ํ์ฌ ํ์ตํ์์ต๋๋ค.
์ฝ๋ฃจํด ๋น๋
- ์ค๋จ ํจ์๋ ์ปจํฐ๋ด์์ด์
๊ฐ์ฒด๋ฅผ ๋ค๋ฅธ ํจ์๋ก ์ ๋ฌํด์ผ ํฉ๋๋ค.
- ์ฆ ์ค๋จ ํจ์ → ์ผ๋ฐ ํจ์ ํธ์ถ์ ๊ฐ๋ฅํ์ง๋ง, ์ผ๋ฐ ํจ์ → ์ค๋จ ํจ์ ํธ์ถ์ ๋ถ๊ฐ๋ฅํฉ๋๋ค.
- ๋ชจ๋ ์ค๋จ ํจ์๋ ๋ ๋ค๋ฅธ ์ค๋จ ํจ์์ ์ํด ํธ์ถ๋์ด์ผ ํ๋ฉฐ, ์ด๋ ์์ ํธ์ถํ ์ค๋จ ํจ์ ๋ํ ๋ง์ฐฌ๊ฐ์ง์ ๋๋ค.
- ์ค๋จ ํจ์๋ฅผ ์ฐ์์ผ๋ก ํธ์ถํ๋ฉด ์์๋๋ ์ง์ ์ด ๋ฐ๋์ ์์ต๋๋ค.
- ์ฝ๋ฃจํด ๋น๋(coroutine builder)๊ฐ ๊ทธ ์ญํ ์ ํ๋ฉฐ, ์ผ๋ฐ ํจ์์ ์ค๋จ ๊ฐ๋ฅํ ์ธ๊ณ๋ฅผ ์ฐ๊ฒฐ์ํค๋ ๋ค๋ฆฌ ์ญํ ์ ํฉ๋๋ค.
- launch
- runBlocking
- async
launch ๋น๋
- launch๋ thread ํจ์๋ฅผ ํธ์ถํ์ฌ ์๋ก์ด ์ค๋ ๋๋ฅผ ์์ํ๋ ๊ฒ๊ณผ ๋น์ทํ๋ฉฐ, ๋ถ๊ฝ๋์ด๋ฅผ ํ ๋ ๋ถ๊ฝ์ด ํ๋ ์๋ก ๊ฐ์ ํผ์ง๋ ๊ฒ์ฒ๋ผ ๋ณ๊ฐ๋ก ์คํ๋ฉ๋๋ค.
fun main() {
GlobalScope.launch {
delay(1000L)
println("launch!")
}
GlobalScope.launch {
delay(1000L)
println("launch!")
}
GlobalScope.launch {
delay(1000L)
println("launch!")
}
println("load")
Thread.sleep(2000L)
}
//load
// 1000L(1์ด) ํ..
//launch!
//launch!
//launch!
- ์ ๋ฐฉ์์ ์๋๋ก์ด๋๋ ๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์
๋ฐฉ์์์ ์ข์ ๋ฐฉ์์ด ์๋์ง๋ง, ์ฝ๋ฃจํด ๋น๋์ ์ํด ์์๋๋ ๊ฒ์ ๋ณผ ์ ์๋ ์์์
๋๋ค.
- ์ค์ ํ์ ์์๋ GlobalScope์ ์ฌ์ฉ์ ์ง์ํด์ผ ํฉ๋๋ค.
- ์ฝํ๋ฆฐ ์ธ์ด๊ฐ ๋น๋๋ฅผ ์๋์ผ๋ก ์์ฑํด์ฃผ๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
- Thread.sleep(2000L)์ ์ค ์ด์ ๋ ์๋์ ๊ฐ์ต๋๋ค.
- ์ค๋ ๋๋ฅผ ์ ๋ค๊ฒ ํ์ง ์์ผ๋ฉด ๋ฉ์ธ ํจ์๋ ์ฝ๋ฃจํด์ ์คํํ์๋ง์ ๋๋๋ฒ๋ฆฌ๋ฉฐ, ์ฝ๋ฃจํด์ด ์ผ์ ํ ๊ธฐํ๋ฅผ ์ฃผ์ง ์์ต๋๋ค.
- delay(1000L)์ ์ค๋ ๋๋ฅผ ๋ธ๋ก์ํค์ง ์๊ณ , ์ฝ๋ฃจํด์ ๋ธ๋ก์ํต๋๋ค.
CoroutineScope
- launch ํจ์๋ CoroutineScope ์ธํฐํ์ด์ค์ ํ์ฅ ํจ์์ ๋๋ค.
- CoroutineScope ์ธํฐํ์ด์ค๋ ๋ถ๋ชจ ์ฝ๋ฃจํด๊ณผ ์์ ์ฝ๋ฃจํด ์ฌ์ด์ ๊ด๊ณ ์ ๋ฆฝ์ ์ํ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
- ๊ตฌ์กฐํ๋ ๋์์ฑ(structued concurrency)์ ๊ฐ์ง๊ฒ ๋ฉ๋๋ค.
- ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์ ๋ ์์ ํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ฝ๊ฒ ๋ง๋๋ ๋์์ธ ํจํด
- ํ๋ก๊ทธ๋๋ฐ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌํ๊ณ , ์์ธก ๊ฐ๋ฅํ ๋ฐฉ์์ผ๋ก ๋์์ฑ์ ๋ค๋ฃธ
- ๊ตฌ์กฐํ๋ ๋์์ฑ์ ์ฌ์ฉํ๋ฉด ์ ์ฝ๋์ Thred.sleep ์์ด ๋์ํ ์ ์์
launch๊ฐ ์๋ํ๋ ๋ฐฉ์
- ์ด๋ ๋ฐ๋ชฌ ์ค๋ ๋์ ์ด๋ ์ ๋ ๋น์ทํ์ง๋ง ํจ์ฌ ๊ฐ๋ณ์ต๋๋ค.
- ๋ฐ๋ชฌ ์ค๋ ๋ : ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋์๊ฐ๋ฉฐ, ์ฐ์ ์์๊ฐ ๋ฎ์ ์ค๋ ๋
- ๋ธ๋กํน๋ ์ค๋ ๋๋ฅผ ์ ์งํ๋ ๊ฒ์ ๋น์ฉ์ด ๋ค์ง๋ง, ์ค๋จ๋ ์ฝ๋ฃจํด์ ์ ์งํ๋ ๊ฒ์ ๊ฐ๋ณ์ต๋๋ค.
- ๋ ๋ค ๋ณ๊ฐ์ ์์ ์ ์์ํ๋ฉฐ ์์ ํ๋ ๋์ ํ๋ก๊ทธ๋จ์ด ๋๋๋ ๊ฑธ ๋ง์ ์ ์์ต๋๋ค.
runBlocking ๋น๋
- ์ฝํฌํด์ด ์ค๋ ๋๋ฅผ ๋ธ๋กํนํ์ง ์๊ณ ์์ ์ ์ค๋จ์ํค๊ธฐ๋ง ํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด์ง๋ง, ๋ธ๋กํน์ด ํ์ํ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
- ๋ฉ์ธ ํจ์์ ๊ฒฝ์ฐ ํ๋ก๊ทธ๋จ์ ๋๋ฌด ๋นจ๋ฆฌ ๋๋ด์ง ์๊ธฐ ์ํด ์ค๋ ๋๋ฅผ ๋ธ๋กํนํด์ผ ํฉ๋๋ค.
- ์ด ๋ runBlocking์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ์๋ก์ด ์ฝ๋ฃจํด์ ์คํํ ๋ค ์๋ฃ๋ ๋๊น์ง ํ์ฌ ์ค๋ ๋๋ฅผ ์ค๋จ ๊ฐ๋ฅํ ์ํ๋ก ๋ธ๋กํนํฉ๋๋ค.
runBlocking ๋์ ๋ฐฉ์
- runBloking ๋ด๋ถ์์ delay(1000L)์ ํธ์ถํ๋ฉด Thread.sleep(1000L)๊ณผ ๋น์ทํ๊ฒ ๋์ํฉ๋๋ค.
fun main() {
runBlocking {
delay(1000L)
println("load")
}
runBlocking {
delay(1000L)
println("load")
}
println("end")
}
// (1์ด ํ_)
// load
// (1์ด ํ_)
// load
// end
fun main() {
Thread.sleep(1000L)
println("load")
Thread.sleep(1000L)
println("load")
println("end")
}
// (1์ด ํ_)
// load
// (1์ด ํ_)
// load
// end
runBlocking์ด ํ์ํ ๊ฒฝ์ฐ
- ํ๋ก๊ทธ๋จ์ด ๋๋๋ ๊ฑธ ๋ฐฉ์งํ๊ธฐ ์ํด ์ค๋ ๋๋ฅผ ๋ธ๋กํนํ ํ์๊ฐ ์๋ ๋ฉ์ธ ํจ์
- ์ค๋ ๋๋ฅผ ๋ธ๋กํนํ ํ์๊ฐ ์๋ ์ ๋ ํ ์คํธ
async ๋น๋
- async ๋น๋๋ launch์ ๋น์ทํ์ง๋ง ๊ฐ์ ์์ฑํ๋๋ก ์ค๊ณ๋์ด ์์ต๋๋ค.
- ๋๋ค ํํ์์ ์ํด ๋ฐํ๋์ด์ผ ํ๋ฉฐ Deferred<T> ํ์ ์ ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ๋ฉฐ, ๊ฐ์ ๋ฐํํ๋ ์ค๋จ ๋ฉ์๋์ธ await๊ฐ ์์ต๋๋ค.
async ๋น๋์ ํน์ง
- launch ๋น๋์ ๋น์ทํ๋ฉฐ, ํธ์ถ๋์๋ง์ ์ฝ๋ฃจํด์ ์์ํฉ๋๋ค.
- ๋ช ๊ฐ์ ์์ ์ ํ๋ฒ์ ์์ํ๊ณ ๋ชจ๋ ๊ฒฐ๊ณผ๋ฅผ ํ๊บผ๋ฒ์ ๊ธฐ๋ค๋ฆด ๋ ์ฌ์ฉํฉ๋๋ค.
- Deferred๋ ๊ฐ์ด ์์ฑ๋๋ฉด ํด๋น ๊ฐ์ ๋ด๋ถ์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ await์์ ๊ฐ์ด ๋ฐํ๋๋ ์ฆ์ ๊ฐ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ๊ฐ์ด ์์ฑ๋๊ธฐ ์ ์ await๋ฅผ ํธ์ถํ ๊ฒฝ์ฐ ๊ฐ์ด ๋์ฌ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๊ฒ ๋ฉ๋๋ค.
fun main() = runBlocking {
val res1 = GlobalScope.async {
delay(1000L)
"Text 1"
}
val res1 = GlobalScope.async {
delay(3000L)
"Text 2"
}
val res1 = GlobalScope.async {
delay(2000L)
"Text 3"
}
println(res1.await())
println(res2.await())
println(res3.await())
// (1์ด ํ)
// Text1
// (2์ด ํ)
// Text2
// Text3
}
- async ๋น๋๋ launch์ ์๋ํ๋ ๋ฐฉ์์ ๋น์ทํ์ง๋ง, ๊ฐ์ด ํ์ํ ๊ฒฝ์ฐ๋ง ํ์ฉํด์ผ ํฉ๋๋ค.
- ๋ํ ๋ ๊ฐ์ง ๋ค๋ฅธ ๊ณณ์์ ๋ฐ์ดํฐ๋ฅผ ์ป์ด์ ํฉ์น๋ ๊ฒฝ์ฐ์ฒ๋ผ, ๋ณ๋ ฌ๋ก ์คํํ ๋ ์ฃผ๋ก ํ์ฉํ๋ค๋ ํน์ง์ด ์์ต๋๋ค.
scope.launch {
val news = async {
newsRepo.getNews()
.sortedByDescending { it.date }
}
val newsSummary = newsRepo.getNewsSummary()
view.showNews(
newsSummary,
news.await()
}
๊ตฌ์กฐํ๋ ๋์์ฑ
- ์ฝ๋ฃจํด์ด GlobalScope์์ ์์๋์๋ค๋ฉด ํ๋ก๊ทธ๋จ์ ํด๋น ์ฝ๋ฃจํด์ ๊ธฐ๋ค๋ฆฌ์ง ์์ต๋๋ค.
- ์ฝ๋ฃจํด์ ์ด๋ค ์ค๋ ๋๋ ๋ธ๋กํ์ง ์๊ธฐ ๋๋ฌธ์ ํ๋ก๊ทธ๋จ์ด ๋๋๋ ๊ฑธ ๋ง์ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
fun main() = runBlocking {
GlobalScope.launch {
delay(1000L)
println("World!")
}
GlobalScope.launch {
delay(2000L)
println("World!")
}
println("Hello,")
}
// Hello,
- launch์ async๋ CoroutineScope์ ํ์ฅํจ์์ด๊ธฐ ๋๋ฌธ์, GlobalScope๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
- block ํ๋ผ๋ฏธํฐ๊ฐ ๋ฆฌ์๋ฒ ํ์ ์ด CoroutineScope์ธ ํจ์ํ ํ์ ์ด๊ธฐ ๋๋ฌธ์ ๋๋ค.
- ํ์ง๋ง ์๋ ์์๋ฅผ ๋ณด๋ฉด ๋์ฒดํ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
fun <T> runBlocking(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> T
): T
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Deferred<T>
- ๋ชจ๋ ์ฝ๋ฃจํด ๋น๋๊ฐ CoroutineScope๋ฅผ ๋ฆฌ์๋ฒ๋ก ๋ฐ๋ ๊ฒ์ ํ์ธํ ์ ์์ผ๋ฉฐ, ์ด ์ด์ ๋ก GlobalScope๋ฅผ ๊ตณ์ด ์ฌ์ฉํ์ง ์๊ณ this.launch๋ฅผ ํตํด์ ๊ฐ์ ๊ธฐ๋ฅ์ ํ ์ ์์ต๋๋ค.
- ์ด ๊ธฐ๋ฅ์ ํตํด์ launch๋ runBlocking์ ์์์ด ๋ฉ๋๋ค.
- ๋ถ๋ชจ๊ฐ ์์์ ๊ธฐ๋ค๋ฆฌ๋ฏ๋ก, runBlocking์ ์์์ ๋ชจ๋ ์์ ์ด ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค.
fun main() = runBlocking {
this.launch {
delay(1000L)
println("World!")
}
println("Hello,")
}
// Hello,
// (1์ด ํ)
// World!
- ๋ถ๋ชจ๋ ์์๋ค์ ์ํ ์ค์ฝํ๋ฅผ ์ ๊ณตํ๊ณ ์์๋ค์ ํด๋น ์ค์ฝํ ๋ด์์ ํธ์ถํฉ๋๋ค.
- ์ด๋ฅผ ๊ตฌ์กฐํ๋ ๋์์ฑ์ด๋ผ๋ ๊ด๊ณ๋ผ๊ณ ํฉ๋๋ค.
๊ตฌ์กฐํ๋ ๋์์ฑ์ ํน์ง
- ์์์ ๋ถ๋ชจ ์ปจํ
์คํธ๋ฅผ ์์๋ฐ์ต๋๋ค.
- ์์์ด ๋ถ๋ชจ ์ปจํ ์คํธ๋ฅผ ์ฌ์ ์ํ ์ ์์ต๋๋ค.
- ๋ถ๋ชจ๋ ๋ชจ๋ ์์์ด ์์ ์ ๋ง์น ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค
- ๋ถ๋ชจ ์ฝ๋ฃจํด์ด ์ทจ์๋๋ฉด ์์๋ ์ทจ์๋ฉ๋๋ค.
- ์์ ์ฝ๋ฃจํด์์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ๋ถ๋ชจ ์ฝ๋ฃจํด๋ ์๋ฌ๋ก ์๋ฉธํฉ๋๋ค.
runBlocking์?
- ์ฝ๋ฃจํด ๋น๋ ์ค์ runBlocking์ CoroutineScope์ ํ์ฅํจ์๊ฐ ์๋๋๋ค.
- ์ด๋ runBlocking์ ์์์ด ๋ ์ ์์ผ๋ฉฐ, ๋ฃจํธ ์ฝ๋ฃจํด์ผ๋ก๋ง ํ์ฉํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
- ๊ณ์ธต์ ๋ชจ๋ ์์์ ๋ถ๋ชจ๊ฐ ๋ฉ๋๋ค.
ํ์ ์์์ ์ฝ๋ฃจํด ์ฌ์ฉ
- ์ค๋จ ํจ์๋ ๋ค๋ฅธ ์ค๋จ ํจ์๋ค๋ก๋ถํฐ ํธ์ถ๋์ด์ผ ํ๋ฉฐ, ๋ชจ๋ ์ค๋จ ํจ์๋ ์ฝ๋ฃจํด ๋น๋๋ก ์์๋์ด์ผ ํฉ๋๋ค.
- ๋ํ runBlocking์ ์ ์ธํ ๋ชจ๋ ์ฝ๋ฃจํด ๋น๋๋ CoroutineScope์์ ์์๋์ด์ผ ํฉ๋๋ค.
- ์ค๋จ ํจ์ ๋ด๋ถ์์ ์ค๋จ๋ ์ ์์ง๋ง ํจ์ ๋ด์๋ ์ค์ฝํ๊ฐ ์์ต๋๋ค.
- ์ค์ฝํ๋ฅผ ์ธ์๋ก ๋๊ธฐ๋ ๊ฑด ์ข์ ๋ฐฉ๋ฒ์ด๋ผ ํ ์ ์์ผ๋ฉฐ, ์ฝ๋ฃจํด ๋น๋๊ฐ ์ฌ์ฉํ ์ค์ฝํ๋ฅผ ๋ง๋ค์ด์ฃผ๋ ์ค๋จ ํจ์์ธ coroutineScope ํจ์๋ฅผ ์ฌ์ฉํ ์ ์ต๋๋ค.
scope๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ
- runBlocking ํ์ฉ
- ์ง์ ์ฝ๋ฃจํด ์ค์ฝํ ์ ์
- ํ๋ ์์ํฌ์์ ์ ๊ณตํ๋ ์ค์ฝํ ์ฌ์ฉ
- ๋ฐฑ์๋ : Ktor
- ์๋๋ก์ด๋ : Android KTX
coroutineScope ์ฌ์ฉํ๊ธฐ
- ์๋์ ์์์ฒ๋ผ ๋น๋๊ธฐ์ ์ผ๋ก ๋ ๊ฐ์ ์์์ด ํ์ํ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
- ์ฌ์ฉ์ ๋ฐ์ดํฐ
- ๊ธ ๋ชฉ๋ก
suspend fun getArticlesForUser(
userToken: String?,
): List<ArticleJson> = coroutineScope {
val articles = async { articleRepository.getArticles() }
val user = userService.getUser(userToken)
articles.await()
.filter { canSeeOnList(user, it) }
.map { toArticleJson(it) }
}
- coroutineScope๋ ๋๋ค ํํ์์ด ํ์๋ก ํ๋ ์ค์ฝํ๋ฅผ ๋ง๋ค์ด ์ฃผ๋ ์ค๋ด ํจ์ ์ ๋๋ค.
- let, run, use ๋๋ runBlocking ์ฒ๋ผ ๋๋ค์์ด ๋ฐํํ๋ ๊ฒ์ด๋ฉด ๋ฌด์์ด๋ ๋ฐํํฉ๋๋ค.
- ์ค๋จ ํจ์ ๋ฐ์์ ์ค์ฝํ๋ฅผ ๋ง๋ค๋ ค๋ฉด coroutineScope๋ฅผ ์ฌ์ฉํด์ผ ํ๋ฉฐ, runBlocking์ ํ์ฉํ๋ ๊ฒ๋ณด๋ค ์ข์ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์์ฝ
- ๋์์ฑ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด์๋ ํจ์๋ฅผ coroutineScope๋ก ๋ํํ ๋ค์, ์ค์ฝํ ๋ด์์ ๋น๋๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
- ๋ชจ๋ ๊ฒ์ ์ค์ฝํ ๋ด์์ ๋น๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ์์๋์ด์ผ ํฉ๋๋ค.
- ๋๋ถ๋ถ์ ํ๋ก์ ํธ์์ ์ค์ฝํ๋ ํ ๋ฒ ์ ์๋๋ฉด ๋ค์ ์ ์ธํ ํ์๊ฐ ์์ผ๋ฉฐ, ๋ค๋ฅธ ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ์์ธ์ฒ๋ฆฌ์ ์ต์ํด์ง๋ ๋ฐฉ๋ฒ๋ ํ์ต์ด ํ์ํฉ๋๋ค !
https://m.yes24.com/Goods/Detail/123034354