ํ๋ก ํธ์๋ ํ๋ก์ ํธ์ ๊ท๋ชจ๊ฐ ์ปค์ง๋ฉด ์ฐ๋ฆฌ๋ ์์ฐ์ค๋ฝ๊ฒ '๊น๋ํ ์ฝ๋'๋ฅผ ๊ณ ๋ฏผํ๊ฒ ๋ฉ๋๋ค. ๊ทธ ๊ณผ์ ์์ ์์ญ ๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ํ๊ณณ์ ๋ชจ์ ๋ด๋ณด๋ด๋ ๋ฐฐ๋ด ํ์ผ(Barrel File) ํจํด์ ํํ ์ฌ์ฉํ๊ณค ํ์ฃ . ํ์ง๋ง ์ด ํธ๋ฆฌํจ ๋ค์๋ ๋ฒ๋ค ์ฌ์ด์ฆ์ ์ฑ๋ฅ์ ๊ฐ์๋จน๋ ์น๋ช
์ ์ธ ํจ์ ์ด ์จ์ด ์์ต๋๋ค.
์ค๋์ ๋ฐฐ๋ด ํ์ผ์ด ์ ์ฑ๋ฅ์ ์ ์ด ๋๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ฏธ ์ปค์ ธ๋ฒ๋ฆฐ ํ๋ก์ ํธ์์ Next.js์ optimizePackageImports๋ฅผ ํตํด ์ด๋ป๊ฒ ์ฐ์ํ๊ฒ ํ์ถํ ์ ์๋์ง ์์๋ณด๊ฒ ์ต๋๋ค.
1. ๋ฐฐ๋ด ํ์ผ(Barrel File)์ด๋?
๋ฐฐ๋ด ํ์ผ์ ์ฌ๋ฌ ๋ชจ๋์ ํ๋์ index.ts ํ์ผ๋ก ๋ชจ์์ ๋ค์ ๋ด๋ณด๋ด๋(Re-export) ๋ฐฉ์์ ๋๋ค.
// components/index.ts (๋ฐฐ๋ด ํ์ผ)
export { default as Button } from './Button';
export { default as Card } from './Card';
export { default as Modal } from './Modal';
์ฌ์ฉํ๋ ์ชฝ์์๋ ๊ฒฝ๋ก๊ฐ ์งง์์ ธ์ ๋ณด๊ธฐ ์ข์ต๋๋ค.
import { Button } from '@/components'; // ๊น๋!
2. ์ ๋ฐฐ๋ด ํ์ผ์ ์ง์ํด์ผ ํ ๊น?
๋ฐฐ๋ด ํ์ผ์ index.ts์์ ์ฌ๋ฌ ๋ชจ๋์ exportํ์ฌ ๊ฒฝ๋ก๋ฅผ ๋จ์ํํ๋ ํจํด์
๋๋ค. ํ์ง๋ง ์ด๋ ์๋ฐ์คํฌ๋ฆฝํธ ์์ง์ด 'ํ์ํ์ง ์์ ์ฝ๋๊น์ง ํด์ํ๊ฒ ๋ง๋๋' ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํฉ๋๋ค.
โ ์์กด์ฑ ๊ทธ๋ํ์ ์ฐ์ ํญ๋ฐ
๋ด๊ฐ Button ํ๋๋ง ์ฌ์ฉํ๋ ค๊ณ ๋ฐฐ๋ด ํ์ผ์ ์ฐธ์กฐํ๋ ์๊ฐ, ๋ฒ๋ค๋ฌ๋ ํด๋น ํ์ผ์ ์ฐ๊ฒฐ๋ Card, Modal์ ๋ฌผ๋ก ์ด๊ณ , ๊ทธ ์ปดํฌ๋ํธ๋ค์ด ์ฌ์ฉํ๋ ๋ฌด๊ฑฐ์ด ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค๊น์ง ๋ชจ๋ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ํด์ผ ํฉ๋๋ค. ์ด๋ ์์กด์ฑ ๊ทธ๋ํ๋ฅผ ๋น์ ์์ ์ผ๋ก ํฌ๊ฒ ๋ง๋ญ๋๋ค.
โก ํธ๋ฆฌ ์ ฐ์ดํน(Tree Shaking)์ ํ๊ณ
React ๊ณต์ ๋ฌธ์์ ๊ด๋ จ ์ํ๊ณ์์๋ ํธ๋ฆฌ ์ ฐ์ดํน์ด ๋ชจ๋ ์ํฉ์์ ์๋ฒฝํ์ง ์์์ ์์ฌํฉ๋๋ค. ํนํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ sideEffects: false ์ค์ ์ด ๋๋ฝ๋์ด ์๊ฑฐ๋ ๋ณต์กํ ์ฐธ์กฐ๊ฐ ์ฝํ ์๋ค๋ฉด, ๋ฒ๋ค๋ฌ๋ ์ฌ์ฉํ์ง ์๋ ์ฝ๋๊น์ง "ํน์ ๋ชจ๋ฅด๋" ๋ฒ๋ค์ ํฌํจํ๊ฒ ๋ฉ๋๋ค.
โข ๊ฐ๋ฐ ํ๊ฒฝ(HMR) ๋ฐ ํ ์คํธ ์ฑ๋ฅ ์ ํ
ํ์ผ ํ๋๋ง ์์ ํด๋ ๋ฐฐ๋ด ํ์ผ๊ณผ ์ฐ๊ฒฐ๋ ์๋ฐฑ ๊ฐ์ ๋ชจ๋ ๊ด๊ณ๋ฅผ ๋ค์ ๊ณ์ฐํด์ผ ํฉ๋๋ค. ์ด๋ Fast Refresh(HMR) ์๋๋ฅผ ๋ฆ์ถ๊ณ , ์ ๋ ํ
์คํธ ์์๋ ๋ถํ์ํ ๋ชจ๋์ ๋ก๋ํ๊ฒ ๋ง๋ค์ด ์ ์ฒด์ ์ธ ๊ฐ๋ฐ ์์ฐ์ฑ์ ๋จ์ด๋จ๋ฆฝ๋๋ค.
3. ์ค์ ํด๊ฒฐ์ฑ : Next.js์ optimizePackageImports
๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ์ง์ ๊ฒฝ๋ก(Direct Import)๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด์ง๋ง, ์ด๋ฏธ ์๋ง ์ค์ ์ฝ๋๊ฐ ๋ฐฐ๋ด ํ์ผ์ ์ฐ๊ณ ์๋ค๋ฉด ์ผ์ผ์ด ์์ ํ๊ธฐ๋ ๋ถ๊ฐ๋ฅ์ ๊ฐ๊น์ต๋๋ค. ์ด๋ Next.js 13.5+์์ ๋์
๋ optimizePackageImports๋ฅผ ํ์ฉํ๋ฉด ๋ฉ๋๋ค.
์๋ ๊ฒฝ๋ก ๋ณํ (Automatic Mapping)
Next.js ๊ณต์ ๋ฌธ์์ ๋ฐ๋ฅด๋ฉด, ์ด ์ต์ ์ ๋น๋ ์์ ์ ๋ฐฐ๋ด ํ์ผ์ ๊ฑฐ์น์ง ์๋๋ก ์ํฌํธ ๊ตฌ๋ฌธ์ ์ง์ ๊ฒฝ๋ก๋ก ์๋ ์ฌ์์ฑํฉ๋๋ค.
- ์ฌ์ฉ์ ์ฝ๋: import { Button } from '@/components'
- ์ปดํ์ผ ํ: import { Button } from '@/components/Button/Button.tsx' (์๋ ๋ณํ)
์ค์ ๋ฐฉ๋ฒ (next.config.js)
์ฃผ๋ก ์์ด์ฝ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์์ฒด UI ํคํธ์ฒ๋ผ ๋ชจ๋ ๊ฐ์๊ฐ ๋ง์ ํจํค์ง๋ฅผ ๋ฑ๋กํ ๋ ํจ๊ณผ๊ฐ ๋๋ผ๋งํฑํฉ๋๋ค.
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
// ๋ฐฐ๋ด ํ์ผ ์ต์ ํ๊ฐ ํ์ํ ํจํค์ง ๋ฆฌ์คํธ ๋ฑ๋ก
optimizePackageImports: ['@my-project/ui', 'heavy-icons-lib'],
},
}
module.exports = nextConfig
4. ์์ฝ ๋ฐ ๊ถ์ฅ ์ฌํญ
๊ธฐ์ ์ ๋ถ์ฑ๋ฅผ ํผํ๊ณ ์ฑ๋ฅ์ ์ ์งํ๊ธฐ ์ํด ๋ค์์ ๊ฐ์ด๋๋ฅผ ๊ถ์ฅํฉ๋๋ค.
- ๊ฐ๊ธ์ ์ง์ ์ฐธ์กฐํ์ธ์: ์ ๊ท ํ๋ก์ ํธ๋ผ๋ฉด import Button from '@/components/Button'์ฒ๋ผ ๋ช ํํ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๋ ์ต๊ด์ ๋ค์ด๋ ๊ฒ์ด ์ข์ต๋๋ค.
- ๊ณต์ ๋ฌธ์์ ๋์์ ๋ฐ์ผ์ธ์: Next.js๋ lucide-react, @mui/material ๋ฑ ์ฃผ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํด ์ด ์ต์ ํ๋ฅผ ์ด๋ฏธ ๊ธฐ๋ณธ์ผ๋ก ์ ์ฉํ๊ณ ์์ต๋๋ค. ๋ด๊ฐ ์ฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํด๋น๋๋์ง ํ์ธํด ๋ณด์ธ์.
- ESLint๋ฅผ ํ์ฉํ์ธ์: no-restricted-imports ๊ท์น์ ์ค์ ํ์ฌ ํ์๋ค์ด ๋ฌด๋ถ๋ณํ๊ฒ ๋ฐฐ๋ด ํ์ผ์ ์ ๊ตฌ(index)๋ฅผ ์ฐธ์กฐํ์ง ๋ชปํ๋๋ก ๊ฐ์ ํ ์ ์์ต๋๋ค.
5. ์ค์ ์ ์ฉ ์ฌ๋ก
์ด๋ก ์ ๋์ด ์ค์ ํ๋ก์ ํธ์ optimizePackageImports๋ฅผ ์ ์ฉํ์ ๋ ์ด๋ค ๋ณํ๊ฐ ์๋์ง ์์น๋ก ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค. ์ด๋ฒ ์ต์ ํ๋ ์ฌ๋ด ๊ณตํต ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฌด๊ฑฐ์ด ์ธ๋ถ ํจํค์ง(lodash, monaco-editor ๋ฑ)๋ฅผ ๋์์ผ๋ก ์งํํ์ต๋๋ค.
์ ์ฉ ์ฝ๋ (next.config.js)
์ฃผ๋ก ๋ฐฐ๋ด ํ์ผ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ์ฌ๋ด ์ปดํฌ๋ํธ ํจํค์ง์ ํฌ๊ธฐ๊ฐ ํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ต์ ํ ๋ชฉ๋ก์ ์ถ๊ฐํ์ต๋๋ค.
// next.config.js
const nextConfig = {
experimental: {
optimizePackageImports: [
'@์ฌ๋ด์ปดํฌ๋ํธ',
'lodash',
'moment',
'monaco-editor/react',
'monaco-editor',
],
},
}์ต์ ํ ๊ฒฐ๊ณผ (First Load JS)
๊ฒฐ๊ณผ๋ ๊ธฐ๋ ์ด์์ด์์ต๋๋ค. ๊ณตํต์ผ๋ก ๋ก๋๋๋ _app.js์ ํฌ๊ธฐ๊ฐ ๋๋ผ๋งํฑํ๊ฒ ์ค์ด๋ค์์ต๋๋ค.
| ํญ๋ชฉ | ์ต์ ํ ์ | ์ต์ ํ ํ | ์ ๊ฐ๋ฅ |
๊ฒฐ๊ณผ ๋ถ์
- _app.js์ ๋ณํ: ์ ์ญ์์ ์ฐธ์กฐํ๋ ๋ฐฐ๋ด ํ์ผ๋ค์ด ์ง์ ์ฐธ์กฐ ๋ฐฉ์์ผ๋ก ์ต์ ํ๋๋ฉด์, ์ด๊ธฐ ๋ก๋ฉ ์ ๋ถํ์ํ๊ฒ ํฌํจ๋์๋ ์ฝ๋๋ค์ด ๋๊ฑฐ ์ ๊ฑฐ๋์์ต๋๋ค.
- ์ฌ์ฉ์ ๊ฒฝํ ๊ฐ์ : ๊ณตํต ๋ฒ๋ค ์ฌ์ด์ฆ๊ฐ 289kB๋ ์ค์ด๋ค์๋ค๋ ๊ฒ์ ๋ชจ๋ ์ฌ์ฉ์๊ฐ ์ฒซ ํ์ด์ง๋ฅผ ์ง์ ํ ๋ ๋ค์ด๋ก๋ํด์ผ ํ JS ์ฉ๋์ด ์ ๋ฐ ์ด์ ๊ฐ๋ฒผ์์ก์์ ์๋ฏธํฉ๋๋ค. ์ฒด๊ฐ ๋ก๋ฉ ์๋๋ ํจ์ฌ ๋นจ๋ผ์ง ๊ฒ์ ๋๋ค.
์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด ๋ณธ ๊ฒฐ๊ณผ, optimizePackageImports๋ ๋จ์ํ ์ด๋ก ์ ์ธ ์ต์ ํ๊ฐ ์๋์์ต๋๋ค. ์ค์ ๋ช ์ค๋ง์ผ๋ก ๊ณตํต ๋ฒ๋ค ์ฌ์ด์ฆ๋ฅผ 56%๋ ์ค์ธ ์์น๊ฐ ๊ทธ ํจ์ฉ์ฑ์ ์ฆ๋ช
ํฉ๋๋ค.
๊ทธ๋์ ๋ฐฐ๋ด ํ์ผ์ ํธ๋ฆฌํจ ๋๋ฌธ์ ์ฑ๋ฅ์ ํฌ๊ธฐํ๊ณ ๊ณ์
จ๋์? ์๋๋ฉด ๊ฑฐ๋ํด์ง ์์กด์ฑ ๋๋ฌธ์ ์๋๋ฅผ ๋ด์ง ๋ชปํ๊ณ ๊ณ์
จ๋์?
์ด์ Next.js์ ์ค๋งํธํ ์ต์ ํ ๊ธฐ๋ฅ์ ๋น๋ ค, ๊ฐ๋ฐ ๊ฒฝํ(DX)๊ณผ ์ฌ์ฉ์ ๊ฒฝํ(UX) ๋ ๋ง๋ฆฌ ํ ๋ผ๋ฅผ ๋ชจ๋ ์ก์๋ณด์๊ธธ ๋ฐ๋๋๋ค.
๋๊ธ