3-19. 0.1.0을 실제로 눌렀다 — 그리고 만든 것을 되돌리는 이틀
기간: 2026년 5월 20일 ~ 5월 31일 (12일) 커밋: zntc 613개 릴리즈: v0.1.0 · v0.1.1 (5/21) — 3-17부터 "직전"에 머물던 npm publish를 실제로 실행 핵심: Rollup 스타일 다중 출력 API,
this.emitFilechunk/asset(#1880·#3664), 코드 스플리팅 이름 정책[dir]/[name](breaking),#3680namespace 인덱스 공유로 link.populate −55%, MCP 에픽을 만들고 이틀 만에 통째로 롤백, mangler R1-a 8번째 NO-GO, RFC #3940 라이프사이클 스코프 재설계(rename_table 단일 출처), Zig 0.16 마이그레이션 +std.Io오버홀, lazy compilation RFC와 PR-1~5
이 편의 위치
3-18은 "배포해도 부끄럽지 않은 품질"을 만드는 7일이었고, 끝까지 publish 버튼은 누르지 않았다. 이 편에서 그 버튼을 누른다. 그리고 같은 12일 안에 만든 것을 되돌리는 결정이 두 번 나온다 — MCP 에픽 전체 롤백, mangler R1-a NO-GO.
5/20 — 출력 API는 Rollup 쪽으로
다중 포맷 빌드를 붙이면서 API 모양을 정하는 논의가 먼저 있었다. esbuild식 output[] 배열이 아니라 Rollup 스타일 체이닝을 택한다.
결과가 zntc(input).write(output).close() API와 OutputConfig + BundleOptions.output[] 조합이다. 한 번의 빌드로 여러 포맷을 낼 수 있게 하면서, Module Federation과 multi-format의 조합은 거부하도록 막고 CSS 공유 자산 정책을 문서화했다.
작업 방식에 대한 지시도 이날 다시 못박힌다.
5/21 — publish 버튼을 누르다
그리고 바로 인증과 패키징에서 막힌다.
이날 하루가 배포 파이프라인 디버깅이었다 — NPM_CONFIG_TOKEN 환경변수, .npmrc 리터럴 토큰, whoami 검증, 그리고 npm publish와 bun publish 사이의 왕복. 최종적으로 bun publish로 복귀했는데, 이유는 workspace protocol(workspace:*)을 자동으로 실제 버전으로 변환해 주기 때문이다. 그 수정이 v0.1.1로 나갔다.
@zntc/core가 npm에 올라간 날이다. 3월 18일 "zig로 swc 같은걸 만들면 어떤 이득이 있을까요?"에서 64일째.
5/22~5/23 — 플러그인이 파일을 낸다
Rollup 플러그인 API의 나머지 절반, 즉 플러그인이 산출물을 만드는 쪽을 채웠다.
테스트는 남의 것을 빌려 왔다.
5/23에는 청크 이름 정책을 바꿨다. 기본 entry_names/css_names를 [dir]/[name]으로 돌리는 breaking change, CSS 청크 경로 충돌 자동 disambiguate, --intro:js/--outro:js. 그리고 debug 빌드용 leak detector(공유 DebugAllocator + atexit dump)를 napi에 넣었다 — watch dev 모드 누수를 추적하려고 메모리 기록까지 남겼다.
5/24~5/25 — 링커 −55%
#3680 namespace access 인덱스 공유가 이 구간의 성능 성과다. link.populate −55%, 전체 wall −20%. 여기에 parser의 contextual keyword first-byte prefilter, RealpathCache 16-shard mutex(#3745 R2)가 붙었다.
dev 서버 쪽에서는 JS dev server의 module-level incremental HMR을 켜고, BoringSSL Zig 바인딩과 TLS 어댑터(Io.Reader/Writer)를 넣어 HTTPS dev server를 in-process로 세웠다.
5/26~5/27 — MCP를 만들고, 이틀 만에 지웠다
5월 26일부터 MCP app channel 에픽(#3867)이 시작된다. RN 앱을 에이전트가 조작할 수 있게 하는 축이었다.
- server의 transport-agnostic dispatcher 분리, stdio transport와
zntc mcp서브커맨드 - WS endpoint(PR-E1), RN MCP runtime WS client(PR-E2), dev 빌드 entry 자동 주입
- 툴 11종 —
find_element,inspect_state,eval_code,take_snapshot,get_logs,get_network,tap_element… MCP.md문서, adb/idb 영역 분리
만들면서도 범위 질문이 계속 붙었다.
그리고 5월 27일, 한 줄로 끝났다.
커밋 로그에 chore(mcp): MCP epic 전체 롤백 — PR-E1~F7 + infra 제거가 남아 있다. 후속으로 EventRing 제거와 SSE 회귀 가드까지 정리했다. 번들러가 해야 할 일과 디버깅 도구가 해야 할 일의 경계를 다시 그은 결정이다.
같은 날 mangler에서도 되돌림이 나왔다. R1-a free-var 분석은 8번째 NO-GO 경로로 기록됐다(RFC #3918 §9). 만들어 본 뒤 "이 방향으로는 크기가 줄지 않는다"를 확인하고 inert 상태로 남긴 것이다.
5/28 — rename_table을 단일 출처로
RFC #3940(라이프사이클 스코프 재설계)의 Sub-PR 시리즈가 이날 몰려 있다. 핵심은 이름을 누가 소유하는가를 하나로 모으는 것이다.
Transformer.initFromOwnedAst— ownership transfer로 복사 회피(PR-1)SymbolID타입 정의(L.2)- computeMangling dedup key를 rename_table lookup으로(L.4c-1)
- carry-over를 rename_table 기반으로 재설계,
syncRenameTableFromCanonical폐기(L.5a) Symbol.canonical_name필드 제거 — rename_table 단일 출처(L.5c)
여기에 식별자 slot 할당을 esbuild식 scope-nesting O(N)으로 교체했고, ProfileSnapshot API와 릴리스 프로파일링 하네스, devserver-HMR 벤치의 phase breakdown을 함께 세웠다. emit incremental RFC는 이 과정에서 CLOSED로 닫혔다 — PoC가 전부 noise/회귀였고 진짜 수정은 lifecycle redesign이라는 결론이었다.
5/29 — Zig 0.16
언어 버전을 올리면서 std.Io를 통째로 손봤다. 판단 기준은 늘 같다.
같은 시기 suji도 Zig 0.16으로 넘어가 있었기 때문에, 옆 프로젝트의 해결을 그대로 참고했다.
마이그레이션 뒤에는 성능 회귀부터 확인했다 — "그리고 바로 머지해주세요 그리고 로컬에서 이전 0.15에 비해 성능 저하없는지 확인"(5/29). managed HashMap 전체를 unmanaged로 옮기는 리팩터도 이 흐름에서 나왔다(napi·linker 포함).
5/30~5/31 — lazy compilation
마지막 이틀은 새 RFC 하나에 들어갔다. 동적 import 경계에서 컴파일을 멈추고, 요청이 오면 그때 청크를 만드는 것이다.
설계 단계에서 남의 구현부터 확인했고, 끝나고는 같은 조건으로 재봤다.
이 12일이 남긴 것
- 배포는 이벤트가 아니라 파이프라인 문제였다. 코드가 준비된 뒤에도 인증·workspace protocol·lockfile 동기화로 하루가 갔다.
- 되돌리는 것도 산출물이다. MCP 에픽 전체 롤백과 mangler 8번째 NO-GO가 같은 주에 나왔다. 두 경우 모두 "만들어 보고 확인한 뒤" 되돌렸고, 그 근거를 RFC에 남겼다.
- 단일 출처는 이름에도 적용된다.
canonical_name필드를 없애고 rename_table 하나로 모은 것이 이후 minify·code splitting 결함을 줄이는 토대가 된다.