๐ฆ 10. ๋ชจ๋ ์์คํ โ ESM๊ณผ CommonJS, ๋น์ ์ ์ฝ๋๊ฐ ์ด๋ ์ธ๊ณ์ ์ด๊ณ ์๋๊ฐ
๐ ๊ฐ์
ESM vs CJS์ ๊ทผ๋ณธ ์ฐจ์ด, import/export ํจํด, ๋์ import, ๋ฒ๋ค๋ฌ ๊ด์ ์์์ Tree Shaking๊น์ง ์ค๋ฌด ์ค์ฌ์ผ๋ก ์ ๋ณตํฉ๋๋ค.
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ESM(
import/export)๊ณผ CommonJS(require/module.exports)์ ๊ทผ๋ณธ ์ฐจ์ด๋ฅผ ์ค๋ช ํ๋ค.- Named export์ Default export๋ฅผ ์ธ์ ๊ฐ๊ฐ ์ฌ์ฉํด์ผ ํ๋์ง ํ๋จํ๋ค.
- ๋์
import()๋ก ์ฝ๋ ์คํ๋ฆฌํ ์ ๊ตฌํํ๊ณ , Tree Shaking ์นํ์ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค 1. ์ ์์์ผ ํ๋๊ฐ?
- ๐ 2. ๋ชจ๋์ ์ญ์ฌ โ ์ ๋ ๊ฐ์ง๊ฐ ์กด์ฌํ๋๊ฐ?
- ๐ 3. ESM vs CJS ํต์ฌ ์ฐจ์ด
- ๐ค 4. ESM Export ํจํด
- โก 5. ๋์ import โ ์ฝ๋ ์คํ๋ฆฌํ
- ๐ณ 6. Tree Shaking ์นํ์ ์ฝ๋ ์์ฑ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 18๋ถ(์ ์ฒด) / ํต์ฌ ํํธ๋ง: 11๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
[CJS vs ESM ์ญ์ฌ] โ [๋ฌธ๋ฒ ์ฐจ์ด] โ [Export ํจํด ์ ํ] โ [๋์ import + Tree Shaking]
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- Node.js์์
import๋ฅผ ์ฐ๋ ค๋ฉด ์ ์ค์ ์ด ํ์ํ์ง ์ค๋ช ํ๋ค. -
barrel export๊ฐ ๋ฒ๋ค ํฌ๊ธฐ์ ์ด๋ค ์ํฅ์ ์ฃผ๋์ง ์๋ค. - ๋ฌด๊ฑฐ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ํ ์์ ์๋ง ๋ก๋ํ๋ ๋์ import๋ฅผ ๊ตฌํํ๋ค.
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
๐ฃ ์์ฒ : "์ํธ ๋, Node.js ์๋ฒ ์ฝ๋์์๋
require()๋ฅผ ์ฐ๋๋ฐ, ํ๋ก ํธ React ์ฝ๋์์๋import๋ฅผ ์จ์. ๊ทธ๋ฆฌ๊ณ Node.js์์๋.mjsํ์ฅ์๋ฅผ ์ฐ๋ฉดimport๊ฐ ๋๋ค๋๋ฐ... ์ด๊ฒ ๋ค ํธํ์ด ๋๋์?require๋import๊ฐ ๋ญ๊ฐ ๋ค๋ฅธ ๊ฑด์ง ์์งํ ์ ๋ชจ๋ฅด๊ฒ ์ด์."
๐ฆ ์ํธ: "์ข์ ์ง๋ฌธ์ด์ผ.
require๋ CJS(CommonJS)๊ณ ,import๋ ESM(ES Modules)์ด์ผ. ์ด ๋์ ๋จ์ํ ๋ฌธ๋ฒ ์ฐจ์ด๊ฐ ์๋๋ผ ๋ชจ๋์ ์ฝ๋ ์์ ๊ณผ ๋ฐฉ์ ์ด ๊ทผ๋ณธ์ ์ผ๋ก ๋ฌ๋ผ. ๊ทธ๋ฆฌ๊ณ ์ด ์ฐจ์ด๊ฐ ๋ฒ๋ค๋ฌ์ Tree Shaking, ์๋ฒ/ํด๋ผ์ด์ธํธ ํ๊ฒฝ ํธํ์ฑ์๋ ์ํฅ์ ์ค. ์ค๋ ์ด๊ฑฐ ํ ๋ฒ ์ ๋๋ก ์ดํดํด๋๋ฉดesm์ด๋cjs๋ ํ๋ ๋น๋ ์ค์ ๋ ๋์ ๋ค์ด์ค๊ฒ ๋ ๊ฑฐ์ผ."
๐ค 1. ์ ์์์ผ ํ๋๊ฐ?
์์ ๋ ๋ฐฑ์๋๋ require, ์์ฒ ์ด ํ๋ก ํธ๋ import โ ์ด ๋์ด ๋ฐฐํฌ ์ง์ ์ ์ฒ์ ์ถฉ๋ํ ๋ ์ด ์ด ์ฑํฐ ๊ณต๋ถ์ ๊ณ๊ธฐ์๋ค. ๋ชจ๋ ์์คํ
์ ์ดํดํ์ง ๋ชปํ๋ฉด ๋ค์ ์ํฉ์์ ๋ฉ๋ถํ๋ค:
- "
require is not defined in ES module scope" ์๋ฌ - Node.js์์
import์ฐ๋ ค๋ค ๋งํ๋ ์ํฉ - ๋ฒ๋ค ํฌ๊ธฐ ์ต์ ํ์์ "์ ์ ์ฐ๋ ์ฝ๋๊ฐ ๋ฒ๋ค์ ํฌํจ๋์ง?"
- TypeScript
moduleResolution์ค์ ์์bundler,node16,NodeNext์ ํ ํผ๋
๐ 2. ๋ชจ๋์ ์ญ์ฌ โ ์ ๋ ๊ฐ์ง๊ฐ ์กด์ฌํ๋๊ฐ?
| ์๊ธฐ | ์ฌ๊ฑด |
|---|---|
| 2009 | Node.js ๋ฑ์ฅ, CommonJS ์ฑํ (require/module.exports) |
| 2015 | ES2015(ES6) โ import/export ํ์คํ (ESM) |
| 2020 | Node.js 12+ โ ESM ๊ณต์ ์ง์ ("type": "module" ๋๋ .mjs) |
๋ธ๋ผ์ฐ์ ์ Node.js๊ฐ ๊ฐ์์ ๋ชจ๋ ์์คํ ์ ๋ฐ์ ์ํค๋ค๊ฐ ๋ค๋ฆ๊ฒ ํฉ๋ฅํ๋ฉด์, ํ์ฌ ๋ ์์คํ ์ด ๊ณต์กดํ๋ ๊ณผ๋๊ธฐ ์ํ๋ค.
๐ 3. ESM vs CJS ํต์ฌ ์ฐจ์ด
์คํ๋์์ ์ํธ ๋ฆฌ๋ ๋์ด ๋งํ "๋ชจ๋์ ์ฝ๋ ์์ ๊ณผ ๋ฐฉ์์ด ๊ทผ๋ณธ์ ์ผ๋ก ๋ฌ๋ผ"์ ์ค์ฒด๊ฐ ๋ฐ๋ก ์ด๊ฒ์ ๋๋ค.
| CommonJS (CJS) | ES Modules (ESM) | |
|---|---|---|
| ๋ฌธ๋ฒ | require(), module.exports | import, export |
| ๋ก๋ฉ ๋ฐฉ์ | ๋๊ธฐ (๋ฐํ์์ ๋์ ๋ก๋) | ๋น๋๊ธฐ (์ ์ ๋ถ์ ํ ๋ก๋) |
| ์คํ ์์ | ๋ฐํ์ | ํ์ฑ ๋จ๊ณ (Top-level) |
| Tree Shaking | โ ์ด๋ ต๋ค | โ ๊ฐ๋ฅ |
| ์ฌ์ฉ ํ๊ฒฝ | Node.js ๊ธฐ๋ณธ๊ฐ | ๋ธ๋ผ์ฐ์ ํ์ค, Node.js (์ค์ ํ์) |
| ๋์ import | require() ์์ฒด๊ฐ ๋์ | import() ํจ์๋ก ์ง์ |
์์ ๋์ด Node.js ์๋ฒ์์ ์์ฑํ๋ ๋ฐฉ์๊ณผ, ์์ฒ ์ด๊ฐ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์ ์์ฑํ๋ ์ฝ๋๋ฅผ ๋๋ํ ๋น๊ตํด ๋ณด๊ฒ ์ต๋๋ค. ์ฝ๋๊ฐ ํ๊ฐ๋๋ ์์ ์ ์ฐจ์ด๋ฅผ ์์ํ๋ฉฐ ์ฝ์ด๋ณด์ธ์.
// CommonJS (CJS)
const fs = require("fs"); // ๋๊ธฐ ๋ก๋
const { readFile } = require("fs"); // ๋์ ์ผ๋ก ๊บผ๋ผ ์ ์์
const moduleName = "fs";
const m = require(moduleName); // ๋ณ์๋ก require ๊ฐ๋ฅ!
module.exports = { myFunction };
module.exports.helper = helperFunction;
// ES Modules (ESM)
import fs from "fs"; // ์ ์ ๋ก๋ (ํ์ฑ ์ ๊ฒฐ์ )
import { readFile } from "fs"; // Named import
// const m = import(moduleName); // ๋์ import๋ ๋ณ๋ ๋ฌธ๋ฒ
// import ๊ฒฝ๋ก์ ๋ณ์ ๋ถ๊ฐ โ ์ ์ ์ผ๋ก ๊ฒฐ์ ๋์ด์ผ ํจ
export const myFunction = () => {}; // Named export
export default myFunction; // Default exportESM์ด Tree Shaking์ ์ ๋ฆฌํ ์ด์ :
// ESM โ ์ ์ ๋ถ์ ๊ฐ๋ฅ (๋น๋ ํ์์ import ๊ด๊ณ ํ์
)
import { specificUtil } from "./utils"; // ๋ฒ๋ค๋ฌ๊ฐ "specificUtil๋ง ํ์" ํ์
๊ฐ๋ฅ
// CJS โ ๋์ ์ด๋ผ ๋ถ์ ๋ถ๊ฐ
const utils = require("./utils");
const fn = utils[dynamicKey]; // ๋ฐํ์์ ๊ฒฐ์ โ ๋ฒ๋ค๋ฌ๊ฐ ๋ญ ์ธ์ง ๋ชจ๋ฆ โ ์ ๋ถ ํฌํจ๐ค 4. ESM Export ํจํด
Named Export
์์ฒ ์ด๊ฐ ๊ณตํต ์ ํธ๋ฆฌํฐ ํจ์๋ค์ ๋ชจ์๋ ํ์ผ์ ๋ง๋ค ๋์ ์ผ์ ๋๋ค. ์ฌ๋ฌ ๋ฒ ์ฌ์ฉํ ํจ์๋ค์ด๋ฏ๋ก ๋ช ํํ ์ด๋ฆํ๋ฅผ ๋ฌ์ ๋ด๋ณด๋ด๋ ๊ฒ์ด ์ ๋ฆฌํฉ๋๋ค. ์ด๋ฅผ Named Export๋ผ๊ณ ๋ถ๋ฆ ๋๋ค.
// utils/postHelpers.js
// Named export โ ์ด๋ฆ์ ๋ช
์
export function formatDate(dateString) {
return new Date(dateString).toLocaleDateString("ko-KR");
}
export const POST_LIMIT = 10;
export class PostValidator {
validate(post) { /* ... */ }
}
// ํ ๋ฒ์ ๋ฌถ์ด์ export
function fetchPost() { /* ... */ }
function createPost() { /* ... */ }
export { fetchPost, createPost };
// ์ด๋ฆ ๋ฐ๊ฟ์ export
export { fetchPost as getPost };// ์ฌ์ฉ ์ธก โ Named import
import { formatDate, POST_LIMIT } from "./utils/postHelpers";
import { formatDate as fd } from "./utils/postHelpers"; // ๋ณ์นญ
// ์ ์ฒด ๊ฐ์ ธ์ค๊ธฐ (Tree Shaking ๋ถ๋ฆฌ)
import * as PostHelpers from "./utils/postHelpers";
PostHelpers.formatDate("2026-03-05");Default Export
๋ฐ๋ฉด, ํ๋ฉด์ ๊ตฌ์ฑํ๋ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ฒ๋ผ ํ ํ์ผ์ด ๋๋ ทํ ๋ฉ์ธ ๋จ์ผ ์ญํ ์ ๋ด๋นํ ๋๋ ํ์ผ์ ๊ธฐ๋ณธ๊ฐ์ ์ง์ ํด ์ฃผ๋ ๋ฐฉ์์ด ์์ฐ์ค๋ฝ์ต๋๋ค. ์ด๋ฅผ Default Export๋ผ๊ณ ๋ถ๋ฆ ๋๋ค.
// components/PostCard.jsx
// Default export โ ํ์ผ๋น ํ๋๋ง
export default function PostCard({ title, author }) {
return <div>{title} by {author}</div>;
}
// ๋๋
const PostCard = ({ title, author }) => (
<div>{title} by {author}</div>
);
export default PostCard;// ์ฌ์ฉ ์ธก โ ์ด๋ฆ์ ์์ ๋กญ๊ฒ ์ง์ ๊ฐ๋ฅ
import PostCard from "./components/PostCard";
import Card from "./components/PostCard"; // ๊ฐ์ ๊ฒ, ์ด๋ฆ ์์ ๋กญ๊ฒNamed vs Default ์ธ์ ์ ํ?
์์ฒ ์ด๋ ๋งค๋ฒ ๋ชจ๋์ ๋ด๋ณด๋ผ ๋๋ง๋ค ์ด๋ค ๋ฐฉ์์ ์ ํํด์ผ ํ ์ง ๊ณ ๋ฏผํ์ต๋๋ค. ์ํธ ๋ฆฌ๋ ๋์ ํ์ ์ผ๊ด์ฑ์ ์ํด ๋ค์๊ณผ ๊ฐ์ ์ค๋ฌด์ ์ธ ๊ฐ์ด๋๋ผ์ธ์ ์ ์ํ์ต๋๋ค.
// โ
Named export๋ฅผ ์จ์ผ ํ ๋
// 1. ์ ํธ ํจ์ ๋ชจ์ (์ฌ๋ฌ ๊ฐ export)
export const formatDate = (d) => { ... };
export const formatViews = (n) => { ... };
export const truncateText = (text, limit) => { ... };
// 2. ์์/ํ์
export
export const API_BASE_URL = "...";
export type PostStatus = "draft" | "published";
// โ
Default export๋ฅผ ์จ์ผ ํ ๋
// React ์ปดํฌ๋ํธ (ํ์ผ๋น ํ๋์ ์ฃผ์ ์ปดํฌ๋ํธ)
export default function PostList() { ... }
// ๐ซ ํผ์ฉ ์ ์ฃผ์ (๊ฐ๋ฅํ์ง๋ง ํผ๋์ค๋ฌ์ธ ์ ์์)
export default function PostCard() { ... }
export const POST_CARD_VARIANTS = ["minimal", "full"];
// โ ์ด ํจํด์ ๊ด์ฐฎ์. ์ฃผ์ ์ปดํฌ๋ํธ + ๊ด๋ จ ์์์ ๊ณ ๊ถ์ฅ์ฌํญ: ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฝ๋๋ Named export ์์ฃผ (Tree Shaking ์ ๋ฆฌ). React ์ปดํฌ๋ํธ๋ Default export ๊ดํ.
โก 5. ๋์ import โ ์ฝ๋ ์คํ๋ฆฌํ
์์ฒ ์ด๊ฐ ๊ฒ์๊ธ ์๋ํฐ ์ปดํฌ๋ํธ๋ฅผ ์ถ๊ฐํ๊ณ ๋์ ์ด๊ธฐ ๋ฒ๋ค์ด 300KB ๊ฐ๊น์ด ๋ถ์ด๋ฌ๋ค. ์ํธ ๋ฆฌ๋ ๋์ด ๋ฒ๋ค ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ๋ฉฐ ๋งํ๋ค: "์๋ํฐ๋ ์ค์ ๋ก ์ธ ๋ ๋ก๋ํ๋ฉด ๋ผ์. ๋์ import() ์จ๋ดค์ด์?" ๊ทธ๋ ๋ถํฐ ์์ฒ ์ด๋ ์ฝ๋ ์คํ๋ฆฌํ
์ ๋ฐฐ์ ๋ค.
// ์ ์ import โ ์ฑ ์์ ์ ๋ฌด์กฐ๊ฑด ๋ก๋
import HeavyEditor from "./components/HeavyEditor"; // ์ด๊ธฐ ๋ฒ๋ค์ ํฌํจ
// โ
๋์ import โ ํ์ํ ์์ ์๋ง ๋ก๋
async function openEditor() {
const { default: HeavyEditor } = await import("./components/HeavyEditor");
// HeavyEditor ์ฌ์ฉ
}
// React์์์ ์ฝ๋ ์คํ๋ฆฌํ
(React.lazy + Suspense)
import React, { lazy, Suspense } from "react";
const HeavyEditor = lazy(() => import("./components/HeavyEditor"));
// HeavyEditor๊ฐ ๋ ๋๋ง๋ ๋ ์ฒญํฌ๋ฅผ ๋ก๋
function App() {
return (
<Suspense fallback={<div>์๋ํฐ ๋ก๋ฉ ์ค...</div>}>
<HeavyEditor />
</Suspense>
);
}
// ๋ผ์ฐํธ ๊ธฐ๋ฐ ์ฝ๋ ์คํ๋ฆฌํ
(Next.js)
// Next.js๋ ํ์ด์ง ํ์ผ์ ์๋์ผ๋ก ์ฝ๋ ์คํ๋ฆฌํ
// ์กฐ๊ฑด๋ถ ๋ก๋ โ ๋ฌด๊ฑฐ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํน์ ์ก์
์์๋ง
async function handleExport() {
// ์์
๋ด๋ณด๋ด๊ธฐ ๋ฒํผ ํด๋ฆญ ์์๋ง xlsx ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ก๋
const { default: xlsx } = await import("xlsx");
const wb = xlsx.utils.book_new();
// ...
}๐ณ 6. Tree Shaking ์นํ์ ์ฝ๋ ์์ฑ
์ฝ๋ ์คํ๋ฆฌํ ์ ์ ์ฉํ๋๋ผ๋, ์ ์ด์ ๋ชจ๋์ ์๋ชป ๋ถ๋ฌ์ค๋ฉด ๋ฒ๋ค๋ฌ๊ฐ ๋ถํ์ํ ์ฝ๋๊น์ง ํจ๊ป ์ง์ ๊พธ๋ฆฌ๊ฒ ๋ฉ๋๋ค. ์์ ๋์ด "ํ์ด์ง ๋ ๋๋ง์ด ์กฐ๊ธ ๋ฌด๊ฒ๊ฒ ๋๊ปด์ ธ์"๋ผ๊ณ ํผ๋๋ฐฑํ์ ๋, ์ํธ ๋ฆฌ๋๊ฐ ๊ฐ์ฅ ๋จผ์ ์ ๊ฒ์ ์ง์ํ ํจํด๋ค์ ๋๋ค.
// โ Barrel export โ Tree Shaking ๋ฐฉํด
// utils/index.js
export { formatDate } from "./formatDate";
export { fetchPost } from "./fetchPost";
export { createPost } from "./createPost";
export { HeavyPostEditor } from "./HeavyPostEditor"; // ๋ฌด๊ฑฐ์ด ์ปดํฌ๋ํธ
// ์ฌ์ฉ ์ธก
import { formatDate } from "./utils"; // formatDate๋ง ์ฐ๋๋ฐ...
// ๋ฒ๋ค๋ฌ์ ๋ฐ๋ผ index.js ์ ์ฒด๋ฅผ ๋ถ์ํด์ผ ํ๋ฏ๋ก HeavyPostEditor๋ ํฌํจ๋ ์ ์์
// โ
์ง์ ๊ฒฝ๋ก import
import { formatDate } from "./utils/formatDate"; // ์ ํํ๊ฒ ์ด ํ์ผ๋ง
// โ
sideEffects ์ค์ (package.json)
// {
// "sideEffects": false // ๋ชจ๋ ํ์ผ์ด ์์ํจ โ ๋ฒ๋ค๋ฌ๊ฐ ์ ๊ทน์ ์ผ๋ก Tree Shaking
// }
// โ
๋ผ์ด๋ธ๋ฌ๋ฆฌ import ๋ฐฉ์ ๋น๊ต
// โ ์ ์ฒด import โ lodash ์ ์ฒด๊ฐ ๋ฒ๋ค์ ํฌํจ (70KB+)
import _ from "lodash";
_.debounce(fn, 300);
// โ
๊ฐ๋ณ ํจ์ import โ debounce๋ง ํฌํจ
import debounce from "lodash/debounce";
debounce(fn, 300);
// โ
ESM ์ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ (lodash-es)
import { debounce } from "lodash-es"; // ESM โ Tree Shaking ๊ฐ๋ฅ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ESM๊ณผ CJS์ ๊ฐ์ฅ ํต์ฌ์ ์ธ ์ฐจ์ด์ ์ ์ค๋ช ํ๋ผ.
โ
์ ๋ต: ESM์ ์ ์ (Static) ์ผ๋ก ํ์ฑ ์ ๋ชจ๋ ๊ด๊ณ๊ฐ ๊ฒฐ์ ๋๊ณ , CJS๋ ๋์ (Dynamic) ์ผ๋ก ๋ฐํ์์ require()๊ฐ ์คํ๋๋ฉฐ ๊ฒฐ์ ๋๋ค.
๐ก ์์ธ ํด์ค:
- ESM:
import๊ฒฝ๋ก๋ ์ ์ ๋ฌธ์์ด์ด์ด์ผ ํ๋ฉฐ, ๋ฒ๋ค๋ฌ๊ฐ ๋น๋ ์ ์ ์ฒด ์์กด ๊ด๊ณ๋ฅผ ํ์ โ Tree Shaking ๊ฐ๋ฅ - CJS:
require(variable)์ฒ๋ผ ๋์ ๊ฒฝ๋ก ๊ฐ๋ฅ, ๋ฐํ์์ ๊ฒฐ์ โ ๋ฒ๋ค๋ฌ๊ฐ ์ฌ์ ์ ๋ถ์ ๋ถ๊ฐ - Node.js์์ ESM์ ์ฌ์ฉํ๋ ค๋ฉด:
package.json์"type": "module"์ค์ ํ๊ฑฐ๋.mjsํ์ฅ์ ์ฌ์ฉ - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "CJS๋ ๋ฐํ์, ESM์ ๋น๋ํ์. ์ด ์ฐจ์ด๊ฐ Tree Shaking์ ๊ฐ๋ฅ ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํ๋ค."
Q2. Named export์ Default export๋ฅผ ๊ฐ๊ฐ ์ธ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ ์ ํ๊ฐ?
โ ์ ๋ต:
- Named export: ์ ํธ ํจ์ ๋ชจ์, ์์, ํ์ ๋ฑ ํ ํ์ผ์ ์ฌ๋ฌ ํญ๋ชฉ ์ exportํ ๋
- Default export: React ์ปดํฌ๋ํธ ๋ฑ ํ์ผ๋น ์ฃผ์ ํญ๋ชฉ ํ๋ ๋ฅผ exportํ ๋
๐ก ์์ธ ํด์ค:
- Named export์ ์ฅ์ : ๋ช ์์ , ์๋์์ฑ ์นํ์ , Tree Shaking์ ์ ๋ฆฌ
- Default export์ ์ฅ์ : import ์ ์ด๋ฆ ์์ ๋กญ๊ฒ ์ง์ ๊ฐ๋ฅ (๋ฆฌ๋ค์ด๋ฐ ์์ด)
- Default export์ ๋จ์ : ํ์ผ๋ช ๊ณผ import ์ด๋ฆ์ด ๋ฌ๋ผ์ง ์ ์์ด ์ถ์ ์ด๋ ค์ (ESLint ๊ท์น์ผ๋ก ๊ฐ์ ํ๋ ํ๋ ์์)
- ๋ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(์: date-fns, lodash-es)๊ฐ Named export ์์ฃผ์ธ ์ด์ : Tree Shaking ์ต์ ํ
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "์ ํธ์ Named, ์ปดํฌ๋ํธ๋ Default. ๋จ, ํ ์ปจ๋ฒค์ ์ด ์ฐ์ ์ด๋ค."
Q3. ์์ฒ ์ด์ ํ ์คํธ ํ์ โ ์๋์ด ๋ฉด์ ๋์
๋ฉด์ ๊ด: "๋ฐฐ๋ด(Barrel) export ํจํด์ด๋ ๋ฌด์์ด๊ณ , ๋ฒ๋ค ํฌ๊ธฐ์ ์ด๋ค ์ํฅ์ ์ค ์ ์๋์? ์ด๋ป๊ฒ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋์?"
โ
์ ๋ต: Barrel export๋ index.js์์ ์ฌ๋ฌ ๋ชจ๋์ re-exportํ๋ ํจํด์ผ๋ก, ํธ๋ฆฌํ์ง๋ง Tree Shaking์ด ์ ๋๋ก ๋์ํ์ง ์๋ ๋ฒ๋ค๋ฌ์์ ๋ถํ์ํ ์ฝ๋๊น์ง ํฌํจ๋ ์ ์๋ค. ์ง์ ๊ฒฝ๋ก import ๋๋ ๋ฒ๋ค๋ฌ ์ค์ ์ผ๋ก ์ํํ๋ค.
๐ก ์์ธ ํด์ค:
- Barrel export๋:
utils/index.js์์export { A } from './a'; export { B } from './b';์ฒ๋ผ ํ ๊ณณ์ ๋ชจ์๋๋ ํจํด. import ๊ฒฝ๋ก๊ฐ ์งง์ ํธ๋ฆฌํ๋ค. - ๋ฌธ์ ์ : ์ผ๋ถ ๋ฒ๋ค๋ฌ + ์ผ๋ถ ์ค์ ์์ index.js๋ฅผ ์ฒ๋ฆฌํ๋ฉฐ B๋ฅผ ์ฐ์ง ์์๋ B ๋ชจ๋์ ์ฌ์ด๋ ์ดํํธ๊ฐ ํฌํจ๋ ์ ์๋ค.
- ํด๊ฒฐ์ฑ
:
- ์ง์ ๊ฒฝ๋ก import:
import { A } from "./utils/a"(๊ฐ์ฅ ํ์ค) package.json์"sideEffects": false์ค์ - Vite, Rollup, webpack ์ต์ ๋ฒ์ ์ ESM barrel export๋ฅผ ์ ์ฒ๋ฆฌ
- ์ง์ ๊ฒฝ๋ก import:
- ์ค๋ฌด ํธ๋ ์ด๋์คํ: ํฐ ์ฑ์์ barrel export ๋จ์ฉ์ ์ด๊ธฐ ๋ชจ๋ ๋ถ์ ์๊ฐ์ ์ํฅ์ ์ค ์ ์์ต๋๋ค. Next.js ํ๋ barrel export ์ต์ํ๋ฅผ ๊ถ๊ณ ํ๊ณ ์์ต๋๋ค.
- ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "Barrel ํจํด์ ํธ๋ฆฌํ์ง๋ง, ์์นซํ๋ฉด ๋ฒ๋ค์ ๋ถํ์ํ ์์ค ์ฝ๋๋ฅผ ํฌํจํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค. ์ฑ๋ฅ์ด ํนํ ์ค์ํ ๊ณณ์์๋ ์ง์ ๊ฒฝ๋ก import๋ฅผ ํ ๋ฒ ๋ ๊ณ ๋ คํด ๋ณด์ธ์."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ ์ง์ง "์ด๊ฒ ๋ค ์ฐ๊ฒฐ๋๋๊ตฌ๋" ํ๋ ๋๋์ด ๋ค์๋ค. ์ import์ require๊ฐ ์์ฌ์๋์ง, ์ lodash-es๊ฐ ๋ฐ๋ก ์๋์ง, ์ ๋ฒ๋ค ๋ถ์ํ ๋ ์ ์ฐ๋ ์ฝ๋๊ฐ ํฌํจ๋๋์ง โ ๋ชจ๋ ESM vs CJS ์ฐจ์ด์์ ๋น๋กฏ๋๋ค๋ ๊ฑธ ์ด์ ์ดํดํ๋ค.
Tree Shaking์ด ๋ง๋ฒ์ด ์๋๋ผ "์ ์ ๋ถ์ ๊ฐ๋ฅํ ESM์ด๊ธฐ ๋๋ฌธ์" ๊ฐ๋ฅํ ๊ฑฐ๋ผ๋ ๊ฒ๋ ์๋ก ์์๋ค. ๊ทธ๋์ ๋น์ฐํ๊ฒ ์ฐ๋ import๊ฐ ์ด๋ ๊ฒ ๊น์ ๋ฐฐ๊ฒฝ์ด ์์๋ค๋.
๐ก ์ค๋์ ๊ตํ: "ESM์ ๋ฒ๋ค๋ฌ์๊ฒ '์ด ์ฝ๋์ ์์กด์ฑ ์ง๋'๋ฅผ ๋ฏธ๋ฆฌ ๋ณด์ฌ์ค๋ค. ์ง๋๊ฐ ์์ผ๋ ์ ์ฐ๋ ์ฝ๋๋ฅผ ์์๋ผ ์ ์๋ค. CJS๋ ๋ฐํ์์์ผ ๊ธธ์ ์์์ ๋ฏธ๋ฆฌ ์์๋ผ ์ ์๋ค."
์ด๋ฒ ์ฃผ๊ฐ ๊ฑฐ์ ๋ค ๋๋ค. ๋ค์ ์ฃผ์๋ ์๋ฌ ์ฒ๋ฆฌ, Map/Set ์ฑํฐ๊ฐ ๋จ์์๋๋ฐ, ์ด๊ฒ๋ ๊ธฐ๋๋๋ค. ์ฃผ๋ง์ ์ค๋ ๋ฐฐ์ด ๋ชจ๋ ์์คํ ์ ๋ฆฌํด์ ์คํฐ๋ ๊ณต์ ๋ฌธ์์ ์ฌ๋ ค์ผ์ง. ์ค๋๋ ์๊ณ ํ๋ค, ๋ ์์ !