2890 ๋‹จ์–ด
14 ๋ถ„
๐Ÿงฉ ๋™์‹œ์„ฑ(Concurrency)
2025-10-26

๐Ÿงฉ ๋™์‹œ์„ฑ(Concurrency)#


**๋™์‹œ์„ฑ(Concurrency)**์ด๋ž€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์—…์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋‹จ์ผ CPU ํ™˜๊ฒฝ์—์„œ **๋น ๋ฅธ ์ „ํ™˜(Context Switching)**์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ ์ž‘์—…์ด ๋™์‹œ์— ์ง„ํ–‰๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•˜๋Š” ๊ธฐ๋ฒ•์œผ๋กœ, CPU๊ฐ€ ํ•œ์ˆœ๊ฐ„ ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ, ๋น ๋ฅธ ์ „ํ™˜ ์†๋„๋กœ ๋งˆ์น˜ ์—ฌ๋Ÿฌ ์ž‘์—…์ด ๊ฒน์ณ ์‹คํ–‰๋˜๋Š” ๋“ฏํ•œ ํšจ๊ณผ๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

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

๐Ÿ†š ๋ณ‘๋ ฌ์„ฑ(Parallelism)๊ณผ์˜ ์ฐจ์ด์ ?#

๋ณ‘๋ ฌ์„ฑ์€ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ์‹ค์ œ๋กœ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

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

๊ตฌ๋ถ„์„ค๋ช…์˜ˆ์‹œ
๋™์‹œ์„ฑ(Concurrency)์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋™์‹œ์— ๊ฒน์ณ์„œ ์ฒ˜๋ฆฌํ•˜๋˜, ์‹ค์ œ CPU๋Š” ํ•˜๋‚˜์”ฉ ๋ฒˆ๊ฐˆ์•„๊ฐ€๋ฉฐ ์ฒ˜๋ฆฌํ•œ ์‚ฌ๋žŒ์ด ์ „ํ™”, ๋ฉ”์ผ, ์ฑ„ํŒ… ์—…๋ฌด๋ฅผ ๋ฒˆ๊ฐˆ์•„ ๊ฐ€๋ฉด์„œ ์ฒ˜๋ฆฌ
๋ณ‘๋ ฌ์„ฑ(Parallelism)์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋™์‹œ์— ์—ฌ๋Ÿฌ CPU ์ฝ”์–ด์—์„œ ์ˆ˜ํ–‰3๋ช…์ด ๊ฐ๊ฐ ์ „ํ™”, ๋ฉ”์ผ, ์ฑ„ํŒ… ์—…๋ฌด๋ฅผ ๋ฐ›์•„์„œ ๋™์‹œ์— ์ฒ˜๋ฆฌ

๐Ÿšฆ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋™์‹œ์„ฑ#

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

  • ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ ์ด๋ฒคํŠธ (Click, Typing input values ๋“ฑ)
  • ๋„คํŠธ์›Œํฌ ํ†ต์‹  (API ์š”์ฒญ)
  • ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ Œ๋”๋ง (CSS, requestAnimationFrame)
  • ํƒ€์ด๋จธ (setTimeout, setInterval)
  • React์˜ ์ƒํƒœ ์—…๋ฐ์ดํŠธ

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

๐Ÿ“ ๋™์‹œ์„ฑ์ด ์ค‘์š”ํ•œ ๋ช‡ ๊ฐ€์ง€ ์‚ฌ๋ก€ (Cases)#

๐Ÿ’ฌ ๊ฒ€์ƒ‰ ์ž๋™์™„์„ฑ / ํ•„ํ„ฐ๋ง UI#

  • Situation:
    • ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ API ์š”์ฒญ
    • ์‚ฌ์šฉ์ž๊ฐ€ ๋น ๋ฅด๊ฒŒ ํƒ€์ดํ•‘ ์‹œ ์ด์ „ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์ด ๋Œ์•„์˜ค๊ธฐ ์ „์— ์ƒˆ๋กœ์šด ์š”์ฒญ ๋ฐœ์ƒ
    • ๋‚˜์ค‘์— ์ฒ˜๋ฆฌ๋œ ์ด์ „ ์š”์ฒญ์˜ ์‘๋‹ต์ด ์ƒˆ๋กœ์šด ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ๋ฎ์–ด์“ฐ๋Š” ์ƒํ™ฉ ๋ฐœ์ƒ
  • Problem:
    • UI๊ฐ€ ์ตœ์‹  ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•˜์ง€ ๋ชปํ•จ
    • ๋’ค๋Šฆ์€ ์‘๋‹ต์ด ์ตœ์‹  ์ž…๋ ฅ์„ ๋ฎ์–ด๋ฒ„๋ฆฌ๋Š” Race Condition ๋ฐœ์ƒ
  • How to solve?
    • AbortController๋กœ ์ด์ „ ์š”์ฒญ์— ๋Œ€ํ•œ ์ค‘๋‹จ์„ ํ†ตํ•ด Race Condition ๋ฐฉ์ง€
    • Concurrent Rendering: useTransition์„ ์‚ฌ์šฉํ•ด ์ž…๋ ฅ(์šฐ์„ ์ˆœ์œ„ High) vs. ๋ Œ๋”๋ง(์šฐ์„ ์ˆœ์œ„ Low) ๊ตฌ๋ถ„์ง€์–ด ์šฐ์„ ์ˆœ์œ„ ๊ธฐ์ค€์œผ๋กœ ๋ณ€๊ฒฝ์‚ฌํ•ญ ์ฒ˜๋ฆฌ

๐Ÿ“‘ ํƒญ ์ „ํ™˜ + API ์š”์ฒญ#

  • Situation:
    • ์‚ฌ์šฉ์ž๊ฐ€ ํƒญ์„ ์ „ํ™˜ํ•˜๋Š” ์ˆœ๊ฐ„, ์ด์ „ ํƒญ์˜ ๋ฐ์ดํ„ฐ ์š”์ฒญ์ด ์•„์ง ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ
    • ์ „ํ™˜ ํ›„ ์ด์ „ ํƒญ ์ „ํ™˜ ์‹œ ํ˜ธ์ถœํ–ˆ๋˜ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต ๋„์ฐฉ โ†’ ์ƒˆ๋กœ์šด ํƒญ์˜ UI๋ฅผ ์ด์ „ ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์ด ๋ฎ์–ด์”€
  • Problem:
    • ์ƒํƒœ ๊ผฌ์ž„ (State inconsistency)
    • ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ €ํ•˜ (์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ ํ‘œ์‹œ ๋˜๋Š” UI ๊นœ๋นก๊ฑฐ๋ฆผ ๋ฐœ์ƒ)
  • How to solve?
    • AbortController ๋˜๋Š” React Query์˜ cancelQueries()๋กœ ์ด์ „ ์š”์ฒญ ์ทจ์†Œ
    • React์˜ Concurrent Rendering ํ™œ์šฉ
      • startTransition์„ ์ด์šฉํ•ด ์ „ํ™˜ ๋ Œ๋”๋ง์„ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋กœ ์ฒ˜๋ฆฌ

๐Ÿ“ƒ ๋Œ€๋Ÿ‰ ๋ฐ์ดํ„ฐ ๋ Œ๋”๋ง + ์‚ฌ์šฉ์ž ์ž…๋ ฅ#

  • Situation:
    • ๋ช‡ ๋งŒ ๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋ง ์ค‘์— ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ ๋ฐœ์ƒ
    • ๋ Œ๋”๋ง์ด ๋๋‚˜๊ธฐ ์ „๊นŒ์ง€ ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์ด UI์— ๋ฐ˜์˜๋˜์ง€ ์•Š์Œ (์ž…๋ ฅ์ด โ€œ๋จนํ†ตโ€์ฒ˜๋Ÿผ ๋ณด์ž„)
  • Problem:
    • ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ Œ๋”๋ง์œผ๋กœ ์ ์œ ๋˜์–ด ์ž…๋ ฅ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์ง€์—ฐ
  • How to solve?
    • Virtualization (react-window, react-virtualized)
    • React Concurrent Mode๋กœ ๋ Œ๋”๋ง์„ ๋‚˜๋ˆ„์–ด โ€œํ”„๋ ˆ์ž„ ๋‹จ์œ„โ€๋กœ ๋ถ„ํ•  ์ฒ˜๋ฆฌ

โœ๏ธ ์ž๋™ ์ €์žฅ(Auto Save) + ์‚ฌ์šฉ์ž ์ž…๋ ฅ#

  • Situation:
    • ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ ์ค‘์ผ ๋•Œ ์ผ์ • ์ฃผ๊ธฐ๋กœ ์ž๋™ ์ €์žฅ API ํ˜ธ์ถœ
    • ๋„คํŠธ์›Œํฌ๊ฐ€ ๋А๋ฆด ๊ฒฝ์šฐ ์ด์ „ ์ž…๋ ฅ ๊ฐ’์œผ๋กœ ๋ฎ์–ด์จ๋ฒ„๋ฆผ
  • Problem:
    • ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ์ƒํƒœ ๋ถˆ์ผ์น˜ (Stale data)
    • ์ž…๋ ฅ ์ค‘๋‹จ์ด๋‚˜ ๊นœ๋นก์ž„ ๋ฐœ์ƒ
  • How to solve?
    • ํด๋ผ์ด์–ธํŠธ์— ๋ฒ„์ „ ํ‚ค(version key) ๊ด€๋ฆฌ
    • ์š”์ฒญ ์™„๋ฃŒ ์‹œ์  ๊ธฐ์ค€์ด ์•„๋‹Œ ์ž…๋ ฅ ํƒ€์ž„์Šคํƒฌํ”„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ˜์˜
    • useDeferredValue๋กœ ์ž…๋ ฅ๊ฐ’์„ ๋Šฆ๊ฒŒ ๋ฐ˜์˜ํ•ด ์ถฉ๋Œ ๋ฐฉ์ง€

๐ŸŽจ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ฒ˜๋ฆฌ + API ํ˜ธ์ถœ#

  • Situation:
    • ์Šคํฌ๋กค ์• ๋‹ˆ๋ฉ”์ด์…˜, ๋ชจ์…˜ ํšจ๊ณผ๊ฐ€ ์žˆ๋Š” ์ƒํƒœ์—์„œ fetch ์š”์ฒญ์ด ๋™์‹œ์— ์ผ์–ด๋‚จ
    • ๋ Œ๋”๋ง ์„ฑ๋Šฅ ๋–จ์–ด์ง€๋ฉด์„œ ํ”„๋ ˆ์ž„ ๋“œ๋ž(Frame drop) ๋ฐœ์ƒ
  • Problem:
    • ๋™์‹œ์„ฑ ๋ถ€ํ•˜๋กœ ์ธํ•ด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋Š๊น€
  • How to solve?
    • requestIdleCallback์œผ๋กœ idle ํƒ€์ž„์— ๋น„๋™๊ธฐ ํ˜ธ์ถœ
    • Concurrent Rendering์œผ๋กœ ๋ Œ๋”๋ง๊ณผ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šค์ผ€์ค„๋ง ๋ถ„๋ฆฌ

๐Ÿงฉ React์˜ Concurrent Rendering ์ด์ „์— ๋™์‹œ์„ฑ ์ œ์–ด#

React 18 ์ดํ›„๋ถ€ํ„ฐ๋Š” React์—์„œ **React ๋‚ด๋ถ€ ์Šค์ผ€์ค„๋Ÿฌ(Fiber Scheduler)**๊ฐ€ ๋ Œ๋”๋ง ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ •ํ•˜์—ฌ ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ์ง€์›ํ•œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด React๊ฐ€ ์ด๋Ÿฌํ•œ ์ œ์–ด๋ฅผ ํ•˜๊ธฐ ์ด์ „์—๋Š” ํ”„๋ก ํŠธ์—”๋“œ์˜ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ง€์—ฐ ๋ฌธ์ œ, API ๊ฒฝ์Ÿ ์ƒํƒœ(Race Condition) ๋“ฑ์„ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ–ˆ์„๊นŒ?

๐Ÿงฉ ์ˆ˜๋™์ ์ธ ์ œ์–ด ๋ฐฉ์‹#

React 18 ์ด์ „์—๋Š” ๋ชจ๋“  ์ƒํƒœ ์—…๋ฐ์ดํŠธ์™€ ๋ Œ๋”๋ง์ด **๋™๊ธฐ์ (Synchronous)**์œผ๋กœ ์ง„ํ–‰๋˜์—ˆ๋‹ค.

์ฆ‰ ์ž…๋ ฅ ์ด๋ฒคํŠธ, API ํ˜ธ์ถœ, ๋ Œ๋”๋ง์ด ๋ชจ๋‘ ๊ฐ™์€ ์šฐ์„ ์ˆœ์œ„๋กœ ์‹คํ–‰๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ค‘์—๋„ UI๊ฐ€ ๋ฒ„๋ฒ…์ด๊ฑฐ๋‚˜, ์ด์ „ ์š”์ฒญ์ด ๋‚˜์ค‘์— ๋„์ฐฉํ•˜๋ฉด์„œ ์ตœ์‹  ์ƒํƒœ๋ฅผ ๋ฎ์–ด์“ฐ๋Š” ๋“ฑ์˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ ํ•ด๊ฒฐ์„ ์œ„ํ•ด์„œ debounce, throttle, request cancellation ๋“ฑ ์ˆ˜๋™์ ์ธ ์ œ์–ด ๋ฐฉ์‹์„ ํ™œ์šฉํ–ˆ๋‹ค.

โš™๏ธ Throttle and Debounce#

debounce ํ•จ์ˆ˜ ๊ตฌํ˜„ ์˜ˆ์‹œ

โ†’ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ์„ ๋ฉˆ์ถ”๊ณ  ์ผ์ • ์‹œ๊ฐ„(delay) ๋™์•ˆ ์•„๋ฌด ์ž…๋ ฅ์ด ์—†์„ ๋•Œ๋งŒ ์š”์ฒญ์„ ๋ณด๋ƒ„

function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// ๊ฒ€์ƒ‰ API
const handleSearch = debounce((value) => {
fetch(`/api/search?q=${value}`);
}, 500);
<input onChange={(e) => handleSearch(e.target.value)} />

throttle ํ•จ์ˆ˜ ๊ตฌํ˜„ ์˜ˆ์‹œ

โ†’ 200ms ๋‹จ์œ„๋กœ ์Šคํฌ๋กค ์ด๋ฒคํŠธ๊ฐ€ ์‹คํ–‰ํ•˜๋„๋ก ์ œํ•œ

function throttle(fn, limit) {
let inThrottle;
return (...args) => {
if (!inThrottle) {
fn(...args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
}
// ์Šคํฌ๋กค ์ด๋ฒคํŠธ
window.addEventListener(
'scroll',
throttle(() => console.log(window.scrollY), 200)
);
ํ•จ์ˆ˜์ž‘๋™ ๋ฐฉ์‹๋Œ€ํ‘œ ์‚ฌ๋ก€ํ•จ์ˆ˜์˜ ์‹คํ–‰ ๋ชฉ์ 
debounce๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ ์ดํ›„ ์ผ์ • ์‹œ๊ฐ„ ๋™์•ˆ ์ถ”๊ฐ€ ์ด๋ฒคํŠธ ์ง„ํ–‰์ด ์—†์„ ๋•Œ ์‹คํ–‰๊ฒ€์ƒ‰ ์ž๋™์™„์„ฑโ€œ๋งˆ์ง€๋ง‰ ์ž…๋ ฅ๋งŒ ๋ฐ˜์˜โ€ โ†’ ๋ถˆํ•„์š”ํ•œ API ํ˜ธ์ถœ ๋ฐฉ์ง€
throttle์ผ์ • ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ๋งˆ๋‹ค ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰์Šคํฌ๋กค/resize ์ด๋ฒคํŠธโ€œ์ผ์ • ์‹œ๊ฐ„์„ ๊ธฐ์ค€์œผ๋กœ ์‹คํ–‰โ€ โ†’ CPU ๋ถ€ํ•˜ ์™„ํ™”

โš™๏ธ AbortController (Request Cancellation - ์š”์ฒญ ์ทจ์†Œ)#

fetch ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ด์ „ ์š”์ฒญ์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ์ƒˆ๋กœ์šด ์š”์ฒญ์ด ๋ฐœ์ƒํ•˜๋ฉด, ์ด์ „ ์š”์ฒญ์˜ ์‘๋‹ต์ด ๋‚˜์ค‘์— ๋„์ฐฉํ•˜๋ฉด์„œ UI๊ฐ€ ์ด์ „ ์ƒํƒœ๋กœ ๋˜๋Œ์•„๊ฐ€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ˆ˜๋™์œผ๋กœ ์š”์ฒญ ์ œ์–ด๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” AbortController๋ฅผ ์‚ฌ์šฉํ•ด ์ด์ „ ์š”์ฒญ์„ ์ทจ์†Œํ•˜๋Š” ๋ฐฉ์‹์ด ๋„๋ฆฌ ์‚ฌ์šฉ๋˜์—ˆ๋‹ค.

function useSearch() {
const controllerRef = useRef();
const fetchSearchResults = async (query) => {
// ์ง„ํ–‰๋˜๋Š” ์š”์ฒญ ์žˆ์œผ๋ฉด ์ทจ์†Œ (abort)
if (controllerRef.current) controllerRef.current.abort();
const controller = new AbortController();
controllerRef.current = controller;
try {
const res = await fetch(`/api/search?q=${query}`, {
signal: controller.signal, // abort controller๋ฅผ ํ†ตํ•œ ํ˜ธ์ถœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ signal
});
const data = await res.json();
console.log('๊ฒฐ๊ณผ:', data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('์š”์ฒญ์ด ์ทจ์†Œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
}
}
};
return { fetchSearchResults };
}

๐Ÿ”ฅ ์ˆ˜๋™์ ์ธ ์ œ์–ด ๋ฐฉ์‹์ด ๊ฐ–๋Š” ํ•œ๊ณ„์ #

  • UX ๋ถˆ์•ˆ์ •์„ฑ
    • ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ ํƒ€์ด๋ฐ์— ๋”ฐ๋ผ ๋ฐ˜์‘์ด ์ง€์—ฐ๋˜๊ฑฐ๋‚˜, ๋ฐ˜์˜์ด ๋А๋ ค ๋ณด์ผ ์ˆ˜ ์žˆ์Œ
    • ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ ํƒ€์ด๋ฐ์„ ์ถ”์ธกํ•˜์—ฌ ๊ทธ์— ๋งž๋Š” ์‹คํ–‰ ์‹œ๊ฐ„์— ๋Œ€ํ•œ ๋ณ„๋„ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•จ
  • ๋ Œ๋”๋ง๊ณผ์˜ ๋ถ„๋ฆฌ ๋ถˆ๊ฐ€
    • ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋นˆ๋„ ์ œ์–ด๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋ Œ๋”๋ง ์ž์ฒด์˜ ์šฐ์„ ์ˆœ์œ„ ์กฐ์ ˆ์€ ๋ถˆ๊ฐ€๋Šฅ
  • React ๋‚ด๋ถ€ ์ƒํƒœ ์—…๋ฐ์ดํŠธ์™€ ๋ณ„๊ฐœ
    • React ๋ Œ๋”๋ง ์‚ฌ์ดํด๊ณผ๋Š” ๋ฌด๊ด€ํ•˜๊ฒŒ ๋™์ž‘ํ•˜์—ฌ ์ƒํƒœ ๋ถˆ์ผ์น˜(stale state) ๊ฐ€๋Šฅ
  • ์ฝ”๋“œ ์ค‘๋ณต / ๋ณต์žก๋„ ์ฆ๊ฐ€
    • debounce, throttle, abort controller ๋“ฑ์„ ๋งค ํ•จ์ˆ˜๋งˆ๋‹ค ๋”ฐ๋กœ ๊ด€๋ฆฌ ํ•„์š”

๐Ÿงฉ React 18 ์ดํ›„์˜ ๋ณ€ํ™” - Concurrent Rendering#

โ“ Concurrent Rendering์ด๋ž€?#

Concurrent Rendering์€ React 18 ๋ฒ„์ „์—์„œ ์ฒ˜์Œ ๋„์ž…๋˜์–ด, React 19์—์„œ๋„ ์ง€์†์ ์œผ๋กœ ๊ฐœ์„ ๋˜๊ณ  ์žˆ๋Š” ๋™์‹œ์  ๋ Œ๋”๋ง(Concurrent Rendering) ๊ธฐ๋Šฅ์ด๋‹ค.

๊ธฐ์กด์˜ ๋™๊ธฐ์ (Synchronous)์œผ๋กœ ์ง„ํ–‰๋˜์—ˆ๋˜ ๋ Œ๋”๋ง ์ž‘์—…์„ ์ž‘์€ ๋‹จ์œ„๋กœ ๋ถ„ํ• ํ•˜์—ฌ ์šฐ์„ ์ˆœ์œ„ ๊ธฐ๋ฐ˜์œผ๋กœ ์Šค์ผ€์ค„๋งํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ฉฐ, ํ•„์š” ์‹œ ์ค‘๋‹จ ๋ฐ ์žฌ๊ฐœ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜์—ฌ **UI์˜ ๋ฐ˜์‘์„ฑ(Responsiveness)**์„ ์œ ์ง€ํ•˜๋Š” ๋™์‹œ์  ๋ Œ๋”๋ง ๋ฐฉ์‹์ด๋‹ค.

Concurrent Rendering์˜ ํŠน์ง•

  • ๋ณ€๊ฒฝ ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ฅธ ๋ Œ๋”๋ง (Prioritized Updates): React๋Š” Fiber ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ณ€๊ฒฝ ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ Fiber ๋…ธ๋“œ๋ฅผ ์Šค์ผ€์ค„๋งํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ณผ ๊ฐ™์€ โ€˜๊ธด๊ธ‰ํ•œ ์—…๋ฐ์ดํŠธ(high priority)โ€™๋Š” ์ฆ‰์‹œ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋ฐ์ดํ„ฐ fetch๋‚˜ ๋ Œ๋”๋ง๊ณผ ๊ฐ™์€ โ€˜๋น„๊ธด๊ธ‰ ์—…๋ฐ์ดํŠธ(low priority)โ€™๋Š” ๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•˜์—ฌ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋˜์–ด์•ผ ํ•˜๋Š” ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์šฐ์„ ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋…ผ ๋ธ”๋กœํ‚น (Non-Blocking): ๋ Œ๋”๋ง์ด ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์™„์ „ํžˆ ์ ์œ ํ•˜์ง€ ์•Š๊ณ , **์ผ์ • ๋‹จ์œ„๋งˆ๋‹ค ์ค‘๋‹จ(yield)**ํ•˜์—ฌ ์ด๋ฒคํŠธ๋‚˜ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ๊ฐ„์„ ํ™•๋ณดํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ž‘์—…๋Ÿ‰์ด ๋งŽ๊ณ  ๋ณต์žกํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋”๋ผ๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์—ฌ์ „ํžˆ ๋Š๊น€ ์—†์ด ์‚ฌ์šฉ์ž๊ฐ€ ์ƒํ˜ธ์ž‘์šฉ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด UX๋ฅผ ์ €ํ•ดํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.
  • ์„ธ๋ฐ€ํ•œ ์ œ์–ด (Fine-Grained Control): React๋Š” Fiber ๋‹จ์œ„๋กœ ๊ฐ ์—…๋ฐ์ดํŠธ์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ณ„์‚ฐํ•ด์„œ ์ž๋™ ์กฐ์ •ํ•œ๋‹ค. ๋˜ํ•œ ๊ฐœ๋ฐœ์ž๋Š” useTransition, useDeferredValue, Suspense ๋“ฑ์„ ํ†ตํ•ด ๊ฐ„์ ‘์ ์œผ๋กœ ์šฐ์„ ์ˆœ์œ„ ํžŒํŠธ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ๋ Œ๋”๋ง ํ๋ฆ„์„ ์ง์ ‘ ์ œ์–ดํ•˜์ง€ ์•Š๊ณ ๋„ UI์˜ ๋ฐ˜์‘์„ฑ๊ณผ ๋ Œ๋”๋ง ํšจ์œจ์„ ๊ท ํ˜• ์žˆ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

โœ… Concurrent Rendering์€ React๊ฐ€ ๋ Œ๋”๋ง ๊ณผ์ •์„ **๋™๊ธฐ์ (Synchronous)**์—์„œ ์Šค์ผ€์ค„๋ง ๊ฐ€๋Šฅํ•œ ๋น„๋™๊ธฐ์ (Asynchronous) ๊ณผ์ •์œผ๋กœ ์ „ํ™˜ํ•จ์œผ๋กœ์จ, ๋ณต์žกํ•œ UI์—์„œ๋„ **์ž…๋ ฅ ๋ฐ˜์‘์„ฑ(Responsiveness)**๊ณผ **๋ Œ๋”๋ง ์œ ์—ฐ์„ฑ(Flexibility)**์„ ํ™•๋ณดํ•˜์—ฌ ๋Š๊น€ ์—†๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(Seamless UX)๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ํ•ต์‹ฌ ๊ธฐ์ˆ ์ด๋‹ค.


๐Ÿ“š ์ฐธ๊ณ ์ž๋ฃŒ#