Concurrent ?

ํ”„๋กœ์„ธ์Šค, ์Šค๋ ˆ๋“œ, ์ฝ”๋ฃจํ‹ด

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹œ์ž‘ํ•  ๋•Œ ์šด์˜์ฒด์ œ๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์—ฌ๊ธฐ์— ์Šค๋ ˆ๋“œ๋ฅผ ์—ฐ๊ฒฐํ•œ ๋‹ค์Œ, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ(main thread)๋กœ ์•Œ๋ ค์ง„ ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋ฅผ ์‹œ์ž‘ํ•œ๋‹ค.

ํ”„๋กœ์„ธ์Šค

ํ”„๋กœ์„ธ์Šค๋Š” ์‹คํ–‰ ์ค‘์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ธ์Šคํ„ด์Šค๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๋กœ ๊ตฌ์„ฑ๋  ์ˆ˜ ์žˆ๋‹ค.

์Šค๋ ˆ๋“œ

์‹คํ–‰ ์Šค๋ ˆ๋“œ๋Š” ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰ํ•  ์ผ๋ จ์˜ ๋ช…๋ น์„ ํฌํ•จํ•œ๋‹ค. ์Šค๋ ˆ๋“œ๊ฐ€ ๋๋‚˜๋ฉด ํ”„๋กœ์„ธ์Šค์˜ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์™€ ์ƒ๊ด€์—†์ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ข…๋ฃŒ๋œ๋‹ค.

fun main(args: Array<String>) {
		doWork()
}

๊ธฐ๋ณธ์ ์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋ฉด main() ํ•จ์ˆ˜์˜ ๋ช…๋ น ์ง‘ํ•ฉ์ด ํฌํ•จ๋œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. doWork()์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ doWork()์ด ์ข…๋ฃŒ๋˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹คํ–‰์ด ์ข…๋ฃŒ๋œ๋‹ค.

์Šค๋ ˆ๋“œ ์•ˆ์—์„œ ๋ช…๋ น์€ ํ•œ ๋ฒˆ์— ๋‚˜๋ผ์”ฉ ์‹คํ–‰๋ผ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ธ”๋ก(block)๋˜๋ฉด ๋ธ”๋ก์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ฐ™์€ ์Šค๋ ˆ๋“œ์—์„œ ๋‹ค๋ฅธ ๋ช…๋ น์„ ์‹คํ–‰ํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋งŽ์€ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค์—์„œ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์„œ๋กœ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฝ”ํ‹€๋ฆฐ์ด ๋™์‹œ์„ฑ์„ ๊ตฌํ˜„ํ•œ ๋ฐฉ์‹์„ ๋ณด๋ฉด ์—ฌ๋Ÿฌ๋ถ„์ด ์ง์ ‘ ์Šค๋ ˆ๋“œ๋ฅผ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ค‘์ง€ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋œ๋‹ค. ํ•œ๋‘ ์ค„์˜ ์ฝ”๋“œ๋กœ ์ฝ”ํ‹€๋ฆฐ์ด ํŠน์ • ์Šค๋ ˆ๋“œ๋‚˜ ์Šค๋ ˆ๋“œ ํ’€์„ ์ƒ์„ฑํ•ด์„œ ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•˜๋„๋ก ์ง€์‹œํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค. ์Šค๋ ˆ๋“œ์™€ ๊ด€๋ จ๋œ ๋‚˜๋จธ์ง€ ์ฒ˜๋ฆฌ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์— ์˜ํ•ด ์ˆ˜ํ–‰๋œ๋‹ค.

์ฝ”๋ฃจํ‹ด

์ฝ”ํ‹€๋ฆฐ ๋ฌธ์„œ์—์„œ๋Š” ์ฝ”๋ฃจํ‹ด์„ ๊ฒฝ๋Ÿ‰ ์Šค๋ ˆ๋“œ๋ผ๊ณ ๋„ ํ•œ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ์Šค๋ ˆ๋“œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ฝ”๋ฃจํ‹ด์ด ํ”„๋กœ์„ธ์„œ๊ฐ€ ์‹คํ–‰ํ•  ๋ช…๋ น์–ด ์ง‘ํ•ฉ์˜ ์‹คํ–‰์„ ์ •์˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋˜ํ•œ ์ฝ”๋ฃจํ‹ด์€ ์Šค๋ ˆ๋“œ์™€ ๋น„์Šทํ•œ ๋ผ์ดํ”„ ์‚ฌ์ดํด์„ ๊ฐ–๊ณ  ์žˆ๋‹ค.

์ฝ”๋ฃจํ‹ด์€ ์Šค๋ ˆ๋“œ ์•ˆ์—์„œ ์‹คํ–‰๋œ๋‹ค. ์Šค๋ ˆ๋“œ ํ•˜๋‚˜์— ๋งŽ์€ ์ฝ”๋ฃจํ‹ด์ด ์žˆ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ์ฃผ์–ด์ง„ ์‹œ๊ฐ„์— ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ์—์„œ ํ•˜๋‚˜์˜ ๋ช…๋ น๋งŒ์ด ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰ ๊ฐ™์€ ์Šค๋ ˆ๋“œ์— 10๊ฐœ์˜ ์ฝ”๋ฃจํ‹ด์ด ์žˆ๋‹ค๋ฉด ํ•ด๋‹น ์‹œ์ ์—๋Š” ํ•˜๋‚˜์˜ ์ฝ”๋ฃจํ‹ด๋งŒ ์‹คํ–‰๋œ๋‹ค. ์Šค๋ ˆ๋“œ์™€ ์ฝ”๋ฃจํ‹ด์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ ์€ ์ฝ”๋ฃจํ‹ด์ด ๋น ๋ฅด๊ณ  ์ ์€ ๋น„์šฉ์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ˆ˜์ฒœ ๊ฐœ์˜ ์ฝ”๋ฃจํ‹ด๋„ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ˆ˜์ฒœ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋น ๋ฅด๊ณ  ์ž์›๋„ ํ›จ์”ฌ ์ ๊ฒŒ ์‚ฌ์šฉํ•œ๋‹ค.

๋‹ค์Œ ํ•จ์ˆ˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ amount์— ์ง€์ •๋œ ์ˆ˜๋งŒํผ ์ฝ”๋ฃจํ‹ด์„ ์ƒ์„ฑํ•ด ๊ฐ ์ฝ”๋ฃจํ‹ด์„ 1์ดˆ ๊ฐ„ ์ง€์—ฐ์‹œํ‚จ ํ›„ ๋ชจ๋“  ์ฝ”๋ฃจํ‹ด์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    println("${Thread.activeCount()} threads active at the start")
    val time = measureTimeMillis {
        createCoroutines(3)
    }
    println("${Thread.activeCount()} threads active at end")
    println("Took $time ms")
}

suspend fun createCoroutines(amount: Int) {
    val jobs = ArrayList<Job>()
    for (i in 1..amount) {
        jobs += GlobalScope.launch {
            delay(1000)
        }
    }
    jobs.forEach {
        it.join()
    }
}

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ amount๋ฅผ 10,000์œผ๋กœ ์‹คํ–‰ํ•  ๋•Œ ์•ฝ 1,160ms๊ฐ€ ๊ฑธ๋ฆฌ๋Š”๋ฐ ๋ฐ˜ํ•ด 100,000์œผ๋กœ ์‹คํ–‰ํ•˜๋Š” ๋ฐ 1,649ms๊ฐ€ ์†Œ์š”๋๋‹ค. ์ฝ”ํ‹€๋ฆฐ์€ ๊ณ ์ •๋œ ํฌ๊ธฐ์˜ ์Šค๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•˜๊ณ  ์ฝ”๋ฃจํ‹ด์„ ์Šค๋ ˆ๋“œ๋“ค์— ๋ฐฐํฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹คํ–‰ ์‹œ๊ฐ„์ด ๋งค์šฐ ์ ๊ฒŒ ์ฆ๊ฐ€ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ˆ˜์ฒœ ๊ฐœ์˜ ์ฝ”๋ฃจํ‹ด์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ๊ฑฐ์˜ ์˜ํ–ฅ์ด ์—†๋‹ค.

์ฝ”๋ฃจํ‹ด์ด ํŠน์ • ์Šค๋ ˆ๋“œ ์•ˆ์—์„œ ์‹คํ–‰๋˜๋”๋ผ๋„ ์Šค๋ ˆ๋“œ์™€ ๋ฌถ์ด์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค. ์ฝ”๋ฃจํ‹ด์˜ ์ผ๋ถ€๋ฅผ ํŠน์ • ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•˜๊ณ , ์‹คํ–‰์„ ์ค‘์ง€ํ•œ ๋‹ค์Œ ๋‚˜์ค‘์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ๊ณ„์† ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

suspend fun createCoroutines(amount: Int) {
    val jobs = ArrayList<Job>()
    for (i in 1..amount) {
        jobs += GlobalScope.launch {
            println("Started $i in ${Thread.currentThread().name}")
            delay(1000)
            println("Finished $i in ${Thread.currentThread().name}")
        }
    }
    jobs.forEach {
        it.join()
    }
}

์ด์™€ ๊ฐ™์ด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์„œ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ์„ ์•Œ๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

์Šค๋ ˆ๋“œ๋Š” ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ฝ”๋ฃจํ‹ด๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ•„์š”์— ๋”ฐ๋ผ ์ฝ”๋ฃจํ‹ด์„ ์Šค๋ ˆ๋“œ๋“ค ์‚ฌ์ด์— ์˜ฎ๊ธฐ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

๋‚ด์šฉ ์ •๋ฆฌ

์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋กํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๊ทธ ์Šค๋ ˆ๋“œ์—์„œ ์ฝ”๋“œ์˜ ์‹คํ–‰์„ ์ค‘์ง€ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ธ๋ฐ, ์‚ฌ์šฉ์ž์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ์Šค๋ ˆ๋“œ๋Š” ๋ธ”๋ก๋˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.

๋™์‹œ์„ฑ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋™์‹œ์— ํ•œ ๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค.

๋™์‹œ์„ฑ์— ๋Œ€ํ•ด

์˜ฌ๋ฐ”๋ฅธ ๋™์‹œ์„ฑ ์ฝ”๋“œ๋Š” ๊ฒฐ์ •๋ก ์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ–์ง€๋งŒ ์‹คํ–‰ ์ˆœ์„œ์—์„œ๋Š” ์•ฝ๊ฐ„์˜ ๊ฐ€๋ณ€์„ฑ์„ ํ—ˆ์šฉํ•˜๋Š” ์ฝ”๋“œ๋‹ค. ๊ทธ๋Ÿฌ๋ ค๋ฉด ์„œ๋กœ ๋‹ค๋ฅธ ๋ถ€๋ถ„์ด ์–ด๋Š ์ •๋„ ๋…๋ฆฝ์„ฑ์ด ์žˆ์–ด์•ผ ํ•˜๋ฉฐ ์•ฝ๊ฐ„์˜ ์กฐ์ •๋„ ํ•„์š”ํ•˜๋‹ค. ๋™์‹œ์„ฑ์„ ์ดํ•ดํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์ˆœ์ฐจ์ ์ธ ์ฝ”๋“œ๋ฅผ ๋™์‹œ์„ฑ ์ฝ”๋“œ์™€ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

fun getProfile(id: Int): Profile {
    val basicUserInfo = getUserInfo(id)
    val contactInfo = getContactInfo(id)

    return createProfile(basicUserInfo, contactInfo)
}

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฒƒ์€ ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ธฐ ์ „๊นŒ์ง€ ์—ฐ๋ฝ์ฒ˜ ์ •๋ณด๋ฅผ ์š”์ฒญํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด๊ฒƒ์ด ์ˆœ์ฐจ ์ฝ”๋“œ์˜ ์žฅ์ ์ด๋‹ค. ์ •ํ™•ํ•œ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์‰ฝ๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์–ด์„œ ์˜ˆ์ธกํ•˜์ง€ ๋ชปํ•œ ์ผ์ด ๋ฒŒ์–ด์ง€์ง€๋Š” ์•Š์„ ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ˆœ์ฐจ ์ฝ”๋“œ์—๋Š” ๋‘ ๊ฐ€์ง€ ํฐ ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค.

  • ๋™์‹œ์„ฑ ์ฝ”๋“œ์— ๋น„ํ•ด ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ๋‹ค.

  • ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ํ•˜๋“œ์›จ์–ด๋ฅผ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ๋‹ค.

getUserInfo๊ฐ€ 5์ดˆ๊ฐ€ ๊ฑธ๋ฆฌ๊ณ  getContactInfo๊ฐ€ 5์ดˆ๊ฐ€ ๊ฑธ๋ฆฐ๋‹ค๋ฉด getProfile์€ ํ•ญ์ƒ 10์ดˆ ์ด์ƒ ๊ฑธ๋ฆด ๊ฒƒ์ด๋‹ค.

getProfile์˜ ๋™์‹œ์„ฑ ๊ตฌํ˜„์— ๊ด€๋ž˜ ์‚ดํŽด๋ณด์ž.

suspend fun getProfile(id: Int): Profile {
    val basicUserInfo = asyncGetUserInfo(id)
    val contactInfo = asyncGetContactInfo(id)

    return createProfile(basicUserInfo.await(), contactInfo.await())
}

asyncGetUserInfo()์™€ asyncGetContactInfo()๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋„๋ก ์ž‘์„ฑ๋๊ธฐ ๋•Œ๋ฌธ์— ๋™์‹œ์„ฑ์ด๋ผ๊ณ  ํ•œ๋‹ค.

getProfile์˜ ๋™์‹œ์„ฑ ๊ตฌํ˜„ ๋ฒ„์ „์€ ์ˆœ์ฐจ์  ๊ตฌํ˜„๋ณด๋‹ค ๋‘ ๋ฐฐ ๋น ๋ฅด๊ฒŒ ์ˆ˜ํ–‰๋  ์ˆ˜ ์žˆ์ง€๋งŒ ์‹คํ–‰ํ•  ๋•Œ ์•ฝ๊ฐ„์˜ ๊ฐ€๋ณ€์„ฑ์ด ์žˆ๋‹ค. ๊ทธ๊ฒƒ์ด createProfile()์„ ํ˜ธ์ถœํ•  ๋•Œ ๋‘๊ฐœ์˜ wait() ํ˜ธ์ถœ์ด ์žˆ๋Š” ์ด์œ ๋‹ค. asyncGetUserInfo()์™€ asyncGetContactInfo()๊ฐ€ ๋ชจ๋‘ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ getProfile()์˜ ์‹คํ–‰์„ ์ผ์‹œ ์ค‘๋‹จํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๊ฒƒ์ด ๋™์‹œ์„ฑ์˜ ๊นŒ๋‹ค๋กœ์šด ๋ถ€๋ถ„์ด๋‹ค. ์ฝ”๋“œ์˜ ์ค€๋…๋ฆฝ์ ์ธ(semi-independent)๋ถ€๋ถ„์ด ์™„์„ฑ๋˜๋Š” ์ˆœ์„œ์— ๊ด€๊ณ„์—†์ด ๊ฒฐ๊ณผ๊ฐ€ ๊ฒฐ์ •์ ์ด์–ด์•ผ ํ•จ์„ ๋ณด์žฅํ•ด์•ผ ํ•œ๋‹ค.

๋™์‹œ์„ฑ์€ ๋ณ‘๋ ฌ์„ฑ์ด ์•„๋‹ˆ๋‹ค

ํ”ํžˆ ๋™์‹œ์„ฑ๊ณผ ๋ณ‘๋ ฌ์„ฑ์„ ํ˜ผ๋™ํ•˜๊ณค ํ•œ๋‹ค. ๋‘ ๊ฐœ์˜ ์ฝ”๋“œ๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋œ๋‹ค๋Š” ์ ์—์„œ ๋‘˜ ๋‹ค ๋น„์Šทํ•ด ๋ณด์ด๊ธด ํ•œ๋‹ค. ๋‘˜์˜ ์ฐจ์ด์ ์€ ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค ์•ˆ์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ๋ช…๋ น ์ง‘ํ•ฉ์˜ ํƒ€์ž„๋ผ์ธ์ด ๊ฒน์น  ๋•Œ ๋™์‹œ์„ฑ์ด ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ๋™์‹œ์„ฑ์€ ์ •ํ™•ํžˆ ๊ฐ™์€ ์‹œ์ ์— ์‹คํ–‰๋˜๋Š”์ง€ ์—ฌ๋ถ€์™€๋Š” ์ƒ๊ด€์ด ์—†๋‹ค. ๋งŒ์•ฝ ์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋‹จ์ผ ์ฝ”์–ด์—์„œ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ๋™์‹œ์„ฑ์ด์ง€๋งŒ ๋ณ‘๋ ฌ์€ ์•„๋‹ˆ๋‹ค. ๋‹จ์ผ ์ฒ˜๋ฆฌ ์žฅ์น˜๋Š” X์™€ Y ์Šค๋ ˆ๋“œ ์‚ฌ์ด์— ๊ต์ฐจ ๋ฐฐ์น˜๋˜๋ฉฐ, ๋‘ ๊ฐœ์˜ ์ „์ฒด ์ผ์ •์ด ๊ฒน์น˜์ง€๋งŒ ์ง€์ •๋œ ์‹œ์ ์— ๋‘˜ ์ค‘ ํ•˜๋‚˜๋งŒ ์‹คํ–‰๋˜๊ณ  ์žˆ๋‹ค.

๋ฐ˜๋ฉด์— ๋ณ‘๋ ฌ ์‹คํ–‰์€ ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ์ •ํ™•ํžˆ ๊ฐ™์€ ์‹œ์ ์— ์‹คํ–‰๋  ๋•Œ๋งŒ ๋ฐœ์ƒํ•œ๋‹ค. ๋‘ ๊ฐœ์˜ ์ฝ”์–ด๊ฐ€ ์žˆ๋Š” ์ปดํ“จํ„ฐ์—์„œ getProfile()์ด ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ ์ฝ”์–ด ํ•˜๋‚˜๋Š” asyncGetUserInfo()์˜ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๊ณ  ๋‹ค๋ฅธ ํ•˜๋‚˜์˜ ์ฝ”์–ด์—์„œ asyncGetContactInfo()์˜ ๋ช…๋ น์„ ์‹คํ–‰ํ•œ๋‹ค. ์š”์•ฝํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ๋™์‹œ์„ฑ์€ ๋‘ ๊ฐœ ์ด์ƒ์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๊ฒน์ณ์งˆ ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค. ๋งŒ์•ฝ ๋‹จ์ผ ์ฝ”์–ด์—์„œ ์‹คํ–‰๋˜๋ฉด ๋ณ‘๋ ฌ์ด ์•„๋‹ˆ๋ผ ๋™์‹œ์— ์‹คํ–‰๋˜๋Š”๋ฐ, ๋‹จ์ผ ์ฝ”์–ด๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ์ธ์ŠคํŠธ๋Ÿญ์…˜์„ ๊ต์ฐจ๋ฐฐ์น˜ํ•ด์„œ ์Šค๋ ˆ๋“œ๋“ค์˜ ์‹คํ–‰์„ ํšจ์œจ์ ์œผ๋กœ ๊ฒน์ณ์„œ ์‹คํ–‰ํ•œ๋‹ค.

  • ๋ณ‘๋ ฌ์€ ๋‘ ๊ฐœ์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์ •ํ™•ํžˆ ๊ฐ™์€ ์‹œ์ ์— ์‹คํ–‰๋  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋ ค๋ฉด 2๊ฐœ ์ด์ƒ์˜ ์ฝ”์–ด์™€ 2๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ์–ด์•ผ ๊ฐ ์ฝ”์–ด๊ฐ€ ๋™์‹œ์— ์Šค๋ ˆ๋“œ์˜ ์ธ์ŠคํŠธ๋Ÿญ์…˜์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

CPU ๋ฐ”์šด๋“œ์™€ I/O ๋ฐ”์šด๋“œ

CPU ๋ฐ”์šด๋“œ

CPU๋งŒ ์™„๋ฃŒํ•˜๋ฉด ๋˜๋Š” ์ž‘์—…์„ ์ค‘์‹ฌ์œผ๋กœ ๊ตฌํ˜„๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๋งŽ๋‹ค. ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ์„ฑ๋Šฅ์€ ์‹คํ–‰ ์ค‘์ธ CPU์˜ ์„ฑ๋Šฅ์— ์ขŒ์šฐ๋˜๋ฉฐ CPU๋งŒ ์—…๊ทธ๋ ˆ์ด๋“œํ•ด๋„ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋œ๋‹ค.

๋‹จ์–ด๋ฅผ ๊ฐ€์ ธ์™€์„œ ์ขŒ์šฐ๊ฐ€ ๊ฐ™์€ ๋‹จ์–ด์ธ์ง€๋ฅผ ํŒ๋ณ„ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ดํŽด๋ณด์ž

fun main(args: Array<String>) {
    filterPalindromes(words).forEach {
        println(it)
    }
}

fun filterPalindromes(words: List<String>) : List<String> {
    return words.filter { isPalindrome(it) }
}

fun isPalindrome(word: String) : Boolean {
    val lcWord = word.toLowerCase()
    return lcWord == lcWord.reversed()
}

์ˆ˜์‹ญ ๋งŒ ๊ฐœ์˜ ๋‹จ์–ด๋ฅผ ๋ณด๋‚ด๋„๋ก ์ฝ”๋“œ๋ฅผ ๋ฐ”๊พธ๋ฉด filterPalindromes()๋Š” ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆด ๊ฒƒ์ด๋‹ค. ์ฝ”๋“œ๋ฅผ ๋” ๋น ๋ฅธ CPU์—์„œ ์‹คํ–‰ํ•˜๋ฉด ์ฝ”๋“œ์˜ ๋ณ€๊ฒฝ ์—†์ด๋„ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋œ๋‹ค.

I/O ๋ฐ”์šด๋“œ

I/O ๋ฐ”์šด๋“œ๋Š” ์ž…์ถœ๋ ฅ ์žฅ์น˜์— ์˜์กดํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค. ์‹คํ–‰ ์‹œ๊ฐ„์€ ์ž…์ถœ๋ ฅ ์žฅ์น˜์˜ ์†๋„์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š”๋ฐ, ์˜ˆ์ปจ๋Œ€ ๋ฌธ์„œ๋ฅผ ์ฝ์–ด์„œ ๋ฌธ์„œ์˜ ๊ฐ ๋‹จ์–ด๋ฅผ filterPalindromes()์— ์ „๋‹ฌํ•ด ์ขŒ์šฐ๊ฐ€ ๊ฐ™์€ ๋‹จ์–ด๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด I/O ๋ฐ”์šด๋“œ๋‹ค. ๋„คํŠธ์›Œํ‚น์ด๋‚˜ ์ปดํ“จํ„ฐ ์ฃผ๋ณ€๊ธฐ๊ธฐ๋กœ๋ถ€ํ„ฐ์˜ ์ž…๋ ฅ์„ ๋ฐ›๋Š” ์ž‘์—…๋“ค๋„ I/O ์ž‘์—…์ด๋‹ค.

CPU ๋ฐ”์šด๋“œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์—์„œ์˜ ๋™์‹œ์„ฑ๊ณผ ๋ณ‘๋ ฌ์„ฑ

CPU ๋ฐ”์šด๋“œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๊ฒฝ์šฐ ๋‹ค์ค‘ ์ฝ”์–ด์—์„œ ๋ณ‘๋ ฌ์„ฑ์„ ํ™œ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹จ์ผ ์ฝ”์–ด์—์„œ ๋™์‹œ์„ฑ์„ ๊ตฌํ˜„ํ•˜๋ฉด ์„ฑ๋Šฅ์ด ์ €ํ•˜๋˜๊ธฐ๋„ ํ•œ๋‹ค.

๋‹จ์ผ ์ฝ”์–ด์—์„œ ์‹คํ–‰

๋‹จ์ผ ์ฝ”์–ด์—์„œ ์‹คํ–‰๋œ๋‹ค๋ฉด ํ•˜๋‚˜์˜ ์ฝ”์–ด๊ฐ€ 3๊ฐœ์˜ ์Šค๋ ˆ๋“œ ์‚ฌ์ด์—์„œ ๊ต์ฐจ ๋ฐฐ์น˜๋˜๋ฉฐ ๋งค๋ฒˆ ์ผ์ •๋Ÿ‰์˜ ๋‹จ์–ด๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ณ  ๋‹ค์Œ ์Šค๋ ˆ๋“œ๋กœ ์ „ํ™˜๋œ๋‹ค. ์ „ํ™˜ ํ”„๋กœ์„ธ์Šค๋ฅผ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์ด๋ผ๊ณ  ํ•œ๋‹ค.

์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์€ ํ˜„์žฌ ์Šค๋ ˆ๋“œ์˜ ์ƒํƒœ๋ฅผ ์ €์žฅํ•œ ํ›„ ๋‹ค์Œ ์Šค๋ ˆ๋“œ์˜ ์ƒํƒœ๋ฅผ ์ ์žฌํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ํ”„๋กœ์„ธ์Šค์— ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ˆœ์ฐจ์  ๊ตฌํ˜„์—์„œ๋Š” ๋‹จ์ผ ์ฝ”์–ด๊ฐ€ ๋ชจ๋“  ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€๋งŒ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋ณ‘๋ ฌ ์‹คํ–‰

๋ณ‘๋ ฌ ์‹คํ–‰์˜ ๊ฒฝ์šฐ ๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ํ•˜๋‚˜์˜ ์ „์šฉ ์ฝ”์–ด์—์„œ ์‹คํ–‰๋œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด isPalindrome()์˜ ์‹คํ–‰์€ ์ˆœ์ฐจ์  ์‹คํ–‰์˜ ์•ฝ 3๋ถ„์˜ 1์ด ๋  ๊ฒƒ์ด๋‹ค.

CPU ๋ฐ”์šด๋“œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์œ„ํ•ด์„œ๋Š” ํ˜„์žฌ ์‚ฌ์šฉ ์ค‘์ธ ์žฅ์น˜์˜ ์ฝ”์–ด ์ˆ˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ ์ ˆํ•œ ์Šค๋ ˆ๋“œ ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ๊ณ ๋ คํ•ด์•ผ๋งŒ ํ•œ๋‹ค.

I/O ๋ฐ”์šด๋“œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์—์„œ์˜ ๋™์‹œ์„ฑ ๋Œ€ ๋ณ‘๋ ฌ์„ฑ

I/O ๋ฐ”์šด๋“œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์ˆœ์ฐจ์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜๋ณด๋‹ค ๋™์‹œ์„ฑ ๊ตฌํ˜„์—์„œ ํ•ญ์ƒ ๋” ๋‚˜์€ ์„ฑ๋Šฅ์„ ๋ฐœํœ˜ํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ผ I/O ์ž‘์—…์€ ๋Š˜ ๋™์‹œ์„ฑ์œผ๋กœ ์‹คํ–‰ํ•˜๋Š” ํŽธ์ด ์ข‹๋‹ค.

๋™์‹œ์„ฑ์ด ์–ด๋ ค์šด ์ด์œ 

์–ด๋–ค ์–ธ์–ด๋“ค์€ ๋„ˆ๋ฌด ๋ฒˆ๊ฑฐ๋กญ๊ฒŒ ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๊ธฐ๋„ ํ•˜๊ณ , ์œตํ†ต์„ฑ ์—†์ด ๋งŒ๋“ค์–ด ์‚ฌ์šฉ์„ฑ์„ ๋–จ์–ด๋œจ๋ฆฌ๋Š” ์–ธ์–ด๋„ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ฝ”ํ‹€๋ฆฐ์€ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ๋™๊ธฐํ™”ํ•˜๊ณ  ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค๊ธฐ ๋•Œ๋ฌธ์— ์‹คํ–‰ ํ๋ฆ„์ด ๋ฐ”๋€Œ์–ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ž‘๋™์—๋Š” ์˜ํ–ฅ์ด ์—†๋‹ค.

๋ ˆ์ด์Šค ์ปจ๋””์…˜

๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๊ฐ€์žฅ ํ”ํ•œ ์˜ค๋ฅ˜์ธ ๋ ˆ์ด์Šค ์ปจ๋””์…˜์€ ์ฝ”๋“œ๋ฅผ ๋™์‹œ์„ฑ์œผ๋กœ ์ž‘์„ฑํ–ˆ์ง€๋งŒ ์ˆœ์ฐจ์  ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋™์ž‘ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์˜ˆ์ƒํ•  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    asyncGetUserInfo(1)
    // Do some other operations
    delay(1000)

    println("User ${user.id} is ${user.name}")
}

fun asyncGetUserInfo(id: Int) = GlobalScope.async {
    delay(1100)
    user = UserInfo(id = id, name = "Susan", lastName = "Calvin")
}

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด user์— ์ •๋ณด๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๋™์•ˆ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•„์„œ main()๊ฐ€ ์ค‘๋‹จ๋œ๋‹ค. ๋ ˆ์ด์Šค ์ปจ๋””์…˜์„ ๊ณ ์น˜๋ ค๋ฉด ์ •๋ณด์— ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•˜๊ธฐ ์ „์— ์ •๋ณด๋ฅผ ์–ป์„ ๋•Œ๊นŒ์ง€ ๋ช…์‹œ์ ์œผ๋กœ ๊ธฐ๋‹ค๋ ค์•ผ๋งŒ ํ•œ๋‹ค.

์›์ž์„ฑ ์œ„๋ฐ˜

์›์ž์„ฑ ์ž‘์—…์ด๋ž€ ์ž‘์—…์ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ„์„ญ ์—†์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ์„ ๋งํ•œ๋‹ค. ๋‹จ์ผ ์Šค๋ ˆ๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๋ชจ๋“  ์ฝ”๋“œ๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  ์ž‘์—…์ด ๋ชจ๋‘ ์›์ž์ผ ๊ฒƒ์ด๋‹ค. ์Šค๋ ˆ๋“œ๊ฐ€ ํ•˜๋‚˜๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ ๊ฐ„์„ญ์ด ์žˆ์„ ์ˆ˜ ์—†๋‹ค.

์ˆ˜์ •์ด ๊ฒน์น  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์€ ๋ฐ์ดํ„ฐ ์†์‹ค์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ธ๋ฐ, ๊ฐ€๋ น ์ฝ”๋ฃจํ‹ด์ด ๋‹ค๋ฅธ ์ฝ”๋ฃจํ‹ด์ด ์ˆ˜์ •ํ•˜๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

fun main(args: Array<String>) = runBlocking {
    val workerA = asyncIncrement(2000)
    val workerB = asyncIncrement(100)

    workerA.await()
    workerB.await()

    print("counter [$counter]")
}

var counter = 0

fun asyncIncrement(by: Int) = GlobalScope.async {
    for (i in 0 until by) {
        counter++
    }
}

main()์„ ์‹คํ–‰ํ•˜๋ฉด ๋Œ€๋ถ€๋ถ„ counter [2100]๋ฅผ ์ถœ๋ ฅํ•˜์ง€๋งŒ, ๊ฝค ๋งŽ์€ ์‹คํ–‰์—์„œ 2,100๋ณด๋‹ค ์ ์€ ๊ฐ’์„ ์ธ์‡„ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

๊ต์ฐฉ ์ƒํƒœ

๋™์‹œ์„ฑ ์ฝ”๋“œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™๊ธฐํ™”๋˜๋ ค๋ฉด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋Š” ๋™์•ˆ ์‹คํ–‰์„ ์ผ์‹œ ์ค‘๋‹จํ•˜๊ฑฐ๋‚˜ ์ฐจ๋‹จํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

// This will never complete execution.
fun main(args: Array<String>) = runBlocking {
    jobA = launch {
        delay(1000)
        // wait for JobB to finish
        jobB.join()
    }

    jobB = launch {
        // wait for JobA to finish
        jobA.join()
    }

    // wait for JobA to finish
    jobA.join()
    println("Finished")
}

๋ผ์ด๋ธŒ ๋ฝ

๋ผ์ด๋ธŒ ๋ฝ(Livelocks)์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์‹คํ–‰์„ ๊ณ„์†ํ•  ์ˆ˜ ์—†์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๊ต์ฐฉ์ƒํƒœ์™€ ์œ ์‚ฌํ•˜๋‹ค. ๋ผ์ด๋ธŒ ๋ฝ์ด ์ง„ํ–‰๋  ๋•Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋Š” ์ง€์†์ ์œผ๋กœ ๋ณ€ํ•˜์ง€๋งŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ •์ƒ ์‹คํ–‰์œผ๋กœ ๋Œ์•„์˜ค์ง€ ๋ชปํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ƒํƒœ๊ฐ€ ๋ณ€ํ•œ๋‹ค๋Š” ์ ์ด ๋‹ค๋ฅด๋‹ค.

๊ต์ฐฉ ์ƒํƒœ๋ฅผ ๋ณต๊ตฌํ•˜๋„๋ก ์„ค๊ณ„๋œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์—์„œ ๋ผ์ด๋ธŒ ๋ฝ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ๊ต์ฐฉ ์ƒํƒœ์—์„œ ๋ณต๊ตฌํ•˜๋ ค๋Š” ์‹œ๋„๊ฐ€ ๋ผ์ด๋ธŒ ๋ฝ์„ ๋งŒ๋“ค์–ด ๋‚ผ ์ˆ˜๋„ ์žˆ๋‹ค.

์ฝ”ํ‹€๋ฆฐ์—์„œ์˜ ๋™์‹œ์„ฑ

๋„Œ ๋ธ”๋กœํ‚น

์Šค๋ ˆ๋“œ๋Š” ๋ฌด๊ฒ๊ณ  ์ƒ์„ฑํ•˜๋Š” ๋ฐ ๋น„์šฉ์ด ๋งŽ์ด ๋“ค๋ฉฐ ์ œํ•œ๋œ ์ˆ˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฝ”ํ‹€๋ฆฐ์€ ์ฑ„๋„(channels), ์•กํ„ฐ(actors), ์ƒํ˜ธ ๋ฐฐ์ œ(mutual exclusions)์™€ ๊ฐ™์€ ํ›Œ๋ฅญํ•œ ๊ธฐ๋ณธํ˜•(primitives)๋„ ์ œ๊ณตํ•ด ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋กํ•˜์ง€ ์•Š๊ณ  ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ํ†ต์‹ ํ•˜๊ณ  ๋™๊ธฐํ™”ํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•œ๋‹ค.

๋ช…์‹œ์ ์ธ ์„ ์–ธ

๋™์‹œ์„ฑ์€ ์—ฐ์‚ฐ์ด ๋™์‹œ์— ์‹คํ–‰๋ผ์•ผ ํ•˜๋Š” ์‹œ์ ์„ ๋ช…์‹œ์ ์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค. ์ผ์‹œ ์ค‘๋‹จ ๊ฐ€๋Šฅํ•œ ์—ฐ์‚ฐ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋œ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    val time = measureTimeMillis {
        val name = getName()
        val lastName = getLastName()

        println("Hello, ${name} ${lastName}")
    }

    println("Execution took $time ms")
}

suspend fun getName(): String {
    delay(1000)
    return "Susan"
}

suspend fun getLastName(): String {
    delay(1000)
    return "Calvin"
}

์ฝ”๋“œ์—์„œ main()์€ ํ˜„์žฌ ์Šค๋ ˆ๋“œ์—์„œ ์ผ์‹œ ์ค‘๋‹จ ๊ฐ€๋Šฅํ•œ ์—ฐ์‚ฐ getName()๊ณผ getLastName()์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ getLastName()๊ณผ getName() ๊ฐ„์— ์˜์กด์„ฑ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋™์‹œ์— ์ˆ˜ํ–‰ํ•˜๋Š” ํ‡์ด ๋” ๋‚ซ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    val time = measureTimeMillis {
        val name = async { getName() }
        val lastName = async { getLastName() }

        println("Hello, ${name.await()} ${lastName.await()}")
    }

    println("Execution took $time ms")
}

async { ... }๋ฅผ ํ˜ธ์ถœํ•ด ๋‘ ํ•จ์ˆ˜๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•ด์•ผ ํ•˜๋ฉฐ await()๋ฅผ ํ˜ธ์ถœํ•ด ๋‘ ์—ฐ์‚ฐ์— ๋ชจ๋‘ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚  ๋•Œ๊นŒ์ง€ main()์ด ์ผ์‹œ ์ค‘๋‹จ๋˜๋„๋ก ์š”์ฒญํ•œ๋‹ค.

๊ฐ€๋…์„ฑ

์ฝ”ํ‹€๋ฆฐ์˜ ๋™์‹œ์„ฑ ์ฝ”๋“œ๋Š” ์ˆœ์ฐจ์  ์ฝ”๋“œ๋งŒํผ ์ฝ๊ธฐ ์‰ฝ๋‹ค. suspend ๋ฉ”์†Œ๋“œ๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋  ๋‘ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ „์— ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฐ๋‹ค.

๊ธฐ๋ณธํ˜• ํ™œ์šฉ

์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์—ฌ๋Ÿฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๊ฐ€์žฅ ์–ด๋ ค์šด ๋ถ€๋ถ„ ์ค‘ ํ•˜๋‚˜๋‹ค. ์–ธ์ œ ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ธ๊ฐ€๋ฅผ ์•„๋Š” ๊ฒƒ ๋ชป์ง€ ์•Š๊ฒŒ ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“œ๋Š”์ง€๋ฅผ ์•„๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ค. ๋˜ํ•œ I/O ์ž‘์—… ์ „์šฉ ์Šค๋ ˆ๋“œ์™€ CPU ๋ฐ”์šด๋“œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ์–ด์•ผ ํ•˜๋Š”๋ฐ, ์Šค๋ ˆ๋“œ๋ฅผ ํ†ต์‹ /๋™๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์€ ๊ทธ ์ž์ฒด๋กœ ์–ด๋ ค์šด ์ผ์ด๋‹ค.

์ฝ”ํ‹€๋ฆฐ์€ ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ ๊ธ‰ ํ•จ์ˆ˜์™€ ๊ธฐ๋ณธํ˜•์„ ์ œ๊ณตํ•œ๋‹ค.

  • ์Šค๋ ˆ๋“œ๋Š” ์Šค๋ ˆ๋“œ ์ด๋ฆ„์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ•˜๋Š” newSingleThreadContext()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ƒ์„ฑ๋œ๋‹ค. ์ผ๋‹จ ์ƒ์„ฑ๋˜๋ฉด ํ•„์š”ํ•œ ๋งŒํผ ๋งŽ์€ ์ฝ”๋ฃจํ‹ด์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์Šค๋ ˆ๋“œ ํ’€์€ ํฌ๊ธฐ์™€ ์ด๋ฆ„์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํ•˜๋Š” newFixedThreadPoolContext()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

  • CommonPool์€ CPU ๋ฐ”์šด๋“œ ์ž‘์—…์— ์ตœ์ ์ธ ์Šค๋ ˆ๋“œ ํ’€์ด๋‹ค. ์ตœ๋Œ€ ํฌ๊ธฐ๋Š” ์‹œ์Šคํ…œ์˜ ์ฝ”์–ด์—์„œ 1์„ ๋บ€ ๊ฐ’์ด๋‹ค.

  • ์ฝ”๋ฃจํ‹ด์„ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋กœ ์ด๋™์‹œํ‚ค๋Š” ์—ญํ• ์€ ๋Ÿฐํƒ€์ž„์ด ๋‹ด๋‹นํ•œ๋‹ค.

  • ํƒœ๋„, ๋ฎคํ…์Šค ๋ฐ ์Šค๋ ˆ๋“œ ํ•œ์ •๊ณผ ๊ฐ™์€ ์ฝ”๋ฃจํ‹ด์˜ ํ†ต์‹ ๊ณผ ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•ด ํ•„์š”ํ•œ ๋งŽ์€ ๊ธฐ๋ณธํ˜•๊ณผ ๊ธฐ์ˆ ์ด ์ œ๊ณต๋œ๋‹ค.

์ฝ”ํ‹€๋ฆฐ ๋™์‹œ์„ฑ ๊ด€๋ จ ๊ฐœ๋…๊ณผ ์šฉ์–ด

์ผ์‹œ ์ค‘๋‹จ ์—ฐ์‚ฐ

์ผ์‹œ ์ค‘๋‹จ ์—ฐ์‚ฐ์€ ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ์‹คํ–‰์„ ์ธ์‹œ ์ค‘์ง€ํ• ์ˆ˜ ์žˆ๋Š” ์—ฐ์‚ฐ์ด๋‹ค.

์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜

์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜ ํ˜•์‹์˜ ์ผ์‹œ ์ค‘๋‹จ ์—ฐ์‚ฐ์ด๋‹ค.

suspend fun greetAfter(name: String, delayMillis: Long) {
		delay(delayMillis)
		println("Hello, $name")
}

์•ž์˜ ์˜ˆ์ œ์—์„œ greetAfter()์˜ ์‹คํ–‰์€ delay()๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ์ผ์‹œ ์ค‘๋‹จ๋œ๋‹ค. delay()๋Š” ์ž์ฒด๊ฐ€ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜์ด๋ฉฐ, ์ฃผ์–ด์ง„ ์‹œ๊ฐ„ ๋™์•ˆ ์‹คํ–‰์„ ์ผ์‹œ ์ค‘๋‹จํ•œ๋‹ค. delay()๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด greetAfter()๊ฐ€ ์‹คํ–‰์„ ์ •์ƒ์ ์œผ๋กœ ๋‹ค์‹œ ์‹œ์ž‘ํ•œ๋‹ค. greetAfter()๊ฐ€ ์ผ์‹œ ์ค‘์ง€๋œ ๋™์•ˆ ์‹คํ–‰ ์Šค๋ ˆ๋“œ๊ฐ€ ๋‹ค๋ฅธ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.

๋žŒ๋‹ค ์ผ์‹œ ์ค‘๋‹จ

์ผ๋ฐ˜์ ์ธ ๋žŒ๋‹ค์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ์ผ์‹œ ์ค‘๋‹จ ๋žŒ๋‹ค๋Š” ์ต๋ช…์˜ ๋กœ์ปฌ ํ•จ์ˆ˜๋‹ค. ์ผ์‹œ ์ค‘๋‹จ ๋žŒ๋‹ค๋Š” ๋‹ค๋ฅธ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ์ž์‹ ์˜ ์‹คํ–‰์„ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ ๋ณดํ†ต์˜ ๋žŒ๋‹ค์™€ ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

์ฝ”๋ฃจํ‹ด ๋””์ŠคํŒจ์ฒ˜

์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์žฌ๊ฐœํ•  ์Šค๋ ˆ๋“œ๋ฅผ ๊ฒฐ์ •ํ•˜๊ธฐ ์œ„ํ•ด ์ฝ”๋ฃจํ‹ด ๋””์ŠคํŒจํ„ฐ๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค. ๋ชจ๋“  ์ฝ”๋ฃจํ‹ด ๋””์ŠคํŒจํ„ฐ๋Š” CoroutineDispatcher ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

  • DefaultDispatcher: ํ˜„์žฌ๋Š” CommonPool๊ณผ ๊ฐ™๋‹ค. ์•ž์œผ๋กœ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋‹ค.

  • CommonPool: ๊ณต์œ ๋œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ ํ’€์—์„œ ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•˜๊ณ  ๋‹ค์‹œ ์‹œ์ž‘ํ•œ๋‹ค. ๊ธฐ๋ณธ ํฌ๊ธฐ๋Š” CPU ๋ฐ”์šด๋“œ ์ž‘์—…์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ์— ์ ํ•ฉํ•˜๋‹ค.

  • Unconfined: ํ˜„์žฌ ์Šค๋ ˆ๋“œ์—์„œ ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•˜์ง€๋งŒ ์–ด๋–ค ์Šค๋ ˆ๋“œ์—์„œ๋„ ์ฝ”๋ฃจํ‹ด์ด ๋‹ค์‹œ ์žฌ๊ฐœ๋  ์ˆ˜ ์žˆ๋‹ค. ๋””์ŠคํŒจํ„ฐ์—์„œ๋Š” ์Šค๋ ˆ๋“œ ์ •์ฑ…์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

๋””์ŠคํŒจ์ฒ˜์™€ ํ•จ๊ป˜ ํ•„์š”์— ๋”ฐ๋ผ Pool ๋˜๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋นŒ๋”๊ฐ€ ์žˆ๋‹ค.

  • newSingleThreadContext() : ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋กœ ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์‹คํ–‰๋˜๋Š” ์ฝ”๋ฃจํ‹ด์€ ํ•ญ์ƒ ๊ฐ™์€ ์Šค๋ ˆ๋“œ์—์„œ ์‹œ์ž‘๋˜๊ณ  ์žฌ๊ฐœ๋œ๋‹ค.

  • newFixedThreadPoolContext() : ์ง€์ •๋œ ํฌ๊ธฐ์˜ ์Šค๋ ˆ๋“œ ํ’€์ด ์žˆ๋Š” ๋””์ŠคํŒจ์ฒ˜๋ฅผ ๋งŒ๋“ ๋‹ค. ๋Ÿฐํƒ€์ž„์€ ๋””์ŠคํŒจ์ฒ˜์—์„œ ์‹คํ–‰๋œ ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•˜๊ณ  ์žฌ๊ฐœํ•  ์Šค๋ ˆ๋“œ๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค.

์ฝ”๋ฃจํ‹ด ๋นŒ๋”

์ฝ”๋ฃจํ‹ด ๋นŒ๋”๋Š” ์ผ์‹œ ์ค‘๋‹จ ๋žŒ๋‹ค๋ฅผ ๋ฐ›์•„ ๊ทธ๊ฒƒ์„ ์‹คํ–‰์‹œํ‚ค๋Š” ์ฝ”๋ฃจํ‹ด์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋‹ค.

  • async() : ๊ฒฐ๊ณผ๊ฐ€ ์˜ˆ์ƒ๋˜๋Š” ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค. async()๋Š” ์ฝ”๋ฃจํ‹ด ๋‚ด๋ถ€์—์„œ ์ผ์–ด๋‚˜๋Š” ๋ชจ๋“  ์˜ˆ์™ธ๋ฅผ ์บก์ฒ˜ํ•ด์„œ ๊ฒฐ๊ณผ์— ๋„ฃ๊ธฐ ๋•Œ๋ฌธ์— ์กฐ์‹ฌํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

  • launch() : ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•œ๋‹ค. ์ž์ฒด ํ˜น์€ ์ž์‹ ์ฝ”๋ฃจํ‹ด์˜ ์‹คํ–‰์„ ์ทจ์†Œํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Job์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • runBlocking() : ๋ธ”๋กœํ‚น ์ฝ”๋“œ๋ฅผ ์ผ์‹œ ์ค‘์ง€ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋กœ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑ๋๋‹ค. ๋ณดํ†ต main() ๋ฉ”์†Œ๋“œ์™€ ์œ ๋‹› ํ…Œ์ŠคํŠธ์—์„œ ์‚ฌ์šฉ๋œ๋‹ค.

val result = GlobalScope.async(Dispatchers.Unconfined) { // ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. 
		isPalindrome(word = "Sample")
}
result.await()

์š”์•ฝ

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์žˆ๋‹ค. ๊ฐ๊ฐ์€ ์ ์–ด๋„ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋ฅผ ๊ฐ–๊ณ  ์žˆ๊ณ  ์ฝ”๋ฃจํ‹ด์€ ์Šค๋ ˆ๋“œ ์•ˆ์—์„œ ์‹คํ–‰๋œ๋‹ค.

  • ์ฝ”๋ฃจํ‹ด์€ ์žฌ๊ฐœ๋  ๋•Œ๋งˆ๋‹ค ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์ง€๋งŒ ํŠน์ • ์Šค๋ ˆ๋“œ์—๋งŒ ๊ตญํ•œ๋  ์ˆ˜๋„ ์žˆ๋‹ค.

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํ•˜๋‚˜ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ์— ์ค‘์ฒฉ๋ผ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ๋Š” ๋™์‹œ์  ์‹คํ–‰์ด๋‹ค.

  • ์˜ฌ๋ฐ”๋ฅธ ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ ค๋ฉด ์„œ๋กœ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ ๊ฐ„์˜ ํ†ต์‹ ๊ณผ ๋™๊ธฐํ™” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›Œ์•ผ ํ•˜๋ฉฐ, ์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ์ฝ”๋ฃจํ‹ด์˜ ํ†ต์‹ ๊ณผ ๋™๊ธฐํ™” ๋ฐฉ๋ฒ•์˜ ํ•™์Šต์„ ์˜๋ฏธํ•œ๋‹ค.

  • ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋Š” ๋™์‹œ ์ฒ˜๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ์ ์–ด๋„ ๋‘ ๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์ด ์‹คํ–‰๋  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค.

  • ๋™์‹œ ์ฒ˜๋ฆฌ๋Š” ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์—†์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋‹ค. ํ˜„๋Œ€์  ์ฒ˜๋ฆฌ ์žฅ์น˜๋Š” ์Šค๋ ˆ๋“œ ๊ฐ„์—์„œ ๊ต์ฐจ ๋ฐฐ์น˜ ํ•  ๊ฒƒ์ด๊ณ  ํšจ๊ณผ์ ์œผ๋กœ ์Šค๋ ˆ๋“œ๋ฅผ ์ค‘์ฒฉ์‹œํ‚ฌ ๊ฒƒ์ด๋‹ค.

  • ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ์—๋Š” ์–ด๋ ค์›€์ด ๋งŽ๋‹ค. ๋Œ€๋ถ€๋ถ„ ์˜ฌ๋ฐ”๋ฅธ ํ†ต์‹ ๊ณผ ์Šค๋ ˆ๋“œ ๋™๊ธฐํ™”์™€ ๊ด€๋ จ์ด ์žˆ๋Š”๋ฐ ๋ ˆ์ด์Šค ์ปจ๋””์…˜, ์›์ž์„ฑ ์œ„๋ฐ˜, ๊ต์ฐฉ ์ƒํƒœ ๋ฐ ๋ผ์ด๋ธŒ๋ฝ์ด ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ์ ์ด๋‹ค.

  • ์ฝ”ํ‹€๋ฆฐ์€ ๋™์‹œ์„ฑ์— ๋Œ€ํ•ด ํ˜„๋Œ€์ ์ด๊ณ  ์‹ ์„ ํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ทจํ–ˆ๋‹ค. ์ฝ”ํ‹€๋ฆฐ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋„Œ ๋ธ”๋กœํ‚น์ด๋ฉฐ, ๊ฐ€๋…์„ฑ ์žˆ๊ฒŒ ํ™œ์šฉ๋  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์œ ์—ฐํ•œ ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Last updated

Was this helpful?