์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Grand Central Dispatch์™€ OperationQueue๋กœ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ •๋ณตํ•˜๊ธฐ

๐Ÿš€ Grand Central Dispatch์™€ OperationQueue๋กœ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ •๋ณตํ•˜๊ธฐ

์•ˆ๋…•! ์˜ค๋Š˜์€ iOS์™€ macOS ๊ฐœ๋ฐœ์—์„œ ๋นผ๋†“์„ ์ˆ˜ ์—†๋Š” ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ด์•ผ๊ธฐ๋ฅผ ํ•ด๋ณผ๊นŒ ํ•ด. ๐ŸŽฏ
ํ˜น์‹œ ์•ฑ์„ ๋งŒ๋“ค๋‹ค๊ฐ€ ํ™”๋ฉด์ด ๋ฒ„๋ฒ…๊ฑฐ๋ฆฌ๊ฑฐ๋‚˜, ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๋™์•ˆ ์•ฑ์ด ๋ฉˆ์ถฐ๋ฒ„๋ฆฐ ๊ฒฝํ—˜ ์žˆ์–ด?
๊ทธ๋Ÿด ๋•Œ ํ•„์š”ํ•œ ๊ฒŒ ๋ฐ”๋กœ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์ด์•ผ. ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•ด์„œ ์•ฑ์„ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋งŒ๋“œ๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ธฐ์ˆ ์ด์ง€.

Apple์€ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ๋‘ ๊ฐ€์ง€ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•ด. ๋ฐ”๋กœ Grand Central Dispatch(GCD)์™€ OperationQueue์•ผ.
์ด ๋‘˜์€ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š”๋ฐ, ๊ฐ๊ฐ ํŠน์ง•์ด ๋‹ฌ๋ผ์„œ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒํ•ด์•ผ ํ•ด.
์žฌ๋Šฅ๋„ท์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ง€์‹์„ ๊ณต์œ ํ•˜๋Š” ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค๋„ ์ด ์ฃผ์ œ์— ๋Œ€ํ•ด ์ž์ฃผ ์งˆ๋ฌธํ•˜๊ณ  ๋‹ต๋ณ€์„ ๋‚˜๋ˆ„๊ณค ํ•˜์ง€. ๐Ÿ˜Š

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์˜ ์„ธ๊ณ„ GCD Grand Central Dispatch โœ“ ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฆ„ โœ“ ํด๋กœ์ € ๊ธฐ๋ฐ˜ โœ“ ๋‚ฎ์€ ์ˆ˜์ค€ ์ œ์–ด โœ“ ๊ฐ€๋ฒผ์šด ์ž‘์—…์— ์ตœ์  dispatch_async OperationQueue NSOperation ๊ธฐ๋ฐ˜ โœ“ ๊ฐ์ฒด ์ง€ํ–ฅ์  โœ“ ์˜์กด์„ฑ ๊ด€๋ฆฌ โœ“ ์ทจ์†Œ ๊ฐ€๋Šฅ โœ“ ๋ณต์žกํ•œ ์ž‘์—…์— ์ตœ์  addOperation VS

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์ด ๋ญ๊ธธ๋ž˜? ๐Ÿค”

๋จผ์ € ๊ธฐ๋ณธ๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์€ ๋ง ๊ทธ๋Œ€๋กœ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์•ผ.
์Šค๋ ˆ๋“œ๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ.
์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“œ๋Š” ์•ฑ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ(Main Thread)์—์„œ ์‹คํ–‰๋˜๋Š”๋ฐ, ์ด ์นœ๊ตฌ๊ฐ€ UI๋ฅผ ๋‹ด๋‹นํ•ด.

๋ฉ”์ธ ์Šค๋ ˆ๋“œ์˜ ์—ญํ•  ๐ŸŽจ
๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ํ™”๋ฉด์— ๋ณด์ด๋Š” ๋ชจ๋“  ๊ฒƒ์„ ๊ด€๋ฆฌํ•ด. ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ฐ˜์‘ํ•˜๊ณ , ์Šคํฌ๋กคํ•˜๋ฉด ํ™”๋ฉด์ด ์›€์ง์ด๊ณ ,
์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์‹คํ–‰๋˜๋Š” ๊ฒƒ๋„ ๋‹ค ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ๋•๋ถ„์ด์•ผ.
๊ทธ๋Ÿฐ๋ฐ ๋งŒ์•ฝ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…(๋„คํŠธ์›Œํฌ ์š”์ฒญ, ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์ฒ˜๋ฆฌ ๋“ฑ)์„ ํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?
์•ฑ์ด ๋ฉˆ์ถ˜ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ณ , ์‚ฌ์šฉ์ž๋Š” ๋‹ต๋‹ตํ•จ์„ ๋А๋ผ๊ฒŒ ๋ผ. ๐Ÿ˜ฐ

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

Grand Central Dispatch(GCD) ํŒŒํ—ค์น˜๊ธฐ ๐Ÿ”

GCD๋Š” Apple์ด 2009๋…„์— ๋„์ž…ํ•œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ๊ธฐ์ˆ ์ด์•ผ.
C ์–ธ์–ด ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์–ด์กŒ์ง€๋งŒ, Swift์—์„œ๋„ ์•„์ฃผ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.
GCD์˜ ํ•ต์‹ฌ ๊ฐœ๋…์€ Dispatch Queue์•ผ. ์ž‘์—…์„ ํ์— ๋„ฃ์œผ๋ฉด ์‹œ์Šคํ…œ์ด ์•Œ์•„์„œ ์ ์ ˆํ•œ ์Šค๋ ˆ๋“œ์— ๋ฐฐ์ •ํ•ด์„œ ์‹คํ–‰ํ•ด์ค˜.

Dispatch Queue์˜ ์ข…๋ฅ˜ ๐Ÿ“ฆ

GCD์—๋Š” ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ํƒ€์ž…์˜ ํ๊ฐ€ ์žˆ์–ด.

Serial Queue (์ง๋ ฌ ํ)

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

Concurrent Queue (๋™์‹œ ํ)

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

GCD ์‚ฌ์šฉ๋ฒ• - ๊ธฐ๋ณธํŽธ ๐Ÿ’ป

์ž, ์ด์ œ ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ๋ณผ๊นŒ? Swift์—์„œ GCD๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฑด ์ •๋ง ๊ฐ„๋‹จํ•ด.

// ๋ฉ”์ธ ํ์—์„œ UI ์—…๋ฐ์ดํŠธ
DispatchQueue.main.async {
    self.label.text = "์ž‘์—… ์™„๋ฃŒ!"
    self.activityIndicator.stopAnimating()
}

// ๊ธ€๋กœ๋ฒŒ ํ์—์„œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…
DispatchQueue.global(qos: .background).async {
    // ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…
    let result = self.processHeavyData()
    
    // ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌ
    DispatchQueue.main.async {
        self.displayResult(result)
    }
}

์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด DispatchQueue.main๊ณผ DispatchQueue.global์ด ๋ณด์ด์ง€?
main์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์˜๋ฏธํ•˜๊ณ , global์€ ์‹œ์Šคํ…œ์ด ๊ด€๋ฆฌํ•˜๋Š” ๊ธ€๋กœ๋ฒŒ ํ๋ฅผ ์˜๋ฏธํ•ด.

๐Ÿ’ก QoS(Quality of Service)๋ž€?
์ž‘์—…์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฑฐ์•ผ. ์ข…๋ฅ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„:
โ€ข userInteractive: ์‚ฌ์šฉ์ž์™€ ์ง์ ‘ ์ƒํ˜ธ์ž‘์šฉ (์ตœ๊ณ  ์šฐ์„ ์ˆœ์œ„) ๐Ÿ”ด
โ€ข userInitiated: ์‚ฌ์šฉ์ž๊ฐ€ ์‹œ์ž‘ํ•œ ์ž‘์—… (๋†’์€ ์šฐ์„ ์ˆœ์œ„) ๐ŸŸ 
โ€ข default: ๊ธฐ๋ณธ๊ฐ’ ๐ŸŸก
โ€ข utility: ์‹œ๊ฐ„์ด ์ข€ ๊ฑธ๋ ค๋„ ๋˜๋Š” ์ž‘์—… ๐ŸŸข
โ€ข background: ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—… (๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„) ๐Ÿ”ต
์‹œ์Šคํ…œ์€ ์ด ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋ณด๊ณ  CPU ์ž์›์„ ๋ฐฐ๋ถ„ํ•ด.

์ปค์Šคํ…€ ํ ๋งŒ๋“ค๊ธฐ ๐Ÿ› ๏ธ

์‹œ์Šคํ…œ ์ œ๊ณต ํ ๋ง๊ณ  ์ง์ ‘ ํ๋ฅผ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์–ด. ํŠน์ • ์ž‘์—…์„ ์œ„ํ•œ ์ „์šฉ ํ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์œ ์šฉํ•˜์ง€.

// Serial Queue ์ƒ์„ฑ
let serialQueue = DispatchQueue(label: "com.myapp.serialQueue")

// Concurrent Queue ์ƒ์„ฑ
let concurrentQueue = DispatchQueue(label: "com.myapp.concurrentQueue", 
                                    attributes: .concurrent)

// ์‚ฌ์šฉ ์˜ˆ์‹œ
serialQueue.async {
    print("์ž‘์—… 1 ์‹œ์ž‘")
    Thread.sleep(forTimeInterval: 2)
    print("์ž‘์—… 1 ์™„๋ฃŒ")
}

serialQueue.async {
    print("์ž‘์—… 2 ์‹œ์ž‘")
    Thread.sleep(forTimeInterval: 1)
    print("์ž‘์—… 2 ์™„๋ฃŒ")
}

// ์ถœ๋ ฅ ์ˆœ์„œ: ์ž‘์—… 1 ์‹œ์ž‘ โ†’ ์ž‘์—… 1 ์™„๋ฃŒ โ†’ ์ž‘์—… 2 ์‹œ์ž‘ โ†’ ์ž‘์—… 2 ์™„๋ฃŒ

Serial Queue๋Š” ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋˜๋‹ˆ๊นŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ์ด๋‚˜ ํŒŒ์ผ ์“ฐ๊ธฐ ๊ฐ™์€ ์ž‘์—…์— ์ ํ•ฉํ•ด.
์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ์ž์›์— ์ ‘๊ทผํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ๊ฑฐ๋“ . ๐Ÿ”’

๋™๊ธฐ vs ๋น„๋™๊ธฐ ์‹คํ–‰ โฑ๏ธ

GCD์—์„œ๋Š” ์ž‘์—…์„ ๋™๊ธฐ(sync) ๋˜๋Š” ๋น„๋™๊ธฐ(async)๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด.

async (๋น„๋™๊ธฐ)
์ž‘์—…์„ ํ์— ๋„ฃ๊ณ  ๋ฐ”๋กœ ๋‹ค์Œ ์ฝ”๋“œ๋กœ ๋„˜์–ด๊ฐ€. ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์•„.
๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์ด๊ฑธ ์‚ฌ์šฉํ•ด. ์•ฑ์ด ๋ฉˆ์ถ”์ง€ ์•Š์œผ๋‹ˆ๊นŒ! ๐Ÿƒโ€โ™‚๏ธ

sync (๋™๊ธฐ)
์ž‘์—…์„ ํ์— ๋„ฃ๊ณ  ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค. ๋‹ค์Œ ์ฝ”๋“œ๋Š” ์ž‘์—…์ด ๋๋‚œ ํ›„์— ์‹คํ–‰๋ผ.
์ฃผ์˜ํ•  ์ : ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋ฉ”์ธ ํ์— sync๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ฐ๋“œ๋ฝ์ด ๋ฐœ์ƒํ•ด! โš ๏ธ

// ๋น„๋™๊ธฐ ์‹คํ–‰ (์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ)
DispatchQueue.global().async {
    print("๋น„๋™๊ธฐ ์ž‘์—… ์‹œ์ž‘")
    // ๋ฌด๊ฑฐ์šด ์ž‘์—…
    print("๋น„๋™๊ธฐ ์ž‘์—… ์™„๋ฃŒ")
}
print("์ด ์ฝ”๋“œ๋Š” ๋ฐ”๋กœ ์‹คํ–‰๋จ")

// ๋™๊ธฐ ์‹คํ–‰ (์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉ)
DispatchQueue.global().sync {
    print("๋™๊ธฐ ์ž‘์—… ์‹œ์ž‘")
    // ์ž‘์—…
    print("๋™๊ธฐ ์ž‘์—… ์™„๋ฃŒ")
}
print("์ด ์ฝ”๋“œ๋Š” ์œ„ ์ž‘์—…์ด ๋๋‚œ ํ›„ ์‹คํ–‰๋จ")

Dispatch Group์œผ๋กœ ์—ฌ๋Ÿฌ ์ž‘์—… ๊ด€๋ฆฌํ•˜๊ธฐ ๐Ÿ‘ฅ

์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋น„๋™๊ธฐ ์ž‘์—…์ด ๋ชจ๋‘ ๋๋‚ฌ์„ ๋•Œ ๋ญ”๊ฐ€๋ฅผ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? DispatchGroup์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ผ!

let group = DispatchGroup()

// ์ฒซ ๋ฒˆ์งธ ์ž‘์—…
group.enter()
DispatchQueue.global().async {
    print("์ด๋ฏธ์ง€ 1 ๋‹ค์šด๋กœ๋“œ ์ค‘...")
    Thread.sleep(forTimeInterval: 2)
    print("์ด๋ฏธ์ง€ 1 ์™„๋ฃŒ")
    group.leave()
}

// ๋‘ ๋ฒˆ์งธ ์ž‘์—…
group.enter()
DispatchQueue.global().async {
    print("์ด๋ฏธ์ง€ 2 ๋‹ค์šด๋กœ๋“œ ์ค‘...")
    Thread.sleep(forTimeInterval: 3)
    print("์ด๋ฏธ์ง€ 2 ์™„๋ฃŒ")
    group.leave()
}

// ์„ธ ๋ฒˆ์งธ ์ž‘์—…
group.enter()
DispatchQueue.global().async {
    print("์ด๋ฏธ์ง€ 3 ๋‹ค์šด๋กœ๋“œ ์ค‘...")
    Thread.sleep(forTimeInterval: 1)
    print("์ด๋ฏธ์ง€ 3 ์™„๋ฃŒ")
    group.leave()
}

// ๋ชจ๋“  ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด ์‹คํ–‰
group.notify(queue: .main) {
    print("๋ชจ๋“  ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ! ๐ŸŽ‰")
    // UI ์—…๋ฐ์ดํŠธ
}

enter()๋กœ ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ณ , leave()๋กœ ์™„๋ฃŒ๋ฅผ ์•Œ๋ ค์ค˜.
๋ชจ๋“  ์ž‘์—…์ด ๋๋‚˜๋ฉด notify์— ๋“ฑ๋กํ•œ ํด๋กœ์ €๊ฐ€ ์‹คํ–‰๋ผ. ์ •๋ง ํŽธ๋ฆฌํ•˜์ง€? ๐Ÿ˜Š

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ
enter()์™€ leave()์˜ ํ˜ธ์ถœ ํšŸ์ˆ˜๋Š” ๋ฐ˜๋“œ์‹œ ๊ฐ™์•„์•ผ ํ•ด!
enter()๋ฅผ 3๋ฒˆ ํ–ˆ์œผ๋ฉด leave()๋„ 3๋ฒˆ ํ•ด์•ผ notify๊ฐ€ ์‹คํ–‰๋ผ.
๋งŒ์•ฝ ๊ท ํ˜•์ด ์•ˆ ๋งž์œผ๋ฉด notify๊ฐ€ ์˜์›ํžˆ ์‹คํ–‰๋˜์ง€ ์•Š๊ฑฐ๋‚˜, ์•ฑ์ด ํฌ๋ž˜์‹œ๋  ์ˆ˜ ์žˆ์–ด.

Dispatch Barrier๋กœ ๋™๊ธฐํ™”ํ•˜๊ธฐ ๐Ÿšง

Concurrent Queue์—์„œ ํŠน์ • ์ž‘์—…๋งŒ ๋…์ ์ ์œผ๋กœ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์–ด.
์˜ˆ๋ฅผ ๋“ค์–ด, ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๋Š” ๊ฑด ๊ดœ์ฐฎ์ง€๋งŒ, ์“ฐ๊ธฐ ์ž‘์—…์€ ํ˜ผ์ž ํ•ด์•ผ ํ•  ๋•Œ ๋ง์ด์•ผ.

let concurrentQueue = DispatchQueue(label: "com.myapp.dataQueue", 
                                    attributes: .concurrent)
var data: [String] = []

// ์ฝ๊ธฐ ์ž‘์—… (๋™์‹œ ์‹คํ–‰ ๊ฐ€๋Šฅ)
func readData() {
    concurrentQueue.async {
        print("๋ฐ์ดํ„ฐ ์ฝ๊ธฐ: \(data)")
    }
}

// ์“ฐ๊ธฐ ์ž‘์—… (๋…์  ์‹คํ–‰)
func writeData(_ value: String) {
    concurrentQueue.async(flags: .barrier) {
        print("๋ฐ์ดํ„ฐ ์“ฐ๊ธฐ: \(value)")
        data.append(value)
    }
}

// ์‚ฌ์šฉ
readData()  // ๋™์‹œ ์‹คํ–‰ ๊ฐ€๋Šฅ
readData()  // ๋™์‹œ ์‹คํ–‰ ๊ฐ€๋Šฅ
writeData("์ƒˆ ๋ฐ์ดํ„ฐ")  // ๋‹ค๋ฅธ ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
readData()  // writeData๊ฐ€ ๋๋‚œ ํ›„ ์‹คํ–‰

barrier ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ์ž‘์—…์ด ์‹คํ–‰๋  ๋•Œ ๋‹ค๋ฅธ ์ž‘์—…๋“ค์ด ๋ชจ๋‘ ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค.
๊ทธ๋ฆฌ๊ณ  barrier ์ž‘์—…์ด ๋๋‚˜๋ฉด ๋‹ค์‹œ ๋™์‹œ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ด์ ธ. ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์„ ์ง€ํ‚ค๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ! ๐Ÿ›ก๏ธ

OperationQueue ๊นŠ์ด ์•Œ์•„๋ณด๊ธฐ ๐ŸŽฏ

์ด์ œ OperationQueue๋กœ ๋„˜์–ด๊ฐ€๋ณผ๊นŒ? GCD๊ฐ€ ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฅด๋‹ค๋ฉด, OperationQueue๋Š” ์ข€ ๋” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด.
๊ฐ์ฒด ์ง€ํ–ฅ์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์ด๋ผ ๋ณต์žกํ•œ ์ž‘์—… ํ๋ฆ„์„ ๊ด€๋ฆฌํ•˜๊ธฐ์— ์ข‹์•„.

Operation์˜ ๊ธฐ๋ณธ ๊ฐœ๋… ๐Ÿ“š

OperationQueue๋Š” Operation ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด. Operation์€ ์‹คํ–‰ํ•  ์ž‘์—…์„ ์บก์Аํ™”ํ•œ ๊ฐ์ฒด์•ผ.
๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด:

BlockOperation

ํด๋กœ์ €๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์ด์•ผ.
GCD์˜ ํด๋กœ์ € ๋ฐฉ์‹๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ, Operation์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.

let operation = BlockOperation {
    print("์ž‘์—… ์‹คํ–‰")
}

Custom Operation

Operation ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„์„œ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์ด์•ผ.
๋ณต์žกํ•œ ๋กœ์ง์ด๋‚˜ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ž‘์—…์— ์ ํ•ฉํ•ด.

class MyOperation: Operation {
    override func main() {
        print("์ปค์Šคํ…€ ์ž‘์—…")
    }
}

OperationQueue ์‚ฌ์šฉํ•˜๊ธฐ ๐Ÿš€

// OperationQueue ์ƒ์„ฑ
let operationQueue = OperationQueue()

// ๋™์‹œ ์‹คํ–‰ ๊ฐœ์ˆ˜ ์„ค์ • (๊ธฐ๋ณธ๊ฐ’์€ ์‹œ์Šคํ…œ์ด ๊ฒฐ์ •)
operationQueue.maxConcurrentOperationCount = 3

// BlockOperation ์ถ”๊ฐ€
let operation1 = BlockOperation {
    print("Operation 1 ์‹œ์ž‘")
    Thread.sleep(forTimeInterval: 2)
    print("Operation 1 ์™„๋ฃŒ")
}

let operation2 = BlockOperation {
    print("Operation 2 ์‹œ์ž‘")
    Thread.sleep(forTimeInterval: 1)
    print("Operation 2 ์™„๋ฃŒ")
}

// ํ์— ์ถ”๊ฐ€
operationQueue.addOperation(operation1)
operationQueue.addOperation(operation2)

// ๋˜๋Š” ํด๋กœ์ €๋กœ ๋ฐ”๋กœ ์ถ”๊ฐ€
operationQueue.addOperation {
    print("๊ฐ„๋‹จํ•œ ์ž‘์—…")
}

maxConcurrentOperationCount๋ฅผ ์„ค์ •ํ•˜๋ฉด ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ์ž‘์—… ์ˆ˜๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ์–ด.
1๋กœ ์„ค์ •ํ•˜๋ฉด Serial Queue์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๊ณ , ๊ธฐ๋ณธ๊ฐ’(-1)์€ ์‹œ์Šคํ…œ์ด ์•Œ์•„์„œ ๊ฒฐ์ •ํ•ด.

์˜์กด์„ฑ ๊ด€๋ฆฌ - OperationQueue์˜ ๊ฐ•๋ ฅํ•œ ๋ฌด๊ธฐ ๐Ÿ”—

OperationQueue์˜ ๊ฐ€์žฅ ํฐ ์žฅ์  ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ ์˜์กด์„ฑ ๊ด€๋ฆฌ์•ผ.
"A ์ž‘์—…์ด ๋๋‚˜์•ผ B ์ž‘์—…์„ ์‹œ์ž‘ํ•œ๋‹ค"๋Š” ์‹์˜ ์ˆœ์„œ๋ฅผ ์‰ฝ๊ฒŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด.

let downloadOperation = BlockOperation {
    print("์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ์ค‘...")
    Thread.sleep(forTimeInterval: 2)
    print("๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ")
}

let filterOperation = BlockOperation {
    print("ํ•„ํ„ฐ ์ ์šฉ ์ค‘...")
    Thread.sleep(forTimeInterval: 1)
    print("ํ•„ํ„ฐ ์ ์šฉ ์™„๋ฃŒ")
}

let uploadOperation = BlockOperation {
    print("์—…๋กœ๋“œ ์ค‘...")
    Thread.sleep(forTimeInterval: 1)
    print("์—…๋กœ๋“œ ์™„๋ฃŒ")
}

// ์˜์กด์„ฑ ์„ค์ •
filterOperation.addDependency(downloadOperation)  // ๋‹ค์šด๋กœ๋“œ ํ›„ ํ•„ํ„ฐ
uploadOperation.addDependency(filterOperation)    // ํ•„ํ„ฐ ํ›„ ์—…๋กœ๋“œ

let queue = OperationQueue()
queue.addOperations([uploadOperation, filterOperation, downloadOperation], 
                    waitUntilFinished: false)

// ์‹คํ–‰ ์ˆœ์„œ: ๋‹ค์šด๋กœ๋“œ โ†’ ํ•„ํ„ฐ โ†’ ์—…๋กœ๋“œ (์˜์กด์„ฑ์— ๋”ฐ๋ผ ์ž๋™ ์ •๋ ฌ!)

ํ์— ์ถ”๊ฐ€ํ•˜๋Š” ์ˆœ์„œ๋Š” ์ƒ๊ด€์—†์–ด. ์˜์กด์„ฑ๋งŒ ์ œ๋Œ€๋กœ ์„ค์ •ํ•˜๋ฉด ์‹œ์Šคํ…œ์ด ์•Œ์•„์„œ ์˜ฌ๋ฐ”๋ฅธ ์ˆœ์„œ๋กœ ์‹คํ–‰ํ•ด์ค˜! ๐ŸŽฏ
์ด๊ฑด GCD๋กœ๋Š” ๊ตฌํ˜„ํ•˜๊ธฐ ๊นŒ๋‹ค๋กœ์šด ๊ธฐ๋Šฅ์ด์•ผ. ์žฌ๋Šฅ๋„ท์˜ ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ๋„ ์ด ๊ธฐ๋Šฅ ๋•Œ๋ฌธ์— OperationQueue๋ฅผ ์„ ํ˜ธํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์ด ๋งŽ์•„.

Operation ์˜์กด์„ฑ ํ๋ฆ„ ๋‹ค์šด๋กœ๋“œ Operation A ํ•„ํ„ฐ ์ ์šฉ Operation B ์—…๋กœ๋“œ Operation C ์˜์กด์„ฑ ์˜์กด์„ฑ B.addDependency(A) C.addDependency(B) โ†’ ์‹คํ–‰ ์ˆœ์„œ: A โ†’ B โ†’ C ์ž๋™ ๋ณด์žฅ

์ž‘์—… ์ทจ์†Œํ•˜๊ธฐ โŒ

OperationQueue์˜ ๋˜ ๋‹ค๋ฅธ ์žฅ์ ์€ ์ž‘์—…์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑฐ์•ผ.
์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์šด๋กœ๋“œ๋ฅผ ์ค‘๋‹จํ•˜๊ฑฐ๋‚˜, ํ™”๋ฉด์„ ๋ฒ—์–ด๋‚ฌ์„ ๋•Œ ๋ถˆํ•„์š”ํ•œ ์ž‘์—…์„ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ์–ด.

class DownloadOperation: Operation {
    override func main() {
        // ์ทจ์†Œ ํ™•์ธ
        guard !isCancelled else {
            print("์ž‘์—…์ด ์ทจ์†Œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค")
            return
        }
        
        print("๋‹ค์šด๋กœ๋“œ ์‹œ์ž‘")
        
        for i in 1...10 {
            // ์ฃผ๊ธฐ์ ์œผ๋กœ ์ทจ์†Œ ํ™•์ธ
            if isCancelled {
                print("๋‹ค์šด๋กœ๋“œ ์ค‘๋‹จ๋จ (์ง„ํ–‰๋ฅ : \(i * 10)%)")
                return
            }
            
            Thread.sleep(forTimeInterval: 0.5)
            print("์ง„ํ–‰๋ฅ : \(i * 10)%")
        }
        
        print("๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ")
    }
}

let operation = DownloadOperation()
let queue = OperationQueue()
queue.addOperation(operation)

// 2์ดˆ ํ›„ ์ทจ์†Œ
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
    operation.cancel()
    print("์ทจ์†Œ ์š”์ฒญ ์ „์†ก")
}

isCancelled ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ™•์ธํ•ด์„œ ์ทจ์†Œ ์š”์ฒญ์ด ์žˆ์œผ๋ฉด ์ž‘์—…์„ ์ค‘๋‹จํ•ด.
GCD์—์„œ๋Š” ์ด๋Ÿฐ ์ทจ์†Œ ๊ธฐ๋Šฅ์„ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š”๋ฐ, Operation์€ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•ด์ค˜์„œ ํŽธ๋ฆฌํ•ด! ๐Ÿ‘

๐Ÿ’ก ์ทจ์†Œ ์‹œ ์ฃผ์˜์‚ฌํ•ญ
cancel()์„ ํ˜ธ์ถœํ•œ๋‹ค๊ณ  ์ž‘์—…์ด ์ฆ‰์‹œ ๋ฉˆ์ถ”๋Š” ๊ฑด ์•„๋‹ˆ์•ผ.
Operation ๋‚ด๋ถ€์—์„œ isCancelled๋ฅผ ํ™•์ธํ•˜๊ณ  ์ง์ ‘ ์ข…๋ฃŒ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ค˜์•ผ ํ•ด.
ํŠนํžˆ ๊ธด ์ž‘์—…์˜ ๊ฒฝ์šฐ, ์ค‘๊ฐ„์ค‘๊ฐ„ ์ทจ์†Œ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒŒ ์ข‹์•„.

์šฐ์„ ์ˆœ์œ„ ์„ค์ •ํ•˜๊ธฐ โญ

Operation์—๋Š” ์šฐ์„ ์ˆœ์œ„๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด. ์ค‘์š”ํ•œ ์ž‘์—…์„ ๋จผ์ € ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์œ ์šฉํ•˜์ง€.

let highPriorityOp = BlockOperation {
    print("์ค‘์š”ํ•œ ์ž‘์—…")
}
highPriorityOp.queuePriority = .veryHigh

let normalOp = BlockOperation {
    print("์ผ๋ฐ˜ ์ž‘์—…")
}
normalOp.queuePriority = .normal

let lowPriorityOp = BlockOperation {
    print("๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„ ์ž‘์—…")
}
lowPriorityOp.queuePriority = .veryLow

let queue = OperationQueue()
queue.addOperations([lowPriorityOp, normalOp, highPriorityOp], 
                    waitUntilFinished: false)

// ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ์‹คํ–‰ ์ˆœ์„œ๊ฐ€ ์กฐ์ •๋จ

์šฐ์„ ์ˆœ์œ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„:
โ€ข veryHigh: ๊ฐ€์žฅ ๋†’์Œ
โ€ข high: ๋†’์Œ
โ€ข normal: ๋ณดํ†ต (๊ธฐ๋ณธ๊ฐ’)
โ€ข low: ๋‚ฎ์Œ
โ€ข veryLow: ๊ฐ€์žฅ ๋‚ฎ์Œ

KVO๋กœ ์ž‘์—… ์ƒํƒœ ๊ด€์ฐฐํ•˜๊ธฐ ๐Ÿ‘€

Operation์€ KVO(Key-Value Observing)๋ฅผ ์ง€์›ํ•ด์„œ ์ž‘์—…์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ์–ด.

let operation = BlockOperation {
    print("์ž‘์—… ์‹คํ–‰ ์ค‘...")
    Thread.sleep(forTimeInterval: 2)
    print("์ž‘์—… ์™„๋ฃŒ")
}

// ์™„๋ฃŒ ์ƒํƒœ ๊ด€์ฐฐ
operation.completionBlock = {
    print("โœ… Operation ์™„๋ฃŒ๋จ!")
}

// isFinished ํ”„๋กœํผํ‹ฐ ๊ด€์ฐฐ
var observer: NSKeyValueObservation?
observer = operation.observe(\.isFinished, options: [.new]) { op, change in
    if change.newValue == true {
        print("์ž‘์—…์ด ์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค")
    }
}

let queue = OperationQueue()
queue.addOperation(operation)

completionBlock์€ ์ž‘์—…์ด ๋๋‚˜๋ฉด ์ž๋™์œผ๋กœ ์‹คํ–‰๋ผ.
UI ์—…๋ฐ์ดํŠธ๋‚˜ ํ›„์† ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ข‹์•„. ๋‹จ, ์ด ๋ธ”๋ก์€ ์ž‘์—…์ด ์‹คํ–‰๋œ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋‹ˆ๊นŒ
UI ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•˜๋ฉด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋กœ ์ „ํ™˜ํ•ด์•ผ ํ•ด!

GCD vs OperationQueue: ์–ธ์ œ ๋ญ˜ ์จ์•ผ ํ• ๊นŒ? ๐Ÿคทโ€โ™‚๏ธ

์ž, ์ด์ œ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ๋‹ค ๋ฐฐ์› ์œผ๋‹ˆ ์„ ํƒ์˜ ๊ธฐ๋กœ์— ์„ฐ์–ด.
"๋„๋Œ€์ฒด ์–ธ์ œ GCD๋ฅผ ์“ฐ๊ณ , ์–ธ์ œ OperationQueue๋ฅผ ์จ์•ผ ํ•˜์ง€?" ๐Ÿค”
๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ ์„ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ.

GCD๋ฅผ ์„ ํƒํ•ด์•ผ ํ•  ๋•Œ โšก

โœ… ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฅธ ๋น„๋™๊ธฐ ์ž‘์—…
โœ… ์ฝ”๋“œ๊ฐ€ ์งง๊ณ  ๋ช…ํ™•ํ•  ๋•Œ
โœ… ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ
โœ… ํด๋กœ์ € ๊ธฐ๋ฐ˜์œผ๋กœ ์ถฉ๋ถ„ํ•  ๋•Œ
โœ… ์‹œ์Šคํ…œ ๋ ˆ๋ฒจ์˜ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•  ๋•Œ

์˜ˆ์‹œ:
โ€ข API ํ˜ธ์ถœ ํ›„ UI ์—…๋ฐ์ดํŠธ
โ€ข ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
โ€ข ๊ฐ„๋‹จํ•œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๊ณ„์‚ฐ
โ€ข ํƒ€์ด๋จธ ๊ธฐ๋ฐ˜ ์ž‘์—…

OperationQueue๋ฅผ ์„ ํƒํ•ด์•ผ ํ•  ๋•Œ ๐ŸŽฏ

โœ… ๋ณต์žกํ•œ ์ž‘์—… ํ๋ฆ„ ๊ด€๋ฆฌ
โœ… ์ž‘์—… ๊ฐ„ ์˜์กด์„ฑ์ด ์žˆ์„ ๋•Œ
โœ… ์ž‘์—… ์ทจ์†Œ๊ฐ€ ํ•„์š”ํ•  ๋•Œ
โœ… ์šฐ์„ ์ˆœ์œ„ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•  ๋•Œ
โœ… ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ž‘์—… ๋‹จ์œ„

์˜ˆ์‹œ:
โ€ข ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์˜ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
โ€ข ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ (์ทจ์†Œ ๊ฐ€๋Šฅ)
โ€ข ๋ณต์žกํ•œ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ
โ€ข ๋ฐฐ์น˜ ์ž‘์—… ๊ด€๋ฆฌ

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

์‹ค์ „ ์˜ˆ์ œ: ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ์•ฑ ๋งŒ๋“ค๊ธฐ ๐Ÿ“ฑ

์ด๋ก ๋งŒ ๋ฐฐ์šฐ๋ฉด ์žฌ๋ฏธ์—†์œผ๋‹ˆ๊นŒ, ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊นŒ?
์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€๋ฅผ ๋™์‹œ์— ๋‹ค์šด๋กœ๋“œํ•˜๊ณ , ์ง„ํ–‰ ์ƒํ™ฉ์„ ๋ณด์—ฌ์ฃผ๊ณ , ์ทจ์†Œ๋„ ๊ฐ€๋Šฅํ•œ ์•ฑ์ด์•ผ.

GCD ๋ฒ„์ „ ๐Ÿš€

class ImageDownloaderGCD {
    func downloadImages(urls: [URL], completion: @escaping ([UIImage]) -> Void) {
        var images: [UIImage] = []
        let group = DispatchGroup()
        let queue = DispatchQueue(label: "com.myapp.imageDownload", 
                                  attributes: .concurrent)
        
        for url in urls {
            group.enter()
            queue.async {
                if let data = try? Data(contentsOf: url),
                   let image = UIImage(data: data) {
                    // ์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐฐ์—ด์— ์ถ”๊ฐ€
                    DispatchQueue.main.async {
                        images.append(image)
                        group.leave()
                    }
                } else {
                    group.leave()
                }
            }
        }
        
        group.notify(queue: .main) {
            completion(images)
        }
    }
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
let downloader = ImageDownloaderGCD()
let urls = [
    URL(string: "https://example.com/image1.jpg")!,
    URL(string: "https://example.com/image2.jpg")!,
    URL(string: "https://example.com/image3.jpg")!
]

downloader.downloadImages(urls: urls) { images in
    print("๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ: \(images.count)๊ฐœ์˜ ์ด๋ฏธ์ง€")
    // UI ์—…๋ฐ์ดํŠธ
}

OperationQueue ๋ฒ„์ „ ๐ŸŽฏ

class ImageDownloadOperation: Operation {
    let url: URL
    var downloadedImage: UIImage?
    
    init(url: URL) {
        self.url = url
        super.init()
    }
    
    override func main() {
        guard !isCancelled else { return }
        
        if let data = try? Data(contentsOf: url),
           let image = UIImage(data: data) {
            guard !isCancelled else { return }
            downloadedImage = image
        }
    }
}

class ImageDownloaderOperation {
    private let queue = OperationQueue()
    
    init() {
        queue.maxConcurrentOperationCount = 3  // ๋™์‹œ์— 3๊ฐœ๊นŒ์ง€
    }
    
    func downloadImages(urls: [URL], completion: @escaping ([UIImage]) -> Void) {
        var operations: [ImageDownloadOperation] = []
        
        for url in urls {
            let operation = ImageDownloadOperation(url: url)
            operations.append(operation)
        }
        
        // ์™„๋ฃŒ ๋ธ”๋ก ์„ค์ •
        let completionOperation = BlockOperation {
            let images = operations.compactMap { $0.downloadedImage }
            DispatchQueue.main.async {
                completion(images)
            }
        }
        
        // ๋ชจ๋“  ๋‹ค์šด๋กœ๋“œ ์ž‘์—…์— ์˜์กด์„ฑ ์ถ”๊ฐ€
        for operation in operations {
            completionOperation.addDependency(operation)
        }
        
        queue.addOperations(operations, waitUntilFinished: false)
        queue.addOperation(completionOperation)
    }
    
    func cancelAll() {
        queue.cancelAllOperations()
    }
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
let downloader = ImageDownloaderOperation()
let urls = [
    URL(string: "https://example.com/image1.jpg")!,
    URL(string: "https://example.com/image2.jpg")!,
    URL(string: "https://example.com/image3.jpg")!
]

downloader.downloadImages(urls: urls) { images in
    print("๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ: \(images.count)๊ฐœ์˜ ์ด๋ฏธ์ง€")
}

// ํ•„์š”ํ•˜๋ฉด ์ทจ์†Œ
// downloader.cancelAll()

๋‘ ๋ฒ„์ „ ๋ชจ๋‘ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ํ•˜์ง€๋งŒ, OperationQueue ๋ฒ„์ „์€ ์ทจ์†Œ ๊ธฐ๋Šฅ์ด ์žˆ๊ณ  ๋™์‹œ ์‹คํ–‰ ๊ฐœ์ˆ˜๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ์–ด.
์ƒํ™ฉ์— ๋”ฐ๋ผ ์„ ํƒํ•˜๋ฉด ๋ผ! ๐ŸŽจ

์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ โšก

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ์‚ฌ์šฉํ•  ๋•Œ ์„ฑ๋Šฅ์„ ์ตœ๋Œ€๋กœ ๋Œ์–ด์˜ฌ๋ฆฌ๋Š” ๋ช‡ ๊ฐ€์ง€ ํŒ์„ ์•Œ๋ ค์ค„๊ฒŒ.

1. ์Šค๋ ˆ๋“œ๋ฅผ ๋„ˆ๋ฌด ๋งŽ์ด ๋งŒ๋“ค์ง€ ๋งˆ ๐Ÿšซ
์Šค๋ ˆ๋“œ๋ฅผ ๋ฌดํ•œ์ • ๋งŒ๋“ค๋ฉด ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์ด ๋–จ์–ด์ ธ. ์Šค๋ ˆ๋“œ ๊ฐ„ ์ „ํ™˜(Context Switching)์—๋„ ๋น„์šฉ์ด ๋“ค๊ฑฐ๋“ .
GCD๋Š” ์‹œ์Šคํ…œ์ด ์•Œ์•„์„œ ๊ด€๋ฆฌํ•˜์ง€๋งŒ, OperationQueue๋Š” maxConcurrentOperationCount๋ฅผ ์ ์ ˆํžˆ ์„ค์ •ํ•ด์ค˜.

2. ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” UI๋งŒ ๐ŸŽจ
๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ๋Š” ์ ˆ๋Œ€ ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ํ•˜์ง€ ๋งˆ. 0.1์ดˆ๋งŒ ๋ธ”๋กœํ‚น๋˜์–ด๋„ ์‚ฌ์šฉ์ž๋Š” ๋ฒ„๋ฒ…์ž„์„ ๋А๊ปด.
๋„คํŠธ์›Œํฌ ์š”์ฒญ, ํŒŒ์ผ I/O, ๋ณต์žกํ•œ ๊ณ„์‚ฐ์€ ๋ชจ๋‘ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ!

3. ์ ์ ˆํ•œ QoS ์„ ํƒ ๐ŸŽฏ
์ž‘์—…์˜ ์ค‘์š”๋„์— ๋งž๋Š” QoS๋ฅผ ์„ ํƒํ•ด. ๋ชจ๋“  ๊ฑธ userInteractive๋กœ ํ•˜๋ฉด ์‹œ์Šคํ…œ์ด ํ˜ผ๋ž€์Šค๋Ÿฌ์›Œํ•ด.
โ€ข ์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ž‘์—…: userInitiated
โ€ข ๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋™๊ธฐํ™”: utility ๋˜๋Š” background

4. Race Condition ์กฐ์‹ฌ โš ๏ธ
์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์–ด.
Serial Queue, Dispatch Barrier, ๋˜๋Š” Lock์„ ์‚ฌ์šฉํ•ด์„œ ๋™๊ธฐํ™”ํ•ด์ค˜.

Race Condition ์˜ˆ๋ฐฉํ•˜๊ธฐ ๐Ÿ”’

// ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ์ฝ”๋“œ โŒ
class Counter {
    var count = 0
    
    func increment() {
        DispatchQueue.global().async {
            self.count += 1  // ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ๋™์‹œ ์ ‘๊ทผ!
        }
    }
}

// ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 1: Serial Queue โœ…
class SafeCounter1 {
    private var count = 0
    private let queue = DispatchQueue(label: "com.myapp.counter")
    
    func increment() {
        queue.async {
            self.count += 1
        }
    }
    
    func getCount() -> Int {
        return queue.sync {
            return count
        }
    }
}

// ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• 2: Dispatch Barrier โœ…
class SafeCounter2 {
    private var count = 0
    private let queue = DispatchQueue(label: "com.myapp.counter", 
                                      attributes: .concurrent)
    
    func increment() {
        queue.async(flags: .barrier) {
            self.count += 1
        }
    }
    
    func getCount() -> Int {
        return queue.sync {
            return count
        }
    }
}

๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ์€ ์ •๋ง ์ค‘์š”ํ•ด. ๋ฒ„๊ทธ๋ฅผ ์ฐพ๊ธฐ๋„ ์–ด๋ ต๊ณ , ์žฌํ˜„ํ•˜๊ธฐ๋„ ํž˜๋“ค๊ฑฐ๋“ .
์ฒ˜์Œ๋ถ€ํ„ฐ ์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ด์ž! ๐Ÿ›ก๏ธ

๋””๋ฒ„๊น…๊ณผ ํ…Œ์ŠคํŠธ ๐Ÿ›

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ฝ”๋“œ๋Š” ๋””๋ฒ„๊น…์ด ๊นŒ๋‹ค๋กœ์›Œ. ํƒ€์ด๋ฐ์— ๋”ฐ๋ผ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๊ฑฐ๋“ .
๋ช‡ ๊ฐ€์ง€ ์œ ์šฉํ•œ ๋””๋ฒ„๊น… ํŒ์„ ์•Œ๋ ค์ค„๊ฒŒ.

๐Ÿ’ก Thread Sanitizer ์‚ฌ์šฉํ•˜๊ธฐ
Xcode์˜ Thread Sanitizer๋Š” Race Condition์„ ์ž๋™์œผ๋กœ ์ฐพ์•„์ค˜.
Scheme ์„ค์ •์—์„œ "Thread Sanitizer"๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  ์•ฑ์„ ์‹คํ–‰ํ•˜๋ฉด ๋ผ.
๋ฌธ์ œ๊ฐ€ ๋ฐœ๊ฒฌ๋˜๋ฉด ์ •ํ™•ํ•œ ์œ„์น˜๋ฅผ ์•Œ๋ ค์ฃผ๋‹ˆ๊นŒ ์ •๋ง ์œ ์šฉํ•ด!

// ํ˜„์žฌ ์Šค๋ ˆ๋“œ ํ™•์ธํ•˜๊ธฐ
func printCurrentThread() {
    if Thread.isMainThread {
        print("๐ŸŽจ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ ์ค‘")
    } else {
        print("โš™๏ธ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ ์ค‘")
        print("์Šค๋ ˆ๋“œ ์ด๋ฆ„: \(Thread.current.name ?? "์ด๋ฆ„ ์—†์Œ")")
    }
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
DispatchQueue.main.async {
    printCurrentThread()  // ๋ฉ”์ธ ์Šค๋ ˆ๋“œ
}

DispatchQueue.global().async {
    printCurrentThread()  // ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ
}

์„ฑ๋Šฅ ์ธก์ •ํ•˜๊ธฐ โฑ๏ธ

// ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •
func measureTime(label: String, block: () -> Void) {
    let start = CFAbsoluteTimeGetCurrent()
    block()
    let end = CFAbsoluteTimeGetCurrent()
    print("\(label) ์‹คํ–‰ ์‹œ๊ฐ„: \(String(format: "%.4f", end - start))์ดˆ")
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
measureTime(label: "์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ") {
    // ๋ฌด๊ฑฐ์šด ์ž‘์—…
    processImages()
}

// ๋น„๋™๊ธฐ ์ž‘์—… ์ธก์ •
let start = CFAbsoluteTimeGetCurrent()
DispatchQueue.global().async {
    // ์ž‘์—…
    let end = CFAbsoluteTimeGetCurrent()
    print("์‹คํ–‰ ์‹œ๊ฐ„: \(end - start)์ดˆ")
}

์‹ค๋ฌด์—์„œ ์ž์ฃผ ํ•˜๋Š” ์‹ค์ˆ˜๋“ค โš ๏ธ

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

์‹ค์ˆ˜ 1: ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ sync ํ˜ธ์ถœ ๐Ÿ’ฅ

// ๋ฐ๋“œ๋ฝ ๋ฐœ์ƒ! โŒ
DispatchQueue.main.sync {
    print("์ด ์ฝ”๋“œ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Œ")
}

๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋ฉ”์ธ ํ์— sync๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋ฐ๋“œ๋ฝ์ด ๋ฐœ์ƒํ•ด.
๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ์ž๊ธฐ ์ž์‹ ์ด ๋๋‚˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ๋˜๊ฑฐ๋“ . ์ ˆ๋Œ€ ํ•˜์ง€ ๋งˆ!

์‹ค์ˆ˜ 2: ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ UI ์—…๋ฐ์ดํŠธ ๐ŸŽจ

// ํฌ๋ž˜์‹œ ์œ„ํ—˜! โŒ
DispatchQueue.global().async {
    self.label.text = "์™„๋ฃŒ"  // UI๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ๋งŒ!
}

UIKit์€ ์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•˜์ง€ ์•Š์•„. ๋ชจ๋“  UI ์—…๋ฐ์ดํŠธ๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ํ•ด์•ผ ํ•ด.

// ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ• โœ…
DispatchQueue.global().async {
    let result = self.heavyCalculation()
    DispatchQueue.main.async {
        self.label.text = result
    }
}

์‹ค์ˆ˜ 3: Strong Reference Cycle ๐Ÿ”„

// ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜! โŒ
DispatchQueue.global().async {
    self.processData()  // self๋ฅผ ๊ฐ•ํ•˜๊ฒŒ ์ฐธ์กฐ
}

ํด๋กœ์ €์—์„œ self๋ฅผ ์บก์ฒ˜ํ•˜๋ฉด ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด.

// ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ• โœ…
DispatchQueue.global().async { [weak self] in
    guard let self = self else { return }
    self.processData()
}

์‹ค์ˆ˜ 4: DispatchGroup์˜ enter/leave ๋ถˆ๊ท ํ˜• โš–๏ธ

// notify๊ฐ€ ์‹คํ–‰ ์•ˆ ๋จ! โŒ
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
    // ์ž‘์—…
    // leave()๋ฅผ ๊นœ๋นกํ•จ!
}
group.notify(queue: .main) {
    print("์˜์›ํžˆ ์‹คํ–‰ ์•ˆ ๋จ")
}

enter()์™€ leave()๋Š” ๋ฐ˜๋“œ์‹œ ์Œ์„ ์ด๋ค„์•ผ ํ•ด. ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด notify๊ฐ€ ์‹คํ–‰ ์•ˆ ๋ผ!

๊ณ ๊ธ‰ ํŒจํ„ด: async/await์™€์˜ ์กฐํ•ฉ ๐Ÿš€

iOS 13๋ถ€ํ„ฐ Swift์— async/await๊ฐ€ ๋„์ž…๋˜๋ฉด์„œ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ํ›จ์”ฌ ์‰ฌ์›Œ์กŒ์–ด.
GCD, OperationQueue์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋”์šฑ ๊ฐ•๋ ฅํ•ด์ ธ!

// GCD๋ฅผ async/await๋กœ ๊ฐ์‹ธ๊ธฐ
func downloadImage(from url: URL) async throws -> UIImage {
    return try await withCheckedThrowingContinuation { continuation in
        DispatchQueue.global().async {
            do {
                let data = try Data(contentsOf: url)
                if let image = UIImage(data: data) {
                    continuation.resume(returning: image)
                } else {
                    continuation.resume(throwing: ImageError.invalidData)
                }
            } catch {
                continuation.resume(throwing: error)
            }
        }
    }
}

// ์‚ฌ์šฉ
Task {
    do {
        let image = try await downloadImage(from: url)
        imageView.image = image
    } catch {
        print("์—๋Ÿฌ: \(error)")
    }
}

async/await๋Š” ์ฝ”๋“œ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜์„œ ๊ฐ€๋…์„ฑ์ด ํ›จ์”ฌ ์ข‹์•„.
ํ•˜์ง€๋งŒ GCD์™€ OperationQueue์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ๋“ค(QoS, ์˜์กด์„ฑ ๊ด€๋ฆฌ ๋“ฑ)์€ ์—ฌ์ „ํžˆ ์œ ์šฉํ•˜๋‹ˆ๊นŒ
์ƒํ™ฉ์— ๋งž๊ฒŒ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ตœ๊ณ ์•ผ! ๐ŸŽฏ

์‹ค์ „ ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด ๐Ÿ’ก

๋ฐฐ์šด ๋‚ด์šฉ์„ ์‹ค์Šตํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด๋ฅผ ๋ช‡ ๊ฐ€์ง€ ์ œ์•ˆํ• ๊ฒŒ.

ํ”„๋กœ์ ํŠธ 1: ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ์•ฑ

์—ฌ๋Ÿฌ ํ•„ํ„ฐ๋ฅผ ๋™์‹œ์— ์ ์šฉํ•ด์„œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์•ฑ.
์‚ฌ์šฉ ๊ธฐ์ˆ :
โ€ข OperationQueue๋กœ ํ•„ํ„ฐ ์ž‘์—… ๊ด€๋ฆฌ
โ€ข ์˜์กด์„ฑ์œผ๋กœ ์ˆœ์ฐจ ์ฒ˜๋ฆฌ
โ€ข ์ทจ์†Œ ๊ธฐ๋Šฅ์œผ๋กœ ๋ถˆํ•„์š”ํ•œ ์ž‘์—… ์ค‘๋‹จ

ํ”„๋กœ์ ํŠธ 2: ํŒŒ์ผ ๋‹ค์šด๋กœ๋”

์—ฌ๋Ÿฌ ํŒŒ์ผ์„ ๋™์‹œ์— ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์ง„ํ–‰๋ฅ ์„ ํ‘œ์‹œํ•˜๋Š” ์•ฑ.
์‚ฌ์šฉ ๊ธฐ์ˆ :
โ€ข GCD๋กœ ๋™์‹œ ๋‹ค์šด๋กœ๋“œ
โ€ข DispatchGroup์œผ๋กœ ์™„๋ฃŒ ๊ฐ์ง€
โ€ข ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ UI ์—…๋ฐ์ดํŠธ

ํ”„๋กœ์ ํŠธ 3: ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™” ์•ฑ

๋กœ์ปฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์„œ๋ฒ„๋ฅผ ๋™๊ธฐํ™”ํ•˜๋Š” ์•ฑ.
์‚ฌ์šฉ ๊ธฐ์ˆ :
โ€ข Serial Queue๋กœ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๋ณด์žฅ
โ€ข OperationQueue๋กœ ๋ณต์žกํ•œ ๋™๊ธฐํ™” ๋กœ์ง
โ€ข ์šฐ์„ ์ˆœ์œ„๋กœ ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ ๋จผ์ € ์ฒ˜๋ฆฌ

๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ ๐ŸŽ“

์™€, ์—ฌ๊ธฐ๊นŒ์ง€ ์˜ค๋А๋ผ ์ˆ˜๊ณ ํ–ˆ์–ด! ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์€ ์ฒ˜์Œ์—” ์–ด๋ ต๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ,
๊ณ„์† ์—ฐ์Šตํ•˜๋‹ค ๋ณด๋ฉด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์†์— ์ต๊ฒŒ ๋ผ. ๐ŸŽฏ

ํ•ต์‹ฌ ์ •๋ฆฌ ๐Ÿ“

GCD (Grand Central Dispatch)
โ€ข ๊ฐ„๋‹จํ•˜๊ณ  ๋น ๋ฅธ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
โ€ข ํด๋กœ์ € ๊ธฐ๋ฐ˜์˜ ์ง๊ด€์ ์ธ API
โ€ข ์‹œ์Šคํ…œ ๋ ˆ๋ฒจ ์ตœ์ ํ™”
โ€ข ๊ฐ€๋ฒผ์šด ์ž‘์—…์— ์ตœ์ 

OperationQueue
โ€ข ๊ฐ์ฒด ์ง€ํ–ฅ์  ์ ‘๊ทผ
โ€ข ์˜์กด์„ฑ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ
โ€ข ์ž‘์—… ์ทจ์†Œ ๋ฐ ์šฐ์„ ์ˆœ์œ„ ์„ค์ •
โ€ข ๋ณต์žกํ•œ ์ž‘์—… ํ๋ฆ„์— ์ตœ์ 

๊ณตํ†ต ์ฃผ์˜์‚ฌํ•ญ
โ€ข UI๋Š” ํ•ญ์ƒ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ
โ€ข Race Condition ์กฐ์‹ฌ
โ€ข Strong Reference Cycle ๋ฐฉ์ง€
โ€ข ์ ์ ˆํ•œ QoS ์„ ํƒ

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์€ ํ˜„๋Œ€ ์•ฑ ๊ฐœ๋ฐœ์—์„œ ํ•„์ˆ˜์ ์ธ ๊ธฐ์ˆ ์ด์•ผ.
์‚ฌ์šฉ์ž๋“ค์€ ๋ถ€๋“œ๋Ÿฝ๊ณ  ๋ฐ˜์‘์„ฑ ์ข‹์€ ์•ฑ์„ ์›ํ•˜๊ฑฐ๋“ . ๐Ÿ“ฑ
์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ๋ฉ‹์ง„ ์•ฑ์„ ๋งŒ๋“ค์–ด๋ด!

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค๊ณผ ์ง€์‹์„ ๋‚˜๋ˆ„๋Š” ๊ฒƒ๋„ ์ข‹์€ ํ•™์Šต ๋ฐฉ๋ฒ•์ด์•ผ.
์งˆ๋ฌธํ•˜๊ณ , ๋‹ต๋ณ€ํ•˜๊ณ , ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ•˜๋ฉด์„œ ํ•จ๊ป˜ ์„ฑ์žฅํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ . ๐ŸŒฑ
ํ˜น์‹œ ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ์œผ๋ฉด ์ฃผ์ €ํ•˜์ง€ ๋ง๊ณ  ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๋ฌผ์–ด๋ด. ๋‹ค๋“ค ๊ธฐ๊บผ์ด ๋„์™€์ค„ ๊ฑฐ์•ผ!

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ๋งˆ์Šคํ„ฐ๊ฐ€ ๋˜๋Š” ๊ทธ๋‚ ๊นŒ์ง€, ํ™”์ดํŒ…! ๐Ÿ’ชโœจ
๋ฒ„๊ทธ ์—†๋Š” ์ฝ”๋“œ, ๋ถ€๋“œ๋Ÿฌ์šด ์•ฑ์„ ๋งŒ๋“œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ธธ ์‘์›ํ• ๊ฒŒ! ๐Ÿš€

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.