TRENDSHIFT #20 · 2026.06.11

BuilderIO/mitosis 딥다이브
— "한 번 짜면 모든 프레임워크로 컴파일되는" 컴포넌트 컴파일러

.lite.tsx 파일 하나로 컴포넌트를 짜면, React·Vue·Angular·Svelte·Solid·Qwik·Alpine·Lit·Stencil·React Native·Liquid 등 21개 타깃으로 자동 변환해 주는 컴파일러다. 비결은 Mitosis JSON이라는 중간 표현(IR) — JSX를 일단 프레임워크 중립적인 트리로 바꿔두고, 타깃별 제너레이터(generator)가 그 트리를 각 프레임워크 코드로 "출력"한다. 이건 Babel·SWC 같은 컴파일러가 쓰는 "파싱 → IR → 코드 생성" 패턴을 프런트엔드 컴포넌트에 적용한 사례라, 이 레포 하나로 컴파일러 설계여러 프레임워크의 공통점·차이를 동시에 배울 수 있다. (저장소: BuilderIO/mitosis · ★13.8k · Fork 637 · MIT · TypeScript 95.8% · 1,898 commits · core v0.13.2 · TrendShift 20위)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 시스템 요구사항 & 설치
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

이 레포가 무엇을 하는 물건인가.

핵심 메시지

"컴포넌트계의 통역사 — 한 가지 말(.lite.tsx)로 쓰면
React·Vue·Angular… 21개 언어로 동시 번역해 준다."

여러 프레임워크를 쓰는 회사에서 똑같은 버튼·카드를 React용, Vue용, Angular용으로 세 번 따로 만드는 일이 흔하다. Mitosis는 그걸 한 번만 짜면 나머지를 기계가 번역하게 만든다. 단, 런타임 라이브러리가 아니라 빌드 타임 컴파일러다 — 결과물은 Mitosis가 끼어들지 않는 100% 네이티브 React/Vue/… 코드다.

Mitosis(@builder.io/mitosis)는 Builder.io가 만든 컴포넌트 컴파일러다. 개발자는 React와 매우 비슷한 문법(JSX + 훅)으로 컴포넌트를 .lite.tsx 확장자 파일에 작성한다. 그러면 Mitosis가 이를 분석해 Mitosis JSON이라는 프레임워크 중립 트리로 바꾸고, 선택한 타깃마다 별도의 제너레이터가 그 트리를 진짜 React·Vue·Angular·Svelte·Solid·Qwik 코드로 뽑아낸다.

핵심은 "한 번 쓰고, 어디서나 실행(write once, run everywhere)"인데, 웹 컴포넌트(Web Components)처럼 런타임 추상화 레이어를 끼우는 방식과는 정반대다. Mitosis는 컴파일이 끝나면 자기 흔적을 남기지 않는다. 그래서 각 팀은 자기 프레임워크의 표준 코드를 받아 그대로 쓰고 트리쉐이킹·최적화도 정상적으로 누린다. 대표 활용처는 디자인 시스템(여러 프레임워크에 같은 UI를 배포)과 Figma → 코드 동기화다.

용어
Mitosis (미토시스)
생물학에서 "세포 분열(유사분열)"을 뜻하는 단어. 컴포넌트 하나(.lite.tsx)가 여러 프레임워크 구현으로 "분열"한다는 비유다. npm 패키지명은 @builder.io/mitosis이고, 과거 이름은 jsx-lite였다(레포 토픽에 흔적이 남아 있다).
용어
컴파일러 vs 런타임 라이브러리
런타임 라이브러리(예: React 자체)는 앱이 돌아가는 동안 같이 실행되며 자리를 차지한다. 컴파일러(예: Mitosis·Babel·Svelte)는 빌드할 때만 한 번 일하고 사라진다. Mitosis의 출력물엔 Mitosis 코드가 한 줄도 안 들어간다 — 순수 타깃 프레임워크 코드만 남는다.

2왜 주목받는가

트렌딩 이유 · 경쟁 접근 대비 장점.

2026년 현재 프런트엔드 생태계는 React 천하가 아니다. Vue·Svelte·Solid·Qwik·Angular가 각자 자리를 지키고, 한 회사 안에서도 팀마다 다른 프레임워크를 쓴다. 이 "프레임워크 파편화" 속에서 같은 UI를 여러 번 만드는 비용이 커졌고, Mitosis는 그 통증을 정확히 겨냥한다. 단순 인기가 아니라 ★13.8k · 253개 릴리스 · 1,898 커밋으로 오래 다져진 성숙한 프로젝트라는 점이 신뢰를 준다. 차별점은 넷이다.

비교 항목웹 컴포넌트(Web Components)Mitosis
접근 방식런타임 추상화(브라우저 표준 위에 한 겹)빌드 타임 컴파일(흔적 0)
출력물커스텀 엘리먼트(프레임워크 밖 존재)네이티브 React/Vue/… 코드
생태계 호환SSR·props 전달 등 마찰 잦음각 프레임워크 관용구 그대로
지원 타깃(표준 1개)21개(React~Swift까지)
디자인 도구Figma·Builder.io 연동
차별점 ① — "컴파일 결과가 네이티브 코드"
웹 컴포넌트의 함정을 피한다

README가 직접 내세우는 셀링포인트다: "Avoid the pitfalls of web components by compiling to native framework code". 웹 컴포넌트는 SSR·폼 연동·prop 타입 등에서 프레임워크와 자주 부딪친다. Mitosis는 아예 각 프레임워크의 진짜 코드를 뽑아주므로 그런 마찰이 없다. 받은 팀은 "이게 Mitosis로 만들어졌다"는 사실조차 몰라도 된다.

차별점 ② — 압도적인 타깃 수
React부터 Swift·Liquid까지 21개

packages/core/src/generators/에 제너레이터가 21개 들어 있다 — react(+preact), vue, angular, svelte, solid, qwik, alpine, lit, marko, stencil, taro, liquid(Shopify), swift, html/webcomponent, react-native, rsc(React Server Components), template, mitosis(자기 자신), builder. 웹을 넘어 Shopify 테마(Liquid)와 iOS(Swift)까지 한 소스에서 나온다.

차별점 ③ — 디자인-투-코드 파이프라인
Figma → Mitosis → 모든 프레임워크 npm 패키지

Builder.io의 Figma 플러그인으로 디자인을 Mitosis 컴포넌트로 뽑고, 그걸 다시 React/Vue/… 패키지로 컴파일해 npm에 배포하는 흐름을 지원한다. 디자인 시스템을 코드와 동기화하려는 조직에 강력하다(레포 토픽: figma·no-code·visual·declarative).

차별점 ④ — 도망갈 구멍(escape hatch)이 있다
useTarget으로 타깃별 분기

"한 소스"가 모든 차이를 못 메울 때를 대비해 useTarget({ react: ..., vue: ... }) 같은 타깃별 코드 분기를 공식 제공한다. 또 overrides/ 디렉토리로 특정 타깃 파일을 통째로 대체할 수도 있다. "추상화의 누수"를 부정하지 않고 현실적으로 다루는 설계.

비유

원고와 번역본. 작가가 한국어로 소설 한 권(.lite.tsx)을 쓰면, 번역가들이 영어·일본어·프랑스어판을 각각 자연스러운 그 나라 문장으로 옮긴다. 독자(각 프레임워크 팀)는 번역본만 읽으므로 원문이 무엇이었는지 신경 쓸 필요가 없다. useTarget은 "이 문장만은 일본어판에서 이렇게 의역해 달라"는 작가의 각주에 해당한다.

3기술 스택 전체 지도

컴파일러 코어 / 입력(파서) / 출력(제너레이터) / 모노레포 운영의 4축으로 본다.

Mitosis는 서버·DB가 있는 웹앱이 아니라 개발자 도구(컴파일러)다. 그래서 "백/프론트/인프라" 대신 컴파일러 파이프라인 + 모노레포 운영 관점으로 스택을 그리는 게 정확하다.

① 컴파일러 코어 — @builder.io/mitosis (packages/core)

분류라이브러리역할
JSX 파싱@babel/core, @babel/preset-typescript, plugin-transform-react-jsx.lite.tsx를 AST로 읽어 Mitosis JSON으로 변환
타입 처리ts-morph, typescriptprops/state의 TypeScript 타입을 분석·이식
다른 입력 파서@angular/compiler, svelte+svelte-preprocessAngular·Svelte 컴포넌트도 입력으로 받음
코드 출력 포맷prettier(2.x), astring생성된 코드를 사람이 읽을 수 있게 정렬
트리/유틸neotraverse, lodash, fp-ts, json5, object-hashIR 트리 순회·함수형 유틸·해시
Builder 연동@builder.io/sdkBuilder.io 비주얼 콘텐츠 ↔ Mitosis 변환
용어
AST (Abstract Syntax Tree, 추상 구문 트리)
소스 코드를 컴퓨터가 다루기 쉬운 트리 구조로 표현한 것. <div>{x}</div> 같은 글자를 "div 요소 노드 + 그 자식인 표현식 노드" 형태의 객체로 바꾼다. Babel이 JSX를 AST로 읽어주면, Mitosis가 그걸 자기 IR로 옮겨 담는다.

② 입력(파서) — 무엇을 받아들이나

packages/core/src/parsers/에 입력 파서가 있다. 기본은 JSX 파서(parsers/jsx)이고, 그 외 angular.ts·svelte/·builder/·context.ts가 있다. 즉 React식 JSX뿐 아니라 Angular·Svelte·Builder.io 콘텐츠도 "원본"으로 삼아 Mitosis JSON으로 끌어올 수 있다. 설정에서 parser를 직접 넣어 커스텀 입력 형식을 만들 수도 있다.

③ 출력(제너레이터) — 어디로 내보내나

핵심 자산. packages/core/src/generators/ 하위에 타깃별 폴더 21개가 있고, 각각 componentToReact·componentToVue처럼 "Mitosis JSON → 문자열 코드"를 만드는 함수를 export 한다. 이들을 한데 모은 게 targets.ts다.

// packages/core/src/targets.ts — 타깃 레지스트리(요약)
export const targets = {
  alpine, angular, customElement, html, mitosis, liquid,
  react, reactNative, solid, svelte, swift, template,
  webcomponent, vue, stencil, qwik, marko, preact, lit,
  rsc, taro,
} as const;
export type Targets = keyof typeof targets;

④ 모노레포 운영 — Yarn 4 + Nx

저장소 전체는 Yarn 4 워크스페이스 + Nx(빌드 캐시·태스크 오케스트레이션)로 묶인 모노레포다. package.jsonworkspacespackages/*·e2e/*·examples/**가 등록돼 있고, 릴리스는 @changesets/cli로 버전·체인지로그를 관리한다.

패키지이름역할
core@builder.io/mitosis파서·IR·제너레이터 = 컴파일러 본체
cli@builder.io/mitosis-climitosis build/compile/new 커맨드
eslint-plugin@builder.io/eslint-plugin-mitosis.lite.tsx 작성 규칙 린트(허용 문법 강제)
fiddle(웹 플레이그라운드)브라우저에서 입력↔출력을 실시간 비교
startercreate-mitosisnpm create용 프로젝트 스캐폴딩
docs(Qwik City 사이트)mitosis.builder.io 문서
용어
모노레포 · Nx · Changesets
모노레포는 여러 패키지를 한 저장소에 두는 방식. Nx는 패키지 간 의존을 파악해 바뀐 것만 빌드/테스트하고 결과를 캐시한다(nx run-many). Changesets는 PR마다 "이 변경이 어느 패키지의 어떤 버전 업인지"를 적어 두었다가 릴리스 때 한꺼번에 버전·체인지로그를 만든다.

4아키텍처 심화 분석

파싱 → IR → 제너레이트, 그리고 그 사이에 끼는 플러그인.

4-1. 3단 파이프라인 (컴파일러의 정석)

Mitosis는 거의 모든 컴파일러가 쓰는 "앞단(parse) → 가운데(IR) → 뒷단(generate)" 구조다. 가운데의 프레임워크 중립 IR이 핵심 — 입력이 N개, 출력이 M개여도 N×M개의 변환기를 짤 필요 없이 N+M개만 있으면 된다.

입력(여러 형식) 가운데(단일 IR) 출력(21개 타깃) ┌───────────────┐ ┌──────────────────┐ │ .lite.tsx(JSX)│──┐ ┌──▶│ componentToReact │ │ Angular 컴포넌트│──┤ ┌───────────────────┐ ├──▶│ componentToVue │ │ Svelte 컴포넌트 │──┼──▶ │ Mitosis JSON │ ──┼──▶│ componentToSvelte│ │ Builder.io 콘텐츠│──┘ │ (MitosisComponent)│ ├──▶│ componentToSolid │ └───────────────┘ │ + MitosisNode 트리 │ ├──▶│ componentToQwik │ parsers/ └───────────────────┘ │ │ … (총 21개) │ ▲ └──▶│ componentToSwift │ │ └──────────────────┘ plugins(전/후처리) generators/ • compile-away-components • map-styles • symbol-processor

입력 형식이 늘어도 IR만 잘 채우면 21개 출력이 공짜로 따라오고, 새 프레임워크 타깃을 추가할 때도 제너레이터 하나만 더 쓰면 된다. 이게 "한 번 쓰고 어디서나"가 가능한 구조적 이유다.

용어
IR (Intermediate Representation, 중간 표현)
컴파일러가 입력과 출력 "사이에" 두는 중립적 데이터 구조. Mitosis의 IR은 MitosisComponent 객체다 — 이름·임포트·상태(state)·훅(hooks)·자식 노드 트리(MitosisNode[])·컨텍스트를 담는다. 프레임워크 색이 빠진 "컴포넌트의 본질"만 남긴 형태.

4-2. IR의 실제 모양 — MitosisComponent / MitosisNode

IR이 추상적으로 들리면, 실제 타입을 보면 분명해진다. 컴포넌트는 메타데이터 + 노드 트리로 쪼개진다.

// types/mitosis-component.ts (요약) — 컴포넌트 = 메타+상태+훅+트리
interface MitosisComponent {
  name: string;
  imports: MitosisImport[];        // 어떤 모듈을 import 했나
  state: MitosisState;             // useStore/useState → 상태 맵
  hooks: { onMount, onUpdate, ... }; // 라이프사이클
  children: MitosisNode[];         // JSX 트리(렌더 결과)
  context: { get, set };           // useContext/setContext
  props?, refs?, meta? ...
}

// types/mitosis-node.ts (요약) — 노드 = 이름+속성+바인딩+자식
type MitosisNode = {
  '@type': '@builder.io/mitosis/node';
  name: string;                    // 'div', 'Show', 컴포넌트명…
  properties: { [k: string]: string };  // 정적 속성(class="x")
  bindings: { [k: string]: Binding };   // 동적 바인딩(checked={...})
  children: MitosisNode[];
};

여기서 properties(정적)bindings(동적)를 나눈 게 영리하다. class="view"는 properties로, checked={props.todo.completed}는 bindings로 들어간다. 제너레이터는 이 구분을 보고 React는 checked={...}, Vue는 :checked="...", Angular는 [checked]="..."처럼 각 프레임워크의 바인딩 문법으로 번역한다.

4-3. 입력 예시 한 컷 — 진짜 .lite.tsx

레포의 examples/todo에 있는 실제 컴포넌트(요약). React를 닮았지만 상태는 useStore, 조건부는 <Show>로 쓴다 — 이 "Mitosis 사투리"가 모든 타깃으로 번역 가능한 최소 공통 문법이다.

// examples/todo/src/components/todo.lite.tsx (요약)
import { Show, useStore } from '@builder.io/mitosis';

export default function Todo(props) {
  const state = useStore({
    editing: false,
    toggle() { props.todo.completed = !props.todo.completed; },
  });
  return (
    <li>
      <input type="checkbox" checked={props.todo.completed}
             onClick={() => state.toggle()} />
      <label onDblClick={() => { state.editing = true; }}>
        {props.todo.text}
      </label>
      <Show when={state.editing}>
        <input class="edit" value={props.todo.text} />
      </Show>
    </li>
  );
}
용어
useStore · Show · For (Mitosis 표준 어휘)
useStore는 반응형 상태 객체(React의 useState/Vue의 reactive를 모두 커버하는 중립형). <Show when={...}>는 조건부 렌더, <For each={...}>는 반복 렌더. JS의 &&·.map() 대신 명시적 컴포넌트를 쓰는 이유는, 그래야 파서가 "이건 조건/반복이다"를 확실히 알아 Vue v-if·Angular *ngIf 등으로 정확히 번역할 수 있기 때문이다.

4-4. 핵심 설계 패턴 3가지

패턴 ① — 컴파일-어웨이(compile-away) 훅

useState·useStore·onMount·useContext 같은 Mitosis 훅은 실제 함수가 아니다. index.ts에서 declare function useStore...로 선언만 돼 있고, 컴파일 때 각 타깃의 진짜 메커니즘으로 치환되어 사라진다 — React면 useState, Vue면 ref, Svelte면 let 변수. "흔적 0"의 정체가 이것이다.

패턴 ② — 제너레이터 = 순수 함수

각 타깃은 componentToX(options) => (component) => string 꼴의 함수다. 같은 IR을 넣으면 항상 같은 코드가 나오는 순수 변환이라, 테스트하기 쉽고 타깃을 독립적으로 늘릴 수 있다. __tests__/의 스냅샷 테스트가 "이 입력 → 이 출력"을 고정해 회귀를 막는다.

패턴 ③ — 플러그인 파이프라인

파싱 전/후, 생성 전/후에 끼어드는 플러그인 훅이 있다(plugins/). 예: compile-away-components(특정 컴포넌트를 더 단순한 것으로 치환), map-styles(스타일 변환). 코어를 안 건드리고 변환 동작을 확장하는 개방-폐쇄 원칙의 실전 사례다.

흔한 오해
"Mitosis는 런타임에 끼어든다" — 아니다

Mitosis는 앱 번들에 들어가지 않는다. .lite.tsx오직 빌드 입력이고, 배포되는 건 생성된 네이티브 코드뿐이다. 그래서 "Mitosis를 쓰면 번들이 커진다"는 걱정은 사실이 아니다. 대신 제약이 있다 — 아무 JS·아무 npm 라이브러리나 컴포넌트 안에서 자유롭게 쓸 수 없고, 번역 가능한 부분집합(useStore·Show·For 등)을 지켜야 한다.

5디렉토리 구조 해부

모노레포 → 컴파일러 코어 → 그 안의 핵심 3폴더 순으로 좁혀 본다.

5-1. 저장소 전체

mitosis/ ├─ packages/ # 배포되는 모든 패키지(워크스페이스) │ ├─ core/ # @builder.io/mitosis — 컴파일러 본체 │ ├─ cli/ # mitosis build/compile/new 커맨드 │ ├─ eslint-plugin/ # .lite.tsx 작성 규칙 린트 │ ├─ fiddle/ # 브라우저 플레이그라운드 │ ├─ starter/ # create-mitosis 스캐폴딩 │ └─ docs/ # mitosis.builder.io 문서(Qwik City) ├─ e2e/ # 프레임워크별 통합 테스트 앱(아래 참고) │ ├─ e2e-react/ e2e-vue3/ e2e-angular/ e2e-solid/ │ ├─ e2e-svelte/ e2e-qwikcity/ e2e-alpine/ e2e-stencil/ ├─ examples/ # basic · todo · metadata 샘플 ├─ nx.json # Nx 태스크/캐시 설정 ├─ package.json # Yarn 4 워크스페이스 루트 └─ .changeset/ # 버전·체인지로그 관리
관찰 — e2e가 곧 신뢰의 증거

e2e/ 아래에 React·Vue·Angular·Solid·Svelte·Qwik·Alpine·Stencil 각각의 실제 앱이 따로 있다. 같은 .lite.tsx를 컴파일해 8개 프레임워크에서 돌려보고 동작이 동일한지를 검증하는 장치다. "21개로 번역한다"는 주장을 말이 아니라 실행되는 테스트로 받쳐 주는 부분.

5-2. 컴파일러 코어 packages/core/src/

core/src/ ├─ index.ts # 공개 API(훅 선언 + 모든 제너레이터/파서 re-export) ├─ targets.ts # 21개 제너레이터를 모은 레지스트리 ├─ parsers/ # 입력 → IR (jsx · angular · svelte · builder) │ └─ jsx/ # 기본 파서: ast·state·props·hooks·element-parser… ├─ generators/ # IR → 출력 (타깃 21개 폴더) │ ├─ react/ vue/ angular/ svelte/ solid/ qwik/ … │ └─ context/ # createContext 등 타깃별 컨텍스트 변환 ├─ types/ # IR 타입 정의(mitosis-component·mitosis-node·config) ├─ helpers/ # 트리 순회·이름·시그널 등 공용 유틸 ├─ plugins/ # compile-away-components · map-styles 등 ├─ symbols/ # Builder 심볼 처리 └─ __tests__/ # 스냅샷 기반 회귀 테스트

읽는 순서를 추천하면 types/(IR이 무엇인가) → ② parsers/jsx/(입력을 어떻게 IR로 채우나) → ③ generators/react/(IR을 어떻게 코드로 푸나). 이 셋만 따라가면 컴파일러 전체 그림이 잡힌다.

5-3. 입력 파서 parsers/jsx/ 내부

파서가 "통짜 함수" 하나가 아니라 관심사별로 쪼개진 게 학습 포인트다 — 컴포넌트의 각 부분을 따로 읽는다.

파일읽는 대상
jsx.ts / index.ts파서 진입점 · 전체 오케스트레이션
element-parser.tsJSX 엘리먼트 → MitosisNode(속성/바인딩 분리)
state.tsuseStore/useState → state 맵
props.ts · props-types.tsprops와 그 TypeScript 타입
hooks/onMount·useTarget·useMetadata 등 훅
imports.ts · exports.ts모듈 import/export 보존
signals.ts반응형 시그널(useState(.., {reactive:true}))

6학습 포인트 (기술별)

이 레포 하나로 "컴파일러 + 멀티 프레임워크 + 모노레포"를 동시에 챙긴다.

① 컴파일러 사고법 — parse / IR / generate

가장 값진 배움. 입력 N개·출력 M개를 N×M이 아니라 N+M으로 푸는 "중간 표현(IR)" 발상은 Babel·SWC·LLVM·Svelte까지 관통하는 핵심 아이디어다. Mitosis는 이걸 친숙한 "컴포넌트"라는 소재로 보여줘서 입문용으로 최적이다.

실습: types/mitosis-node.ts의 properties vs bindings 구분을 읽고, "왜 정적/동적을 나눠야 타깃별 바인딩 문법으로 번역되는가"를 한 단락으로 설명해 보기.

② 프레임워크들의 공통점과 차이

같은 IR이 React·Vue·Svelte·Solid·Angular 코드로 어떻게 다르게 풀리는지 비교하면, 각 프레임워크의 반응성 모델이 선명해진다. useStore가 React에선 useState, Vue에선 ref, Svelte에선 그냥 변수가 되는 걸 보면 "상태 관리란 결국 같은 문제의 다른 해법"임이 보인다.

실습: 플레이그라운드(mitosis.builder.io/playground)에 같은 컴포넌트를 넣고 React/Vue/Svelte/Solid 출력 탭을 나란히 놓고 diff 해 보기.

③ "번역 가능한 부분집합" 설계 — 제약이 곧 힘

.map() 대신 <For>, && 대신 <Show>를 강제할까? 자유로운 JS는 번역 불가능하기 때문이다. eslint-plugin이 이 제약을 강제한다. "표현력을 일부러 줄여서 이식성을 얻는" 설계 트레이드오프를 체험할 수 있다.

// mitosis.config.js — 컴파일 설정의 뼈대(요약)
module.exports = {
  files: 'src/**',
  targets: ['react', 'vue', 'svelte', 'solid', 'qwik'],
  dest: 'output',
  options: {
    react: { stylesType: 'styled-jsx', stateType: 'hooks' },
    vue:   { api: 'composition' },
  },
};
실습: eslint-plugin-mitosis 규칙 목록을 훑고, 일부러 규칙을 어긴 .lite.tsx를 만들어 어떤 경고가 뜨는지 확인.

④ 모노레포 + Nx + Changesets 운영

core·cli·docs·e2e가 한 저장소에서 어떻게 의존하고, Nx가 nx run-many로 바뀐 패키지만 빌드/테스트하며, Changesets가 릴리스를 자동화하는지를 실물로 본다. 실제 OSS 운영을 배우는 좋은 교본이다.

실습: 루트 package.jsonci:build/ci:test 스크립트를 읽고 Nx 타깃 그래프가 어떻게 그려지는지 추적.

⑤ 스냅샷 테스트로 "출력 고정"

__tests__/의 스냅샷은 "이 입력 → 정확히 이 React 코드"를 박제한다. 제너레이터를 고칠 때 의도치 않은 출력 변화를 즉시 잡아내는 컴파일러 회귀 테스트의 정석이다.

7시스템 요구사항 & 설치

서버·DB 없음 — Node 환경이면 끝. 빌드 타임 도구다.

항목요구사항메모
런타임Node.js ≥ 16루트 package.jsonengines 기준
패키지 매니저(기여 시) Yarn 4.1.1packageManager 고정. 사용만 할 땐 npm도 OK
언어TypeScript 5.x 권장props/state 타입이 그대로 이식됨
대상 프레임워크타깃별 빌드 환경출력 코드는 각 프레임워크 표준 프로젝트에서 빌드
네트워크설치 시에만컴파일 자체는 로컬에서 동작

새 프로젝트로 시작

# 대화형 스캐폴딩(starter 패키지가 생성)
npm create @builder.io/mitosis@latest

# 생성된 폴더에서 — 컴포넌트를 모든 타깃으로 컴파일
npm run build       # 내부적으로 mitosis build 실행

기존 프로젝트에 추가

npm i -D @builder.io/mitosis-cli @builder.io/mitosis

# mitosis.config.js를 만든 뒤
npx mitosis build            # config의 targets로 일괄 컴파일
npx mitosis compile -t=vue ./Button.lite.tsx  # 단일 파일 → Vue

CLI 커맨드는 packages/cli/src/commands/build.ts(설정 기반 일괄 빌드)·compile.ts(단일 파일 변환)·new.ts(스캐폴딩) 셋으로 단순하게 나뉜다. 브라우저에서 빠르게 감을 잡고 싶으면 설치 없이 플레이그라운드를 쓰면 된다.

8직접 해볼 수 있는 실습 과제

읽기만 말고, 난이도순으로 손을 움직여 보자.

Lv1. 플레이그라운드 diff 입문 · 30분

mitosis.builder.io/playground에 버튼+카운터 컴포넌트(useStore 상태 1개, <Show> 1개)를 작성하고 React·Vue·Svelte·Solid 출력 탭을 비교한다. "같은 IR이 어떻게 다른 코드가 되나"를 눈으로 확인.

Lv2. 작은 디자인 시스템 컴파일 초급 · 1~2시간

npm create @builder.io/mitosis로 프로젝트를 만들고 Button·Badge·Card 세 컴포넌트를 .lite.tsx로 작성한 뒤 targets: ['react','vue']로 컴파일한다. 나온 React/Vue 코드를 각 프레임워크 샘플 앱에 붙여 실제로 렌더되는지 확인.

Lv3. useTarget로 타깃 분기 중급 · 반나절

한 컴포넌트 안에서 useTarget({ react: ..., vue: ..., default: ... })로 타깃별로 다른 클래스명/속성을 출력하게 만들어 본다. "추상화의 누수"를 공식 장치로 다루는 감각을 익히는 단계.

Lv4. 제너레이터 출력 추적 중상급 · 1일

레포를 클론해 packages/core/src/generators/react/를 읽고, __tests__/ 스냅샷 하나를 골라 "이 IR이 왜 이 React 코드가 되는지" 한 줄씩 매핑한다. yarn test:update로 스냅샷이 갱신되는 흐름도 체험.

Lv5. 미니 제너레이터/플러그인 만들기 고급 · 2~3일

generators/template(가장 단순한 골격)을 본떠 아주 작은 커스텀 타깃을 만들거나, plugins/ 훅으로 "모든 class에 접두사를 붙이는" 플러그인을 작성한다. IR을 직접 순회·변환해 보며 컴파일러 뒷단을 손에 익히는 최종 단계.

9관련 기술 심화 학습 로드맵

4주 코스 — 컴파일러 개념과 프레임워크 비교를 번갈아 다진다.

주차주제핵심 활동
1주차JSX·AST·컴파일러 기초Babel이 JSX를 AST로 바꾸는 과정 이해 · astexplorer.net 실습 · parse→IR→generate 개념 잡기(Lv1)
2주차프레임워크 반응성 비교React hooks · Vue reactivity · Svelte runes/store · Solid signals를 같은 예제로 비교. useStore가 각각 무엇이 되는지 매핑(Lv2)
3주차Mitosis IR & 제너레이터types/parsers/jsx/generators/react/ 코드 읽기 · 스냅샷 테스트 추적(Lv3·Lv4)
4주차확장 & 운영플러그인/커스텀 타깃 작성(Lv5) · Nx/Changesets 모노레포 운영 · eslint-plugin 규칙 분석
학습 순서의 이유

먼저 "컴파일러란 무엇인가"(1주)와 "프레임워크들이 같은 문제를 어떻게 다르게 푸나"(2주)를 알아야, 3~4주차에 Mitosis 코드가 "왜 이렇게 생겼는지"가 비로소 납득된다. 도구의 사용법이 아니라 그 도구가 풀고 있는 문제부터 보는 순서다.

10핵심 키워드 사전

이 레포를 읽다 마주치는 용어들.

용어
.lite.tsx
Mitosis 컴포넌트의 파일 확장자 관례. React식 JSX처럼 보이지만 useStore·<Show>·<For> 같은 "번역 가능한 부분집합"만 쓴다. 컴파일 입력 전용 — 앱에 직접 배포되지 않는다.
용어
Mitosis JSON / MitosisComponent
파서가 만들어내는 프레임워크 중립 IR(중간 표현). 컴포넌트의 이름·임포트·상태·훅·노드 트리를 담은 객체. 모든 제너레이터의 공통 입력.
용어
MitosisNode
IR 트리의 한 노드(엘리먼트). name(태그/컴포넌트명), properties(정적 속성), bindings(동적 바인딩), children으로 구성. 정적/동적 분리가 타깃별 바인딩 번역의 토대다.
용어
generator (제너레이터) / target (타깃)
IR을 특정 프레임워크 코드 문자열로 바꾸는 함수(componentToReact 등)와 그 대상. targets.ts에 21개가 등록돼 있다.
용어
parser (파서)
원본 소스를 IR로 끌어올리는 앞단. 기본은 JSX 파서이며 Angular·Svelte·Builder.io 입력도 지원. config.parser로 커스텀 가능.
용어
useStore / useState (Mitosis)
중립형 반응 상태 훅. 컴파일 시 React useState, Vue ref/reactive, Svelte 변수 등으로 치환되고 자기 자신은 사라진다(compile-away).
용어
Show / For
조건부·반복 렌더를 위한 표준 컴포넌트. JS의 &&·.map() 대신 명시적 컴포넌트를 써야 Vue v-if/v-for, Angular *ngIf/*ngFor 등으로 정확히 번역된다.
용어
useTarget
타깃별로 다른 코드를 끼우는 공식 탈출구. useTarget({ react:..., vue:..., default:... }) 형태. 한 소스로 못 메우는 차이를 현실적으로 처리.
용어
compile-away
"컴파일하면 사라진다"는 뜻. Mitosis 훅·제어 컴포넌트는 런타임 함수가 아니라, 빌드 때 각 타깃의 진짜 메커니즘으로 치환되어 출력물에서 자취를 감춘다. "흔적 0"의 비결.
용어
IR (Intermediate Representation)
입력과 출력 사이의 중립 데이터 구조. 입력 N·출력 M을 N×M이 아닌 N+M으로 푸는 컴파일러의 핵심 발상. Mitosis에선 MitosisComponent가 IR.
용어
Nx · Changesets · Yarn Workspaces
모노레포 운영 3종. Yarn Workspaces로 패키지를 묶고, Nx로 바뀐 것만 빌드/캐시하고, Changesets로 버전·체인지로그·릴리스를 자동화한다.
용어
Liquid · Qwik · RSC · Taro
덜 알려진 타깃들. Liquid=Shopify 테마 언어, Qwik=재개성(resumability) 프레임워크, RSC=React Server Components, Taro=중국발 멀티플랫폼(미니프로그램) 프레임워크. 웹 컴포넌트 한계를 넘는 타깃 폭을 보여준다.

11참고 링크

원본과 더 깊이 파고들 자료.

프로젝트 원본

· GitHub 저장소: github.com/BuilderIO/mitosis
· 공식 문서: mitosis.builder.io/docs
· 플레이그라운드: mitosis.builder.io/playground
· npm 패키지: @builder.io/mitosis, @builder.io/mitosis-cli

코드 안에서 먼저 볼 곳

· IR 타입: packages/core/src/types/mitosis-component.ts, mitosis-node.ts
· 타깃 레지스트리: packages/core/src/targets.ts
· 기본 파서: packages/core/src/parsers/jsx/ · 제너레이터: packages/core/src/generators/react/
· 실제 예제: examples/todo/src/components/todo.lite.tsx

배경 지식

· Figma 연동: mitosis.builder.io/docs/figma
· 컴파일러 개념 실습: astexplorer.net (JSX를 AST로 보는 도구)
· Discord 커뮤니티 / 기여 가이드: 레포 CONTRIBUTING.md