[๐Ÿค–] React Suspense์™€ ErrorBoundary: ๊ฒฌ๊ณ ํ•˜๊ณ  ๋ถ€๋“œ๋Ÿฌ์šด UI ๊ฒฝํ—˜์„ ์œ„ํ•œ ์‹ค์ „ ๊ฐ€์ด๋“œ

React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ˜์‹ ํ•  Suspense์™€ ErrorBoundary์˜ ๊ฐ•๋ ฅํ•œ ์กฐํ•ฉ์„ ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ค„์š”. ๋กœ๋”ฉ ์ƒํƒœ์™€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ์šฐ์•„ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜์—ฌ ๋”์šฑ ๊ฒฌ๊ณ ํ•˜๊ณ  ๋ถ€๋“œ๋Ÿฌ์šด UI๋ฅผ ๋งŒ๋“œ๋Š” ์‹ค์ „ ํŒ๊ณผ ์ฝ”๋“œ ์˜ˆ์‹œ๋ฅผ ํ™•์ธํ•ด ๋ณด์„ธ์š”.

22๋ถ„
๋‹จ์–ด: 1,910๊ฐœ
๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ
์ •๋ณด

๐Ÿค– ์ด ํฌ์ŠคํŒ…์€ Gemini 2.5 Flash AI๊ฐ€ ์ž‘์„ฑํ–ˆ์–ด์š”.
๋‚ด์šฉ์˜ ์ •ํ™•์„ฑ์„ ์œ„ํ•ด ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณค์ง€๋งŒ, ์‹ค๋ฌด ์ ์šฉ ์ „ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์œ ์šฉํ•œ ํŒ

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

์•ˆ๋…•ํ•˜์„ธ์š”, 10๋…„ ์ด์ƒ ํ”„๋ก ํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ๋ฅผ ๋„˜๋‚˜๋“ค๋ฉฐ ๊ฐœ๋ฐœ ํ˜„์žฅ์—์„œ ๊ตฌ๋ฅธ ์‹œ๋‹ˆ์–ด ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž ๋ธ”๋ฃจ์˜ˆ์š”. ์˜ค๋Š˜์€ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์„ ํ•œ ๋‹จ๊ณ„ ๋Œ์–ด์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ๋‘ ๊ฐ€์ง€ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ, ๋ฐ”๋กœ Suspense์™€ ErrorBoundary์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด ๋ณด๋ ค๊ณ  ํ•ด์š”.

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

๐Ÿค” ๋ฌธ์ œ/๋ฐฐ๊ฒฝ: ๋กœ๋”ฉ๊ณผ ์—๋Ÿฌ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ•ด์น˜๋Š” ์ฃผ๋ฒ”

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

0๏ธโƒฃ ๊ธฐ์กด ๋กœ๋”ฉ ์ฒ˜๋ฆฌ์˜ ํ•œ๊ณ„: โ€˜์Šคํ”ผ๋„ˆ ์ง€์˜ฅโ€™๊ณผ ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ

๊ธฐ์กด์—๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ isLoading๊ณผ ๊ฐ™์€ ์ƒํƒœ ๋ณ€์ˆ˜๋ฅผ ์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ๊ด€๋ฆฌํ•˜๋ฉฐ ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด์—ˆ์–ด์š”. ํ•˜์ง€๋งŒ ์ด ๋ฐฉ์‹์€ ๋ช‡ ๊ฐ€์ง€ ํ•œ๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”.

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

1๏ธโƒฃ ๊ธฐ์กด ์—๋Ÿฌ ์ฒ˜๋ฆฌ์˜ ํ•œ๊ณ„: ํ™”๋ฉด ์ „์ฒด ํฌ๋ž˜์‹œ์™€ ์ผ๊ด€์„ฑ ์—†๋Š” UI

JavaScript ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์—๋Ÿฌ๋Š” ์–ธ์ œ๋“  ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์ด์—์š”. ํŠนํžˆ React ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๋ฅผ ๋ฉˆ์ถ”๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”.

  • ํ™”๋ฉด ์ „์ฒด ํฌ๋ž˜์‹œ: ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐœ์ƒํ•œ ๋ Œ๋”๋ง ์—๋Ÿฌ๊ฐ€ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „ํŒŒ๋˜์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๊ฐ€ ๋ฉˆ์ถ”๊ณ  ๋นˆ ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ”ํ–ˆ์–ด์š”.
  • ์ผ๊ด€์„ฑ ์—†๋Š” ์—๋Ÿฌ UI: ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ try-catch ๋“ฑ์œผ๋กœ ์—๋Ÿฌ๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋‹ค ๋ณด๋ฉด, ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋‚˜ UI ์Šคํƒ€์ผ์ด ์ผ๊ด€๋˜์ง€ ์•Š์•„ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ˜ผ๋ž€์„ ์ค„ ์ˆ˜ ์žˆ์–ด์š”.
  • ์—๋Ÿฌ ๋กœ๊น…์˜ ์–ด๋ ค์›€: ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ด๋ฅผ ์ค‘์•™์—์„œ ์ˆ˜์ง‘ํ•˜๊ณ  ๋กœ๊น…ํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ์—†์œผ๋ฉด ๋ฌธ์ œ ์ง„๋‹จ๊ณผ ํ•ด๊ฒฐ์ด ์–ด๋ ค์›Œ์ ธ์š”.

โš™๏ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: Suspense์™€ ErrorBoundary์˜ ์‹œ๋„ˆ์ง€

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

0๏ธโƒฃ React Suspense๋กœ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ํŽ˜์นญ๊ณผ ๋กœ๋”ฉ UI ํ†ตํ•ฉํ•˜๊ธฐ

React Suspense๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋™์•ˆ ๋ Œ๋”๋ง์„ ์ผ์‹œ ์ค‘์ง€ํ•˜๊ณ , ๋ฐ์ดํ„ฐ ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ง€์ •๋œ fallback UI๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด์—์š”. React 18๋ถ€ํ„ฐ๋Š” React.lazy์™€ ํ•จ๊ป˜ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ ํŽ˜์นญ์—๋„ ์ •์‹์œผ๋กœ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์–ด์š”.

์ •๋ณด

์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋‚˜์š”?
Suspense๋Š” ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋น„๋™๊ธฐ ์ž‘์—…(๋ฐ์ดํ„ฐ ํŽ˜์นญ Promise)์ด ๋ฐœ์ƒํ•˜์—ฌ ๋ Œ๋”๋ง์„ โ€˜์ผ์‹œ ์ค‘๋‹จโ€™(suspend)์‹œํ‚ค๋ฉด, ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋ถ€๋ชจ Suspense ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ทธ ์ž‘์—…์„ ๊ฐ์ง€ํ•˜๊ณ  fallback์œผ๋กœ ์ง€์ •๋œ UI๋ฅผ ๋Œ€์‹  ๋ Œ๋”๋งํ•ด์š”. Promise๊ฐ€ ํ•ด๊ฒฐ๋˜๋ฉด ๋‹ค์‹œ Suspense ๋‚ด๋ถ€์˜ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง์„ ์žฌ๊ฐœํ•œ๋‹ต๋‹ˆ๋‹ค.

์ฃผ์š” ์ด์ :

  • ์„ ์–ธ์ ์ธ ๋กœ๋”ฉ ์ƒํƒœ ๊ด€๋ฆฌ: isLoading ์ƒํƒœ๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•  ํ•„์š” ์—†์ด, Suspense๋กœ ๊ฐ์‹ธ๊ธฐ๋งŒ ํ•˜๋ฉด ๋น„๋™๊ธฐ ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ์ž๋™์œผ๋กœ fallback UI๋ฅผ ๋ณด์—ฌ์ค˜์š”.
  • ๋กœ๋”ฉ UI์˜ ์ค‘์•™ ์ง‘์ค‘ํ™”: ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์˜ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด '์Šคํ”ผ๋„ˆ ์ง€์˜ฅ'์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์ผ๊ด€๋œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๋กœ์ง ๋ถ„๋ฆฌ: ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ๋กœ์ง๊ณผ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๋กœ์ง์„ ๋”์šฑ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค˜์š”. react-query, swr ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด Suspense๋ฅผ ์ง€์›ํ•˜๋ฉฐ ๋”์šฑ ํŽธ๋ฆฌํ•œ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด์š”.

1๏ธโƒฃ React ErrorBoundary๋กœ ์ปดํฌ๋„ŒํŠธ ์—๋Ÿฌ ๊ฒฉ๋ฆฌํ•˜๊ธฐ

ErrorBoundary๋Š” React ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์—์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋˜์ง„ JavaScript ์—๋Ÿฌ๋ฅผ ์žก์•„๋‚ด๊ณ , ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ง€์ •๋œ fallback UI๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” React ์ปดํฌ๋„ŒํŠธ์˜ˆ์š”. ๋งˆ์น˜ try-catch ๋ธ”๋ก์ด JavaScript ์ฝ”๋“œ์—์„œ ์—๋Ÿฌ๋ฅผ ์žก๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ErrorBoundary๋Š” React ์ปดํฌ๋„ŒํŠธ ํŠธ๋ฆฌ์—์„œ ์—๋Ÿฌ๋ฅผ ์žก๋Š” ์—ญํ• ์„ ํ•ด์š”.

๊ฒฝ๊ณ 

์ฃผ์˜ํ•  ์ : ErrorBoundary๋Š” ๋ Œ๋”๋ง ๋‹จ๊ณ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ(์˜ˆ: render ๋ฉ”์„œ๋“œ ์•ˆ์—์„œ ์—๋Ÿฌ ๋ฐœ์ƒ)๋งŒ ์žก์•„๋‚ผ ์ˆ˜ ์žˆ์–ด์š”. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋‚ด๋ถ€๋‚˜ ๋น„๋™๊ธฐ ์ฝ”๋“œ(Promise catch ๋ธ”๋ก ์™ธ๋ถ€)์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋Š” ์žก์•„๋‚ด์ง€ ๋ชปํ•ด์š”. ์ด๋Ÿฌํ•œ ์—๋Ÿฌ๋Š” ์ผ๋ฐ˜์ ์ธ try-catch๋‚˜ window.onerror ๋“ฑ์˜ ์ „์—ญ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ต๋‹ˆ๋‹ค.

์ฃผ์š” ์ด์ :

  • ๋ถ€๋ถ„์ ์ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ: ์ž์‹ ์ปดํฌ๋„ŒํŠธ์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ , ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๋ถ€๋ถ„๋งŒ fallback UI๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ์ผ๊ด€๋œ ์—๋Ÿฌ UI: ๋ชจ๋“  ์—๋Ÿฌ๋ฅผ ErrorBoundary๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•จ์œผ๋กœ์จ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „๋ฐ˜์— ๊ฑธ์ณ ์ผ๊ด€๋œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์™€ ๋””์ž์ธ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ์—๋Ÿฌ ๋กœ๊น…: componentDidCatch ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  Sentry ๊ฐ™์€ ์—๋Ÿฌ ๋กœ๊น… ์„œ๋น„์Šค๋กœ ์ „์†กํ•˜์—ฌ ๋ฌธ์ œ ์ง„๋‹จ์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.

๐Ÿงช ์‹ค์ „ ์˜ˆ์‹œ: ๋ฐ์ดํ„ฐ ํŽ˜์นญ๊ณผ ์—๋Ÿฌ ์ฒ˜๋ฆฌ์˜ ์šฐ์•„ํ•œ ๊ตฌํ˜„

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

0๏ธโƒฃ Suspense๋ฅผ ํ™œ์šฉํ•œ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ

Suspense์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Promise๋ฅผ ๋˜์งˆ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๋กœ์ง์ด ํ•„์š”ํ•ด์š”. react-query๋‚˜ swr ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด ๋ถ€๋ถ„์ด ํ›จ์”ฌ ํŽธ๋ฆฌํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” ๊ฐœ๋… ์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ ํ—ฌํผ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”.

// utils/createSuspenseResource.ts interface Resource<T> { read(): T; } function createSuspenseResource<T>(promise: Promise<T>): Resource<T> { let status = "pending"; let result: T | Error; const suspender = promise.then( (r) => { status = "success"; result = r; }, (e) => { status = "error"; result = e; } ); return { read(): T { if (status === "pending") { throw suspender; // Promise๋ฅผ ๋˜์ ธ Suspense๊ฐ€ ์žก๋„๋ก ํ•จ } if (status === "error") { throw result; // ErrorBoundary๊ฐ€ ์žก๋„๋ก ์—๋Ÿฌ๋ฅผ ๋˜์ง } return result as T; }, }; } // ๊ฐ€์ƒ์˜ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ํ•จ์ˆ˜ const fetchUserData = () => new Promise<{ id: number; name: string; email: string; }>((resolve) => { setTimeout(() => { resolve({ id: 1, name: "๋ธ”๋ฃจ", email: "blue@example.com", }); }, 2000); }); // ๋ฐ์ดํ„ฐ๋ฅผ Suspense Resource๋กœ ๋ž˜ํ•‘ export const userResource = createSuspenseResource(fetchUserData()); // components/UserProfile.tsx import React from "react"; import { userResource } from "../utils/createSuspenseResource"; function UserProfile() { const user = userResource.read(); // ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ Suspense๊ฐ€ ๊ธฐ๋‹ค๋ฆผ return ( <div className="user-profile"> <h3>โœจ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„</h3> <p>์ด๋ฆ„: {user.name}</p> <p>์ด๋ฉ”์ผ: {user.email}</p> </div> ); } export default UserProfile; // components/LoadingSpinner.tsx import React from "react"; function LoadingSpinner() { return ( <div className="loading-spinner"> <p>โณ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ด์—์š”...</p> </div> ); } export default LoadingSpinner; // App.tsx ๋˜๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ import React, { Suspense } from "react"; import UserProfile from "./components/UserProfile"; import LoadingSpinner from "./components/LoadingSpinner"; function App() { return ( <div className="app"> <h1>๐Ÿš€ Suspense ์˜ˆ์‹œ</h1> <Suspense fallback={<LoadingSpinner />}> <UserProfile /> </Suspense> </div> ); } export default App;

์œ„ ์ฝ”๋“œ์—์„œ UserProfile ์ปดํฌ๋„ŒํŠธ๋Š” userResource.read()๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด createSuspenseResource ๋‚ด๋ถ€์—์„œ ๋˜์ ธ์ง„ Promise๋ฅผ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด Suspense ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žก์•„ LoadingSpinner๋ฅผ ๋ณด์—ฌ์ฃผ๊ฒŒ ๋ผ์š”. ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ์ด ์™„๋ฃŒ๋˜๋ฉด UserProfile์ด ๋ Œ๋”๋ง๋œ๋‹ต๋‹ˆ๋‹ค.

1๏ธโƒฃ ErrorBoundary ์ปดํฌ๋„ŒํŠธ ๊ตฌํ˜„

ErrorBoundary๋Š” ํด๋ž˜์Šค ์ปดํฌ๋„ŒํŠธ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋ฉฐ, static getDerivedStateFromError์™€ componentDidCatch ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•ด์•ผ ํ•ด์š”.

// components/ErrorBoundary.tsx import React, { Component, ErrorInfo, ReactNode } from "react"; interface Props { children: ReactNode; fallback: ReactNode; } interface State { hasError: boolean; } class ErrorBoundary extends Component<Props, State> { public state: State = { hasError: false, }; // ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ๋˜๋ฉฐ, ๋‹ค์Œ ๋ Œ๋”๋ง์—์„œ fallback UI๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด // ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์š”. public static getDerivedStateFromError(_: Error): State { return { hasError: true }; } // ์—๋Ÿฌ์™€ ์—๋Ÿฌ ์ •๋ณด๋ฅผ ๋กœ๊น…ํ•  ์ˆ˜ ์žˆ์–ด์š”. public componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error("๐Ÿšจ Error caught by ErrorBoundary:", error, errorInfo); // ์‹ค์ œ ํ”„๋กœ๋•์…˜์—์„œ๋Š” Sentry์™€ ๊ฐ™์€ ์—๋Ÿฌ ๋กœ๊น… ์„œ๋น„์Šค๋กœ ์ „์†กํ•ด์š”. // logErrorToService(error, errorInfo); } public render() { if (this.state.hasError) { // fallback UI๋ฅผ ๋ Œ๋”๋งํ•ด์š”. return this.props.fallback; } return this.props.children; } } export default ErrorBoundary; // components/BuggyComponent.tsx import React, { useState } from "react"; function BuggyComponent() { const [count, setCount] = useState(0); const handleClick = () => { setCount((prev) => prev + 1); }; if (count > 3) { // ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ์—๋Ÿฌ๋ฅผ ๊ฐ•์ œ๋กœ ๋ฐœ์ƒ์‹œ์ผœ์š”. throw new Error("๐Ÿ”ฅ ์˜๋„์ ์œผ๋กœ ๋ฐœ์ƒ์‹œํ‚จ ์—๋Ÿฌ์˜ˆ์š”!"); } return ( <div className="buggy-component"> <p>์นด์šดํŠธ: {count}</p> <button onClick={handleClick}>์นด์šดํŠธ ์ฆ๊ฐ€</button> <p>์นด์šดํŠธ๊ฐ€ 3์„ ์ดˆ๊ณผํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด์š”.</p> </div> ); } export default BuggyComponent; // components/ErrorFallback.tsx import React from "react"; function ErrorFallback({ error, resetErrorBoundary, }: { error?: Error; resetErrorBoundary?: () => void; }) { return ( <div role="alert" className="error-fallback"> <h3>โš ๏ธ ๋ฌด์–ธ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์–ด์š”!</h3> <p>์—๋Ÿฌ ๋ฉ”์‹œ์ง€: {error?.message}</p> {resetErrorBoundary && ( <button onClick={resetErrorBoundary}>๋‹ค์‹œ ์‹œ๋„</button> )} </div> ); } export default ErrorFallback; // App.tsx ๋˜๋Š” ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ import React from "react"; import ErrorBoundary from "./components/ErrorBoundary"; import BuggyComponent from "./components/BuggyComponent"; import ErrorFallback from "./components/ErrorFallback"; function App() { return ( <div className="app"> <h1>๐Ÿ’ฅ ErrorBoundary ์˜ˆ์‹œ</h1> <ErrorBoundary fallback={<ErrorFallback />}> <BuggyComponent /> </ErrorBoundary> </div> ); } export default App;

BuggyComponent๋Š” count๊ฐ€ 3์„ ์ดˆ๊ณผํ•˜๋ฉด ์—๋Ÿฌ๋ฅผ ๋˜์ง€๋„๋ก ๋งŒ๋“ค์—ˆ์–ด์š”. ์ด ์—๋Ÿฌ๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ErrorBoundary๊ฐ€ ์žก์•„๋‚ด์–ด ErrorFallback ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋Œ€์‹  ๋ Œ๋”๋ง๋œ๋‹ต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๋ถ€๋ถ„๋งŒ ๊ฒฉ๋ฆฌ๋˜์–ด ์ฒ˜๋ฆฌ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

2๏ธโƒฃ Suspense์™€ ErrorBoundary ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ

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

// App.tsx (ํ†ตํ•ฉ ์˜ˆ์‹œ) import React, { Suspense } from "react"; import ErrorBoundary from "./components/ErrorBoundary"; import UserProfile from "./components/UserProfile"; import LoadingSpinner from "./components/LoadingSpinner"; import BuggyComponent from "./components/BuggyComponent"; import ErrorFallback from "./components/ErrorFallback"; // userResource๋Š” ์•ž์„œ ์ •์˜ํ•œ createSuspenseResource๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด์š”. // import { userResource } from "./utils/createSuspenseResource"; // ๊ฐ€์ƒ์˜ ์—๋Ÿฌ ๋ฐœ์ƒ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ํ•จ์ˆ˜ (UserProfile์—์„œ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Œ) const fetchUserDataWithError = () => new Promise<{ id: number; name: string; email: string; }>((resolve, reject) => { setTimeout(() => { const shouldError = Math.random() > 0.5; // 50% ํ™•๋ฅ ๋กœ ์—๋Ÿฌ ๋ฐœ์ƒ if (shouldError) { reject(new Error("๋„คํŠธ์›Œํฌ ์š”์ฒญ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์–ด์š”!")); } else { resolve({ id: 2, name: "๋ธ”๋ฃจ2", email: "blue2@example.com", }); } }, 2500); }); // userResource๋ฅผ ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ๋ฒ„์ „์œผ๋กœ ๊ต์ฒด (์‹ค์ œ ์ฝ”๋“œ์—์„œ๋Š” ๋ถ„๋ฆฌ ๊ด€๋ฆฌ) // export const userResource = createSuspenseResource(fetchUserDataWithError()); function App() { return ( <div className="app-container"> <h1>โœจ Suspense & ErrorBoundary ํ†ตํ•ฉ ์˜ˆ์‹œ โœจ</h1> <section className="section"> <h2>๐Ÿ“š ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ๊ณผ ์—๋Ÿฌ ์ฒ˜๋ฆฌ</h2> <ErrorBoundary fallback={<ErrorFallback error={new Error("๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹คํŒจ!")} />}> <Suspense fallback={<LoadingSpinner />}> <UserProfile /> </Suspense> </ErrorBoundary> </section> <section className="section"> <h2>๐Ÿ› ์ปดํฌ๋„ŒํŠธ ์—๋Ÿฌ ๊ฒฉ๋ฆฌ</h2> <ErrorBoundary fallback={<ErrorFallback error={new Error("์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ์‹คํŒจ!")} />}> <BuggyComponent /> </ErrorBoundary> </section> <section className="section"> <h2>โœ… ์ •์ƒ์ ์ธ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ</h2> <div> <p>์ด ๋ถ€๋ถ„์€ ์—๋Ÿฌ๋‚˜ ๋กœ๋”ฉ์— ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๊ณ  ์ •์ƒ์ ์œผ๋กœ ๋ Œ๋”๋ง๋ผ์š”.</p> <button onClick={() => console.log("์ •์ƒ ๋™์ž‘!")}>ํด๋ฆญ</button> </div> </section> </div> ); } export default App;

์ด ์˜ˆ์‹œ์—์„œ๋Š” UserProfile ์ปดํฌ๋„ŒํŠธ๋ฅผ Suspense๋กœ ๊ฐ์‹ธ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋‹ค์‹œ ์ด Suspense์™€ BuggyComponent๋ฅผ ๊ฐ๊ฐ ErrorBoundary๋กœ ๊ฐ์‹ธ์„œ ์—๋Ÿฌ๋ฅผ ๊ฒฉ๋ฆฌํ–ˆ์–ด์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด:

  • UserProfile์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋™์•ˆ์—๋Š” LoadingSpinner๊ฐ€ ๋ณด์ด๊ณ ,
  • ๋ฐ์ดํ„ฐ ํŽ˜์นญ ์ค‘ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด UserProfile์„ ๊ฐ์‹ผ ErrorBoundary์˜ ErrorFallback์ด ๋‚˜ํƒ€๋‚˜๊ณ ,
  • BuggyComponent์—์„œ ๋ Œ๋”๋ง ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ผ ErrorBoundary์˜ ErrorFallback์ด ๋‚˜ํƒ€๋‚˜์š”.

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

๐Ÿ“ ์ •๋ฆฌ: ๊ฒฌ๊ณ ํ•œ UI๋ฅผ ํ–ฅํ•œ ์ฒซ๊ฑธ์Œ

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

0๏ธโƒฃ ํ•ต์‹ฌ ์š”์•ฝ

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

1๏ธโƒฃ ๋‹ค์Œ ์•ก์…˜

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

๋˜ํ•œ, react-query, swr ๊ฐ™์€ ๋ฐ์ดํ„ฐ ํŽ˜์นญ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์€ Suspense๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€์›ํ•˜๊ณ  ErrorBoundary์™€๋„ ์ž˜ ์—ฐ๋™๋˜๋‹ˆ, ์ด๋“ค์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ๋” ํšจ์œจ์ ์œผ๋กœ ๊ฒฌ๊ณ ํ•œ UI๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ๊ณ ์˜ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•œ ์—ฌ์ •์— Suspense์™€ ErrorBoundary๊ฐ€ ๋“ ๋“ ํ•œ ๋™๋ฐ˜์ž๊ฐ€ ๋˜์–ด์ค„ ๊ฑฐ์˜ˆ์š”. Happy Coding! ๐Ÿ‘‹

๐Ÿ“ฎ ์ฐธ๊ณ 

์—ฐ๊ด€๋œ ํฌ์ŠคํŠธ