๐Ÿ“ฆ 10. ๋ชจ๋“ˆ ์‹œ์Šคํ…œ โ€” ESM๊ณผ CommonJS, ๋‹น์‹ ์˜ ์ฝ”๋“œ๊ฐ€ ์–ด๋А ์„ธ๊ณ„์— ์‚ด๊ณ  ์žˆ๋Š”๊ฐ€

2026๋…„ 3์›” 8์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

ESM vs CJS์˜ ๊ทผ๋ณธ ์ฐจ์ด, import/export ํŒจํ„ด, ๋™์  import, ๋ฒˆ๋“ค๋Ÿฌ ๊ด€์ ์—์„œ์˜ Tree Shaking๊นŒ์ง€ ์‹ค๋ฌด ์ค‘์‹ฌ์œผ๋กœ ์ •๋ณตํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ESM(import/export)๊ณผ CommonJS(require/module.exports)์˜ ๊ทผ๋ณธ ์ฐจ์ด๋ฅผ ์„ค๋ช…ํ•œ๋‹ค.
  • Named export์™€ Default export๋ฅผ ์–ธ์ œ ๊ฐ๊ฐ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ํŒ๋‹จํ•œ๋‹ค.
  • ๋™์  import()๋กœ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…์„ ๊ตฌํ˜„ํ•˜๊ณ , 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. ๋ชจ๋“ˆ์˜ ์—ญ์‚ฌ โ€” ์™œ ๋‘ ๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€?

์‹œ๊ธฐ์‚ฌ๊ฑด
2009Node.js ๋“ฑ์žฅ, CommonJS ์ฑ„ํƒ (require/module.exports)
2015ES2015(ES6) โ€” import/export ํ‘œ์ค€ํ™” (ESM)
2020Node.js 12+ โ€” ESM ๊ณต์‹ ์ง€์› ("type": "module" ๋˜๋Š” .mjs)

๋ธŒ๋ผ์šฐ์ €์™€ Node.js๊ฐ€ ๊ฐ์ž์˜ ๋ชจ๋“ˆ ์‹œ์Šคํ…œ์„ ๋ฐœ์ „์‹œํ‚ค๋‹ค๊ฐ€ ๋’ค๋Šฆ๊ฒŒ ํ•ฉ๋ฅ˜ํ•˜๋ฉด์„œ, ํ˜„์žฌ ๋‘ ์‹œ์Šคํ…œ์ด ๊ณต์กดํ•˜๋Š” ๊ณผ๋„๊ธฐ ์ƒํƒœ๋‹ค.


๐Ÿ”€ 3. ESM vs CJS ํ•ต์‹ฌ ์ฐจ์ด

์˜คํ”„๋‹์—์„œ ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋งํ•œ "๋ชจ๋“ˆ์„ ์ฝ๋Š” ์‹œ์ ๊ณผ ๋ฐฉ์‹์ด ๊ทผ๋ณธ์ ์œผ๋กœ ๋‹ฌ๋ผ"์˜ ์‹ค์ฒด๊ฐ€ ๋ฐ”๋กœ ์ด๊ฒƒ์ž…๋‹ˆ๋‹ค.

CommonJS (CJS)ES Modules (ESM)
๋ฌธ๋ฒ•require(), module.exportsimport, export
๋กœ๋”ฉ ๋ฐฉ์‹๋™๊ธฐ (๋Ÿฐํƒ€์ž„์— ๋™์  ๋กœ๋“œ)๋น„๋™๊ธฐ (์ •์  ๋ถ„์„ ํ›„ ๋กœ๋“œ)
์‹คํ–‰ ์‹œ์ ๋Ÿฐํƒ€์ž„ํŒŒ์‹ฑ ๋‹จ๊ณ„ (Top-level)
Tree ShakingโŒ ์–ด๋ ต๋‹คโœ… ๊ฐ€๋Šฅ
์‚ฌ์šฉ ํ™˜๊ฒฝNode.js ๊ธฐ๋ณธ๊ฐ’๋ธŒ๋ผ์šฐ์ € ํ‘œ์ค€, Node.js (์„ค์ • ํ•„์š”)
๋™์  importrequire() ์ž์ฒด๊ฐ€ ๋™์ 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 export

ESM์ด 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 ๋ชจ๋“ˆ์˜ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๊ฐ€ ํฌํ•จ๋  ์ˆ˜ ์žˆ๋‹ค.
  • ํ•ด๊ฒฐ์ฑ…:
    1. ์ง์ ‘ ๊ฒฝ๋กœ import: import { A } from "./utils/a" (๊ฐ€์žฅ ํ™•์‹ค)
    2. package.json์— "sideEffects": false ์„ค์ •
    3. Vite, Rollup, webpack ์ตœ์‹  ๋ฒ„์ „์€ ESM barrel export๋ฅผ ์ž˜ ์ฒ˜๋ฆฌ
  • ์‹ค๋ฌด ํŠธ๋ ˆ์ด๋“œ์˜คํ”„: ํฐ ์•ฑ์—์„œ barrel export ๋‚จ์šฉ์€ ์ดˆ๊ธฐ ๋ชจ๋“ˆ ๋ถ„์„ ์‹œ๊ฐ„์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Next.js ํŒ€๋„ barrel export ์ตœ์†Œํ™”๋ฅผ ๊ถŒ๊ณ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: "Barrel ํŒจํ„ด์€ ํŽธ๋ฆฌํ•˜์ง€๋งŒ, ์ž์นซํ•˜๋ฉด ๋ฒˆ๋“ค์— ๋ถˆํ•„์š”ํ•œ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ฑ๋Šฅ์ด ํŠนํžˆ ์ค‘์š”ํ•œ ๊ณณ์—์„œ๋Š” ์ง์ ‘ ๊ฒฝ๋กœ import๋ฅผ ํ•œ ๋ฒˆ ๋” ๊ณ ๋ คํ•ด ๋ณด์„ธ์š”."

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜์€ ์ง„์งœ "์ด๊ฒŒ ๋‹ค ์—ฐ๊ฒฐ๋˜๋Š”๊ตฌ๋‚˜" ํ•˜๋Š” ๋А๋‚Œ์ด ๋“ค์—ˆ๋‹ค. ์™œ import์™€ require๊ฐ€ ์„ž์—ฌ์žˆ๋Š”์ง€, ์™œ lodash-es๊ฐ€ ๋”ฐ๋กœ ์žˆ๋Š”์ง€, ์™œ ๋ฒˆ๋“ค ๋ถ„์„ํ•  ๋•Œ ์•ˆ ์“ฐ๋Š” ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋˜๋Š”์ง€ โ€” ๋ชจ๋‘ ESM vs CJS ์ฐจ์ด์—์„œ ๋น„๋กฏ๋๋‹ค๋Š” ๊ฑธ ์ด์ œ ์ดํ•ดํ•œ๋‹ค.

Tree Shaking์ด ๋งˆ๋ฒ•์ด ์•„๋‹ˆ๋ผ "์ •์  ๋ถ„์„ ๊ฐ€๋Šฅํ•œ ESM์ด๊ธฐ ๋•Œ๋ฌธ์—" ๊ฐ€๋Šฅํ•œ ๊ฑฐ๋ผ๋Š” ๊ฒƒ๋„ ์ƒˆ๋กœ ์•Œ์•˜๋‹ค. ๊ทธ๋™์•ˆ ๋‹น์—ฐํ•˜๊ฒŒ ์“ฐ๋˜ import๊ฐ€ ์ด๋ ‡๊ฒŒ ๊นŠ์€ ๋ฐฐ๊ฒฝ์ด ์žˆ์—ˆ๋‹ค๋‹ˆ.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "ESM์€ ๋ฒˆ๋“ค๋Ÿฌ์—๊ฒŒ '์ด ์ฝ”๋“œ์˜ ์˜์กด์„ฑ ์ง€๋„'๋ฅผ ๋ฏธ๋ฆฌ ๋ณด์—ฌ์ค€๋‹ค. ์ง€๋„๊ฐ€ ์žˆ์œผ๋‹ˆ ์•ˆ ์“ฐ๋Š” ์ฝ”๋“œ๋ฅผ ์†Ž์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค. CJS๋Š” ๋Ÿฐํƒ€์ž„์—์•ผ ๊ธธ์„ ์•Œ์•„์„œ ๋ฏธ๋ฆฌ ์†Ž์•„๋‚ผ ์ˆ˜ ์—†๋‹ค."

์ด๋ฒˆ ์ฃผ๊ฐ€ ๊ฑฐ์˜ ๋‹ค ๋๋‹ค. ๋‹ค์Œ ์ฃผ์—๋Š” ์—๋Ÿฌ ์ฒ˜๋ฆฌ, Map/Set ์ฑ•ํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ๋Š”๋ฐ, ์ด๊ฒƒ๋„ ๊ธฐ๋Œ€๋œ๋‹ค. ์ฃผ๋ง์— ์˜ค๋Š˜ ๋ฐฐ์šด ๋ชจ๋“ˆ ์‹œ์Šคํ…œ ์ •๋ฆฌํ•ด์„œ ์Šคํ„ฐ๋”” ๊ณต์œ  ๋ฌธ์„œ์— ์˜ฌ๋ ค์•ผ์ง€. ์˜ค๋Š˜๋„ ์ˆ˜๊ณ ํ–ˆ๋‹ค, ๋‚˜ ์ž์‹ !


๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ