3-18. minify 격차 추격과 Module Federation — 0.1.0은 아직 preview

기간: 2026년 5월 13일 ~ 5월 19일 (7일, 5/19는 오전까지) PR: zntc 약 217개 (5/13 12 · 5/14 26 · 5/15 50 · 5/16 44 · 5/17 49 · 5/18 36) — 두 AI 병행 머지 핵심: RN 런타임 회귀 커밋단위 리뷰(#3156), TS 파서 D 시리즈 + TSC conformance audit, "minify가 esbuild/rolldown/rspack보다 크다"는 단 하나의 격차를 mangler Phase A 1-char pool 잠식까지 루트커즈 추격 (M/N/K 시리즈 — rxjs 누적 −12%대·esbuild −751B), Codex 트랙의 코드 스플리팅 P3 → Module Federation(웹 P1~P4 + mf-manifest/remoteEntry/shareScope 런타임, RN 자체 C/JSI 로더 설계), Ed25519/SHA-256 청크 무결성 sidecar, CLI 옵션 갭 보강(--version/--no-config/--color), Zig 인라인 test 분리(#3497~3499) 진행 중 / 배포 안 함 — 이 구간에 새 npm publish는 없었다. 5/12의 "0.1.0 publish 직전"이 그대로 public preview 취급으로 유지됐고, 무게중심은 배포가 아니라 (a) minify 크기 격차 좁히기 (b) Module Federation 구현으로 옮겨갔다.

이 편의 위치

3-17은 5/8~5/12 — 플러그인 통일, 대규모 파일 분리, 배포 파이프라인 세팅, 그리고 "세팅만 다 해주세요 실 배포는 하지말고" — 으로 끝났다. 이 편은 그 뒤 7일이다. "배포 직전"에서 한 발 더 갈 줄 알았는데, 실제로 일어난 건 두 가지를 끝까지 파고드는 것이었다: 미니파이 결과물이 경쟁 번들러보다 크다는 단 하나의 사실, 그리고 Module Federation이라는 새 에픽.

이 7일의 성격은 3-17과 또 다르다. 3-17이 "배포 가능한 모양 만들기"였다면, 이 편은 **"배포해도 부끄럽지 않은 품질 만들기"**다. 그리고 두 AI의 역할 분담이 가장 선명해진 구간이기도 하다 — Claude는 파서·minify 루트커즈, Codex는 Module Federation·청크 에픽.

5/13 — RN 런타임 회귀, 커밋 하나하나

5/12까지 0.1.0 prep을 끝냈는데, RN 런타임 회귀(#3156 계열)가 남아 있었다. 다운레벨링·Metro 네이티브 라이브러리 로딩 순서까지 건드린 큰 PR이었다.

5/13 19:46 (45570af1)
> "https://github.com/ohah/zntc/pull/3141 이거 브랜치 한번 리뷰 빡세게 봐줘 내용 다 읽고, 저 방향 수정이 맞는지 확인해주세요 / 다운레벨링도, 메트로 라이브러리 네이티브 라이브러리 불러오는 순서도 상당히 많은걸 수정했는데 커밋 하나하나마다 반복 검증하면서 리뷰 봐주세요"

"커밋 하나하나마다 반복 검증" — PR 단위가 아니라 커밋 단위 리뷰. 그 과정에서 단일 책임 위반이 잡힌다:

5/13 20:22 (45570af1)
> "5f22ebe0 단일 책임 위반 (memory leak fix + property name fix 동거) — 다음번부터 분리, 이거 분리해주세요"

한 커밋에 메모리 누수 fix와 프로퍼티명 fix가 섞여 있던 것을 별도 커밋으로 분리. 결과적으로 #3144~3157이 머지됐다 — metadata/resolve/graph_io audit, disableHierarchicalLookup Metro 호환, tslib UMD comptime drift guard.

5/14 — TS 파서 D 시리즈, 그리고 "15초"

이날은 TS 파서 정합성(.d.ts ambient, implicit strict module, await-as-identifier 등 D 시리즈)과 TSC conformance audit 인프라(#3175~3201)가 대량으로 들어갔다. 시작은 늘 그렇듯 "이게 해결된 거야 아니야"부터:

5/14 06:58 (0874b1ca)
> "https://github.com/ohah/zntc/issues/3154 이거 해결된거야 아님 개선 해야하는거야?"

그리고 이날의 진짜 주제는 체감 속도였다. 디스크 캐시 최적화가 콜드 스타트엔 무력하다는 의심을 본인이 먼저 제기한다:

5/14 09:26 (0874b1ca)
> "디스크 캐시는 근데 첫시작은 여전히 느린건 맞자나영?"
5/14 09:28 (0874b1ca)
> "큰 프로젝트로 측정해봐요 / 한 45MB 번들 나오는 RN으로 하면 15초 정도 걸려서요"
5/14 10:11 (0874b1ca)
> "napi를 쓰긴 하거든여 그렇게 하신거예여? 릴리즈 빌드의 napi는 얼마나 나오죠"

측정이 추상적 벤치가 아니라 **사용자 실경로(45MB RN 번들, napi 릴리즈 빌드, 15초)**와 일치해야 한다는 강제. 이건 3-17까지 반복된 "measure-first"의 5월 버전이다.

파서 엣지(blob-util parse error 등)에서 막히면 표준 해법은 정해져 있다:

5/14 10:37 (0874b1ca)
> "바벨 플로우파서랑 비교해서 확인도 해주시고 수정해주세영"
5/14 15:28 (0874b1ca)
> "바벨에서 어떻게 처리하는지 계속 확인해보시면 되는거 아니예여?"
5/14 17:41 (0874b1ca)
> "rolldown swc 는 어떻게 하는데요"

레퍼런스 정독 — babel flow parser, rolldown, swc. 그리고 두 수정안 중 장기적으로 나은 쪽을 다시 확인하고("B가 자익적으론 낫다는거죠?"), 테스트 동반을 작업 전제로 못박는다("반드시 테스트 케이스로 확인하면서 진행해야해요").

5/15 — minify 격차, 이 편의 중심축

이 편 전체를 관통하는 단 하나의 질문이 이날 오전 시작된다.

5/15 11:39 (f69d03bf)
> "minify가 다른 번들러 rolldown, esbuild에 비해 잘 안되는것 같은데 실제 미니파이어 번들에서 결과물 비교해서 어떤게 잘 안되는지 확인해줄 수 있어?"

수정보다 진단이 먼저, 그리고 진단도구 자체의 신뢰도부터:

5/15 11:55 (f69d03bf)
> "진단도구 정확도도 올리셔야 하구요 / 측정부터 진행해서 원인 명확하게 파악 후 진행"
5/15 12:10 (f69d03bf)
> "왜 2글자 잔존율이 하나는 앋뇐거죠 궁금합니다"

mangle 2-char 잔존율이 라이브러리마다 다르다는 게 루트커즈의 첫 단서였다. 그리고 이 편의 반복 모토가 나온다:

5/15 15:23 (f69d03bf)
> "보수적 거부 말고 루트 커즈 해주시면 안되나여"
5/15 15:41 (f69d03bf)
> "리소스가 많이 들더라도 장기적으로 유지보수 가능한, 루트커즈 부분에서 수정하시면 됩니다"

파서/번들러가 어려운 케이스를 "보수적으로 거부"하는 것을 거부한다 — 3-13에서 CLAUDE.md에 박은 "근본 수정 원칙"의 5월 재확인. 비교 베이스라인도 esbuild 단독에서 넓힌다:

5/15 17:13 (f69d03bf)
> "esbuild 말고 rspack이나 rolldown은?"

루트커즈로 확정된 것: mangler Phase A가 cross-module top-level 심볼을 전역 단일 1-char(54개) pool로 소진 → Phase B의 nested 바인딩이 짧은 이름을 못 받는다. 이 추격으로 M/N/K 시리즈 PR(#3223~3273)이 대량 머지됐다 — rxjs 누적 −12%대, esbuild 산출물 −751B.

한편 Codex 트랙에서는 출시 후 계획이 논의된다 — 하지만 이게 "배포했다"가 아니다:

5/15 22:20 [Codex] (62181b25)
> "이제 출시 슬슬 하려고 하는데 출시 후에 해야할것들을 고민하고 싶어요"

AI 답변은 **"0.1.0 public preview 시점"**을 전제로 출시 후 P0(SECURITY.md / 이슈 템플릿 / Discussions / release-canary)만 논의했다. 실제 npm publish나 버전 bump는 이 구간 어디에도 없다.

5/16 — 추격의 장기화, 그리고 Module Federation의 시작

minify 격차 추격이 길어지면서 자동 루프(매 PR /simplify → 오토머지 → 루트커즈)로 굴러간다. three.js가 rspack 대비 1.55배 같은 비교표가 등장한다:

5/16 00:58 (f69d03bf)
> "일단 여기서 중간정리, 스모크 테스드들 이제 용량 얼마나 차이나?"
5/16 12:44 (f69d03bf)
> "원인이 뭔지 설명해줄 수 있어?? / 아니 우리가 용량이 큰 번들러들 원인"
5/16 15:15 (0874b1ca)
> "그럼 완전한 근본해결을 위한 설계를 가시죠"

임시 fix가 아니라 근본 재설계 채택. 그리고 같은 날, Codex 트랙에서 이 편의 두 번째 축이 열린다:

5/16 15:27 [Codex] (1f839901)
> "그리고 모듈 페더레이션 및 청크 배포로 분할 배포 계획인데 RN도 지원할 생각이야 어떻게 해야할까??"
5/16 15:40 [Codex] (1f839901)
> "어쨋든 상태공유는 전역적으로 관리되잖아? 그럼 각각 모듈 페더레이션 단위마다 상태 관리를 별도로 두는 재설계가 필요한거야?"
5/16 15:53 [Codex] (1f839901)
> "RN에선 빌드시 JS파일이 단일로 포함되고 청크 레이지로드 안되는걸로 아는데"
5/16 16:10 [Codex] (1f839901)
> "자체 C/JSI 로더 만들거예요 / 그리고 터보모듈만 지원할거임"

Module Federation을 웹뿐 아니라 RN까지. RN은 단일 번들 제약이 있으니 자체 C/JSI 로더 + TurboModule 한정으로 간다는 설계 결정. 이날 Codex 쪽은 P3-B 청크 인프라 PR(#33243371)을 대량 머지했다 — 이건 2026-05-16 today-commit의 #33423371 P3 시리즈와 정확히 겹친다.

Claude 트랙은 emit 아키텍처 전면 개편 가능성까지 타진한다:

5/16 20:15 (0874b1ca)
> "rolldown식 번들 chunk 재파싱(emit 아키텍처 전면 변경) 변경해야 하지 않아요? 그럼 ??"

5/17 — 천장, 그리고 격리된 스파이크

minify 격차에는 천장이 있었다 — rspack이 three.js에서 압도적으로 작은 건 GLSL strip 같은 도메인 특화 최적화 때문이다.

5/17 00:29 (92717c1f)
> "three 216KB ... rspack 139KB ... 이거 rspack이 젤 작은 이유 확인해주라"
5/17 01:30 (92717c1f)
> "크기문제가 아니라 실제 동작되는지 확인을 해주세여"
5/17 17:22 (92717c1f)
> "직접 빌드된 파일 비교해서 어떤게 큰지 근본을 왜 자꾸 못찾는거야??"

추상 분석이 아니라 실제 산출물 diff로 근본을 찾으라는 압박. 그리고 위험한 emit 오버홀은 메인에 바로 넣지 않는다:

5/17 14:45 (92717c1f)
> "왜 그거 한다고 증분 병렬이 위험 받아? / 두마리 토끼를 잡는 설계 방법은 없어?"
5/17 15:14 (92717c1f)
> "하는 대신 메인에 바로 머지하지말고 별도 브랜치로 작업해서 거기에 PR 넣는식으로 진행해보시죠"

chunk 재파싱이 증분/병렬 빌드와 상충하므로, emit 스파이크는 별도 브랜치로 격리. bundle-context export const coalescing RFC(#3411)가 이 흐름에서 머지됐다.

Codex 쪽은 Module Federation 스파이크 S0S5 + webpack/rspack 호환 mf-manifest.json·remoteEntry·shareScope 런타임을 P1 전 단계까지 머지한다(#33753450). 표준 구현체를 직접 클론해 테스트 갭을 봉인:

5/17 13:57 [Codex] (96022bca)
> "module-federation/enhanced 보면 쉐어드 트리쉐이킹이나 이것저것 전략이 많은데 이거 다 흡수 가능한거야?"
5/17 19:33 [Codex] (96022bca)
> "rspack이나 모듈페더레이션 코드 깃허브에 있는거 레퍼런스 폴더에 클론해서 가져와서 테스트 부족한게 있는지 확인해줘"

5/18 — MF 웹 완성, CLI 갭, Zig 테스트 분리

Module Federation 웹 구현이 완성도로 들어간다 — strictVersion fail-fast, named shareScope 다중, expose CSS, 무결성 sidecar(Ed25519/SHA-256), 사용자 가이드·예제앱·레시피(#3464~3500). 무결성 sidecar는 사용자가 직접 제기한 요구에서 나왔다:

5/18 17:56 [Codex] (96022bca)
> "사용자가 노출하거나 수정할 수 있으면 안되는데 암호화나 인증을 통해 해시값을 통해 에러를 뿜을 수 있는걸까요? repack도 그렇게 하고 있는걸까요?"
5/18 17:52 [Codex] (96022bca)
> "@zntc/react-native/module-federation/register 뭐 이런식으로 되어야 하지 않을까요?"

RN MF 사용자 API 형태(@zntc/react-native/module-federation/register)를 사용자가 직접 제안하고, CodePush 공존성·repack 한계까지 추궁한다. CLI 옵션 표면은 4개 경쟁 번들러와 갭 분석:

5/18 16:42 (d2bc56ef)
> "롤다운이나 swc, rspack, webpack에 비해 세부 옵션 부족한게 있을까요?"

--version/-v/--no-config/--color 추가(#3489/#3490). react-query v5 ES5 다운레벨 회귀(class async optional-chaining temp hoist)도 이날 우선 수정. 그리고 코드베이스 청결:

5/18 22:43 (377eda7c)
> "테스트 파일이랑 실제 실행 파일이랑 묶여있는 부분이 있어서 좀 별론데 지그쪽, 이거 분리 가능해?"
5/18 23:13 (377eda7c)
> "근데 다른것들 pub으로 노출해서 테스트코드 분리하지 않았어요?"

Zig 인라인 test를 *_test.zig로 분리(#3497/#3498), 그리고 "테스트 위해 pub 노출"이라는 안티패턴을 금지로 박제(#3499).

5/19 (오전까지) — TDD 마무리, 문서 전수조사

5/19 00:04 (377eda7c)
> "지금 바로 진행하시죠 TDD로 최대한 사이드 이펙트 없어지게"
5/19 01:00 (377eda7c)
> "장기적으로 코드 베이스가 깔끔해진다면 그냥 지금 하고 싶어요"
5/19 01:42 (377eda7c)
> "https://github.com/ohah/zntc/issues/3509 이거 다른 번들러들은 어떻게 함??"
5/19 01:47 (377eda7c)
> "그럼 바벨처럼 해주자"
5/19 01:56 (377eda7c)
> "핵심조각 먼저하고 나머지는 이슈탭에 적어서 백로그로 기록해서 잊어먹지 않게 하자"

Zig 테스트 분리 후속 + es5 다운레벨 정합성(#3509대)을 babel 동작 기준으로 TDD 마무리, 핵심만 선처리하고 나머지는 백로그화. Codex 쪽은 MF 레시피 문서·예제를 GitHub Pages에 배포(=문서 배포, npm 아님)하고, 문서 vs 실제 기능 전수 대조를 했다:

5/19 09:08 [Codex] (5ce2c0e7)
> "깃허브 문서가 실제 지원기능이나 없는 기능 잘못 쓰고있는지, 아니면 그반대있는지 전수조사 한번해주시고 있으면 업데이트 해주세여"

결과 — 문서는 정확했고, 에이전트가 보고한 불일치 4건은 전부 거짓양성(Zig 바이너리로 잘못 테스트한 것 — 사용자 실경로는 npm Node.js CLI)이었다.

7일간 관통한 것들

귀결
minify 크기 격차 루트커즈mangler Phase A 1-char(54) pool 잠식 → Phase B nested가 짧은 이름 못 받음. M/N/K 시리즈 (rxjs −12%대, esbuild −751B)
"보수적 거부 말고 루트커즈"파서/번들러의 conservative reject 거부, 유지보수성 우선 재설계 (3-13 원칙의 재확인)
레퍼런스 정독babel flow parser / rolldown / swc / rspack / module-federation enhanced 클론 대조가 표준 해법
measure-first45MB RN 번들·napi 릴리즈·15초 — 사용자 실경로와 일치하는 측정 강제
Module Federation (Codex)코드 스플리팅 P3 → 웹 P1~P4, mf-manifest/remoteEntry/shareScope 런타임, RN 자체 C/JSI 로더 + TurboModule 한정, Ed25519/SHA-256 무결성 sidecar
TS 파서 D 시리즈.d.ts ambient, implicit strict module, await-as-identifier + TSC conformance audit 인프라
위험 변경 격리emit 오버홀 스파이크는 메인 직머지 금지, 별도 브랜치 PR
코드베이스 청결Zig 인라인 test → *_test.zig 분리, "test 위한 pub 노출" 금지 박제
두 AI 역할 분담 최선명Claude=파서·minify 루트커즈, Codex=MF·청크 에픽. 같은 이슈/PR 풀, 수동 동기화

"배포 직전"에서 한 발도 안 나갔다 — 의도적으로

3-17의 마지막에 더했던 다섯 문장에, 이 7일이 다섯을 더한다:

  1. "배포 직전"은 생각보다 오래 머무는 상태다 — 5/12에 "publish 직전"이었는데 5/19에도 publish는 안 됐다. 그 사이 217개 PR이 머지됐는데도. 배포를 늦추는 게 게으름이 아니라 "minify가 경쟁사보다 큰 채로 내보낼 순 없다"는 품질 기준이었다.
  2. 단 하나의 격차가 일주일을 먹는다 — "minify가 rolldown보다 크다"는 한 문장이 5/15~18 수십 개 PR을 만들었다. 그리고 그 루트커즈는 의외로 깊은 곳(mangler Phase A의 전역 1-char pool 설계)에 있었다.
  3. 두 AI를 쓰면 에픽을 병렬로 굴릴 수 있다 — Claude가 minify를 일주일 파는 동안 Codex가 Module Federation 풀스택을 동시에 진행했다. 한 트랙이 한 우물을 깊게 파도 다른 트랙이 새 기능을 넓힌다.
  4. "근본 수정"은 한 번 박으면 계속 인용된다 — 3-13에서 CLAUDE.md에 넣은 그 원칙이 5/15 "보수적 거부 말고 루트커즈"로 그대로 돌아왔다. 규칙은 한 번 세우면 본인이 본인에게 강제한다.
  5. 천장이 있는 격차도 있다 — three.js에서 rspack이 작은 건 GLSL strip 같은 도메인 최적화 때문이고, 그건 따라간다고 따라잡히는 종류가 아니다. 모든 격차가 루트커즈로 0이 되는 건 아니라는 것도 측정이 알려준다.
3/18 ~ 4/07 ████████████████████  3-1 ~ 3-10 (20일 — 기준선 확보)
4/08 ~ 4/23 ████████████████      3-11 ~ 3-14 (16일 — Metro 동등성·RFC 시리즈)
4/24 ~ 4/30 ████████              3-15 (7일 — "프로덕션이라는 말")
5/01 ~ 5/07 ████████              3-16 (7일 — binding-lite·패키지 분리·이름 변경)
5/08 ~ 5/12 ██████                3-17 (5일 — 0.1.0을 향해)
5/13 ~ 5/19 ████████              3-18 (7일 — minify 격차·Module Federation, 여전히 preview)
5/20 ~        →→→→→→→→→→→→→       계속 진행 중 — 다음 편은 "0.1.0이 실제로 npm에 올라간 뒤"

3-17 끝에 "다음 편은 npm publish가 눌리고 나면"이라고 썼는데, 7일이 더 지나도 그 버튼은 안 눌렸다. 대신 그 7일은 "눌러도 되는 상태"를 만드는 데 쓰였다 — minify를 경쟁권으로, Module Federation을 추가 무기로. publish는 여전히 다음 편의 몫이다.