[React] React 18 - Concurrent Features

@์ตœํ˜„์›…(Harry) ยท June 24, 2024 ยท 21 min read

๐Ÿช„ ๋™์‹œ์„ฑ ๋ Œ๋”๋ง?

๋ฆฌ์•กํŠธ 18 ๋ฒ„์ ผ์ด ๋“ฑ์žฅํ•˜๋ฉด์„œ, ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š” ์—ฌ๋Ÿฌ ํ›…๋“ค์ด ๋‚˜์™”๋‹ค.

๋ฆฌ์•กํŠธ ๊ณต์‹๋ฌธ์„œ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งํ•œ๋‹ค.

React 18 adds the long-awaited concurrent renderer and updates to Suspense without any major breaking changes. Apps can upgrade to React 18 and begin gradually adopting concurrent features with the amount of effort on par with any other major release.

This means there is no concurrent mode, only concurrent features.

React 18์€ ์˜ค๋žซ๋™์•ˆ ๊ธฐ๋‹ค๋ ค์˜จ ๋™์‹œ renderer๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  Suspense๋ฅผ ํฐ ๋ณ€๊ฒฝ ์—†์ด ์—…๋ฐ์ดํŠธํ–ˆ๋‹ค. ์•ฑ์„ React 18๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜์—ฌ ๋‹ค๋ฅธ ์ฃผ์š” ์ถœ์‹œ์™€ ๋™๋“ฑํ•œ ์ˆ˜์ค€์˜ ๋…ธ๋ ฅ์œผ๋กœ ๋™์‹œ ๊ธฐ๋Šฅ์„ ์ ์ง„์ ์œผ๋กœ ๋„์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๋Š” ๋™์‹œ ๋ชจ๋“œ๊ฐ€ ์—†๊ณ  ๋™์‹œ ๊ธฐ๋Šฅ๋งŒ ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค.

์•ฑ์˜ ์ „์ฒด๋ฅผ ๋™์‹œ์„ฑ ๋ชจ๋“œ๋กœ ๊ฒฐ์ •ํ•˜๊ณ  ๊ฐœ๋ฐœ์„ ํ•ด๊ฐ€๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๋ฆฌ์•กํŠธ๊ฐ€ ์ œ๊ณตํ•ด์ฃผ๋Š” ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง€๋Š” API๋“ค ์ฆ‰, useTransition, useDeferredValue ํ›…์„ ์‚ฌ์šฉํ•ด์„œ ์•ฑ์— ๋ถ€๋ถ„์ ์œผ๋กœ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)

์กฐ๊ธˆ ๋งŒ ๋” ์ž์„ธํ•˜๊ฒŒ ๋“ค์–ด๊ฐ€ ๋ณด์ž๋ฉด, ๋ฆฌ์•กํŠธ 18 ๋ฒ„์ ผ๋ถ€ํ„ฐ createRoot ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด์„œ ์ „์ฒด ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ concurrent mode ๊ฐ€ ์‹คํ–‰๋œ๋‹ค. ํ•˜์ง€๋งŒ concurrent mode ๊ฐ€ ์‹คํ–‰๋œ๋‹ค๊ณ  ํ•ด์„œ, ๋ฌด์กฐ๊ฑด ๋ชจ๋“  ๋ Œ๋”๋ง์— ๋™์‹œ์„ฑ์ด ์ ์šฉ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ concurrent features ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋ถ€๋ถ„์ ์œผ๋กœ ๋™์‹œ์„ฑ ๋ Œ๋”๋ง์ด ์ ์šฉ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

(concurrent features ๋Š” useTransition, useDeferredValue ํ›…์ด๋‚˜ Suspense, streaming SSR ๋“ฑ์„ ๋งํ•œ๋‹ค.)

๋ฆฌ์•กํŠธ๊ฐ€ ๋งํ•˜๋Š” ๋™์‹œ์„ฑ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ธฐ ์ „, ์ปดํ“จํ„ฐ ๊ณผํ•™์—์„œ ๋งํ•˜๋Š” ๋™์‹œ์„ฑ์„ ๋ณ‘๋ ฌ์„ฑ๊ณผ ๋น„๊ตํ•ด ๋ณด๋ฉด์„œ ์•Œ์•„๋ณด์ž.

concurrency(๋™์‹œ์„ฑ)

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

๋„์„œ๊ด€ ์‚ฌ์„œ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด์„œ ๋™์‹œ์„ฑ๊ณผ ๋ณ‘๋ ฌ์„ฑ์„ ์ข€ ๋” ์ดํ•ดํ•ด๋ณด์ž. ๐Ÿ˜Š

  1. ๋™์‹œ์„ฑ

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

lib concurrency

์‚ฌ์„œ๋Š” ์ฑ…์„ ์ •๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€(task 1), ๊ผฌ๋งˆ ์•„์ด์˜ ์งˆ๋ฌธ์— ๋‹ต์„ ํ•ด์ค˜์•ผ ํ•œ๋‹ค. (task 2)

์ฆ‰, ๋„์„œ๊ด€ ์‚ฌ์„œ๋Š” ์ฑ…์„ ์ •๋ฆฌํ•˜๋Š” ๋…ผ๋ฆฌ์  ํ๋ฆ„๊ณผ ์งˆ๋ฌธ์— ๋‹ต์„ ํ•ด์ฃผ๋Š” ๋…ผ๋ฆฌ์  ํ๋ฆ„์„ ๋‘˜ ๋‹ค ๊ฐ€์ง€๊ฒŒ ๋˜๋ฉฐ ์‚ฌ์„œ ํ•œ ๋ช…์ด ๋‘ ๊ฐ€์ง€ ๋…ผ๋ฆฌ์  ํ๋ฆ„์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

  1. ๋ณ‘๋ ฌ์„ฑ

lib parrell

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

๋‹ค์Œ ์ด๋ฏธ์ง€๋ฅผ ํ†ตํ•ด์„œ ํ•œ ๋ฒˆ ๋” ์ •๋ฆฌํ•ด ๋ณด์ž.

compare con par

์œ„ ๊ทธ๋ฆผ์—์„œ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋™์‹œ์„ฑ๊ณผ ๋ณ‘๋ ฌ์„ฑ์˜ ์ฐจ์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
๋™์‹œ์„ฑ์€ ์ตœ์†Œ ๋‘๊ฐœ์˜ ๋…ผ๋ฆฌ์  ํ๋ฆ„(task)๊ฐ€ ์กด์žฌํ•œ๋‹ค. ๋™์‹œ์„ฑ์€ ์‹ฑ๊ธ€ ์ฝ”์–ด(๋‹จ์ผ CPU) ์—์„œ๋„ ๋™์ž‘ํ•˜๋ฉฐ, ๋ณ‘๋ ฌ์„ฑ์€ ๋ฉ€ํ‹ฐ ์ฝ”์–ด(๋‹ค์ค‘ CPU) ์—์„œ ๋™์ž‘ํ•œ๋‹ค.

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

๋ฆฌ์•กํŠธ์—์„œ ๋™์‹œ์„ฑ(concurrency)

๋ฆฌ์•กํŠธ ๊ณต์‹๋ฌธ์„œ์—์„œ๋Š” ๋™์‹œ์„ฑ์— ๋Œ€ํ•ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๋ช…ํ•œ๋‹ค.

Concurrency is not a feature, per se. Itโ€™s a new behind-the-scenes mechanism that enables React to prepare multiple versions of your UI at the same time.

Concurrency ์ž์ฒด๋Š” ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋‹ค. React๊ฐ€ ๋™์‹œ์— ์—ฌ๋Ÿฌ ๋ฒ„์ „์˜ UI๋ฅผ ์ค€๋น„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์ƒˆ๋กœ์šด ๋น„ํ•˜์ธ๋“œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋‹ค.

A key property of Concurrent React is that rendering is interruptible. When you first upgrade to React 18, before adding any concurrent features, updates are rendered the same as in previous versions of React โ€” in a single, uninterrupted, synchronous transaction. With synchronous rendering, once an update starts rendering, nothing can interrupt it until the user can see the result on screen.

Concurrent React์˜ ํ•ต์‹ฌ์€ ๋ Œ๋”๋ง ์ž‘์—…์ด ์ค‘๋‹จ ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. React 18๋กœ ์ฒ˜์Œ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ๋•Œ concurrent ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „ ์—…๋ฐ์ดํŠธ๋Š” ์ด์ „ ๋ฒ„์ „์˜ React์™€ ๋™์ผํ•˜๊ฒŒ ์ค‘๋‹จ๋˜์ง€ ์•Š๋Š” ๋‹จ์ผ ๋™๊ธฐ์‹ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ Œ๋”๋ง ๋˜์—ˆ๋‹ค. ๋™๊ธฐ์‹ ๋ Œ๋”๋ง์˜ ๊ฒฝ์šฐ ํ•œ ๋ฒˆ ๋ Œ๋”๋ง์ด ์‹œ์ž‘๋˜๋ฉด, ์‚ฌ์šฉ์ž๊ฐ€ ํ™”๋ฉด์—์„œ ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์„ ๋•Œ๊นŒ์ง€ ๊ทธ ์–ด๋–ค ๊ฒƒ๋„ ๋ Œ๋”๋ง์„ ๋ฐฉํ•ดํ•  ์ˆ˜ ์—†์—ˆ๋‹ค.

In a concurrent render, this is not always the case. React may start rendering an update, pause in the middle, then continue later. It may even abandon an in-progress render altogether. React guarantees that the UI will appear consistent even if a render is interrupted. โ€ฆ
With this capability, React can prepare new screens in the background without blocking the main thread. This means the UI can respond immediately to user input even if itโ€™s in the middle of a large rendering task, creating a fluid user experience.

Concurrent ๋ Œ๋”๋ง์—์„œ๋Š” ํ•ญ์ƒ ๊ทธ๋ ‡์ง€๋Š” ์•Š๋‹ค. React๋Š” ์—…๋ฐ์ดํŠธ๋œ ๋ Œ๋”๋ง์„ ์‹œ์ž‘ํ•˜๊ณ  ์ค‘๊ฐ„์— ์ผ์‹œ ์ค‘์ง€ํ–ˆ๋‹ค๊ฐ€ ๋‚˜์ค‘์— ๊ณ„์†ํ•  ์ˆ˜ ์žˆ๋‹ค. ์‹ฌ์ง€์–ด ์ง„ํ–‰ ์ค‘์ธ ๋ Œ๋”๋ง์„ ์™„์ „ํžˆ ์ค‘๋‹จํ•  ์ˆ˜๋„ ์žˆ๋‹ค. React๋Š” ๋ Œ๋”๋ง์ด ์ค‘๋‹จ๋˜๋”๋ผ๋„ UI๊ฐ€ ์ผ๊ด€๋˜๊ฒŒ ํ‘œ์‹œ๋˜๋„๋ก ๋ณด์žฅํ•œ๋‹ค.

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

๊ณต์‹๋ฌธ์„œ์˜ ๋‚ด์šฉ์„ ์ •๋ฆฌํ•ด ๋ณด์ž๋ฉด, ๋ฆฌ์•กํŠธ๊ฐ€ ๋งํ•˜๋Š” ๋™์‹œ์„ฑ ๋ Œ๋”๋ง์ด๋ž€,

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

๋™์‹œ์„ฑ ๋ Œ๋”๋ง์˜ ํ•ต์‹ฌ์€ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•ด์„œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์ด๋‹ค.(ํ•ต์‹ฌ์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜!)

๊ทธ๋ ‡๋‹ค๋ฉด, ์–ด๋–ค ์ƒํ™ฉ์—์„œ ๋™์‹œ์„ฑ ๊ฐœ๋…์ด ํ•„์š”ํ•˜๊ฒŒ ๋ ๊นŒ? ๐Ÿค”

๐Ÿ’ญ Blocking Rendering

๋ธŒ๋ผ์šฐ์ €์˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋Š” ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ๋ผ์„œ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

critical render path

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

์ด๋ฅผ Blocking Rendering ์ด๋ผ๊ณ  ํ•œ๋‹ค.

๋ฆฌ์•กํŠธ์—์„œ์˜ ๋ Œ๋”๋ง ์ฆ‰, ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๋Š” ์ž‘์—…, ์žฌ๊ท€์ ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•ด์„œ ์ด์ „ ๋ Œ๋”๋ง๊ณผ ๋น„๊ตํ•ด ๋‹ค๋ฅด๊ฒŒ ๋ณด์—ฌ์•ผ ํ•˜๋Š” UI๋ฅผ ํŒŒ์•…ํ•˜๊ณ (render phase) ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์— ์ปค๋ฐ‹ํ•ด(commit phase) ์‹ค์ œ ์‚ฌ์šฉ์ž์˜ ๋ˆˆ์— ๋ณด์ด๋Š” ์ž‘์—…์ด ํ•œ ๋ฒˆ ์‹œ์ž‘๋˜๋ฉด ๋ฉˆ์ถœ ์ˆ˜ ์—†๋Š” ์ž‘์—…์ด์—ˆ๋‹ค.

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

ํ•˜์ง€๋งŒ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ˆ˜ํ•œ ์ƒํ™ฉ์„ ์ƒ๊ฐํ•ด๋ณด์ž.

import React, { useState } from "react"

function App() {
  const [text, setText] = useState("")

  return (
    <div className="container">
      <h1>Blocking ({text.length})</h1>
      <input
        type="text"
        value={text}
        maxLength={100}
        onChange={({ target }) => {
          setText(target.value)
        }}
      />
      <ColorList length={text.length} />
    </div>
  )
}

color blocking

input ์— ์ž…๋ ฅ์„ ํ•  ๋•Œ๋งˆ๋‹ค, App ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋˜๋ฉฐ ColorList ๋˜ํ•œ ๋ Œ๋”๋ง ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ, ColorList๋Š” ๋ฌด๊ฑฐ์šด ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํ…์ŠคํŠธ๊ฐ€ UI์— ๋ฐ”๋กœ ๋ฐ˜์˜๋˜์ง€ ์•Š์•„ ์ข‹์ง€ ๋ชปํ•œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

concurrent features์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฆฌ์•กํŠธ์—์„œ ๋ Œ๋”๋ง์€ ํ•œ ๋ฒˆ ์‹œ์ž‘ํ•˜๋ฉด ๋ฉˆ์ถœ ์ˆ˜ ์—†๋Š” ์ž‘์—…์ด์—ฌ์„œ, ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ํ…์ŠคํŠธ๋ฅผ UI์— ๋ฐ˜์˜ํ•ด์ค˜์•ผ ํ•˜๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ธฐ์ค€ ๋†’์€ ์šฐ์„ ์ˆœ์œ„ ์ž‘์—…์ด ColorList ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์— ์˜ํ•ด ๋ฐ€๋ฆฌ๊ฒŒ ๋œ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ๋ฆฌ์•กํŠธ 18 ๋ฒ„์ ผ๋ถ€ํ„ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” concurrent features ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ํŠน์ • ์ด๋ฒคํŠธ์— ๋” ๋น ๋ฅด๊ฒŒ ๋ฐ˜์‘ํ•  ์ˆ˜ ์žˆ๋Š” UI๋ฅผ ์šฐ์„ ์‹œํ•˜๊ณ , ํ˜„์žฌ ๋ Œ๋”๋ง ์ž‘์—…์„ ์ผ์‹œ์ ์œผ๋กœ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰, ๋” ๋น ๋ฅด๊ฒŒ ๋ฐ˜์‘ํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…(input UI ํ‘œ์‹œ)์ด ์‹คํ–‰๋˜๋ฉด, ํ˜„์žฌ ๋ Œ๋”๋ง(ColorList ๋ Œ๋”๋ง)์„ ์ผ์‹œ ์ค‘๋‹จํ•˜๊ณ  ์šฐ์„  ์ž‘์—…์„ ๋จผ์ € ์ฒ˜๋ฆฌํ•œ๋‹ค.

์ด์ œ ์–ด๋–ค concurrent features๊ฐ€ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด์ž.

โœจ useTransition

๊ณต์‹๋ฌธ์„œ์—์„œ useTransition ํ›…์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๋ช…ํ•œ๋‹ค.

useTransition is a React Hook that lets you update the state without blocking the UI.

useTransition์€ UI๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฆฌ์•กํŠธ ํ›…์ด๋‹ค.

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

const [isPending, startTransition] = useTransition()

useTransition ํ›…์—์„œ ์ œ๊ณตํ•˜๋Š” startTransition ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ธด๊ธ‰ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค. ๋น ๋ฅธ UI ์—…๋ฐ์ดํŠธ, ๋ฐ˜์‘์„ ๋ฐฉํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ธด๊ธ‰ํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ โ€œํŠธ๋žœ์ง€์…˜โ€์œผ๋กœ ํ‘œ์‹œํ•ด์„œ ๋ฆฌ์•กํŠธ์—๊ฒŒ ์•Œ๋ ค ์˜๋„์ ์œผ๋กœ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

(a ์ƒํƒœ ์—…๋ฐ์ดํŠธ? ๊ธ‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค! ํ•ด๋‹น ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ณด๋‹ค ๋” ์ค‘์š”ํ•œ ์ž‘์—…์ด ์žˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ๋ถ€ํ„ฐ ํ•ด์ฃผ์„ธ์š”~ ์™€ ๊ฐ™์€ ์š”์ฒญ)

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

ux compare

์œ„ Input , ColorList ์ปดํฌ๋„ŒํŠธ ์˜ˆ์‹œ๋ฅผ ๋‹ค์‹œ ์ƒ๊ฐํ•ด๋ณด๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ํ˜„์žฌ ์–ด๋–ค ๊ฐ’์„ ์ž…๋ ฅํ•ด ์คฌ๋Š”์ง€๋ฅผ UI์— ํ‘œ์‹œํ•ด์ฃผ๋Š” ์ž‘์—…์ด ๋” ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์€ ์ž‘์—…์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. startTransition API๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ธด๊ธ‰ํ•˜์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•ด์„œ ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง ์ž‘์—…์ธ ColorList ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ์ž‘์—…์„ ๋’ค๋กœ ๋ฏธ๋ค„๋ณด์ž.

import React, { useState, useTransition } from "react"

function TextInput({ onChange }) {
  const [text, setText] = useState("")
  return (
    <input
      type="text"
      value={text}
      onChange={({ target }) => {
        setText(target.value)
        onChange(target.value)
      }}
    />
  )
}

function App() {
  const [size, setSize] = useState(0)
  const [isPending, startTransition] = useTransition()

  function handleChange(text) {
    startTransition(() => {
      setSize(text.length)
    })
  }

  return (
    <div className="container">
      <h1>Concurrent ({size})</h1>
      <TextInput onChange={handleChange} />
      <div className={isPending ? "pending" : ""}>
        <ColorList length={size} />
      </div>
    </div>
  )
}

export default App

color non blocking

ColorList ์ปดํฌ๋„ŒํŠธ๊ฐ€ props๋กœ ๋ฐ›๋Š” size ์ƒํƒœ๋ฅผ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ  ํ•ด๋‹น ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ์€ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ผ๋Š” ๊ฒƒ์„ ๋ฆฌ์•กํŠธ์—๊ฒŒ ์•Œ๋ฆฐ๋‹ค.

React can prepare new screens in the background without blocking the main thread.

React๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ  ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ƒˆ ํ™”๋ฉด์„ ์ค€๋น„ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ถœ์ฒ˜ : ๋ฆฌ์•กํŠธ ๊ณต์‹๋ฌธ์„œ

๋ฆฌ์•กํŠธ๋Š” ์ด์ œ size ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง์„ ์œ ๋ฐœํ•˜๋Š” ๊ฒƒ์ž„์„ ์•Œ๊ธฐ ๋•Œ๋ฌธ์—, ์—…๋ฐ์ดํŠธ๋ฅผ ์ฆ‰์‹œ ๋ฐ˜์˜ํ•˜๋Š” ๋Œ€์‹  ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ์ค€๋น„ํ•˜๋ฉด์„œ ํ˜„์žฌ์˜ UI๋ฅผ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•˜๊ฒŒ ์œ ์ง€ํ•œ๋‹ค.

useTransition ํ›…์ด ์ œ๊ณตํ•˜๋Š” isPending ๋ถˆ๋ฆฌ์–ธ ๊ฐ’์„ ์‚ฌ์šฉํ•ด์„œ ๊ธด๊ธ‰ํ•˜์ง€ ์•Š์€ ์ƒํƒœ ์—…๋ฐ์ดํŠธ์˜ ์ง„ํ–‰ ์ƒํƒœ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๊ณ , ์ด๋ฅผ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ loading feedback ๋˜ํ•œ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

<div className={isPending ? "pending" : ""}>
  {isPending && <h1>loading...</h1>}
  <ColorList length={size} />
</div>

โœจ useDeferredValue

๊ณต์‹๋ฌธ์„œ์—์„œ useDeferredValue ํ›…์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค๋ช…ํ•œ๋‹ค.

useDeferredValue is a React Hook that lets you defer updating a part of the UI.

useDeferredValue๋Š” UI ์ผ๋ถ€ ์—…๋ฐ์ดํŠธ๋ฅผ ์ง€์—ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” React Hook์ด๋‹ค.

useTransition ํ›…์ด ์ œ๊ณตํ•˜๋Š” startTransition ์€ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ž˜ํ•‘ํ•ด์„œ ํ•ด๋‹น ์ž‘์—…์˜ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ๋‹ค๋Š” ๊ฒƒ์„ ๋ฆฌ์•กํŠธ์—๊ฒŒ ์•Œ๋ฆฌ์ง€๋งŒ, useDeferredValue ํ›…์€ ์ธ์ž๋กœ ๋“ค์–ด์˜ค๋Š” ๊ฐ’ ์—…๋ฐ์ดํŠธ์˜ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๋ฆฐ๋‹ค.

const deferredValue = useDeferredValue(value)

๋งŒ์•ฝ ๊ฐ’์˜ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•ด์„œ ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง ์ž‘์—…์ด ์ˆ˜ํ–‰๋œ๋‹ค๋ฉด, ํ•ด๋‹น ํ›…์œผ๋กœ ๊ฐ์‹ผ ํ›„ ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง์„ ์ง€์—ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

์ด๋ฒˆ์—๋Š” ์œ„ ์˜ˆ์‹œ๋ฅผ useDeferredValue ํ›…์„ ์‚ฌ์šฉํ•ด์„œ ๊ฐœ์„ ํ•ด๋ณด์ž.

import React, { useState, useDeferredValue } from "react"

function TextInput({ onChange }) {
  const [text, setText] = useState("")
  return (
    <input
      type="text"
      value={text}
      onChange={({ target }) => {
        setText(target.value)
        onChange(target.value)
      }}
    />
  )
}

function App() {
  const [size, setSize] = useState(0)
  const deferredSize = useDeferredValue(size)

  function handleChange(text) {
    setSize(text.length)
  }

  return (
    <div className="container">
      <h1>Concurrent ({size})</h1>
      <TextInput onChange={handleChange} />
      <div className={deferredSize !== size ? "pending" : ""}>
        <ColorList length={deferredSize} />
      </div>
    </div>
  )
}

export default App

๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง์„ ์œ ๋ฐœํ•˜๋Š” ๊ฐ’์ธ size ์—…๋ฐ์ดํŠธ์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋‚ฎ์ถฐ์„œ UI๋ฅผ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•˜๊ฒŒ ์œ ์ง€๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

(๊ฒฐ๊ณผ๋Š” useTransition๊ณผ ๊ฐ™๋‹ค.)

useDeferredValue ํ›…์€ ์ง€์—ฐํ•ด์•ผ ํ•˜๋Š” ๊ฐ’ ์—…๋ฐ์ดํŠธ ์ง์ ‘ ์ œ์–ดํ•  ์ˆ˜ ์—†๊ณ , props๋กœ ์ „๋‹ฌ๋ฐ›์„ ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

function SomeComponent({ value }) {
  const deferredValue = useDeferredValue(size)

  // heavy work using deferredValue

  return <JSX />
}

๐Ÿงš ์ •๋ฆฌ

  • ๋ฆฌ์•กํŠธ๋Š” ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ์„ ๋„์ž…ํ•ด์„œ, ์‹œ์ž‘ํ•˜๋ฉด ๋ฉˆ์ถœ ์ˆ˜ ์—†์—ˆ๋˜ ๋ Œ๋”๋ง ๊ณผ์ •์„ ์ผ์‹œ ์ค‘์ง€ํ–ˆ๋‹ค๊ฐ€ ๋‚˜์ค‘์— ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๋Š” ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง ์ž‘์—…์ด ์ง„ํ–‰๋˜๊ณ  ์žˆ๋”๋ผ๋„ ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ณผ ๊ฐ™์€ ๋” ๋น ๋ฅธ ๋ฐ˜์‘์„ ํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์— ๋Œ€ํ•ด UI๊ฐ€ ์ฆ‰๊ฐ์ ์œผ๋กœ ๋ฐ˜์‘ํ•ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.
  • ๋ฆฌ์•กํŠธ๋Š” ์•ฑ ์ „์ฒด ๋™์‹œ์„ฑ ๋ชจ๋“œ๋Š” ์กด์žฌํ•˜์ง€ ์•Š๊ณ , concurrent features ๋ฅผ ํ†ตํ•ด ๋ถ€๋ถ„์ ์œผ๋กœ ๋™์‹œ์„ฑ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์–ด๋–ค ์ƒํ™ฉ์— ์–ด๋””์— ์ ์šฉํ• ์ง€๋Š” ๊ฐœ๋ฐœ์ž์ธ ์šฐ๋ฆฌ๊ฐ€ ๊ฒฐ์ •ํ•ด์•ผ ํ•œ๋‹ค.
  • ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ์ž‘์—…(size ์ƒํƒœ ์—…๋ฐ์ดํŠธ), ๋ Œ๋”๋ง ์ž‘์—…( ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง)์„ ์ž˜๊ฒŒ ๋‚˜๋ˆ„๊ณ  ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ •ํ•œ ํ›„ ๋น ๋ฅธ Context Switching์„ ํ†ตํ•ด์„œ ๋งˆ์น˜ ๋‘ ์ž‘์—…์ด ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•œ๋‹ค.
  • useTransition ํ›…์€ โ€˜ํŠน์ • ์ƒํƒœ ์—…๋ฐ์ดํŠธโ€™๋ฅผ ์ง€์—ฐ์‹œ์ผœ์„œ ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง ์ž‘์—…์„ ๋’ค๋กœ ๋ฏธ๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
  • useDeferredValue ํ›…์€ โ€˜๊ฐ’โ€™ ์—…๋ฐ์ดํŠธ๋ฅผ ์ง€์—ฐ์‹œ์ผœ์„œ ๋ฌด๊ฑฐ์šด ๋ Œ๋”๋ง ์ž‘์—…์„ ๋’ค๋กœ ๋ฏธ๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
  • โœจ ๋ฆฌ์•กํŠธ ๋™์‹œ์„ฑ ๋ Œ๋”๋ง์˜ ํ•ต์‹ฌ์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ํ–ฅ์ƒ์ด๋‹ค!

๐Ÿ”— Ref

@์ตœํ˜„์›…(Harry)
์˜๋ฏธ ์—†๋Š” ๊ธฐ๋ก์€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ, ํ•™์Šตํ•˜๊ณ  ๋Š๋‚€ ๊ฒƒ๋“ค์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค :)