๐ŸŒ€ iOS์˜ Main Run Loop ์ดํ•ดํ•˜๊ธฐ

SeBinยท2025๋…„ 3์›” 8์ผ
post-thumbnail

๐ŸŒ€ iOS์˜ Main Run Loop

iOS ์•ฑ์ด ์‹คํ–‰๋˜๋ฉด, ํ™”๋ฉด์€ ๊ณ„์† ๋ฐ˜์‘ํ•˜๊ณ , ํ„ฐ์น˜๋‚˜ ์ œ์Šค์ฒ˜์—๋„ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋ฐ˜์‘ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ๊ฒƒ์˜ ์ค‘์‹ฌ์—๋Š” ๋ฐ”๋กœ Main Run Loop๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.


Run Loop๋ž€?

Run Loop๋Š” ๋ง ๊ทธ๋Œ€๋กœ '๋ฃจํ”„(Loop)'์ž…๋‹ˆ๋‹ค.

iOS ๋‚ด์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๊ณ , UI ์—…๋ฐ์ดํŠธ๊นŒ์ง€ ์ œ์–ดํ•˜๋Š” ์ค‘์‹ฌ ์‹œ์Šคํ…œ ๋ฃจํ”„์ž…๋‹ˆ๋‹ค.

macOS์™€ iOS ์•ฑ์€ ๋ชจ๋‘ ์ด RunLoop๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.

์•ฑ์ด ์ผœ์ง„ ์ˆœ๊ฐ„๋ถ€ํ„ฐ ๊บผ์งˆ ๋•Œ ๊นŒ์ง€, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๊ณ„์† ์ˆœํ™˜ํ•ฉ๋‹ˆ๋‹ค.


Main Run Loop๋Š” ์–ด๋–ค ์ผ์„ ํ• ๊นŒ?

  • ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ์ง€ (ํ„ฐ์น˜, ํ‚ค๋ณด๋“œ ๋“ฑ)
  • ์ œ์Šค์ฒ˜ ์ฒ˜๋ฆฌ
  • ํƒ€์ด๋จธ (Timer)
  • ๋„คํŠธ์›Œํฌ ์‘๋‹ต ์ฒ˜๋ฆฌ (์˜ˆ: URLSession)
  • UI ์—…๋ฐ์ดํŠธ (layoutSubviews)
  • ์• ๋‹ˆ๋ฉ”์ด์…˜

RunLoop์˜ ์‹คํ–‰ ์ˆœ์„œ

์•„๋ž˜๋Š” RunLoop๊ฐ€ ํ•œ ๋ฐ”ํ€ด ๋„๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค

  1. Input ์†Œ์Šค ๊ฐ์ง€
    • Touch, Timer, Network ๋“ฑ์˜ ์ด๋ฒคํŠธ๊ฐ€ ๋“ค์–ด์˜ด
  2. Event Queue์—์„œ ์ด๋ฒคํŠธ ๊บผ๋ƒ„
    • UIEvent ๋“ฑ์ด ํ์— ์Œ“์—ฌ ์žˆ๋‹ค๊ฐ€ ์ฒ˜๋ฆฌ๋จ
  3. ์ ์ ˆํ•œ Handler์—๊ฒŒ ์ „๋‹ฌ
    • ์˜ˆ: UIResponder โ†’ ViewController โ†’ View
  4. ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
  5. Display ์—…๋ฐ์ดํŠธ ํƒ€์ด๋ฐ ์ฒดํฌ
    • ์ด ์‹œ์ ์— setNeedsLayout()์ด๋‚˜ setNeedsDisplay()๋กœ ์š”์ฒญ๋œ ๋ทฐ ์—…๋ฐ์ดํŠธ๊ฐ€ ์‹คํ–‰๋จ
  6. ๋ฃจํ”„ ๋ฐ˜๋ณต

RunLoop๋Š” ์ด๋ฒคํŠธ ๊ฐ์ง€ โ†’ ์ฒ˜๋ฆฌ โ†’ UI ์—…๋ฐ์ดํŠธ ์ˆœ์„œ๋กœ ํ•œ ๋ฐ”ํ€ด๋ฅผ ๋•๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ํ™”๋ฉด ์ฃผ์‚ฌ์œจ(60Hz, ์ฆ‰ 1์ดˆ์— 60๋ฒˆ)์— ๋งž์ถฐ UI๋ฅผ ๊ฐฑ์‹ ํ•˜๋ฉฐ, ์ด ๊ณผ์ •์—์„œ ๋ถ€๋“œ๋Ÿฌ์šด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.


RunLoop๋Š” ์–ธ์ œ ์ข…๋ฃŒ๋ ๊นŒ?

RunLoop๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ฑ์ด ์ข…๋ฃŒ๋˜๊ฑฐ๋‚˜, ๋ช…์‹œ์ ์œผ๋กœ ์ค‘๋‹จ๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

์ด๋ฒคํŠธ ์†Œ์Šค(ํƒ€์ด๋จธ, ์ž…๋ ฅ ๋“ฑ)๊ฐ€ ์—†์œผ๋ฉด ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ์ „ํ™˜๋˜์ง€๋งŒ, ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ๋Š” ์‹œ์Šคํ…œ์ด ์ด๋ฅผ ์ž๋™ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ข…๋ฃŒ๋ฅผ ๊ฑฑ์ •ํ•  ์ผ์€ ๊ฑฐ์˜ ์—†์Šต๋‹ˆ๋‹ค.


iOS์—์„œ Main Run Loop๋Š” ์–ด๋””์— ์žˆ์„๊นŒ?

iOS ์•ฑ์—์„œ Main Run Loop๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

DispatchQueue.main์ด๋‚˜ UIApplicationMain()์ด ์‹คํ–‰๋˜๋ฉด ๋ฉ”์ธ ๋ฃจํ”„๊ฐ€ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

let runLoop = RunLoop.main
runLoop.run() // ๋ฃจํ”„๋ฅผ ์ˆ˜๋™์œผ๋กœ ๋Œ๋ฆด ์ˆ˜๋„ ์žˆ์Œ

RunLoop์™€ UI ์—…๋ฐ์ดํŠธ์˜ ๊ด€๊ณ„

RunLoop๋Š” ๋‹ค์Œ ๋‘ phase ์ค‘ ํ•˜๋‚˜์—์„œ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค:

  • beforeWaiting phase: ๋‹ค์Œ ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ ์ง์ „, layoutSubviews()๋‚˜ draw(_:) ํ˜ธ์ถœ.
  • afterWaiting phase: ์ƒˆ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•œ ์งํ›„

๋”ฐ๋ผ์„œ setNeedsLayout()์ด๋‚˜ setNeedsDisplay()๋Š” ์ฆ‰์‹œ UI๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , Run Loop์˜ ๋‹ค์Œ ์‚ฌ์ดํด์—์„œ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.


UI๊ฐ€ ๋ฉˆ์ถ”๊ฑฐ๋‚˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋Š๊ธด๋‹ค๋ฉด?

UI๋ฅผ Main Run Loop๋กœ ๊ทธ๋ ค๋‚ด๋Š”๋ฐ, ์ด ์ž‘์—…์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ด๋ค„์ง‘๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ UI๊ฐ€ ๋ฉˆ์ถ”๊ฑฐ๋‚˜ ๋Š๊ธด๋‹ค๋ฉด ๋Œ€๋ถ€๋ถ„ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณผ๋„ํ•œ ์ž‘์—…์œผ๋กœ ๋ฐ”๋น ์„œ RunLoop๋ฅผ ์ œ๋Œ€๋กœ ๋Œ๋ฆฌ์ง€ ๋ชปํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.


ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€?

RunLoop์˜ ํ๋ฆ„์„ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๋„๋ก, ๋ฌด๊ฑฐ์šด ์ž‘์—…(๋„คํŠธ์›Œํฌ ํ†ต์‹  ๋“ฑ)์€ ๋ฐ˜๋“œ์‹œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

1. GCD (Grand Central Dispatch)

DispatchQueue.global(qos: .background).async {
    // ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋„คํŠธ์›Œํฌ or ์—ฐ์‚ฐ
    let result = heavyWork()

    // ๋‹ค์‹œ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ UI ์—…๋ฐ์ดํŠธ
    DispatchQueue.main.async {
        self.label.text = result
    }
}

2. Swift Concurrency (async/await)

Task {
    let data = await fetchHeavyData()
    self.updateUI(with: data)
}

์ด ๊ธ€์—์„œ๋Š” iOS ์•ฑ์˜ ์ค‘์‹ฌ์ธ Main Run Loop์™€ ๊ทธ ์ž‘๋™ ์›๋ฆฌ, ๊ทธ๋ฆฌ๊ณ  ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฌธ์ œ๊นŒ์ง€ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€