|
| 1 | +--- |
| 2 | +title: '[누가 시키지도 않았는데 번들러 만들기] 0. 프롤로그: 질문이 구현이 되는 순간' |
| 3 | +description: '도구가 대신 해주던 선택을 직접 내려보며, 라이브러리 번들러의 내부 흐름을 바닥부터 구현해보는 시리즈를 시작합니다.' |
| 4 | +date: '2026-02-2' |
| 5 | +slug: 'no-one-asked-library-bundler-00-prologue' |
| 6 | +thumbnail: 'bundler-prologue-thumb.png' |
| 7 | +published: true |
| 8 | +tags: ['bundler', 'library', 'build', 'tooling', 'javascript'] |
| 9 | +excerpt: '수많은 소스 코드가 어떻게 하나의 결과물로 합쳐질까요? 당연하게 사용하던 번들러의 블랙박스를 열고, 직접 손으로 구현하며 그 답을 찾아봅니다.' |
| 10 | +--- |
| 11 | + |
| 12 | +# 0. 프롤로그: 질문이 구현이 되는 순간 |
| 13 | + |
| 14 | +> 이전에 작성한 **[하루 만에 끝날 줄 알았던 디자인 시스템 배포가 3주 걸린 이유](https://blog.sangwook.dev/posts/npm-deploy-series-0-prologue/)** 에서는 "패키지를 배포하는 과정" 그 자체에 집중했습니다. |
| 15 | +> 이번에는 시선을 조금 더 안쪽으로 돌려보려 합니다. 바로 배포물이 만들어지는 **생성 과정** 입니다. |
| 16 | +
|
| 17 | +배포는 성공적으로 마쳤지만, 정작 결과물이 어떤 원리로 만들어지는지 제대로 이해하지 못한 채 도구에만 의존하고 있다는 의문이 들었습니다. **"남들이 다 쓰니까"**, **"이게 표준이라니까"** 라며 무심코 넘겼던 설정값들이 실제로는 번들러 내부에서 어떤 판단을 내리게 만드는 걸까요? |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## 🚀 시리즈의 출발점: 당연한 것에 던지는 질문 |
| 22 | + |
| 23 | +npm 배포 과정을 정리하면서 머릿속을 떠나지 않았던 질문들이 있습니다. |
| 24 | + |
| 25 | +- **결합의 원리**: 소스 코드는 수십 개인데, 왜 결과물은 딱 하나로 합쳐질까요? 그 경계는 누가, 어떤 기준으로 정하는 걸까요? |
| 26 | +- **설정의 실체**: `package.json`의 `main`, `module`, `exports`, `sideEffects` 값들은 번들러의 동작에 어떤 물리적인 영향을 줄까요? |
| 27 | +- **의존성의 선별**: 모든 의존성을 번들에 포함해야 할까요? `external`과 `peerDependencies`는 어떤 기준으로 솎아내는 것일까요? |
| 28 | +- **트리 쉐이킹(Tree Shaking)**: 코드를 직접 실행해보지도 않고, 어떻게 '사용하지 않는 코드'라고 확신하며 지워버릴 수 있을까요? |
| 29 | + |
| 30 | +이 시리즈는 이러한 질문들에 대해 이론적인 답변을 찾는 대신, **직접 손으로 구현하며 몸소 답을 찾아가는 실험 기록**입니다. **“누가 시키지도 않았지만”** 라이브러리 번들러를 직접 만들어보며 그 블랙박스를 열어보려 합니다. |
| 31 | + |
| 32 | +## 💡 "Tsup 쓰면 1초면 되는데...?" (이걸 왜 만드나요?) |
| 33 | + |
| 34 | +"요즘 세상에 누가 웹팩 설정을 직접 만지나요? 그냥 `tsup` 하면 끝인데." |
| 35 | +맞습니다. 우리는 레이저 커터(Tsup, Turbopack)가 있는 시대에 살고 있습니다. 하지만 굳이 돌도끼(직접 만든 번들러)를 깎아보는 이유는 **"도구에 잡아먹히지 않기 위해서"** 입니다. |
| 36 | + |
| 37 | +이 과정을 통해 여러분은 **'실무적 초능력'** 3가지를 얻게 됩니다. |
| 38 | + |
| 39 | +1. **에러 메시지를 투시하는 능력** |
| 40 | + - `Uncaught ReferenceError`, `Module not found` 에러를 만났을 때, 번들러가 그래프를 그리는 과정(AST, Resolve)을 알면 원인이 머릿속에 투시도처럼 그려집니다. |
| 41 | +2. **'트리 쉐이킹 친화적'인 코드 작성** |
| 42 | + - 왜 `export default`보다 `Named Export`가 유리한지, `sideEffects: false`가 정확히 어떤 동작을 끄는 것인지, 이론이 아닌 구현 레벨에서 이해하게 됩니다. |
| 43 | +3. **차세대 도구에 대한 적응력** |
| 44 | + - Tsup(Rollup), Next.js(Turbopack) 등 도구의 이름은 바뀌어도, **Parsing → Graph → Linking**이라는 본질적인 아키텍처는 변하지 않습니다. |
| 45 | + |
| 46 | +## ⚖️ 무엇을 다루고, 무엇을 버릴까? |
| 47 | + |
| 48 | +이 시리즈는 "실무에서 당장 쓸 수 있는 고성능 번들러"를 만드는 것이 아닙니다. 복잡도는 걷어내고, **핵심 원리**가 잘 보이도록 뼈대만 남기는 것이 목표입니다. |
| 49 | + |
| 50 | +### ✅ 다루는 것 (Core Scope) |
| 51 | + |
| 52 | +우리는 **'간단한 번들러'** 를 목표로 다음 기능들을 직접 구현합니다. |
| 53 | + |
| 54 | +- **모듈 해석 (Resolve)**: Node.js의 알고리즘을 따라 `import` 경로를 찾아내는 규칙 |
| 55 | +- **의존성 그래프 (Dependency Graph)**: 파일을 읽어 AST로 변환하고, 파일 간의 관계를 데이터 구조로 연결 |
| 56 | +- **변환 (Transform)**: `magic-string`을 활용해 코드를 래핑하고 조작하는 과정 |
| 57 | +- **출력 (Generate)**: IIFE로 스코프를 격리하여 하나의 파일로 합치는 과정 (기초적인 **Dead Code Elimination** 포함) |
| 58 | +- **소스맵 (Sourcemap)**: 디버깅을 위해 원본 코드와 번들 코드를 연결하는 원리 |
| 59 | + |
| 60 | +### ❌ 다루지 않는 것 (Out of Scope) |
| 61 | + |
| 62 | +핵심 흐름을 가리는 과도한 기능들은 과감히 생략합니다. |
| 63 | + |
| 64 | +- **애플리케이션 번들링 기능**: HMR(Hot Module Replacement), Code Splitting, 개발 서버 등 |
| 65 | +- **복잡한 엣지 케이스**: **순환 참조(Circular Dependency)** 해결, 다이나믹 임포트(`import()`) 등 |
| 66 | +- **JS 이외의 에셋 처리**: CSS, 이미지, 폰트 로더 등 |
| 67 | +- **고도화된 최적화**: 복잡한 Tree Shaking 알고리즘(Scope Hoisting 등), Minification 등 |
| 68 | + |
| 69 | +## 🛠️ 우리가 만들게 될 것 (Roadmap) |
| 70 | + |
| 71 | +거창한 기능은 다 뺍니다. 오직 **'모듈을 찾아서 하나로 합친다'** 는 본질에만 집중한 **라이브러리 전용 번들러**를 만듭니다. |
| 72 | + |
| 73 | +### [Step 1. 개념과 도구](https://blog.sangwook.dev/posts/bundler/no-one-asked-library-bundler-01-concept) |
| 74 | + |
| 75 | +- 번들러가 왜 필요해졌는지 역사적 맥락을 짚어봅니다. |
| 76 | +- **ESM(ECMAScript Modules)** 이 번들링에 왜 유리한지, 그리고 코드를 조작할 **Magic String**이 무엇인지 알아봅니다. |
| 77 | + |
| 78 | +### Step 2. 그래프 그리기 (Resolve & AST) `Coming Soon` |
| 79 | + |
| 80 | +- 파일을 읽어 **AST(추상 구문 트리)** 로 변환합니다. |
| 81 | +- `import` 문을 추적하여 파일 간의 **의존성 그래프** 를 구축합니다. "파일을 찾는다"는 행위를 코드로 구현합니다. |
| 82 | + |
| 83 | +### Step 3. 번들링과 스코프 (Bundling) `Coming Soon` |
| 84 | + |
| 85 | +- 그래프를 순회하며 코드를 하나로 합칩니다. |
| 86 | +- **IIFE(즉시 실행 함수)** 패턴을 사용하여 모듈별 스코프를 격리하는 원리를 배웁니다. (최신 라이브러리는 ESM으로 배포하지만, 번들러의 격리 원리를 배우기에 IIFE가 가장 직관적입니다.) |
| 87 | +- 브라우저에서 동작하는 `require` 함수를 직접 구현해봅니다. |
| 88 | + |
| 89 | +### Step 4. 완성도 높이기 (SourceMap & Externals) `Coming Soon` |
| 90 | + |
| 91 | +- 디버깅을 위한 **소스맵(SourceMap)** 의 원리를 파악하고 구현합니다. |
| 92 | +- `react` 같은 외부 라이브러리를 번들에서 제외하는 **Externals** 기능을 추가해 실무 레벨의 도구로 다듬습니다. |
| 93 | + |
| 94 | +--- |
| 95 | + |
| 96 | +## 🎯 이런 분들께 추천합니다 |
| 97 | + |
| 98 | +아래 체크리스트 중 하나라도 해당한다면, 이 시리즈가 분명한 해답이 될 것입니다. |
| 99 | + |
| 100 | +- [ ] `tsup.config.ts` 설정이 꼬일 때마다 "그냥 새로 만들까" 고민해본 적이 있다. |
| 101 | +- [ ] 라이브러리 배포 후, 사용자가 "모듈을 못 찾겠어요"라고 하면 식은땀부터 난다. |
| 102 | +- [ ] CJS(CommonJS)와 ESM이 섞여 있을 때 왜 에러가 나는지 정확히 설명하기 어렵다. |
| 103 | +- [ ] "어떻게든 돌아가기만 하면 되지"라는 생각에서 벗어나, **원리를 장악하고 싶다.** |
| 104 | + |
| 105 | +이제, 안전벨트를 매세요. 번들러의 블랙박스를 열러 들어갑니다. |
0 commit comments