[๐Ÿค–] React/Next.js ๋ฒˆ๋“ค ์ตœ์ ํ™”: ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๊ณผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

React์™€ Next.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ์ค„์ด๊ณ  ๋กœ๋”ฉ ์†๋„๋ฅผ ๊ฐœ์„ ํ•˜๋Š” ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๊ณผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ๊ธฐ๋ฒ•์„ ์‹ค์šฉ์ ์ธ ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์ž์„ธํžˆ ์•Œ์•„๋ด์š”. ์›นํŒฉ ์„ค์ •๋ถ€ํ„ฐ React.lazy, Next.js dynamic import๊นŒ์ง€ ๋‹ค๋ค„์š”.

24๋ถ„
๋‹จ์–ด: 2,019๊ฐœ
๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ
์ •๋ณด

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

์œ ์šฉํ•œ ํŒ

์ด ๊ธ€์—์„œ๋Š” React์™€ Next.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์„ฑ๋Šฅ์„ ํš๊ธฐ์ ์œผ๋กœ ๊ฐœ์„ ํ•˜๋Š” ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๊ณผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ๊ธฐ๋ฒ•์„ ์‹ค์ œ ์ฝ”๋“œ ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์ž์„ธํžˆ ๋‹ค๋ฃจ๊ณ , ์ตœ์ ํ™” ์ „๋žต๊ณผ ๋ฒˆ๋“ค ํฌ๊ธฐ ๋ถ„์„ ๋ฐฉ๋ฒ•๊นŒ์ง€ ์•Œ์•„๋ด์š”.

๐Ÿš€ ์›น ์„ฑ๋Šฅ์˜ ํ•ต์‹ฌ: ๋ฒˆ๋“ค ์ตœ์ ํ™”์˜ ์ค‘์š”์„ฑ

0๏ธโƒฃ ์™œ ๋ฒˆ๋“ค ์ตœ์ ํ™”๊ฐ€ ํ•„์ˆ˜์ ์ผ๊นŒ์š”?

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

1๏ธโƒฃ ๊ธฐ์กด ๋ฐฉ์‹์˜ ํ•œ๊ณ„์™€ ์ƒˆ๋กœ์šด ์ ‘๊ทผ

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

๐Ÿ’ก ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๊ณผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ, ๋ฌด์—‡์ด ๋‹ค๋ฅธ๊ฐ€์š”?

0๏ธโƒฃ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ

๋งŽ์€ ๋ถ„๋“ค์ด ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๊ณผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ์„ ํ˜ผ์šฉํ•ด์„œ ์‚ฌ์šฉํ•˜์‹œ๊ฑฐ๋‚˜, ์ •ํ™•ํ•œ ์ฐจ์ด๋ฅผ ํ—ท๊ฐˆ๋ ค ํ•˜์‹œ๋Š”๋ฐ์š”. ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•ด ๋“œ๋ฆด๊ฒŒ์š”.

์ •๋ณด

**์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ… (Code Splitting)**์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ์ฝ”๋“œ ๋ฒˆ๋“ค์„ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์€ ๋ฒˆ๋“ค๋กœ ๋‚˜๋ˆ„๋Š” ๊ณผ์ •์„ ๋งํ•ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ์›นํŒฉ(Webpack)๊ณผ ๊ฐ™์€ ๋ฒˆ๋“ค๋Ÿฌ๊ฐ€ ์ด ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜์ฃ . ์‚ฌ์šฉ์ž๊ฐ€ ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ์— ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๋Œ€์‹ , ํ•„์š”ํ•œ ์ฝ”๋“œ๋งŒ ๊ทธ๋•Œ๊ทธ๋•Œ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ์ค€๋น„ํ•˜๋Š” ๊ณผ์ •์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋ผ์š”.

์ •๋ณด

**๋ ˆ์ด์ง€ ๋กœ๋”ฉ (Lazy Loading)**์€ ์ฝ”๋“œ๋ฅผ ์‹ค์ œ๋กœ ํ•„์š”ํ•  ๋•Œ๊นŒ์ง€ ๋กœ๋”ฉ์„ ์ง€์—ฐ์‹œํ‚ค๋Š” ๊ธฐ๋ฒ•์ด์—์š”. ์ฆ‰, ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์œผ๋กœ ๋‚˜๋‰œ ์ž‘์€ ๋ฒˆ๋“ค๋“ค์„ ์–ธ์ œ ๋กœ๋“œํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ์‹คํ–‰ ์ „๋žต์ด์ฃ . ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๊ฑฐ๋‚˜, ํŠน์ • ๊ฒฝ๋กœ๋กœ ์ด๋™ํ–ˆ์„ ๋•Œ ํ•ด๋‹น ๋ฒˆ๋“ค์„ ๋™์ ์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์ด ๋ ˆ์ด์ง€ ๋กœ๋”ฉ์˜ ํ•ต์‹ฌ์ด์—์š”.

๊ฒฐ๋ก ์ ์œผ๋กœ, ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์ด '์–ด๋–ป๊ฒŒ ์ชผ๊ฐค ๊ฒƒ์ธ๊ฐ€'์— ๋Œ€ํ•œ ์ „๋žต์ด๋ผ๋ฉด, ๋ ˆ์ด์ง€ ๋กœ๋”ฉ์€ '์–ธ์ œ ์ชผ๊ฐ  ๊ฒƒ์„ ๋ถˆ๋Ÿฌ์˜ฌ ๊ฒƒ์ธ๊ฐ€'์— ๋Œ€ํ•œ ์ „๋žต์ด๋ผ๊ณ  ์ดํ•ดํ•˜์‹œ๋ฉด ์‰ฌ์šธ ๊ฑฐ์˜ˆ์š”.

1๏ธโƒฃ ์™œ ์ด๋“ค์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ์š”?

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

โš™๏ธ React์—์„œ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๊ณผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ์ ์šฉํ•˜๊ธฐ

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

0๏ธโƒฃ React.lazy์™€ Suspense ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

React.lazy๋Š” ๋™์  import(import())๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ ํ•„์š”ํ•œ ์ฝ”๋“œ๋ฅผ ๋กœ๋“œํ•˜๊ฒŒ ํ•ด์ค˜์š”. ๊ทธ๋ฆฌ๊ณ  Suspense๋Š” ๋ ˆ์ด์ง€ ๋กœ๋“œ๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋กœ๋”ฉ๋˜๋Š” ๋™์•ˆ ๋ณด์—ฌ์ค„ ํด๋ฐฑ(fallback) UI๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ์ฃ .

// src/components/HeavyComponent.tsx import React from 'react'; const HeavyComponent = () => { // ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ๋ณต์žกํ•œ ๋กœ์ง์ด๋‚˜ ๋งŽ์€ ์˜์กด์„ฑ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์–ด์š”. return ( <div style={{ padding: '20px', border: '1px solid blue' }}> <h2>์•ˆ๋…•ํ•˜์„ธ์š”! ์ €๋Š” ๋ฌด๊ฑฐ์šด ์ปดํฌ๋„ŒํŠธ์˜ˆ์š”.</h2> <p>์ œ๊ฐ€ ๋กœ๋”ฉ๋  ๋•Œ๊นŒ์ง€ ์ž ์‹œ ๊ธฐ๋‹ค๋ ค ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ด์š”!</p> </div> ); }; export default HeavyComponent;

์ด์ œ ์ด HeavyComponent๋ฅผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณผ๊ฒŒ์š”.

// src/App.tsx import React, { useState, Suspense } from 'react'; import './App.css'; // HeavyComponent๋ฅผ React.lazy๋กœ ๊ฐ์‹ธ ๋™์  ์ž„ํฌํŠธํ•ด์š”. const LazyHeavyComponent = React.lazy(() => import('./components/HeavyComponent')); function App() { const [showHeavyComponent, setShowHeavyComponent] = useState(false); const handleClick = () => { setShowHeavyComponent(true); }; return ( <div className="App"> <h1>React ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ์˜ˆ์‹œ</h1> <button onClick={handleClick}>๋ฌด๊ฑฐ์šด ์ปดํฌ๋„ŒํŠธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ</button> {showHeavyComponent && ( <Suspense fallback={<div>โœจ ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ ์ค‘...</div>}> <LazyHeavyComponent /> </Suspense> )} </div> ); } export default App;

์œ„ ์˜ˆ์‹œ์—์„œ LazyHeavyComponent๋Š” showHeavyComponent ์ƒํƒœ๊ฐ€ true๊ฐ€ ๋˜์–ด ์‹ค์ œ๋กœ ๋ Œ๋”๋ง๋  ๋•Œ๊นŒ์ง€ ๋กœ๋”ฉ๋˜์ง€ ์•Š์•„์š”. ์‚ฌ์šฉ์ž๊ฐ€ "๋ฌด๊ฑฐ์šด ์ปดํฌ๋„ŒํŠธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด์•ผ๋งŒ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ ์ฝ”๋“œ๊ฐ€ ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ๋‹ค์šด๋กœ๋“œ๋˜๊ณ  ๋ Œ๋”๋ง๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.
Suspense ์ปดํฌ๋„ŒํŠธ์˜ fallback prop์€ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ์ค‘์ธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค„ UI๋ฅผ ์ •์˜ํ•ด์š”. ์ด๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ์•„์ฃผ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•œ๋‹ต๋‹ˆ๋‹ค.

1๏ธโƒฃ ์ด๋ฆ„์ด ์ง€์ •๋œ(Named) export ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

React.lazy๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ default export๋งŒ ์ง€์›ํ•ด์š”. ๋งŒ์•ฝ named export๋œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ค‘๊ฐ„ ๋ชจ๋“ˆ์„ ๋งŒ๋“ค์–ด์„œ default export๋กœ ๋‹ค์‹œ ๋‚ด๋ณด๋‚ด์•ผ ํ•ด์š”.

// src/components/NamedExports.tsx export const NamedComponentA = () => <div>์ด๋ฆ„ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ A</div>; export const NamedComponentB = () => <div>์ด๋ฆ„ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ B</div>;

์ด ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๋ ˆ์ด์ง€ ๋กœ๋”ฉํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•ด์š”.

// src/App.tsx (์ผ๋ถ€) import React, { useState, Suspense } from 'react'; import './App.css'; const LazyNamedComponentA = React.lazy(() => import('./components/NamedExports').then(module => ({ default: module.NamedComponentA })) ); const LazyNamedComponentB = React.lazy(() => import('./components/NamedExports').then(module => ({ default: module.NamedComponentB })) ); function App() { const [showNamedA, setShowNamedA] = useState(false); const [showNamedB, setShowNamedB] = useState(false); return ( <div className="App"> <h1>React ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ์˜ˆ์‹œ</h1> <button onClick={() => setShowNamedA(true)}>Named A ๋ถˆ๋Ÿฌ์˜ค๊ธฐ</button> <button onClick={() => setShowNamedB(true)}>Named B ๋ถˆ๋Ÿฌ์˜ค๊ธฐ</button> {showNamedA && ( <Suspense fallback={<div>Named A ๋กœ๋”ฉ ์ค‘...</div>}> <LazyNamedComponentA /> </Suspense> )} {showNamedB && ( <Suspense fallback={<div>Named B ๋กœ๋”ฉ ์ค‘...</div>}> <LazyNamedComponentB /> </Suspense> )} </div> ); } export default App;

์ด๋ ‡๊ฒŒ .then()์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ ์œผ๋กœ ์ž„ํฌํŠธ๋œ ๋ชจ๋“ˆ์—์„œ ์›ํ•˜๋Š” named export๋ฅผ default ์†์„ฑ์œผ๋กœ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋ฉด ๋ผ์š”.

2๏ธโƒฃ ์—๋Ÿฌ ์ฒ˜๋ฆฌ: ErrorBoundary ํ™œ์šฉ

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

// src/components/MyErrorBoundary.tsx import React, { Component, ErrorInfo, ReactNode } from 'react'; interface Props { children?: ReactNode; fallback: ReactNode; } interface State { hasError: boolean; } class MyErrorBoundary extends Component<Props, State> { public state: State = { hasError: false }; public static getDerivedStateFromError(_: Error): State { // ๋‹ค์Œ ๋ Œ๋”๋ง์—์„œ ํด๋ฐฑ UI๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์š”. return { hasError: true }; } public componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error("Uncaught error:", error, errorInfo); } public render() { if (this.state.hasError) { return this.props.fallback; } return this.props.children; } } export default MyErrorBoundary;

์ด์ œ ์ด ErrorBoundary๋ฅผ Suspense์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ๋ณผ๊ฒŒ์š”.

// src/App.tsx (์ผ๋ถ€) import React, { useState, Suspense } from 'react'; import MyErrorBoundary from './components/MyErrorBoundary'; // ErrorBoundary ์ž„ํฌํŠธ // ... const LazyHeavyComponent = React.lazy(() => import('./components/HeavyComponent')); function App() { // ... return ( <div className="App"> {/* ... */} {showHeavyComponent && ( <MyErrorBoundary fallback={<div>โš ๏ธ ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ์— ์‹คํŒจํ–ˆ์–ด์š”!</div>}> <Suspense fallback={<div>โœจ ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ ์ค‘...</div>}> <LazyHeavyComponent /> </Suspense> </MyErrorBoundary> )} </div> ); } // ...

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

๐Ÿš€ Next.js์—์„œ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๊ณผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ์ ์šฉํ•˜๊ธฐ

Next.js๋Š” React ๊ธฐ๋ฐ˜์ด๋ฏ€๋กœ React.lazy์™€ Suspense๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, Next.js ์ž์ฒด์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” next/dynamic์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ๊ฐ•๋ ฅํ•˜๊ณ  Next.js์˜ SSR/SSG/ISR ํ™˜๊ฒฝ๊ณผ ๋” ์ž˜ ํ†ตํ•ฉ๋ผ์š”.

0๏ธโƒฃ next/dynamic ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

next/dynamic์€ Next.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ๋งŒ ๋ Œ๋”๋ง๋  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋™์ ์œผ๋กœ ์ž„ํฌํŠธํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ๋ผ์š”.

// components/ClientOnlyComponent.tsx 'use client'; // ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์ž„์„ ๋ช…์‹œํ•ด์š”. import React from 'react'; const ClientOnlyComponent = () => { const [count, setCount] = React.useState(0); return ( <div style={{ padding: '20px', border: '1px solid green' }}> <h3>์ €๋Š” ํด๋ผ์ด์–ธํŠธ ์ „์šฉ ์ปดํฌ๋„ŒํŠธ์˜ˆ์š”.</h3> <p>ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•˜๊ฒŒ ๋™์ž‘ํ•ด์š”! Count: {count}</p> <button onClick={() => setCount(prev => prev + 1)}>์ฆ๊ฐ€</button> </div> ); }; export default ClientOnlyComponent;

์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ํŽ˜์ด์ง€์—์„œ ๋™์ ์œผ๋กœ ๋ถˆ๋Ÿฌ์™€ ๋ณผ๊ฒŒ์š”.

// app/page.tsx (App Router) ๋˜๋Š” pages/index.tsx (Pages Router) import dynamic from 'next/dynamic'; import React from 'react'; // next/dynamic์„ ์‚ฌ์šฉํ•˜์—ฌ ClientOnlyComponent๋ฅผ ๋™์ ์œผ๋กœ ์ž„ํฌํŠธํ•ด์š”. const DynamicClientOnlyComponent = dynamic( () => import('../components/ClientOnlyComponent'), { loading: () => <p>ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๋กœ๋”ฉ ์ค‘...</p>, // ๋กœ๋”ฉ ์ค‘ ๋ณด์—ฌ์ค„ UI ssr: false, // ์ด ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์•„์š”. } ); export default function HomePage() { const [showComponent, setShowComponent] = React.useState(false); return ( <main style={{ padding: '20px' }}> <h1>Next.js Dynamic Import ์˜ˆ์‹œ</h1> <button onClick={() => setShowComponent(true)}>ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ</button> {showComponent && <DynamicClientOnlyComponent />} </main> ); }

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

1๏ธโƒฃ next/dynamic with Server Components (App Router)

Next.js App Router์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ Server Component๋กœ ๋™์ž‘ํ•ด์š”. Server Component์—์„œ ๋™์  ์ž„ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ํ•ด๋‹น ๋ชจ๋“ˆ์€ ์—ฌ์ „ํžˆ ์„œ๋ฒ„์—์„œ ๋ฒˆ๋“ค๋ง๋˜๊ณ  ๋ Œ๋”๋ง๋  ์ˆ˜ ์žˆ์–ด์š”. ํ•˜์ง€๋งŒ next/dynamic์„ ์‚ฌ์šฉํ•˜๊ณ  ssr: false ์˜ต์…˜์„ ์ฃผ๋ฉด, ๊ทธ ์ปดํฌ๋„ŒํŠธ๋Š” ํด๋ผ์ด์–ธํŠธ ์ „์šฉ์œผ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ๋กœ๋“œ๋ผ์š”.
Server Component ๋‚ด๋ถ€์—์„œ use client ์ง€์‹œ์–ด๊ฐ€ ์žˆ๋Š” Client Component๋ฅผ ๋™์ ์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์€ next/dynamic์˜ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ํŒจํ„ด ์ค‘ ํ•˜๋‚˜์˜ˆ์š”.

2๏ธโƒฃ import() ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

Next.js๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์›นํŒฉ์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, import() ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์„ ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ์–ด์š”. ์ด๋Š” React.lazy๋‚˜ next/dynamic์ด ์ œ๊ณตํ•˜๋Š” ์ถ”์ƒํ™” ์—†์ด ๋” ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.

// app/dashboard/page.tsx (์˜ˆ์‹œ) 'use client'; // ์ด ํŽ˜์ด์ง€๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์˜ˆ์š”. import React, { useState, useEffect } from 'react'; export default function DashboardPage() { const [ChartComponent, setChartComponent] = useState<React.ComponentType | null>(null); useEffect(() => { // ์‚ฌ์šฉ์ž๊ฐ€ ๋Œ€์‹œ๋ณด๋“œ์— ์ ‘๊ทผํ–ˆ์„ ๋•Œ๋งŒ ์ฐจํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋™์ ์œผ๋กœ ๋ถˆ๋Ÿฌ์™€์š”. import('../components/ChartComponent').then((module) => { setChartComponent(() => module.default); }).catch(error => { console.error('Failed to load ChartComponent:', error); }); }, []); return ( <div> <h1>๋Œ€์‹œ๋ณด๋“œ</h1> {ChartComponent ? <ChartComponent /> : <p>์ฐจํŠธ ๋กœ๋”ฉ ์ค‘...</p>} </div> ); }

์ด ๋ฐฉ์‹์€ React.lazy๋‚˜ next/dynamic์ฒ˜๋Ÿผ Suspense๋‚˜ loading ์˜ต์…˜์„ ์ž๋™์œผ๋กœ ์ œ๊ณตํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์ง์ ‘ ๋กœ๋”ฉ ์ƒํƒœ์™€ ์—๋Ÿฌ ํ•ธ๋“ค๋ง์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ด์š”. ํ•˜์ง€๋งŒ ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋กœ๋“œํ•ด์•ผ ํ•˜๋Š” ๋ชจ๋“ˆ์ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹ ๋•Œ(์˜ˆ: ํฐ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ) ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.

๐Ÿ’ก ๋ฒˆ๋“ค ์ตœ์ ํ™” ์ „๋žต๊ณผ ํŒ

0๏ธโƒฃ ์–ธ์ œ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์„ ์ ์šฉํ•ด์•ผ ํ• ๊นŒ์š”?

๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋‚˜ ๋ชจ๋“ˆ์— ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋Šฅ์‚ฌ๋Š” ์•„๋‹ˆ์—์š”. ์˜คํžˆ๋ ค ๋ฒˆ๋“ค ์˜ค๋ฒ„ํ—ค๋“œ๋‚˜ ๋ณต์žก์„ฑ๋งŒ ์ฆ๊ฐ€์‹œํ‚ฌ ์ˆ˜ ์žˆ์ฃ . ๋‹ค์Œ์˜ ๊ฒฝ์šฐ์— ์ ์šฉ์„ ๊ณ ๋ คํ•ด ๋ณด์„ธ์š”.

์œ ์šฉํ•œ ํŒ

  • ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์Šคํ”Œ๋ฆฌํŒ… (Route-based Splitting): ๊ฐ ๋ผ์šฐํŠธ(ํŽ˜์ด์ง€)๋ณ„๋กœ ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ด๊ณ  ํšจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ•์ด์—์š”. ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•  ๋•Œ๋งŒ ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ์ฝ”๋“œ๋ฅผ ๋กœ๋“œํ•˜๊ฒŒ ๋ผ์š”.
  • ํฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ/์ปดํฌ๋„ŒํŠธ: ์ดˆ๊ธฐ ๋กœ๋“œ ์‹œ์ ์— ํ•„์š”ํ•˜์ง€ ์•Š์€ ํฐ ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ ์ž์ฒด ๊ฐœ๋ฐœํ•œ ๋ฌด๊ฑฐ์šด ์ปดํฌ๋„ŒํŠธ(์˜ˆ: ์ง€๋„, ์ฐจํŠธ, ๋ณต์žกํ•œ ์—๋””ํ„ฐ)์— ์ ์šฉํ•ด์š”.
  • ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง ์ปดํฌ๋„ŒํŠธ: ๋ชจ๋‹ฌ, ํƒญ ์ฝ˜ํ…์ธ , ๊ด€๋ฆฌ์ž ์ „์šฉ ๊ธฐ๋Šฅ ๋“ฑ ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ ๋ Œ๋”๋ง๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉํ•ด์š”.

1๏ธโƒฃ ๋ฒˆ๋“ค ํฌ๊ธฐ ๋ถ„์„ ๋„๊ตฌ ํ™œ์šฉํ•˜๊ธฐ

์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์„ ์ ์šฉํ•˜๊ธฐ ์ „์—, ์–ด๋–ค ๋ฒˆ๋“ค์ด ๊ฐ€์žฅ ํฐ ๋ถ€๋ถ„์„ ์ฐจ์ง€ํ•˜๋Š”์ง€ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด์š”. webpack-bundle-analyzer์™€ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฒˆ๋“ค ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ์–ด์š”.

# Next.js ํ”„๋กœ์ ํŠธ์˜ ๊ฒฝ์šฐ # package.json ์Šคํฌ๋ฆฝํŠธ์— ์ถ”๊ฐ€: # "analyze": "ANALYZE=true next build" # ๋˜๋Š” # "analyze": "cross-env ANALYZE=true next build" (cross-env ์„ค์น˜ ํ•„์š”) # ์‹คํ–‰ ์˜ˆ์‹œ npm run analyze # ๋˜๋Š” yarn analyze

์ด ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋นŒ๋“œ ํ›„ ๋ฒˆ๋“ค์˜ ๊ตฌ์กฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋Œ€ํ™”ํ˜• ์›น ํŽ˜์ด์ง€๊ฐ€ ์—ด๋ ค์š”. ์–ด๋–ค ํŒŒ์ผ์ด ๋ฒˆ๋“ค ํฌ๊ธฐ์— ๊ฐ€์žฅ ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ์ง๊ด€์ ์œผ๋กœ ํŒŒ์•…ํ•˜๊ณ  ์ตœ์ ํ™” ๋Œ€์ƒ์„ ์„ ์ •ํ•˜๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋œ๋‹ต๋‹ˆ๋‹ค.

2๏ธโƒฃ ์ถ”๊ฐ€์ ์ธ ์ตœ์ ํ™” ํŒ

  • ํ”„๋ฆฌ๋กœ๋“œ/ํ”„๋ฆฌํŽ˜์น˜ (Preload/Prefetch): Next.js์˜ Link ์ปดํฌ๋„ŒํŠธ๋Š” ๋ทฐํฌํŠธ์— ๋“ค์–ด์˜ค๋ฉด ์ž๋™์œผ๋กœ ํ•ด๋‹น ํŽ˜์ด์ง€์˜ ์ฝ”๋“œ๋ฅผ ํ”„๋ฆฌํŽ˜์น˜ํ•ด์š”. React.lazy์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์Œ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๊ธฐ ์ „์— ๋ฏธ๋ฆฌ ๊ด€๋ จ ๋ฒˆ๋“ค์„ ๋‹ค์šด๋กœ๋“œํ•˜์—ฌ ์ „ํ™˜ ์†๋„๋ฅผ ๋”์šฑ ๋น ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ํŠธ๋ฆฌ ์‰์ดํ‚น (Tree Shaking): ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ๋ฅผ ์ตœ์ข… ๋ฒˆ๋“ค์—์„œ ์ œ๊ฑฐํ•˜๋Š” ๊ธฐ๋ฒ•์ด์—์š”. ๋ชจ๋˜ JavaScript ๋ชจ๋“ˆ ๋ฒˆ๋“ค๋Ÿฌ(Webpack, Rollup ๋“ฑ)๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•˜์ง€๋งŒ, ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ ์—†๋Š” ๋ชจ๋“ˆ์„ ์ž‘์„ฑํ•˜๊ณ  package.json์— sideEffects: false๋ฅผ ๋ช…์‹œํ•˜๋Š” ๋“ฑ ๊ฐœ๋ฐœ์ž์˜ ๋…ธ๋ ฅ๋„ ํ•„์š”ํ•ด์š”.
  • ์••์ถ• (Compression): Brotli๋‚˜ Gzip๊ณผ ๊ฐ™์€ ์••์ถ• ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก๋˜๋Š” ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์–ด์š”. ๋Œ€๋ถ€๋ถ„์˜ ํ˜ธ์ŠคํŒ… ์„œ๋น„์Šค๋‚˜ CDN์—์„œ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด ์ฃผ์ง€๋งŒ, ์ง์ ‘ ์„ค์ •ํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์–ด์š”.

๐Ÿ“ ์ •๋ฆฌํ•˜๋ฉฐ

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

์˜ค๋Š˜์€ React์™€ Next.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์›น ์„ฑ๋Šฅ์„ ํš๊ธฐ์ ์œผ๋กœ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…๊ณผ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ ๊ธฐ๋ฒ•์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ดค์–ด์š”.


์„ฑ๊ณต

  • ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์€ ๋ฒˆ๋“ค์„ ์—ฌ๋Ÿฌ ์ž‘์€ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ„๋Š” ๊ณผ์ •์ด๊ณ , ๋ ˆ์ด์ง€ ๋กœ๋”ฉ์€ ์ด ์กฐ๊ฐ๋“ค์„ ํ•„์š”ํ•  ๋•Œ๋งŒ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์‹คํ–‰ ์ „๋žต์ด์—์š”.
  • React์—์„œ๋Š” React.lazy์™€ Suspense๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”. ErrorBoundary๋ฅผ ํ†ตํ•œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋„ ์žŠ์ง€ ๋งˆ์„ธ์š”.
  • Next.js์—์„œ๋Š” next/dynamic์„ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋™์ ์œผ๋กœ ์ž„ํฌํŠธํ•˜๊ณ , ssr: false ์˜ต์…˜์œผ๋กœ ์„œ๋ฒ„ ๋ฒˆ๋“ค์—์„œ ์ œ์™ธํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ์ตœ์ ํ™” ์ „๋žต์œผ๋กœ๋Š” ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์Šคํ”Œ๋ฆฌํŒ…, ํฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ/์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ, ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง ์ปดํฌ๋„ŒํŠธ ์ ์šฉ ๋“ฑ์„ ๊ณ ๋ คํ•ด์•ผ ํ•ด์š”.
  • webpack-bundle-analyzer์™€ ๊ฐ™์€ ๋ถ„์„ ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ตœ์ ํ™” ๋Œ€์ƒ์„ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด์š”.

1๏ธโƒฃ ๋‹ค์Œ ์•ก์…˜: ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง

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

๐Ÿ“ฎ ์ฐธ๊ณ 

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