ํ๋ก์ ํธ ๊ท๋ชจ๊ฐ ์ปค์ง์๋ก Next.js ํ๋ก๋์
๋น๋ ์๊ฐ์ด ์ ์ ๋์ด๋๋ ๋ฌธ์ ๋ฅผ ์ฒด๊ฐํ๊ฒ ๋๋ค.
“์ฝ๋๊ฐ ๋๋ฉด ๋น๋๊ฐ ๋๋ ค์ง๋ ๊ฑด ๋น์ฐํ ๊ฑฐ ์๋๊ฐ?” ์ถ์ง๋ง,
์์ธํ ๋ณด๋ฉด ํ๋ก๋์ ์์ ์ฌ์ฉํ์ง ์๋ ์ฝ๋๊น์ง ๋น๋ ํ์ดํ๋ผ์ธ์ด ์ฒ๋ฆฌํ๋ฉด์ ์๊ฐ์ ์ก์๋จน๋ ๊ตฌ๊ฐ๋ค์ด ์๋ค.
์ด๋ฒ ๊ธ์ ๊ทธ์ค ํ๋์๋ React Query / Jotai DevTools๋ฅผ _app์ ์ ์ import๋ก ๋ฃ์ด๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ ํด์,
๋น๋ ์๊ฐ์ ์ค์ธ ๊ณผ์ ์ ์ ๋ฆฌํ ๊ฒ์ด๋ค.
1. ๋ฌธ์ : DevTools๋ฅผ ๊ทธ๋ฅ ๋ฃ์ผ๋ฉด?
๊ฐ๋ฐ ํธ์๋ฅผ ์ํด @tanstack/react-query-devtools, jotai-devtools๋ฅผ ์ฐ๋ ๊ฒฝ์ฐ,
๋ณดํต์ ์๋จ์์ ์ ์ import ํ ๋ค _app ์์์ ์ปดํฌ๋ํธ๋ก ๋ ๋๋งํ๊ฒ ๋๋ค.
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { DevTools } from 'jotai-devtools';
function App() {
return (
<>
{/* ... */}
<ReactQueryDevtools initialIsOpen={false} />
<DevTools position="bottom-left" />
</>
);
}
์ด๋ ๊ฒ ํ๋ฉด ๋ช ๊ฐ์ง ๋ถ์์ฉ์ด ์๊ธด๋ค.
๋จผ์ ํ๋ก๋์ ์์๋ DevTools ์ฝ๋๊ฐ ๋ฒ๋ค์ ํฌํจ๋ ์ ์๋ค. ์ ์ import๋ “์ด ํ์ผ์ ๋ฌด์กฐ๊ฑด ๋ถ๋ฌ์จ๋ค”๋ ์๋ฏธ์ด๊ธฐ ๋๋ฌธ์, Webpack·Next.js๊ฐ ํ๋ก๋์ ๋น๋๋ฅผ ํ ๋๋ ํด๋น ๋ชจ๋์ ๋ฒ๋ค์ ๋ฃ์ ์ ์๋ค. ์ฌ์ฉ์๋ DevTools๋ฅผ ์ฐ์ง ์๋๋ฐ, ๋ถํ์ํ ์ฝ๋๊ฐ ๋ด๋ ค๊ฐ์ ๋ฒ๋ค ํฌ๊ธฐ์ ํ์ฑ ๋น์ฉ์ด ๋์ด๋๋ค.
๋์งธ๋ก ์ด๊ธฐ ๋ก๋ฉ ์์ ์ DevTools ์ฒญํฌ๊น์ง ๊ฐ์ด ๋ก๋๋ ์ ์๋ค. ๋ฉ์ธ ๋ฒ๋ค๊ณผ ํจ๊ป ํน์ ๊ทธ ์งํ์ DevTools ๊ด๋ จ ์ฒญํฌ๊ฐ ์์ฒญ๋๊ธฐ ๋๋ฌธ์, “์ฒซ ํ๋ฉด์ ๊ทธ๋ฆฌ๊ธฐ๊น์ง ๊ฑธ๋ฆฌ๋ ์๊ฐ”(TTI)์ด ๋์ด๋ ์ ์๋ค. ๊ฐ๋ฐํ ๋๋ ์ฒซ ๋ก๋ฉ์ด ๋ฌด๊ฑฐ์์ง๋ฉด ์ฒด๊ฐ ์๋๊ฐ ๋จ์ด์ง๋ค.
์ ์งธ๋ก ๋น๋ํ ๋๋ง๋ค DevTools ์์กด์ฑ์ ํ์ฑ·์ฒ๋ฆฌํ๊ฒ ๋๋ค. ํ๋ก๋์ ๋น๋์์๋ ํด๋น ํจํค์ง๋ฅผ ํด์ํ๊ณ , tree-shaking·์ต์ ํ ๋์์ผ๋ก ๋ฃ์ด์ผ ํ๋ฏ๋ก ๋น๋ ์๊ฐ๊ณผ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์กฐ๊ธ์ด๋ผ๋ ๋์ด๋๋ค.
๊ฒฐ๊ตญ DevTools๊ฐ “๊ฐ๋ฐ์ฉ”์์๋ ๋น๋ ๊ณผ์ ์์ ๊พธ์คํ ์ฒ๋ฆฌ ๋์์ด ๋๋ค
์ ๋ฆฌํ๋ฉด, “๊ฐ๋ฐ์ฉ” ์ฝ๋์ธ DevTools๊ฐ ํ๋ก๋์ ๋ฒ๋ค, ๋น๋ ๊ณผ์ , ๊ฐ๋ฐ ์ ๋ก๋ฉ ๊ฒฝํ ๋ชจ๋์ ์ํฅ์ ์ฃผ๋ ๊ตฌ์กฐ๊ฐ ๋๋ค.
2. ํด๊ฒฐ: ํ๊ฒฝ ๋ถ๊ธฐ + Dynamic Import
๋ฐ๋ผ์, ํ๋ก์ ํธ์์๋ “ํ๊ฒฝ์ ๋ฐ๋ผ DevTools๋ฅผ ์์ ๋ถ๋ฅด์ง ์๊ฑฐ๋, ๋ถ๋ฅผ ๋๋ง ๋์ ์ผ๋ก ๋ถ๋ฌ์ค๊ธฐ”๋ก ๋ฐ๊ฟจ๋ค.
๋ค์ ์ ๋ฆฌํ๋ฉด ์ด๋ฐ ์ ๋ต์ด๋ค.
- ํ๋ก๋์ ์์๋ ์์ DevTools๋ฅผ ๋ ๋ํ์ง ์๋๋ค
- ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ํ์ํ ๋๋ง ๋์ ์ผ๋ก ๋ก๋ํ๋ค
- DevTools๋ ๋ธ๋ผ์ฐ์ API(window/document)์ ์์กดํ๋ฏ๋ก SSR์ ๋๋ค
์ฝ๋๋ ์๋์ ๊ฐ๋ค.
import dynamic from 'next/dynamic';
const isProd = process.env.NODE_ENV === 'production';
const ReactQueryDevtools = isProd
? () => null
: dynamic(
() =>
import('@tanstack/react-query-devtools').then(
(mod) => mod.ReactQueryDevtools,
),
{ ssr: false },
);
const JotaiDevTools = isProd
? () => null
: dynamic(() => import('jotai-devtools').then((mod) => mod.DevTools), {
ssr: false,
});
ํ๋ก๋์ (NODE_ENV === 'production')์ผ ๋๋ ReactQueryDevtools์ JotaiDevTools๋ฅผ “์๋ฌด๊ฒ๋ ๊ทธ๋ฆฌ์ง ์๋ ์ปดํฌ๋ํธ” () => null๋ก ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ํ๋ก๋์ ๋น๋ ์ isProd๊ฐ true๋ก ๊ณ ์ ๋๊ธฐ ๋๋ฌธ์, dynamic(() => import(...))๋ฅผ ํธ์ถํ๋ ๋ถ๊ธฐ๋ ์คํ๋์ง ์๋ dead code๊ฐ ๋๋ค. ๋ฒ๋ค๋ฌ๋ ์ด ๊ฒฝ๋ก๋ฅผ ์ ๊ฑฐํ ์ ์๊ณ , DevTools ํจํค์ง๋ฅผ ๋ถ๋ฌ์ค๋ ์ฝ๋๋ ์คํ ๊ฒฝ๋ก์ ๋จ์ง ์๋๋ค.
๊ฐ๋ฐ ํ๊ฒฝ์์๋ next/dynamic์ผ๋ก ํด๋น ํจํค์ง๋ฅผ ๋์ importํ๋ค. “์ฑ์ด ๋ก๋๋ ๋ ํ ๋ฒ์”๊ฐ ์๋๋ผ,
ํด๋น ์ปดํฌ๋ํธ๊ฐ ์ค์ ๋ก ๋ง์ดํธ๋ ๋ ๊ทธ๋ ๊ฐ์ ํด๋น ๋ชจ๋์ ์์ฒญํ๋ค. ๊ทธ๋ฆฌ๊ณ ssr: false๋ฅผ ์ฃผ์ด ์๋ฒ์์๋ ์ด ์ปดํฌ๋ํธ๋ฅผ ๋ ๋ํ์ง ์๊ณ , ๋ธ๋ผ์ฐ์ ์์๋ง ๋ง์ดํธ๋๋๋ก ํ๋ค.
DevTools๋ DOM·window ๊ฐ์ ๋ธ๋ผ์ฐ์ ์ ์ฉ API๋ฅผ ์ฐ๊ธฐ ๋๋ฌธ์, SSR ๋จ๊ณ์์ ๋ ๋๋งํ๋ฉด ์๋ฌ๊ฐ ๋๊ฑฐ๋ ๋ถํ์ํ ์ฝ๋๊ฐ ์๋ฒ์ ์ฌ๋ผ๊ฐ ์ ์์ด์ ํด๋ผ์ด์ธํธ ์ ์ฉ์ผ๋ก ๋๋ ๊ฒ์ด ๋ง๋ค.
์ฌ์ฉํ๋ ์ชฝ ์ฝ๋๋ ๊ธฐ์กด๊ณผ ๋์ผํ๋ค. _app ์์์ ๊ทธ๋๋ก <JotaiDevTools />, <ReactQueryDevtools />๋ง ์ฐ๋ฉด ๋๊ณ , ํ๊ฒฝ์ ๋ฐ๋ผ ์์์ ์ ์ํ “๋น ์ปดํฌ๋ํธ” ๋๋ “๋์ ์ผ๋ก ๋ถ๋ฌ์จ DevTools”๊ฐ ๋ ๋๋ง๋๋ค.
<JotaiDevTools position="bottom-left" />
<ReactQueryDevtools initialIsOpen={false} />
3. ์ ์ด๋ ๊ฒ ํ๋ฉด ์ข์๊ฐ?
3.1 ํ๋ก๋์ ๋ฒ๋ค·๋น๋
Next.js·Webpack์ ๋น๋ ์์ ์ process.env.NODE_ENV๋ฅผ ์ค์ ๊ฐ(์: 'production')์ผ๋ก ์นํํ๋ค.
๊ทธ๋์ ํ๋ก๋์ ๋น๋์์๋ isProd๊ฐ ํญ์ true๊ฐ ๋๊ณ , ์ผํญ ์ฐ์ฐ์์์ dynamic(() => import(...)) ์ชฝ์ “์ ๋ ์คํ๋์ง ์๋ ์ฝ๋”๊ฐ ๋๋ค.
๋ฒ๋ค๋ฌ๋ ์ด๋ฐ dead code๋ฅผ ์ ๊ฑฐํ ์ฌ์ง๊ฐ ์๊ธฐ๊ฒ ๋๋ค.
์ต์ํ “ํ๋ก๋์ ์คํ ๊ฒฐ๊ณผ๋ฌผ” ๊ด์ ์์๋ DevTools ํจํค์ง๋ฅผ ๊ฐ๋ฆฌํค๋ import(...) ๊ฒฝ๋ก๋ ํ๋ก๋์ ๋ฒ๋ค์ ํฌํจ๋์ง ์๋๋ค.
๊ทธ ๊ฒฐ๊ณผ, DevTools ํจํค์ง๋ ํ๋ก๋์ ๋ฒ๋ค์ ๋ค์ด๊ฐ์ง ์์ ๋ฒ๋ค ํฌ๊ธฐ๊ฐ ์ค๊ณ , ๋น๋ํ ๋๋ ํด๋น ๋ชจ๋์ ํ์ฑ·์ต์ ํํ์ง ์์ ๋น๋ ์๊ฐ์ด ์กฐ๊ธ์ด๋ผ๋ ๋จ์ถ๋๋ค. “๊ฐ๋ฐ์ฉ” ์ฝ๋๊ฐ ํ๋ก๋์ ๊ฒฐ๊ณผ๋ฌผ๊ณผ ๋น๋ ๊ณผ์ ์์ชฝ์์ ๋ถ๋ฆฌ๋๋ ํจ๊ณผ๊ฐ ์๋ค.
3.2 ๊ฐ๋ฐ ํ๊ฒฝ ๋ก๋ฉ
dynamic(() => import(...))๋ฅผ ์ฐ๋ฉด Webpack์ด ๊ทธ ๋ถ๋ถ์ ๋ณ๋ ์ฒญํฌ(chunk)๋ก ๋ถ๋ฆฌํ๋ค.
์ฆ, ๋ฉ์ธ ๋ฒ๋ค(_app์ด ํฌํจ๋ ์ฒญํฌ)๊ณผ DevTools๊ฐ ๋ค์ด ์๋ ์ฒญํฌ๊ฐ ๋ฐ๋ก ๋ง๋ค์ด์ง๋ค.
์ค์ ๋ก๋ฉ ์์๋ ์ด๋ ๊ฒ ๋๋ค. ๋ธ๋ผ์ฐ์ ๊ฐ ๋จผ์ ๋ฉ์ธ ๋ฒ๋ค์ ๋ฐ์์ ์ฑ์ ๊ทธ๋ฆฐ๋ค. ๊ทธ๋ค์ <ReactQueryDevtools />, <JotaiDevTools />๊ฐ ๋ง์ดํธ๋ ๋ ๊ฐ๊ฐ์ ๋์ import๊ฐ ์คํ๋๋ฉด์ DevTools ์ฒญํฌ๋ฅผ ์์ฒญํ๋ค.
๋ฐ๋ผ์ ์ด๊ธฐ ๋ก๋ฉ์์๋ DevTools ์ฝ๋๋ฅผ ๊ธฐ๋ค๋ฆฌ์ง ์์๋ ๋๊ณ , “์ฒซ ํ๋ฉด์ด ๋์ค๋ ์๊ฐ”์ด ์งง์์ง๋ค. ๊ฐ๋ฐํ ๋ ์ฒซ ํ๋ฉด์ด ๋นจ๋ฆฌ ๋จ๊ณ , DevTools๋ ํ์ํ ๋๋ง ๋ณ๋๋ก ๋ก๋๋๋ ๊ตฌ์กฐ๊ฐ ๋๋ค.
3.3 SSR
DevTools๋ window, document, React DevTools ํ์ฅ๊ณผ ๊ฐ์ DOM·๋ธ๋ผ์ฐ์ ์ ์ฉ API์ ์์กดํ๋ค.
์๋ฒ์๋ ์ด๋ฐ API๊ฐ ์๊ธฐ ๋๋ฌธ์, SSR ๋จ๊ณ์์ DevTools ์ปดํฌ๋ํธ๋ฅผ ๋ ๋ํ๋ ค๊ณ ํ๋ฉด ์๋ฌ๊ฐ ๋๊ฑฐ๋ ๋ถํ์ํ polyfill·๋ถ๊ธฐ๊ฐ ๋ค์ด๊ฐ ์ ์๋ค.
next/dynamic์ ssr: false๋ฅผ ์ฃผ๋ฉด, ํด๋น ์ปดํฌ๋ํธ๋ ์๋ฒ์์๋ ์์ ๋ ๋๋์ง ์๊ณ , ํด๋ผ์ด์ธํธ์์๋ง ๋ง์ดํธ๋๋ค.
๊ทธ๋์ ์๋ฒ ๋ฒ๋ค์๋ DevTools๊ฐ ๋ค์ด๊ฐ์ง ์๊ณ , ๋ธ๋ผ์ฐ์ ์์๋ง “๋์ import → ๋ง์ดํธ”๊ฐ ์ผ์ด๋๋๋ก ํ ์ ์๋ค.
DevTools์ฒ๋ผ ๋ธ๋ผ์ฐ์ ์ ์ฉ์ธ UI๋ ssr: false๋ก ๋๋ ๊ฒ์ด ๋ง๋ค.
4. ๊ฒฐ๊ณผ : ๋น๋ ์๊ฐ์ด ํฌ๊ฒ ์ค์๋ค.
- Before: Done in 183.94s
- After: Done in 52.03s
๋ฐ๋ฉด, First Load JS shared by all์ 4.23 MB๋ก ๋์ผํ๋ค.
์ฆ ์ด๋ฒ ๋ณ๊ฒฝ์ “๋ฒ๋ค ํฌ๊ธฐ ๊ฐ์”๋ณด๋ค๋ ๋น๋ ํ์ดํ๋ผ์ธ ๋ถ๋ด ๊ฐ์(=๋น๋ ์๊ฐ ๋จ์ถ) ์ชฝ ํจ๊ณผ๊ฐ ๋ ํฌ๊ฒ ๋ํ๋ ์ผ์ด์ค์ ๊ฐ๊น๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก: “ํ๋ก๋์ ์์ ํ์ ์๋ ์ฝ๋๊ฐ _app ๊ฒฝ๋ก์ ์นํ ์๋ ๊ตฌ์กฐ”๋ฅผ ๋ถ๋ฆฌํ๋๋ ๋น๋ ์๊ฐ ๊ฐ์ ์ด ํฌ๊ฒ ๋ฐ์ํ๋ค.
5. ์ ๋ฆฌ
| ๊ตฌ๋ถ | ์ผ๋ฐ import | ํ๊ฒฝ ๋ถ๊ธฐ + Dynamic Import |
| ํ๋ก๋์ ๋ฒ๋ค | DevTools ํฌํจ ๊ฐ๋ฅ | ์ ์ธ๋จ (dead code ์ ๊ฑฐ) |
| ๊ฐ๋ฐ ์ด๊ธฐ ๋ก๋ฉ | ๋ฉ์ธ ๋ฒ๋ค๊ณผ ํจ๊ป ๋ก๋ | ๋ณ๋ ์ฒญํฌ๋ก ์ง์ฐ ๋ก๋ |
| ๋น๋ | ๋งค๋ฒ DevTools ์ฒ๋ฆฌ | ํ๋ก๋์ ์์๋ ๋ฏธํฌํจ |
Next.js์์ DevTools๋ฅผ ์ธ ๋ “ํ๊ฒฝ ๋ถ๊ธฐ + dynamic import + ssr: false” ์กฐํฉ์ ์ฐ๋ฉด, ํ๋ก๋์ ๋ฒ๋ค์๋ DevTools๊ฐ ๋ค์ด๊ฐ์ง ์๊ณ , ํ๋ก๋์ ๋น๋ ์๊ฐ์ ์ค๋ฉฐ, ๊ฐ๋ฐ ์์๋ ์ด๊ธฐ ๋ก๋ฉ์ด ๊ฐ๋ฒผ์์ง๋ค.
“๊ฐ๋ฐ์ฉ ์ฝ๋๋ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ง, ํ์ํ ๋๋ง” ๋ก๋ ๋๋๋ก ๋ถ๋ฆฌํ๋ ํจํด์ด ๋๋ค.
๋ค์์ ๊ฐ์ด ๋ณด๋ฉด ์ข์ ๊ฒ๋ค
- Bundle Analyzer๋ก DevTools ์ฒญํฌ ํฌํจ ์ฌ๋ถ ํ์ธ
์ด ๊ธ๊ฐ๋ณด๋ค ๋จผ์ ์์ฑ๋์์ง๋ง, ํด๋น ๊ธ๊ฐ์ด ๋ ๋จผ์ ์์ ํ๋ ์ผ์ด์๋ค.
์์ง ๋ฒ๋ค ์ฌ์ด์ฆ ๊ฐ์ ๊ณผ ๋น๋ ์๊ฐ ๋จ์ถ ๋ธ๋์น๋ฅผ ๋จธ์งํ๋ ์์ ์ ํ์ง ์์์ด์..
๋จธ์ง ํ ํด๋น ๊ธ์ ๊ฒฐ๊ณผ๋ฅผ ์ถ๊ฐํด๋ณด๊ฒ ๋ค..
๋๊ธ