3-4. 번들러 Phase B1~B2
기간: 2026년 3월 21~22일 커밋: 164개 (81 + 83) 세션: 3/21 3개, 3/22 3개 (가장 긴 세션: b4303c97 — 110개 유저 메시지) 핵심: Zig 업그레이드 → 27개 설계 결정 → 모듈 그래프 → 링커 → scope hoisting → tree-shaking → code splitting
3/21: Zig 업그레이드와 번들러 설계
Zig 0.14.0 → 0.15.2
첫 세션의 요청:
"지그 버전업 진행해주세요 최신버전으로"
업그레이드 후 extern union 패턴의 UB(Undefined Behavior) 수정 등이 필요했다:
"유니온 패턴말고 저걸로 바꾸면서 문제생긴거 또 없어?"
Arena Allocator 2~3단계
"아레나 얼로케이터 2,3 단계 진행하자 그럼"
Test262 러너와 테스트 헬퍼에도 Arena를 적용:
27개 번들러 설계 결정 — 프로젝트 최다 의사결정
"이제 다음에 해야할게?" "번들러 의사 결정 들어가는게 낫지 않을까?"
이 세션은 110개의 유저 메시지가 오간, 프로젝트에서 가장 긴 단일 세션이었다. D056~D082까지 27개의 설계 결정이 하나씩 논의됐다.
모듈 ID 체계 (D070):
"모듈 ID 체계는 어떻게 가져가는게 나아?"
모듈 그래프 순회 (D076):
"DFS가 속도 빠르지 않아?" "그리고 병렬 파싱이 어려운가?" "B로 가자 속도 영향 없는거잖아 거의"
양방향 인접 리스트 (D078) — 이것은 3/22 HMR에서 핵심이 됨:
"근데 둘다를 안하는 이유는?" "어 HMR 고려하고 있어서 양방향으로 가자"
CJS/ESM 런타임 헬퍼 (D068):
"esm이나 cjs시 프리픽스같은거 있었던거같은데 우린 어떻게 붙일꺼야??" "네 __로 가시죠" —
__commonJS,__toESM등의 접두사 결정
플러그인 시스템 (D080):
"롤업처럼 할꺼야? 속도나 성능 안정성에선 어떻게 할건데?" "롤다운이나 swc, rspack의 방식은?"
모듈 분리 (D081):
"롤다운처럼 최대한 나눠서 할거지?" "A로 가자 그리고 중간 중간 의사결정이 필요하거나, 까먹고 누락되어서 필요하다고 생각되면 무조건 나한테 물어봐 임의로 진행하지말고"
이 마지막 말은 중요한 원칙이었다 — AI가 임의로 결정하지 말고 반드시 사용자에게 물어보라.
레퍼런스 비교가 철저했다:
"babel, oxc, swc 등은 어떻게 하고 있는데?" "롤업도 레퍼런스에 클론 해오고 있나?" "bun은 왜 고려 안했어?" "너가 추천하는 전략은? 이유까지 포함해서, 배제한 이유도"
React Native 고려:
"나중에 react-native도 지원 계획이라, strictExecutionOrder를 제공하려면 고민해야할 부분이 뭘까요" "근데 메트로는 CJS 순서 보장 중요한데 ESM에서 그걸 가능하게 하는 옵션이 있었던거 같은데"
Phase B1: 모듈 그래프 → 링커 → 이미터
설계 결정을 마친 후 구현 돌입:
Scope Hoisting — esbuild의 핵심 최적화:
처음에 "scope hoisting을 안 하고 나중에 할 수 있지 않나"는 질문이 있었다:
"처음부터 호이스팅 하는게 낫지 않아요? 어차피 거기로 갈건데 중간 과정을 왜"
결론: 처음부터 scope hoisting으로 구현.
심볼 인덱스 결정:
"레퍼런스 번들러들은 어떻게 하고 있나요" "그럼 심볼인덱스 필수로 가는게 낫지 않아요? oxc는 어떻게 하고 있나요??" "어느게 성능상으로 나을까요??"
품질에 대한 집착:
"계속 진행해 실수 하나도 용납하지 않을테니 정말 정밀하게 점검해줘" "지금까지 잘 진행하셨는데 진행한거 다시 /simplify 리뷰 전체적으로 봐주실 수 있나요?" "네 다 반영해주세요 안정성 확보가 최우선이라 다 해야합니다"
3/22: Tree-Shaking + Code Splitting
B2 tree-shaking — TDD 방식
"B2 tree-shaking 1단계(미사용 export 제거)부터 시작해줘. 테스트 먼저 작성하고, 코드 고쳐서 통과시켜"
TDD(테스트 주도 개발) 방식을 명시적으로 요구한 것이 특징적이다.
"다른 번들러의 테스트 케이스 좀 더 가져와줄 수 있어?" "네 그런 테스트케이스들 미리가져와서 실패하는거 확인 후 그걸 고쳐주세요"
테스트 충분성에 대한 반복적 확인:
"또 추가할거 없어? 그리고 우리가 고친 코드 방향이 저 테스트케이스만 통과하게하려고 짠게 아니라 근본적으로 잘 고친건지?"
extra_data vs 노드 플래그 — 설계 논쟁
@__PURE__ 정보를 AST 노드에 어떻게 저장할 것인지에 대한 큰 논쟁이 있었다 (D082):
"번은 왜 2비트고 아까 처음에 제안한 방법이랑 뭐가 달라?" "우리가 왜 저 제한을 뒀었지?" — 24바이트 노드의 인자 개수 제한 "번은 왜 괜찮아?" — Bun도 24바이트인데 제한이 없는 이유
40개 이상의 메시지가 이 하나의 설계 결정에 할애됐다:
"extra_data 때문에 성능 나빠지는거 있어?" "왜 처음엔 노드플래그 제안한거야?" "extra_data는 제한 크기 없어?" "그럼 extra_data가 나아? 니가 제안한 노드플래그가 나아?"
결론:
"문서에 써주고, 진행하자 그럼"
6개 노드 타입을 extra_data 방식으로 변환:
@PURE / @NO_SIDE_EFFECTS
렉서에서 추적하던 정보가 tree-shaking에서 활용됨:
Code Splitting
"좋아 코드스플리팅 가자" "왜 esbuild를 따라했어요? oxc가 아니라" — 항상 왜 그 선택인지 물어봄
"남은것도 여기에서 바로해" "아직 chunk가 덜 구현된거 같은데" "아뇨 이 세션에서 다 이어서 해주세요 미구현된것들"
CJS 지원
"아뇨 여기서 바로 CJS 진행해주세요" "oxc나 bun swc rspack 등은 어떻게 가는지 알려줘요"
문장 수준 tree-shaking에 대한 결정:
"문장수준 트리쉐이킹은 rollup만 하고 나머진 안해서 동일하게 가기로 했는데" "문서에 업데이트 해"
이 시기의 핵심 고민: "esbuild를 얼마나 따를 것인가"
번들러 구현에서 가장 큰 고민은 esbuild의 설계를 그대로 따를 것인가, 아니면 독자적인 구조를 가져갈 것인가였다.
esbuild가 수년간 실전에서 검증한 알고리즘을 재발명할 이유가 없었다. 동시에 oxc의 구조도 참고:
"안전한 접근: else에서 false를 반환하되, 누락 가능한 중요 노드 타입들을 명시적으로 추가합니다. 이라고 했는데 중요 노드 타입만 해도 괜찮나요?" "oxc는 어떻게 하는데요?" "oxc와 esbuild의 차이점을 알려줘" "각각 방법이 다른 이유랑 왜 그렇게 했는지" "우린 지금 어느 방향에 가까워? 어떻게 설계 되어있어?"
결론은 **"핵심 알고리즘은 esbuild를 따르되, 파서/AST 구조는 oxc를 따르고, Zig 언어 특성에 맞게 재설계한다"**였다.