[๐ค] React/Next.js ๋ฒ๋ค ์ต์ ํ: ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ์๋ฒฝ ๊ฐ์ด๋
React์ Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ์ค์ด๊ณ ๋ก๋ฉ ์๋๋ฅผ ๊ฐ์ ํ๋ ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๋ ์ด์ง ๋ก๋ฉ ๊ธฐ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์นํฉ ์ค์ ๋ถํฐ React.lazy, Next.js dynamic import๊น์ง ๋ค๋ค์.
์ ๋ณด๐ค ์ด ํฌ์คํ ์ 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 ์งํ๋ฅผ ํ์ธํ๊ณ , ๋ฒ๋ค ํฌ๊ธฐ ๋ณํ๋ฅผ ์ถ์ ํ๋ฉฐ ์ถ๊ฐ์ ์ธ ์ต์ ํ ๊ธฐํ๋ฅผ ์ฐพ์๋ณด์ธ์.
์ฌ์ฉ์์๊ฒ ๋ ๋น ๋ฅด๊ณ ์พ์ ํ ๊ฒฝํ์ ์ ๊ณตํ๊ธฐ ์ํ ์ฌ๋ฌ๋ถ์ ๋
ธ๋ ฅ์ ๊ฒฐ์ฝ ํ๋์ง ์์ ๊ฑฐ์์! ๊ถ๊ธํ ์ ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ๋๊ธ๋ก ๋จ๊ฒจ์ฃผ์๊ฑฐ๋ ๊ด๋ จ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์.
๐ฎ ์ฐธ๊ณ
- React ๊ณต์ ๋ฌธ์: Code Splitting
- Next.js ๊ณต์ ๋ฌธ์: Dynamic Imports
- MDN Web Docs: import()
- Google Developers: Code-splitting
์ฐ๊ด๋ ํฌ์คํธ
๋จ์ด: 2,122๊ฐ24๋ถ[๐ค] JavaScript Proxy์ Reflect ์ฌ์ธต ๋ถ์: ๋ฉํ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ์ฝ๋ ๊ฐํํ๊ธฐ
JavaScript Proxy์ Reflect API๋ฅผ ํ์ฉํ ๋ฉํ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ์ฌ์ธต ๋ถ์ํด์. ๊ฐ์ฒด ์ ๊ทผ ์ ์ด, ์ ํจ์ฑ ๊ฒ์ฌ, ๋ก๊น , ๋ฐ์ํ ์์คํ ๊ตฌํ ๋ฑ ์ค์ฉ์ ์ธ ํ์ฉ ์ฌ๋ก๋ฅผ ํตํด ์ฝ๋์ ์ ์ฐ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
- ๋จ์ด: 1,769๊ฐ20๋ถ
[๐ค] React์ `useOptimistic` ํ ์ผ๋ก ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ตฌํํ๊ธฐ: Server Actions์ ํจ๊ป
React 18/19์ `useOptimistic` ํ ์ ํ์ฉํ์ฌ Server Actions์ ์ฐ๋๋๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ฉ์ ์ธ ์์์ ํจ๊ป ์์ธํ ์์๋ด์. ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
๋จ์ด: 1,557๊ฐ17๋ถ[๐ค] TypeScript const Type Parameters: ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ๊ฐํ์ ์ค์ฉ์ ์ธ ํ์ฉ๋ฒ
TypeScript 5.0์ ๋์ ๋ const Type Parameters๋ฅผ ํ์ฉํ์ฌ ์ ๋ค๋ฆญ ํจ์์ ๋ฆฌํฐ๋ด ํ์ ์ถ๋ก ์ ์ ๊ตํ๊ฒ ์ ์ดํ๊ณ , ๋์ฑ ๊ฒฌ๊ณ ํ ํ์ ์์คํ ์ ๊ตฌ์ถํ๋ ์ค์ฉ์ ์ธ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. as const์์ ์ฐจ์ด์ ๊ณผ ์ค์ ์ฝ๋ ์์๋ฅผ ํตํด ์ด์ค๊ธ ๊ฐ๋ฐ์๋ ์ฝ๊ฒ ์ดํดํ ์ ์๋๋ก ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,015๊ฐ22๋ถ
[๐ค] Next.js/React ์ฑ CLS ์ต์ ํ: ์ํํธ ์๋ ์ฌ์ฉ์ ๊ฒฝํ ๋ง๋ค๊ธฐ
Next.js์ React ์ ํ๋ฆฌ์ผ์ด์ ์์ Cumulative Layout Shift(CLS) ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๋ ์ค์ง์ ์ธ ์ ๋ต๊ณผ ์ฝ๋ ์์๋ฅผ ์์ธํ ์์๋ณด์ธ์. ์น ์ฑ๋ฅ ์ต์ ํ์ ํต์ฌ ์์์ธ CLS๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,735๊ฐ21๋ถ
[๐ค] Next.js SSR, SSG, ISR ๋ ๋๋ง ์ ๋ต: App Router์์ ์ต์ ์ ์ ํ์?
Next.js App Router์์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR), ์ ์ ์ฌ์ดํธ ์์ฑ(SSG), ์ฆ๋ถ ์ ์ ์ฌ์์ฑ(ISR) ๊ฐ ๋ ๋๋ง ์ ๋ต์ ๋์ ์๋ฆฌ, ์ฅ๋จ์ , ์ค์ ํ์ฉ ๋ฐ ์ต์ ํ ๋ฐฉ๋ฒ์ ๋น๊ต ๋ถ์ํด๋๋ ค์.
- ๋จ์ด: 1,460๊ฐ17๋ถ
[๐ค] React Context API์ Zustand: ์ ์ญ ์ํ ๊ด๋ฆฌ, ์ธ์ ๋ฌด์์ ์จ์ผ ํ ๊น์?
React ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ญ ์ํ ๊ด๋ฆฌ๋ฅผ ๊ณ ๋ฏผํ๊ณ ๊ณ์ ๊ฐ์? Context API์ ๊ฐ๋ฒผ์ด ์ธ๋ถ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ Zustand๋ฅผ ๋น๊ต ๋ถ์ํ๊ณ , ์ค๋ฌด์์ ๊ฐ ๋๊ตฌ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๋ ์ ๋ต์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,004๊ฐ24๋ถ
[๐ค] Turborepo๋ก Next.js ๋ชจ๋ ธ๋ ํฌ ๊ตฌ์ถ: ํจ์จ์ ์ธ ๊ฐ๋ฐ ๋ฐ ์ต์ ํ ์ ๋ต
Turborepo๋ฅผ ํ์ฉํ์ฌ Next.js ํ๋ก์ ํธ๋ฅผ ๋ชจ๋ ธ๋ ํฌ๋ก ๊ตฌ์ฑํ๊ณ , ๊ณต์ ์ปดํฌ๋ํธ, ์ ํธ๋ฆฌํฐ, CI/CD ์ต์ ํ ๋ฐฉ์์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์ค๋ช ํด ๋๋ ค์.
- ๋จ์ด: 2,318๊ฐ27๋ถ
[๐ค] React useEffect ํ , ์ด์ ํท๊ฐ๋ฆฌ์ง ๋ง์ธ์! (์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ์๋ฒฝ ๊ฐ์ด๋)
React ๊ฐ๋ฐ์์ ํ์์ ์ธ useEffect ํ ์ ๋์ ์๋ฆฌ๋ถํฐ ์์กด์ฑ ๋ฐฐ์ด, ํด๋ฆฐ์ ํจ์ ํ์ฉ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ค๋ฌด์์ ์์ฃผ ๊ฒช๋ ์ค์์ ์ต์ ํ ์ ๋ต๊น์ง, ์ด์ค๊ธ ๊ฐ๋ฐ์๋ฅผ ์ํ ์๋ฒฝ ๊ฐ์ด๋๋ฅผ ์ ๊ณตํด์.
- ๋จ์ด: 3,270๊ฐ31๋ถ
[๐ค] Next.js Server Actions ์ค์ : ์๋ฌ ์ฒ๋ฆฌ, ์ ํจ์ฑ ๊ฒ์ฌ, ๋๊ด์ UI ์ ๋ฐ์ดํธ
Next.js Server Actions๋ฅผ ์ค๋ฌด์ ์ ์ฉํ ๋ ๋ง์ฃผํ๋ ์๋ฌ ์ฒ๋ฆฌ, ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ ๊ธฐ๋ฒ์ ์์ธํ ์ฝ๋ ์์์ ํจ๊ป ์์๋ณด์ธ์.
๋จ์ด: 1,981๊ฐ21๋ถ[๐ค] TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์๋ฒฝ ๊ฐ์ด๋: ์ค์ ํ์ฉ ํจํด
TypeScript ์ ํธ๋ฆฌํฐ ํ์ ์ ํต์ฌ ๊ฐ๋ ๊ณผ ์ค์ ํ์ฉ๋ฒ์ ๊น์ด ์๊ฒ ๋ค๋ค์. Pick, Omit, Partial, Required ๋ฑ ์์ฃผ ์ฐ๋ ์ ํธ๋ฆฌํฐ ํ์ ์ผ๋ก ๋ณต์กํ ํ์ ์ ํจ๊ณผ์ ์ผ๋ก ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์. ํ์ ์คํฌ๋ฆฝํธ ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์์ ์ฑ์ ๋์ด๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํด์.
- ๋จ์ด: 1,707๊ฐ20๋ถ
[๐ค] Next.js App Router ๋ฏธ๋ค์จ์ด: ๊ฐ๋ ฅํ ์์ฒญ ์ฒ๋ฆฌ ์ ๋ต๊ณผ ์ค์ ์์
Next.js App Router ํ๊ฒฝ์์ ๋ฏธ๋ค์จ์ด๋ฅผ ํ์ฉํด ์ฌ์ฉ์ ์ธ์ฆ, ๋ฆฌ๋ค์ด๋ ์ , ๊ตญ์ ํ ๋ฑ์ ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง์ ํจ์จ์ ์ผ๋ก ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์ค์ ์์ ์ ํจ๊ป ์์ธํ ์์๋ณด์ธ์.
- ๋จ์ด: 1,625๊ฐ19๋ถ
[๐ค] ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ ์ฌํ: ์ค์ฉ์ ์ธ ํจํด๊ณผ ํํ ์คํด๋ค
ํ์ ์คํฌ๋ฆฝํธ ์ ๋ค๋ฆญ(Generics)์ ๊น์ด ์ดํดํ๊ณ , ์ค๋ฌด์์ ์์ฃผ ์ฌ์ฉ๋๋ ์ ๋ค๋ฆญ ํจํด๊ณผ ํํ ๊ฒช๋ ์คํด๋ค์ ์ค์ ์ฝ๋ ์์์ ํจ๊ป ์ฝ๊ณ ๋ช ํํ๊ฒ ์ค๋ช ํด ๋๋ ค์. ํ์ ์์ ์ฑ๊ณผ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.
๋จ์ด: 1,846๊ฐ18๋ถ[๐ค] Next.js Route Handler: App Router์์ ์์ ํ๊ณ ํจ์จ์ ์ธ API ๊ตฌ์ถํ๊ธฐ (์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ ํฌํจ)
Next.js App Router์ Route Handler๋ฅผ ์ฌ์ฉํ์ฌ API ์๋ํฌ์ธํธ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐฉ๋ฒ์ ์์ธํ ์์๋ด์. ์ธ์ฆ, ์๋ฌ ์ฒ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์บ์ฑ ์ ๋ต์ ํฌํจํ ์ค์ฉ์ ์ธ ํ์ผ๋ก ์์ ํ๊ณ ํจ์จ์ ์ธ ์๋ฒ๋ฆฌ์ค ํจ์๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์ตํ๋ด์.
- ๋จ์ด: 1,932๊ฐ22๋ถ
[๐ค] Next.js Image ์ปดํฌ๋ํธ ์ต์ ํ: Core Web Vitals ๊ฐ์ ๋ถํฐ ์ค์ ํ์ฉ๊น์ง
Next.js์ Image ์ปดํฌ๋ํธ๋ฅผ ํ์ฉํ์ฌ ์น ์ฑ๋ฅ ํต์ฌ ์งํ์ธ Core Web Vitals๋ฅผ ๊ฐ์ ํ๊ณ , ๋ค์ํ ์ต์ ํ ์ต์ ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ธ๋ฃจ๊ฐ ์์ธํ ์๋ ค๋๋ ค์.
- ๋จ์ด: 2,173๊ฐ25๋ถ
[๐ค] Next.js 14.1+์ ํ์ : Partial Prerendering (PPR) ์๋ฒฝ ๊ฐ์ด๋์ ์ค์ ์ต์ ํ ์ ๋ต
Next.js 14.1๋ถํฐ ๋์ ๋ Partial Prerendering (PPR)์ ํตํด ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ๊ทน๋ํํ๊ณ ๋์ ์ฝํ ์ธ ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ฃจ์ด์. PPR์ ๋์ ์๋ฆฌ๋ถํฐ ์ค์ ํ๋ก์ ํธ ์ ์ฉ ์ ๋ต๊น์ง, ๊ฐ๋ฐ์๋ค์ด ๊ถ๊ธํดํ๋ ๋ชจ๋ ๊ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,786๊ฐ19๋ถ
[๐ค] TypeScript ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋: ๋ณต์กํ ํ์ ๋ ์์ฝ๊ฒ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ
TypeScript ๊ฐ๋ฐ์์ ๋ง์ฃผํ๋ ๋ณต์กํ ํ์ ์ถ๋ก ๋ฌธ์ , ์กฐ๊ฑด๋ถ ํ์ ๊ณผ infer ํค์๋๋ฅผ ํ์ฉํ๋ฉด ํจ์ฌ ์ฐ์ํ๊ณ ๊ฐ๋ ฅํ๊ฒ ํด๊ฒฐํ ์ ์์ด์. ์ค์ ์์ ์ ํจ๊ป ๊ทธ ํ์ฉ๋ฒ์ ์ฌ๋ ์๊ฒ ๋ค๋ค๋ด ๋๋ค.
- ๋จ์ด: 1,697๊ฐ21๋ถ
[๐ค] JavaScript ์ด๋ฒคํธ ๋ฃจํ(Event Loop) ์์ ์ ๋ณต: ๋น๋๊ธฐ ์ฒ๋ฆฌ์ ๋ฐํ์ ๋์ ์๋ฆฌ
JavaScript์ ํต์ฌ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ธ ์ด๋ฒคํธ ๋ฃจํ์ ๋์ ์๋ฆฌ๋ฅผ ์ฌ๋ ์๊ฒ ํํค์ณ ๋ด์. ์ฝ ์คํ, ํ์คํฌ ํ, ๋ง์ดํฌ๋กํ์คํฌ ํ์์ ์ํธ์์ฉ์ ์ดํดํ๊ณ , ์ค๋ฌด์์ ๋ง์ฃผ์น๋ ๋น๋๊ธฐ ์ฝ๋์ ๋์์ ๋ช ํํ ์์ธกํ๋ ๋ฐฉ๋ฒ์ ์๋ ค๋๋ ค์.
- ๋จ์ด: 1,960๊ฐ23๋ถ
[๐ค] Next.js Server & Client Components, ์ค์ ์์ ํ๋ช ํ๊ฒ ์ ํํ๋ ๊ฐ์ด๋
Next.js App Router์์ Server Components์ Client Components ์ค ์ด๋ค ๊ฒ์ ์ฌ์ฉํด์ผ ํ ์ง ๊ณ ๋ฏผ์ด์ ๊ฐ์? ์ด ๊ธ์์ ๋ ์ปดํฌ๋ํธ์ ํต์ฌ ์ฐจ์ด์ , ์ฌ์ฉ ์์ , ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ์ค์ ์ ๋ต์ ๋ธ๋ฃจ๊ฐ ์๋ ค๋๋ฆด๊ฒ์.
- ๋จ์ด: 1,878๊ฐ21๋ถ
[๐ค] TypeScript satisfies ์ฐ์ฐ์: ํ์ ์ถ๋ก ๊ณผ ์์ ์ฑ์ ๋์์ ์ก๋ ๋น๋ฒ
TypeScript์ `satisfies` ์ฐ์ฐ์๋ฅผ ํ์ฉํ์ฌ ํ์ ์ถ๋ก ์ ์ ์ฐ์ฑ์ ์ ์งํ๋ฉด์๋ ์๊ฒฉํ ํ์ ์์ ์ฑ์ ํ๋ณดํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ์ค์ฉ์ ์ธ ์์๋ฅผ ํตํด ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ ๋ ธํ์ฐ๋ฅผ ๊ณต์ ํฉ๋๋ค.
- ๋จ์ด: 1,207๊ฐ15๋ถ
[๐ค] React 19 ์๋ก์ด ๊ธฐ๋ฅ: use ํ , Actions, ๊ทธ๋ฆฌ๊ณ ์ปดํ์ผ๋ฌ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
React 19์ ํต์ฌ ๋ณ๊ฒฝ ์ฌํญ์ธ use ํ , ์๋ฒ ์ก์ , ๊ทธ๋ฆฌ๊ณ React ์ปดํ์ผ๋ฌ์ ๋์ ๋ฐฐ๊ฒฝ๊ณผ ์ค์ ํ์ฉ ์์๋ฅผ ์ด์ค๊ธ ๊ฐ๋ฐ์ ๋๋์ด์ ๋ง์ถฐ ์์ธํ ์ค๋ช ํฉ๋๋ค. ์ต์ React ์ ๋ฐ์ดํธ๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ๊ณผ ๊ฐ๋ฐ ๊ฒฝํ์ ํฅ์์ํค๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์.
- ๋จ์ด: 1,512๊ฐ16๋ถ
[๐ค] Next.js App Router ์บ์ฑ ์ ๋ต: ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ (revalidatePath, revalidateTag) ์๋ฒฝ ๊ฐ์ด๋
Next.js 14 App Router์์ ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต๊ณผ revalidatePath, revalidateTag๋ฅผ ์ด์ฉํ ๋ฐ์ดํฐ ์ฌ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ์ค๋ฌด ์์์ ํจ๊ป ์์ธํ ์์๋ณด๊ณ ์น ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋ณด์ธ์.