Ionify vs Vite: 빌드 툴 내부에서 실제로 일어나는 일

발행: (2026년 4월 8일 AM 04:24 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

대부분의 개발자는 Vite를 사용하고 신뢰합니다. 빠르고, 잘 설계되었으며, 방해가 되지 않습니다.
하지만 Ionify가 도전하는 Vite 아키텍처와 모든 주요 빌드 도구에 내재된 근본적인 가정이 있습니다.

가정: 모든 빌드는 처음부터 시작한다.

Stateless Build 문제

vite build를 실행하면 대략 다음과 같은 과정이 진행됩니다:

  1. Vite가 esbuild/Rollup(요즘은 주로 Rolldown + Oxc로 구동) 을 시작합니다.

  2. 각 파일이 플러그인 체인을 독립적으로 통과합니다

    main.tsx   → TS 플러그인 → JSX 플러그인 → 출력
    utils.ts   → TS 플러그인 → 출력
    index.css  → CSS 플러그인 → 출력
  3. 모든 것이 번들링됩니다.

다음에 다시 실행하면 동일한 과정이 반복됩니다. 이미 실행된 내용에 대한 기억이 없으며, TS 플러그인은 JSX 플러그인이 파일을 이미 처리했는지 알지 못하고, 번들러는 어떤 파일이 변경되었는지도 알지 못합니다. 파이프라인은 설계상 상태가 없는(stateless) 것입니다. 이는 버그가 아니라 단순함과 예측 가능성을 위한 의도적인 선택이지만, 규모가 커질수록 실제 비용이 발생합니다.

Ionify가 이를 다르게 생각하는 방식

파일을 반복적으로 변환하는 대신, Ionify는 파일을 직접 다룹니다.

콘텐츠 주소 지정 저장소 (CAS)

각 모듈은 콘텐츠 해시를 갖습니다:

SHA-256(source content + config version) → CAS key

결과는 CAS 디렉터리에 저장됩니다:

.ionify/
  cas/
    /
      /
        transformed.js
        transformed.js.map

동일한 콘텐츠 + 동일한 설정 → 동일한 해시 → 변환을 완전히 건너뛰기. 변경되지 않은 모듈에 대해서는 변환이 전혀 이루어지지 않습니다.

지속적인 그래프

Vite는 개발 서버가 시작될 때마다 모듈 그래프를 재구성합니다. Ionify는 이를 지속합니다:

.ionify/
  graph.db   ← sled‑backed embedded KV store (Rust)

파일이 변경되면 Ionify는 역‑의존성 인덱스를 통해 BFS를 수행하여 정확히 어떤 모듈이 영향을 받는지 찾습니다. 500개의 모듈이 있는 프로젝트에서 하나의 유틸리티 파일을 변경해도 영향을 받는 모듈은 12개에 불과할 수 있으며, 전체 500개가 모두 무효화되는 것은 아닙니다.

Version Isolation: Config Changes Invalidate Everything

영구 캐시는 구성 변경을 처리해야 합니다. Ionify는 구성에서 계산된 결정론적 version hash를 사용하여 이를 해결하며, 모든 구성 수정이 관련 캐시를 무효화하도록 보장합니다.

4계층 CAS 아키텍처

Tier 1 — Module Transform Cache

콘텐츠 해시를 키로 하여 변환된 모듈 출력물을 저장합니다.

Tier 2 — Deps Artifact Store

컴파일된 서드파티 의존성을 캐시합니다.

Tier 3 — Compression CAS

빌드 출력물(Brotli‑11, gzip‑9)을 한 번 미리 압축하여 영구적으로 제공합니다.

Tier 4 — Chunk‑Output CAS

번들러가 생성한 최종 청크 파일을 캐시합니다.

실제 구성: Vite에서 마이그레이션

아래는 Vite에서 전환한 실제 프로젝트의 ionify.config.ts 예시입니다:

import { defineConfig } from 'ionify'

export default defineConfig({
  entry: '/src/main.tsx',
  server: {
    https: true,
  },
  resolve: {
    // Same aliases as tsconfig — Ionify reads these natively
    // Note: if your tsconfig.json is JSONC (comments/trailing commas),
    // auto‑alias parsing currently fails — specify manually here
    alias: {
      '@@': '/',
      '@@/Core': '/Core/src',
      '@': '/src',
    },
    extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'],
    conditions: ['import', 'module', 'browser', 'default'],
    mainFields: ['module', 'jsnext:main', 'jsnext', 'main'],
  },
  optimizeDeps: {
    include: ['moment-hijri'],   // pre‑warm known‑problem deps
    sharedChunks: 'auto',
    packSlimming: 'auto',
    vendorPacks: 'auto',
  },
  build: {
    target: 'esnext',
  },
})

프로젝트에는 10 k 개가 넘는 모듈이 포함되어 있습니다. Ionify로 마이그레이션한 후:

  • Cold build: 3.2 s (Vite 사용 시 3.7 s에서 감소)
  • Warm build: 2.2 s (Vite 사용 시 3.7 s에서 감소)

근본적인 차이점

Build ToolCore Assumption
Vite각 빌드는 독립적이다.
Ionify이전 빌드의 대부분 작업이 여전히 유효하다.

두 가정 모두 타당합니다. Vite는 더 단순하고 예측 가능한 시스템을 제공합니다. Ionify의 접근 방식은 두 번째, 열 번째, 그리고 CI 빌드가 모두 이전 작업의 혜택을 받게 하여 다이어그램의 “transform” 부분을 대부분 비워 둡니다—복잡성이 숨겨져서가 아니라 대부분의 변환이 실행되지 않기 때문입니다.

Ionify는 현재 프로덕션에서 사용 중입니다.

0 조회
Back to Blog

관련 글

더 보기 »