본문으로 건너뛰기
5분 AI.
GITHUB TRENDING RUST WEEKLY #10 딥다이브 · 2026-10-23 분석

vite-plus 딥다이브
— Rust와 TypeScript로 웹 개발 도구를 한 입구에 연결하기

개발도구·CLI

빠른 판단

무엇
개발 서버·포맷·검사·테스트·빌드와 Node·패키지 관리자 관리를 vp 명령으로 묶은 웹 개발 도구 모음
이럴 때
팀의 웹 개발 환경과 반복 명령을 통일하고 여러 도구가 같은 버전·설정으로 실행되게 할 때
개발 서버, 코드 검사, 테스트, 빌드, Node.js와 패키지 관리자까지 하나의 명령어 체계로 연결하는 웹 개발 도구 모음입니다. 사용법을 익히는 데서 한 걸음 더 들어가, Rust CLI가 TypeScript 코드를 부르고 다시 네이티브 모듈로 돌아오는 이유를 살펴봅니다. 설정을 실행하지 않고 읽는 방법, 검사 순서를 보장하는 방법, 동시에 실행한 Git 훅 설정이 잠금 충돌을 겪을 때 재시도하는 방법도 배웁니다. 저장소: voidzero-dev/vite-plus · ⭐ 6,458 · 포크 296 · Rust · MIT · v1.4.0 · 2026-10-23 GitHub 기준.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

여러 도구를 같은 규칙으로 쓰게 만드는 개발용 입구

Vite+는 Vite, Rolldown, Vitest, Oxlint, Oxfmt, tsdown, Vite Task를 연결하고, vp라는 명령으로 웹 개발의 반복 작업을 수행하게 하는 도구입니다. 여기에 Node.js 버전과 패키지 관리자 관리까지 더합니다. 이 저장소의 핵심은 모든 도구를 새로 만드는 것이 아니라, 서로 다른 도구가 같은 환경에서 일하도록 연결하는 설계입니다.

웹 프로젝트를 시작하면 개발 서버, 포매터, 린터, 테스트 도구, 빌드 도구를 각각 골라야 합니다. 명령도 다르고 설정 파일도 늘어납니다. Vite+는 프로젝트의 vite.config.ts에 관련 설정을 모으고, 명령 이름과 도구 선택을 일관되게 만드는 방향을 택했습니다.

핵심 비유

기계를 모두 새로 발명하는 공장이 아니라, 작업대를 연결하는 생산 라인

Vite는 개발 서버와 앱 빌드 작업대, Vitest는 검수 작업대, Oxlint와 Oxfmt는 코드의 규칙과 모양을 검사하는 작업대입니다. Vite+는 작업마다 알맞은 기계를 고르고, 같은 재료와 환경을 넘겨주는 관리자에 가깝습니다.

관리자가 생겨도 기계의 기능과 제약은 사라지지 않습니다. 그래서 이 코드를 읽을 때는 “어떤 알고리즘인가”와 함께 “누구에게 일을 맡기고 무엇을 전달하는가”를 물어야 합니다.

개발자의 하루를 명령으로 읽기

하고 싶은 일명령실제로 맡는 쪽
개발 서버 시작vp dev코어 패키지에 포함된 Vite의 실행 진입점
정적 검사vp checkOxfmt → Oxlint, 설정에 따른 타입 검사
테스트vp testCLI와 함께 배포되는 Vitest 우선 선택
앱 배포용 빌드vp buildVite와 그 내부 번들러 Rolldown
라이브러리 묶기vp packtsdown을 이용하는 패키징 경로
사용자 작업 실행vp run buildVite Task가 선택한 스크립트 또는 작업
환경·의존성 관리vp env, vp installRust의 런타임 관리·패키지 관리자 계층

근거는 README.md, packages/cli/src/bin.ts, packages/cli/src/resolve-vite.ts, packages/cli/src/resolve-test.ts입니다. 명령에 따라 Rust만으로 처리하는 부분과 Node.js를 거치는 부분이 다릅니다.

용어 이해
도구 모음과 CLI
도구 모음(toolchain)은 코드를 검사하고 변환하고 실행하는 도구들의 연결입니다. CLI는 터미널에서 글자로 명령을 내리는 인터페이스입니다. vp는 입구이고, 입구 뒤에 여러 실행 엔진이 있습니다.

이름이 비슷해도 다른 두 명령

# 내장 Vitest를 실행
vp test

# 프로젝트가 정의한 test 스크립트 또는 작업을 실행
vp run test

# vp run의 짧은 이름
vpr test

package.json에 "test": "jest"가 있다면 vp run test는 그 스크립트를 실행합니다. vp test는 여전히 내장 테스트 경로를 택합니다. 같은 이름의 스크립트로 내장 명령을 덮어쓰지 않는 규칙은 docs/guide/run.md와 packages/cli/src/bin.ts에 드러납니다.

이 글에서 얻을 것
프레임워크보다 먼저 “경계”를 읽는 눈

명령 인자, 현재 디렉토리, 환경변수, 설정값, 종료 코드가 어디서 어디로 전달되는지 추적해 봅시다. 이 다섯 가지를 설명할 수 있으면 큰 도구 저장소도 작은 흐름으로 나눠 읽을 수 있습니다.

2왜 주목받는가

더 빠른 엔진에 더해, 도구 사이의 조립 비용을 줄이는 시도

2026-10-23 GitHub 기준 스타는 6,458개, 포크는 296개입니다. GitHub Trending Rust Weekly #10이라는 순위는 발견의 계기입니다. 이 순위만으로 특정 기능이 인기를 끌었다거나 사용자가 급증했다고 증명할 수는 없습니다. 여기서는 소스에서 드러나는 매력을 세 가지로 해석합니다.

  1. 명령과 설정을 함께 묶습니다. 단축 명령만 제공하는 수준을 넘어, packages/cli/src/define-config.ts가 Vite의 설정 타입에 lint, fmt, run, staged 등을 추가합니다.
  2. Rust와 JavaScript 생태계의 역할을 나눕니다. 환경 관리와 네이티브 실행 계층은 Rust로, 도구 탐색과 프로젝트 설정 로딩은 TypeScript로 연결합니다.
  3. 설치 이후의 문제까지 다룹니다. 프로젝트별 CLI 선택, 네이티브 모듈 배포, Git 훅, 마이그레이션, CI 호환성을 같은 제품 범위에 둡니다.

Vite+의 장점은 명령 몇 글자를 줄이는 데서 끝나지 않고, 팀이 합의해야 할 도구 조합과 실행 규칙을 함께 줄이는 데 있습니다.

기존 선택지와 어떻게 다른가

다음은 역할과 구성 방식을 비교한 표입니다. 실행 속도나 호환성 전체를 측정한 결과가 아니며, 다른 도구도 추가 설정과 플러그인으로 범위를 넓힐 수 있습니다.

선택지주로 맡기는 일Vite+가 더 묶는 부분고려할 비용
Vite 단독개발 서버와 앱 빌드검사·테스트·작업 실행·환경 관리의 공통 입구Vite+가 묶어 배포하는 버전 관계도 이해해야 함
pnpm 중심 구성의존성 설치와 스크립트 실행도구 선택·통합 설정·작업 캐시를 덧붙임vp test와 vp run test를 구분해야 함
도구 직접 조합Vite·검사기·테스트 도구를 개별 선택함께 동작할 조합과 설정 타입을 제공개별 도구 버전을 자유롭게 바꾸던 방식의 재검토
Vite+개발 흐름 전체를 하나의 CLI로 연결프로젝트별 CLI와 머신의 환경 관리까지 연결기존 설정·의존성·플러그인에 대한 마이그레이션 확인

Vite와 pnpm은 이 프로젝트가 없애려는 대상으로만 보면 안 됩니다. Vite는 코어에 포함되고 pnpm은 관리 가능한 패키지 관리자 중 하나입니다. 이미 검증된 도구를 조합하면서 반복되는 접착 코드를 제품으로 만든다는 관점이 더 정확합니다.

비유로 이해하기

도구 직접 조합은 부품을 골라 조립하는 데스크톱 PC와 비슷합니다. Vite+는 함께 동작할 부품 조합을 정한 완제품에 가깝습니다. 처음 맞추기는 편해지지만, 특정 부품만 교체할 때는 전체 조합을 확인해야 합니다.

“설정 없이”라는 소개를 어디까지 받아들일까

README.md는 기본 설정만으로 시작하는 도구 모음이라고 소개합니다. 실제로는 vite.config.ts에서 세밀한 설정을 받으며, vp check의 타입 검사도 조건부입니다. 이 저장소의 vite.config.ts는 lint.options.typeAware와 typeCheck를 모두 켭니다. “아무 설정도 필요 없다”보다 “기본값을 주되 한곳에서 조정한다”로 이해하면 맞습니다.

packages/cli/src/resolve-test.ts는 Vitest를 CLI 쪽에서 먼저 찾습니다. 테스트 실행기와 vite-plus/test에서 불러오는 API가 서로 다른 복사본을 가리켜 내부 상태가 갈라지는 일을 줄이려는 선택입니다. 도구 통합에서 버전 정합성이 왜 중요한지 보여줍니다.

도입 판단
속도 숫자는 프로젝트 조건과 함께 읽기

Rust를 썼다는 사실만으로 모든 명령이 빠르다고 결론 낼 수 없습니다. Node.js 시작, 설정 평가, 파일 수, 캐시 적중 여부, 기존 플러그인까지 영향을 줍니다. 이 글은 독립적인 속도 측정치를 제시하지 않으며, 팀 프로젝트에서는 같은 입력과 캐시 상태로 비교해야 합니다.

3기술 스택 전체 지도

실행 코어, JavaScript 연결부, 문서 화면, 배포 기반을 구분하기

이 제품은 데이터베이스를 둔 웹 서비스가 아니라 개발자 컴퓨터와 CI에서 실행하는 도구입니다. 따라서 백엔드는 서버 API보다 실행 코어에 해당합니다. 프론트엔드도 제품의 필수 웹 화면과 문서 사이트를 구분해서 봐야 합니다.

버전 읽는 법
의존성 선언과 잠금 파일
Cargo.toml이나 package.json은 허용할 버전을 선언하고, 잠금 파일은 해결된 버전을 기록합니다. Cargo의 "4.5.40"은 정확히 그 버전만 고정한다는 뜻이 아닙니다. catalog:는 실제 번호를 pnpm-workspace.yaml의 공통 목록에서 찾으라는 뜻입니다.

실행 코어: Rust가 맡는 백엔드 역할

기술선언 → 잠금 값이 저장소에서의 역할
Rustedition 2024, rust-version = "1.92.0"워크스페이스의 문법 기준과 최소 버전 선언
개발 도구 모음nightly-2026-08-02rust-toolchain.toml의 실제 개발용 선택, rust-src 포함
Clap4.5.40 → 4.6.7명령·옵션 파싱과 도움말 체계
Tokio1.48.0 → 1.54.1비동기 파일·프로세스·다운로드 작업의 실행 기반
Serde1.0.219 → 1.0.229설정과 도구 사이 데이터 직렬화
NAPI-RSnapi 3.12.2 → 3.14.2Node.js와 Rust 네이티브 코드 사이 연결
Oxc Parser0.155.0 → 0.155.0설정 파일을 AST로 읽고 정적 값 추출
Oxc Resolver11.24.3 → 11.24.3프로젝트의 패키지 경로 찾기
Vite TaskGit revision a3c087a7…vt, vt_workspace 등 외부 Rust 의존성

선언은 루트 Cargo.toml, 잠금 값은 Cargo.lock 기준입니다. crates/vp_global_cli/Cargo.toml은 vp 실행 파일을 정의하고, packages/cli/binding/Cargo.toml은 crate-type = ["cdylib"]로 Node.js가 불러올 네이티브 라이브러리를 만듭니다.

Rust 최소 버전 선언이 있어도 이 저장소 전체를 stable Rust만으로 빌드할 수 있다고 읽으면 안 됩니다. rust-toolchain.toml은 fspy의 사전 로드 라이브러리 빌드에 필요한 -Z bindeps를 nightly 선택 이유로 설명합니다. Windows용 vp_trampoline도 별도 설정으로 표준 라이브러리를 빌드합니다.

TypeScript 연결부와 실제 포함 도구

항목확인한 버전근거와 역할
Vite+ CLI / 코어1.4.0 / 1.4.0packages/cli/package.json, packages/core/package.json
Vite8.3.5코어의 bundledVersions.vite에 기록
Rolldown1.2.15코어의 bundledVersions.rolldown에 기록
tsdown0.23.1코어의 bundledVersions.tsdown에 기록
Vitest5.0.5공통 catalog의 고정값, CLI의 의존성
Oxlint / Oxfmt=1.89.0 / =0.74.0공통 catalog에서 정확한 버전을 요구
oxlint-tsgolint=7.0.2004타입 정보를 이용하는 검사 경로
TypeScript~7.0.2개발 의존성의 허용 범위, 번들 버전과 구분
pnpm12.11.2루트 packageManager 값

모든 도구가 같은 방식으로 들어오는 것은 아닙니다. packages/cli/toolchain.config.json은 Vite와 tsdown을 bundled, Vitest·Oxlint·Oxfmt를 dependency, Vite Task를 compiled로 구분합니다. README의 “단일 의존성”은 사용자의 진입점이 단순하다는 뜻이며, 내부 npm 의존성이 하나라는 뜻은 아닙니다.

// packages/cli/package.json의 핵심 부분
{
  "version": "1.4.0",
  "dependencies": {
    "vite": "workspace:@voidzero-dev/vite-plus-core@*",
    "vitest": "catalog:",
    "oxlint": "catalog:",
    "oxfmt": "catalog:"
  }
}

vite라는 의존성 이름이 내부 코어 패키지를 가리킵니다. packages/cli/BUNDLING.md는 배포 시 이 관계를 정확한 npm 별칭으로 바꾸고, CLI와 플러그인이 같은 코어를 보도록 유지한다고 설명합니다. 이름을 단순히 바꾸는 것보다 같은 모듈을 공유하도록 보장하는 일이 중요합니다.

프론트엔드: 제품의 필수 GUI와 문서 사이트 구분

주 사용 인터페이스는 터미널입니다. 문서 사이트는 docs/의 VitePress와 Vue로 구성되며, 웹 앱을 만드는 사용자는 React·Vue 등 자신의 프레임워크를 따로 선택합니다. 문서가 Vue로 작성됐다고 Vite+가 Vue 전용 빌드 도구가 되는 것은 아닙니다.

문서 사이트 기술docs/package.json 선언용도
VitePress2.0.0-alpha.20문서 생성과 개발 서버
Vue^3.5.27문서 테마와 화면 구성 요소
Tailwind CSS^4.1.18문서 화면 스타일링 도구
Mermaid / Reka UI^12.0.0 / ^2.7.0도식과 문서 화면 상호작용

docs/는 별도의 pnpm-lock.yaml을 둡니다. 루트 catalog에 적힌 Vue ^3.5.35와 VitePress ^2.0.0-alpha.15를 문서 패키지의 직접 선언과 혼동하지 맙시다. 같은 이름의 의존성도 어느 패키지의 선언인지 확인해야 합니다.

인프라·빌드: 도구 자체를 배포하는 공정

학습 포인트

이 저장소는 Python 서버나 Go API를 중심으로 돌아가지 않습니다. Docker에 Python이 있다는 사실도 Python 제품이라는 뜻이 아닙니다. 네이티브 npm 의존성을 빌드할 도구와 제품의 주 실행 언어를 구분해서 읽는 연습을 할 수 있습니다.

4아키텍처 심화 분석

프로세스, 언어, 설정, 실패 처리의 경계를 따라가는 네 가지 사례

아래 구조도는 vp dev처럼 JavaScript 도구에 위임하는 대표 경로입니다. 모든 명령이 이 전체 경로를 통과하지는 않습니다. 환경·패키지 관리자 명령 중에는 글로벌 Rust CLI가 직접 처리하는 경로도 있습니다.

개발자: vp dev / vp check / vp run … | v +-------------------------------------------------------+ | 프로세스 A: 글로벌 Rust 실행 파일 vp | | main.rs / cli.rs | | 인자 해석, 환경 준비, 프로젝트 CLI 탐색 | +-------------------------------------------------------+ | | | Rust에서 직접 처리 | JS 도구에 위임 v v vp env / 패키지 관리 프로젝트 vite-plus 우선 탐색 없으면 글로벌 배포본 선택 | v +-------------------------------------------------------+ | 프로세스 B: Node.js | | dist/bin.js <-- 소스: packages/cli/src/bin.ts | | | | | +-- create / migrate / config 등의 JS 명령 | | | | | +-- run(options): 같은 프로세스의 NAPI 네이티브 모듈 | | | | | +-- JS resolver 콜백으로 실행 파일 경로 요청 | | +-- Rust의 명령 실행·검사·Vite Task 연결 | +-------------------------------------------------------+ | v 필요한 도구의 자식 프로세스 또는 작업 실행 Vite / Vitest / Oxlint / Oxfmt / 사용자 명령

NAPI 호출은 새 서버에 보내는 요청이 아닙니다. Node.js 프로세스 안에 불러온 Rust 네이티브 모듈과의 호출입니다. 반대로 도구를 자식 프로세스로 실행하는 부분은 운영체제의 프로세스 경계를 넘습니다. 두 종류의 경계를 구별해야 흐름이 보입니다.

설계 1. 로컬 우선 위임과 실행기 교체

crates/vp_global_cli/src/js_executor.rs의 JsExecutor::resolve_local_vite_plus_package_dir()는 Oxc Resolver로 vite-plus/package.json을 찾습니다. 이어 resolve_local_vite_plus()가 dist/bin.js의 존재를 검사합니다. prepare_js_entry()는 그 경로가 있으면 선택하고, 없으면 글로벌 설치의 bin.js를 택합니다.

이 구조 덕분에 한 머신의 글로벌 입구를 유지하면서 프로젝트마다 다른 CLI 버전을 사용할 수 있습니다. 단, 모든 명령이 무조건 로컬 우선인 것은 아닙니다. 같은 파일의 delegate_migrate()는 로컬 버전이 글로벌보다 오래된 업그레이드 상황에서 글로벌 마이그레이션으로 올려 보내는 별도 정책을 가집니다.

// packages/cli/src/bin.ts: run()에 전달하는 항목 일부
const exitCode = await run({
  nodeExecPath: process.execPath,
  lint,
  pack,
  fmt,
  vite,
  test,
  doc,
  // 이 밖에 설정 resolver, 인자, 패키지 경로 등을 전달
});

위 코드는 핵심 항목만 발췌한 것입니다. packages/cli/binding/src/lib.rs의 CliOptions는 이 함수들을 ThreadsafeFunction으로 받습니다. create_resolver()는 JS 콜백이 반환한 Promise를 기다려 bin_path와 envs를 Rust 실행 계층의 결과로 바꿉니다.

설계 패턴
어댑터와 의존성 주입
어댑터는 서로 다른 규격을 맞추는 변환기입니다. 여기서는 JS 함수와 Rust 함수의 호출·결과 모양을 맞춥니다. 의존성 주입은 실행기가 구체적인 도구 탐색 방법을 직접 만들지 않고 바깥에서 받는 방식입니다. Rust 실행 계층은 전달받은 resolver를 통해 도구 경로를 얻습니다.

초보자가 볼 지점은 Rust가 npm 패키지의 모든 배치 규칙을 혼자 구현하지 않는다는 것입니다. TypeScript 쪽의 packages/cli/src/resolve-test.ts나 packages/cli/src/resolve-vite.ts가 경로를 알아내고, Rust는 정해진 결과 형태를 이용합니다. 언어를 나눌 때 책임도 함께 나누는 사례입니다.

설계 2. 정적 분석 우선, 알 수 없을 때만 실행

crates/vp_static_config/src/lib.rs의 resolve_static_config()는 Oxc Parser로 설정을 읽습니다. 설정 파일 자체를 실행하지 않고 AST에서 값을 추출합니다. packages/cli/binding/src/cli/handler.rs의 VitePlusConfigLoader::load_user_config_file()은 그 결과의 run 필드에 따라 세 갈래로 움직입니다.

조회 결과뜻호출자의 선택
Json(value)실행 없이 읽을 수 있는 값바로 UserRunConfig로 역직렬화
NonStatic존재하지만 동적이거나, 있을 수 있어 확정 불가NAPI를 통해 JS 설정 로딩으로 대체
None해당 필드가 없다고 확정이 경로에서 JS 설정 평가 없이 반환
// 학습용 비교: 첫 번째 run은 정적으로 읽을 수 있음
export default {
  run: { tasks: { hello: { command: 'node hello.js' } } },
};

// 변수의 실제 값까지 따라 계산하지 않으므로 대체 경로 필요
const tasks = makeTasks();
export default { run: { tasks } };

이 예시는 서로 다른 두 설정을 비교한 것이므로 한 파일에 함께 넣지는 않습니다. 실제 구현의 FieldMap은 내부에 open이라는 상태를 둡니다. { run: oldRun, ...extra }에서는 뒤의 전개가 run을 바꿀 수 있습니다. extract_object_fields()는 전개를 만나면 앞서 모은 필드를 지우고, 뒤에서 명시한 값만 확실한 정보로 남깁니다.

{ run: oldRun, ...extra } -> run은 확정할 수 없음 { ...extra, run: { tasks: {} } } -> 뒤에서 정한 run은 읽을 수 있음 { plugins: [] } -> run이 없다고 확정 구문 분석 실패 -> run도 알 수 없음, JS 경로 필요

정적 분석기의 중요한 능력은 모든 값을 계산하는 것이 아니라, 확실히 아는 값과 아직 모르는 값을 구분하는 것입니다.

문서와 코드의 차이
반환형은 현재 구현을 기준으로 읽기

crates/vp_static_config/README.md는 반환형을 Option<FxHashMap<…>>으로 설명합니다. 커밋 d3d9f57의 실제 함수는 FieldMap을 반환합니다. 설정 파일 없음은 닫힌 빈 맵, 분석 불가는 열린 빈 맵입니다. README의 설명만으로 “파일 없음”과 “해석 불가”를 같은 상태로 구현하면 현재 동작을 놓칩니다.

대체 경로인 packages/cli/src/resolve-vite-config.ts의 resolveUniversalViteConfig()는 설정을 로딩하고 lint, fmt, check, run, staged 등을 JSON으로 돌려줍니다. 정적 추출이 있다고 해서 모든 명령에서 JavaScript 실행이 없어지는 것은 아닙니다.

설계 3. 여러 검사를 순서 있는 하나의 작업으로 만들기

packages/cli/binding/src/check/mod.rs의 execute_check()는 검사기를 단순히 동시에 켜지 않습니다. 설정과 옵션을 읽고, 형식 검사·수정 뒤 린트 단계를 실행하며, 수정 모드에서는 마지막 포맷을 다시 적용할 수 있습니다.

일반 vp check 설정 해석 -> fmt --check -> 성공하면 lint [타입 검사 포함 가능] | +-- 실패하면 종료 코드 반환 vp check --fix [fmt와 lint가 모두 켜진 경우] fmt 수정 -> lint 수정 -> 성공하면 fmt 재적용 -> 결과 요약 | +-- 실패하면 이미 수행한 수정 사실을 알리고 종료

왜 마지막 포맷이 필요할까요? 린터의 자동 수정이 중괄호를 추가하면 처음 포맷했을 때와 코드 모양이 달라질 수 있기 때문입니다. “수정 단계들의 순서를 어떻게 정해야 최종 결과가 안정적인가”라는 작은 작업 흐름 설계입니다.

// 학습용 최소 설정: 두 옵션을 함께 켬
import { defineConfig } from 'vite-plus';

export default defineConfig({
  lint: {
    options: {
      typeAware: true,
      typeCheck: true,
    },
  },
});

packages/cli/binding/src/check/analysis.rs의 lint_config_type_check_enabled()는 두 옵션의 논리곱을 사용합니다. --no-lint는 린트 규칙 단계를 끄더라도 활성화된 타입 검사는 남길 수 있습니다. --no-fmt --no-lint를 주고 타입 검사도 꺼져 있으면 execute_check()는 검사할 것이 없다는 메시지를 출력하고 종료 상태 1을 반환합니다.

다른 학습 포인트도 있습니다. 결과 분석기가 도구의 요약 출력을 읽으므로, 이 함수는 린트 호출에 --format=default를 넣습니다. GitHub Actions 환경에서 출력 형식이 달라져 파서가 오판하는 일을 막기 위해서입니다. 외부 도구의 출력도 통합 계층에서는 하나의 계약이 됩니다.

설계 4. 잠금 충돌을 재시도하되, 이미 끝난 일은 반복하지 않기

분석 기준 커밋의 주제는 Git 설정 잠금입니다. 연결된 여러 worktree가 같은 Git 설정을 쓰면, 동시에 실행한 vp config가 config.lock을 두고 충돌할 수 있습니다. 핵심 구현은 Rust가 아닌 packages/cli/src/config/hooks.ts의 writeGitConfig()에 있습니다.

// 실제 상수와 제어 흐름을 줄인 의사 코드
const delays = [50, 100, 200, 400, 800];
let result = writeOnce();
for (const delay of delays) {
  if (!isLockFailure(result.status)) break;
  wait(delay);
  if (isApplied()) return success;
  result = writeOnce();
}
return result;

실제 상수 GIT_CONFIG_LOCK_RETRY_DELAYS_MS는 [50, 100, 200, 400, 800]입니다. 잠금 실패 상태 255와 0xffffffff일 때만 기다립니다. 최초 시도에 더해 최대 다섯 번 재시도하고, 명시한 대기 합계는 쓰기 한 번당 최대 1.55초입니다. Git 실행 시간까지 포함한 전체 명령 시간의 상한은 아닙니다.

gitConfigSet()은 값이 이미 같으면 쓰지 않습니다. 기다린 뒤에도 isApplied()로 다시 읽어, 다른 프로세스가 원하는 값을 저장했다면 성공으로 끝냅니다. Git 자체 대기와 중복되지 않도록 core.configLockTimeout=0도 명령 인자로 지정합니다.

동시성 비유

공용 게시판에 같은 공지를 붙이려는 두 사람을 생각해 봅시다. 한 사람이 자리를 쓰고 있으면 잠깐 기다리고, 다시 봤을 때 공지가 이미 붙어 있다면 더 붙일 필요가 없습니다. “기다림”과 “이미 완료됐는지 확인”을 함께 넣는 것이 핵심입니다.

이를 멱등성과 지수형 재시도의 조합으로 읽을 수 있습니다. 다만 sleepSync()는 Atomics.wait()로 현재 스레드를 멈춥니다. 짧게 끝나는 설정 CLI에서 택한 방식이며, 장시간 동작하는 웹 서버의 요청 처리에 그대로 옮기는 방식은 별도 검토가 필요합니다.

packages/cli/src/config/__tests__/hooks.spec.ts에는 값이 같을 때 잠금을 새로 잡지 않는 경우, 잠금이 풀리기를 기다리는 경우, 다른 실행이 같은 값을 쓴 경우를 다루는 테스트가 있습니다. 기능 설명과 테스트 시나리오를 나란히 읽으면 재시도의 목적이 더 선명해집니다.

5디렉토리 구조 해부

사용자에게 보이는 패키지와 내부 실행 크레이트를 따로 찾기

아래는 주요 경로를 추린 지도입니다. 파일 경로는 모두 저장소 루트 기준입니다. 디렉토리 이름만 외우기보다, “이 폴더가 어느 실행 경계를 책임지는가”를 함께 적어 보세요.

vite-plus/ |-- Cargo.toml / Cargo.lock Rust 의존성·워크스페이스 |-- package.json JS 개발 명령·pnpm 버전 |-- pnpm-workspace.yaml 패키지 목록·공통 catalog |-- rust-toolchain.toml 개발용 nightly 선택 |-- vite.config.ts 저장소 자체의 검사 설정 |-- justfile 초기화·빌드·검사 작업 |-- crates/ | |-- vp_global_cli/ 글로벌 vp 실행 파일 | | `-- src/js_executor.rs 로컬 CLI 선택·Node.js 위임 | |-- vp_js_runtime/ Node.js 탐색·다운로드·검증 | |-- vp_pm_cli/ 패키지 관리자 명령 연결 | |-- vp_static_config/ AST 기반 설정 추출 | |-- vp_shared/ 디렉토리·환경·출력 공통 규칙 | |-- vp_command/ 프로세스 실행 공통 처리 | |-- vp_setup/ 설치·환경 설정 공통 기능 | |-- vp_toolchain/ 도구 버전·관계 정보 | |-- vp_cli_snapshots/ 터미널 통합 테스트 | `-- vp_trampoline/ Windows 실행 보조 파일 |-- packages/ | |-- cli/ | | |-- src/bin.ts TypeScript 명령 분배 | | |-- src/resolve-*.ts 도구·설정 탐색 | | |-- src/define-config.ts 통합 설정 타입·연결 | | |-- src/config/hooks.ts Git 훅·설정 쓰기 | | |-- src/migration/ 기존 프로젝트 이전 | | |-- binding/src/ Node.js용 Rust 네이티브 모듈 | | `-- toolchain.config.json 도구의 구성 관계 | |-- core/ Vite·Rolldown·tsdown 묶음 | |-- prompts/ 터미널 질문·선택 UI | |-- tools/ 상류 동기화·개발 보조 도구 | `-- create-vite-plus/ 프로젝트 생성 진입점 |-- docs/ VitePress 문서·가이드 |-- ecosystem-ci/ 외부 프로젝트 호환성 검사 |-- bench/ 성능 비교를 위한 코드 |-- rfcs/ 설계 제안과 논의 문서 |-- docker/ 개발·CI 이미지 `-- .github/ CI·릴리스·재사용 액션
궁금한 질문첫 번째로 열 파일이어 읽을 곳
명령을 누가 받나?crates/vp_global_cli/src/cli.rspackages/cli/src/bin.ts
도구 경로는 누가 찾나?packages/cli/src/resolve-vite.tspackages/cli/src/resolve-core.ts, packages/cli/src/resolve-test.ts
Rust 연결부는?packages/cli/binding/src/lib.rspackages/cli/binding/src/cli/mod.rs
타입 검사 조건은?packages/cli/binding/src/check/analysis.rspackages/cli/binding/src/check/mod.rs, docs/guide/check.md
작업 캐시 설정은?docs/config/run.mdpackages/cli/binding/src/cli/handler.rs, 외부 Vite Task
도구를 어떻게 묶나?packages/core/build.tspackages/cli/build.ts, 각 패키지의 빌드 설명 문서

트리에 없는 중요한 엔진

Vite와 Rolldown의 본체 소스가 이 저장소에 모두 직접 들어 있는 구조는 아닙니다. .github/actions/clone/action.yml은 별도 저장소의 고정 커밋을 각각 vite/, rolldown/, oxc-node/로 가져옵니다. Vite Task 크레이트도 Cargo.toml의 Git 의존성입니다. 이 경계를 알아야 통합 코드의 줄 수를 엔진 전체 규모로 오해하지 않습니다.

packages/core/build.ts에는 buildVite(), bundleRolldown(), bundleTsdown(), mergePackageJson()이 있습니다. packages/core/build-support/rewrite-imports.ts의 RewriteImportsPlugin은 import 경로를 코어 패키지 쪽으로 바꿉니다. 빌드 스크립트도 제품의 모듈 구성을 결정하는 소스입니다.

실제로 센 언어별 규모

커밋 d3d9f57에서 Git이 추적하는 4,949개 경로를 대상으로 확장자별 물리적 줄 수를 합산했습니다. 빈 줄·주석·테스트·예제·생성 파일도 포함합니다. TypeScript는 .ts/.tsx/.mts/.cts, JavaScript는 .js/.jsx/.mjs/.cjs를 합쳤습니다.

언어·형식파일 수줄 수해석할 때 주의할 점
Rust255103,698실행 코드와 Rust 테스트를 함께 포함
TypeScript73094,836CLI·빌드·테스트·타입 선언·픽스처 포함
JavaScript3559,664스크립트와 테스트 입력 파일 등 포함
Vue232,065주로 문서 사이트의 화면 구성
Shell / PowerShell15 / 93,242 / 2,420설치·검증·플랫폼별 보조 스크립트
YAML14831,016CI와 잠금 파일을 포함한 설정 형식

이 표는 선택한 확장자의 집계이며 모든 형식을 합친 전체 줄 수 표는 아닙니다. 숫자는 코드 복잡도나 품질의 점수가 아닙니다. 특히 YAML에는 큰 잠금 파일이 들어가고, 테스트 입력도 파일 수를 늘립니다. 별도 의존성인 Vite·Rolldown·Vite Task의 전체 소스 규모는 포함하지 않습니다.

6학습 포인트 (기술별)

문법을 하나 배우면, 이 코드에서 어떤 설계 질문을 던질 수 있을까

처음부터 전체 저장소를 읽으려 하지 않아도 됩니다. 한 기술마다 “읽을 파일 하나, 설명할 동작 하나, 바꿔 생각할 입력 하나”를 정하면 학습 범위가 구체적이 됩니다.

기술배울 내용과 근거작은 실습 아이디어
Rust의 enumFieldValue로 정상 값과 정적 분석 불가를 표현Json, NonStatic, 필드 부재의 예를 각각 작성
Rust의 Resultexecute_check()가 명령 실행 오류와 검사 종료 상태를 나누어 반환실행 파일 없음과 코드 검사 실패를 다른 칸에 분류
비동기 연결create_resolver()가 JS 호출과 반환 Promise를 기다림콜백 호출 성공·Promise 거절을 별도 오류 경로로 그리기
TypeScript 타입 확장packages/cli/src/define-config.ts의 declare module 'vite'설정 키가 편집기에 어떻게 보일지 타입 정의로 추적
파서와 ASTparse_js_ts_config(), extract_object_fields()전개 위치를 바꾸고 확정 가능한 키를 손으로 계산
Node.js 모듈 탐색packages/cli/src/resolve-test.ts와 packages/cli/src/utils/constants.ts전역 CLI 선택과 Vitest 복사본 선택을 다른 단계로 구분
운영체제 프로세스crates/vp_global_cli/src/js_executor.rs의 명령·인자·현재 디렉토리 전달공백이 있는 경로가 왜 단순 문자열 조합에 취약한지 설명
동시성packages/cli/src/config/hooks.ts의 재시도와 완료 여부 재조회두 실행이 같은 설정을 쓰는 시간표 작성
CI와 테스트.github/workflows/ci.yml, vp_cli_snapshots로직 검증과 터미널 출력 검증의 차이를 표로 정리

문법을 이해했다는 기준은 코드를 그대로 따라 쓰는 것이 아니라, 입력이 달라졌을 때 어느 분기로 가는지 설명할 수 있는가입니다.

Rust: “에러가 났다”를 두 층으로 나누기

execute_check()의 반환형은 Result<ExitStatus, Error>입니다. 바깥 Result는 실행 과정의 오류를 표현하고, 안쪽 종료 상태는 실행한 검사가 성공했는지를 표현합니다. 코드에 린트 문제가 있는 것과 프로세스를 시작하지 못한 것은 사용자에게 다른 안내가 필요합니다.

// 설명용 모델: 실제 함수를 복제한 코드는 아님
enum CheckOutcome {
    Passed,
    DiagnosticsFound,
    CouldNotStart,
}

// 세 결과를 모두 단순한 false로 바꾸면 복구 방법도 사라짐

TypeScript: 타입은 설정을 설명하는 문서이기도 하다

packages/cli/src/define-config.ts는 Vite의 UserConfig에 필드를 추가합니다. 이를 모듈 보강이라고 부릅니다. check.fmt와 check.lint의 주석을 읽으면, 이 설정은 통합 검사에만 적용되고 개별 vp fmt와 vp lint에는 적용되지 않는다는 계약도 알 수 있습니다.

타입이 있다고 값이 실행 시점에도 자동 검증되는 것은 아닙니다. Rust 쪽에서는 JSON을 UserRunConfig로 역직렬화하는 단계가 따로 있습니다. 편집기의 안내, 데이터 전달, 실행 시점 검증을 세 층으로 나누어 보세요.

캐시: “재실행하지 않아도 같은가”를 묻기

docs/config/run.md의 기본값은 run.cache = { scripts: false, tasks: true }입니다. 즉 package.json 스크립트는 기본적으로 캐시하지 않고, vite.config.ts에 정의한 작업은 기본적으로 캐시합니다. 모든 vp run이 자동으로 같은 캐시 정책을 가지지는 않습니다.

// 학습용 작업 설정
import { defineConfig } from 'vite-plus';

export default defineConfig({
  run: {
    tasks: {
      build: {
        command: 'vp build',
        cache: { env: ['NODE_ENV'] },
      },
    },
  },
});

NODE_ENV가 결과를 바꾸는 입력이라면 캐시 판단에도 영향을 주어야 합니다. 이 예시는 설정 계약을 배우기 위한 것입니다. 파일·환경 자동 추적과 스케줄러의 내부 구현은 별도 Vite Task 저장소를 더 읽어야 설명할 수 있습니다.

학습 습관
사실, 추론, 실험 결과를 다른 문장으로 쓰기

“기본값은 scripts: false다”는 문서의 사실입니다. “부작용 있는 스크립트를 무심코 캐시하지 않도록 돕는다”는 설계 해석입니다. “두 번째 실행이 몇 초 빨랐다”는 측정 결과입니다. 이 세 문장을 섞지 않으면 기술 설명의 신뢰도가 올라갑니다.

7하드웨어 / 시스템 요구사항

배포된 도구를 사용하는 환경과 도구 자체를 개발하는 환경은 다르다

Vite+로 작은 웹 앱을 개발하는 것과 Vite+ 자체의 Rust·네이티브 모듈·상류 도구까지 빌드하는 것은 부담이 다릅니다. 요구사항을 판단할 때는 완성된 도구를 쓰는 경우와 그 도구를 만드는 경우를 먼저 나누어야 합니다.

구분확인된 조건근거
운영체제·CPULinux·macOS·Windows, x64·arm64 계열crates/vp_js_runtime/src/platform.rs의 Os, Arch
로컬 CLI의 Node.js^22.18.0 || ^24.11.0 || >=26.0.0packages/cli/package.json의 engines.node
코어 패키지의 Node.js^20.19.0 || ^22.18.0 || >=24.11.0packages/core/package.json, CLI 조건과 구분
저장소 개발용 Node.js22.23.4루트 .node-version
저장소 개발용 pnpm12.11.2루트 package.json
소스 빌드 도구Rust/Cargo, Just, CMake, cargo-binstall 등CONTRIBUTING.md, justfile
특수 가속기핵심 CLI 경로에 전용 GPU 요구 선언 없음실행 코어·패키지 의존성의 성격 기준
RAM·디스크 최소값고정된 공식 최솟값은 위 설정·기여 가이드에 명시되지 않음프로젝트 크기·빌드 범위에 따라 달라짐

글로벌 vp는 사전 설치된 Node.js 없이 시작할 수 있는 Rust 실행 파일입니다. 그러나 Vite·Vitest 같은 JavaScript 도구를 쓸 때는 실제 Node.js가 필요하며, 관리 기능이 적절한 런타임을 준비합니다. “글로벌 바이너리가 독립적”이라는 말과 “Node.js를 전혀 쓰지 않음”은 다릅니다.

바이너리의 플랫폼과 런타임의 플랫폼을 함께 확인

packages/cli/package.json의 napi.targets는 8개 대상을 명시합니다. macOS x64·arm64, Windows x64·arm64, Linux x64·arm64의 GNU·musl 조합입니다. docs/guide/index.md는 Linux x64 musl을 실험적 대상으로 분류하며, Alpine에서는 관리형 Node.js 실행을 위해 libstdc++가 필요하다고 설명합니다.

운영체제 이름이 같아도 C 표준 라이브러리가 다르면 같은 네이티브 파일을 사용할 수 없습니다. 특히 Linux의 GNU와 musl 대상이 나뉘는 이유를 이해하면, npm의 선택적 네이티브 패키지가 왜 필요한지도 배울 수 있습니다.

시스템 용어
타깃 트리플과 네이티브 모듈
x86_64-unknown-linux-gnu 같은 문자열은 CPU·운영체제·실행 환경을 묶어 표시합니다. 네이티브 모듈은 기계어로 빌드되므로 이 조합을 맞춰야 합니다. JavaScript 파일처럼 모든 환경에 똑같이 복사해서 실행하는 파일과 다릅니다.

학습 환경의 규모를 잡는 보수적인 기준

학습 범위계획용 여유 예시먼저 확인할 것
소스·문서 읽기기존 개발용 컴퓨터와 편집기로 시작추가 엔진 빌드나 브라우저 설치는 필요 없음
작은 예제 앱RAM 8GB 안팎, 의존성을 둘 수 GB의 여유앱 크기·브라우저·병렬 실행 수에 따라 조정
도구 자체 전체 빌드RAM 16~32GB, 디스크 수십 GB 여유를 계획Cargo 산출물·상류 소스·npm 캐시·브라우저까지 증가

위 RAM·저장공간 숫자는 공식 최소 사양이나 실측 결과가 아닌 학습 계획용 추정입니다. 새 장비를 고르는 기준으로 단정하지 말고, 작은 범위에서 시작해 실제 사용량을 보며 확장하세요. 병목은 GPU보다 메모리·디스크·병렬 컴파일에서 생길 가능성을 먼저 살펴보는 편이 합리적입니다.

컨테이너도 역할을 구분해서 읽기

docker/Dockerfile은 개발·CI·빌드 단계용으로 여러 도구를 포함합니다. 기본 사용자는 vp이고, 추가 개발 패키지를 설치할 수 있도록 암호 없는 sudo 설정도 둡니다. docs/guide/docker.md는 제품 배포에서는 다단계 빌드로 결과물과 필요한 Node.js만 작은 실행 이미지에 옮기는 방식을 안내합니다.

초보자의 시작점

처음 공부할 때 전체 네이티브 빌드를 목표로 삼을 필요는 없습니다. 설정과 명령 흐름을 종이에 추적하는 것만으로도 어댑터, 정적 분석, 종료 코드, 재시도라는 핵심 주제를 익힐 수 있습니다.

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

설명할 수 있는 작은 결과물 다섯 개 만들기

과제 1·2·4는 읽기와 손으로 추적하기만으로 진행할 수 있습니다. 과제 3의 실행은 Vite+가 준비된 별도 예제 프로젝트에서 선택적으로 진행합니다. 과제 5도 우선 설계와 예측 결과를 작성하고, 구현은 독립된 연습 파일에서 확장하면 됩니다.

각 실습은 실행했다는 기록보다, 예상한 결과와 그 근거가 되는 코드 위치를 함께 남기는 것을 목표로 합니다.

과제 1 · 입문

같은 이름의 명령 두 개를 구분하는 표 만들기 약 30분

목표: 내장 명령과 사용자 스크립트의 차이를 이해합니다. 아래 가상 프로젝트를 보고 각 명령이 무엇을 실행할지 적으세요.

{
  "scripts": {
    "dev": "custom-dev-server",
    "test": "jest",
    "build": "node compile.js"
  }
}
  1. vp dev, vp run dev, vp test, vpr test, vp run build의 실행 대상을 적습니다.
  2. docs/guide/run.md와 packages/cli/src/bin.ts에서 각각 근거 한 군데를 찾습니다.
  3. “test 스크립트를 바꿨는데 왜 vp test가 안 바뀌나?”에 세 문장으로 답합니다.

완료 기준: vp test는 Vitest, vpr test는 Jest 스크립트라고 구분하고, 스크립트가 내장 명령을 덮어쓰지 않는 이유를 설명합니다. 가상 명령은 실제 설치 없이 분류하면 됩니다.

과제 2 · 입문

설정의 “확실함”을 판별하기 약 45분

목표: 정적 분석이 무엇을 확정할 수 있는지 예측합니다. 아래 세 줄은 서로 다른 설정 파일의 예시입니다.

// A
export default { plugins: [] };
// B
export default { run: { tasks: {} } };
// C
export default { run: { tasks: {} }, ...extra };
  1. 각 경우의 FieldMap::get("run")을 None, Json, NonStatic 중에서 고릅니다.
  2. C의 전개를 맨 앞으로 옮기면 결과가 바뀌는 이유를 적습니다.
  3. crates/vp_static_config/src/lib.rs의 extract_object_fields()에서 대응하는 분기를 표시합니다.

완료 기준: A는 None, B는 Json, C는 NonStatic으로 설명합니다. C에서 전개 뒤에 run을 명시하면 그 값은 확정할 수 있다는 점도 연결합니다.

과제 3 · 중급

통합 검사의 네 가지 실패를 구분하기 약 1~2시간

목표: 포맷 문제, 린트 문제, 타입 문제, 실행 시작 실패를 구분합니다. Vite+와 TypeScript가 준비된 별도 예제에 4절의 typeAware/typeCheck 설정을 적용합니다.

// 타입 오류를 포함하는 학습용 입력
export const total: number = '세 개';

// 비교할 명령
vp check
vp check --no-fmt
vp check --no-fmt --no-lint
  1. 입력의 문자열을 숫자 3으로 바꿨을 때와 바꾸기 전을 비교합니다.
  2. 두 타입 옵션을 켠 경우와 끈 경우의 예상 검사 범위를 표로 적습니다.
  3. 실행한다면 종료 코드와 출력에서 검사 단계가 어디까지 갔는지 기록합니다.
  4. execute_check()와 lint_config_type_check_enabled()로 설명을 검산합니다.

완료 기준: 두 옵션이 켜져 있으면 --no-lint여도 타입 검사가 남을 수 있고, 둘 다 꺼진 채 검사 단계까지 모두 끄면 실행할 검사가 없다는 메시지와 함께 종료 코드 1로 끝난다고 설명합니다. 포맷 실패가 있으면 뒤 단계에 도달하지 못한다는 조건도 적습니다.

과제 4 · 중급

캐시와 작업 의존성을 설계하기 약 1시간

목표: 순서가 필요한 작업과 결과 재사용이 가능한 작업을 나눕니다. docs/guide/run.md와 docs/config/run.md를 읽고 가상의 문서 사이트 작업을 설계하세요.

문서 메타데이터 생성 -> 사이트 빌드 -> 배포
                              ^
                              |
                         링크 검사 완료
  1. 작업 이름과 command, dependsOn을 정합니다.
  2. 배포처럼 외부 상태를 바꾸는 작업에는 cache: false를 명시합니다.
  3. 언어 선택 환경변수 DOC_LANG이 결과에 영향을 준다면 캐시 입력에 어떻게 넣을지 적습니다.
  4. 같은 작업 이름을 package.json과 run.tasks에 중복 정의하지 않았는지 확인합니다.

완료 기준: 순환 없는 의존성 그림과 설정 초안을 제출합니다. command: ['vp', 'build']는 인자 배열이 아니라 두 명령을 순서대로 실행하는 설정이므로, command: 'vp build'와 다름도 설명합니다.

과제 5 · 고급

멱등적인 재시도를 상태 기계로 모델링하기 약 2~3시간

목표: Git 설정을 실제로 수정하지 않고 재시도 정책을 검증하는 모델을 설계합니다. packages/cli/src/config/hooks.ts의 writeGitConfig()와 관련 테스트 세 가지를 읽고, 쓰기·대기·재조회를 가짜 함수로 치환한 의사 코드를 작성하세요.

가짜 입력 상황예상 동작
최초 쓰기 성공대기 없이 종료
잠금 실패 후 같은 값 확인한 번 기다린 뒤 추가 쓰기 없이 성공
다른 오류 코드잠금 재시도를 적용하지 않음
잠금 실패가 계속됨대기 다섯 번, 쓰기 최대 여섯 번 후 실패 반환

완료 기준: 각 상황의 쓰기 호출 수, 재조회 횟수, 대기 합계를 적습니다. 대기 함수가 실제 시간을 흘려보내지 않아도 검증할 수 있도록 설계하고, 실제 Git 연동 테스트가 추가로 확인해야 할 부분을 한 문단으로 정리합니다.

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

6주 동안 “쓰는 법”에서 “통합 도구를 설계하는 법”으로

주당 3~5시간의 학습을 가정한 예시입니다. Rust가 처음이라면 3주 차를 두 주로 늘려도 좋습니다. 모든 구성 요소를 설치하는 일보다 매주 하나의 설명 가능한 결과물을 남기는 데 집중합니다.

주차공부할 내용읽을 소스남길 결과물
1주 차CLI, package.json, 내장 명령과 스크립트README.md, docs/guide/run.md, packages/cli/src/bin.ts명령 10개의 실행 대상 표
2주 차TypeScript 설정 타입, 모듈 탐색, 별칭packages/cli/src/define-config.ts, packages/cli/src/resolve-test.ts, packages/cli/BUNDLING.mdCLI·코어·Vitest의 연결 그림
3주 차Rust의 enum, Option, Result, matchcrates/vp_static_config/src/lib.rs정적 분석 결과를 예측하는 입력 8개
4주 차NAPI, 콜백, Promise, 프로세스 실행packages/cli/binding/src/lib.rs, crates/vp_global_cli/src/js_executor.rs언어 경계·프로세스 경계를 따로 그린 도식
5주 차검사 순서, 멱등성, 잠금·재시도packages/cli/binding/src/check/mod.rs, packages/cli/src/config/hooks.ts실패 유형별 복구 안내와 재시도 시간표
6주 차작업 그래프, 캐시, CI, 네이티브 배포docs/config/run.md, .github/workflows/ci.yml, Vite Task 저장소작은 프로젝트의 도구 통합 설계서

마지막 목표는 Vite+를 통째로 재구현하는 것이 아니라, 왜 어떤 책임은 Rust에 있고 어떤 책임은 TypeScript에 있는지 자기 말로 설명하는 것입니다.

각 주차를 마칠 때 답할 질문

  1. 입력: 이 단계가 받는 값은 명령 인자, 설정, 파일 경로 중 무엇인가?
  2. 소유권: 실행할 도구와 버전을 최종적으로 고르는 쪽은 어디인가?
  3. 실패: 실패가 값으로 돌아오는가, 예외가 되는가, 종료 코드가 되는가?
  4. 재현성: 다음 실행이 같은 결과를 내기 위해 무엇을 고정해야 하는가?
읽는 순서의 비유

처음부터 엔진 내부를 분해하기보다 차량의 조작부, 배선, 제어기, 엔진 순서로 살펴보는 방식입니다. 입구와 연결을 이해한 뒤 Vite Task나 Rolldown의 내부로 들어가면, 그 알고리즘이 실제 제품에서 왜 필요한지도 함께 보입니다.

10핵심 키워드 사전

정의를 외우기보다 이 저장소의 사례에 붙여 보기
용어쉬운 설명이 저장소에서 만나는 곳
도구 모음개발·검사·테스트·빌드를 수행하는 여러 도구의 연결vp 명령 체계
런타임프로그램을 실제로 실행하는 환경관리 대상 Node.js
크레이트Rust에서 컴파일·의존성 관리의 단위가 되는 패키지vp_static_config, vp_global_cli
워크스페이스관련 패키지를 묶고 공통 설정을 나누는 범위Cargo와 pnpm의 루트 설정
네이티브 모듈운영체제·CPU에 맞춰 기계어로 빌드한 연결 부품Node.js가 불러오는 Rust 바인딩
NAPI-RSRust 코드를 Node.js의 네이티브 API와 연결하는 도구packages/cli/binding/src/lib.rs
resolver이름이나 설정을 받아 실제 사용할 경로나 값을 찾는 역할packages/cli/src/resolve-vite.ts, packages/cli/src/resolve-test.ts
콜백호출할 시점을 다른 코드에 맡겨 전달하는 함수Rust에 넘기는 JS 도구 탐색 함수
Promise아직 끝나지 않은 작업의 성공·실패 결과를 나타내는 객체JS resolver의 비동기 결과
AST코드의 문법 구조를 나무 모양 데이터로 바꾼 것Oxc Parser의 설정 분석
정적 분석코드를 실제 실행하지 않고 문법과 구조로 성질을 알아내기resolve_static_config()
대체 경로빠른 방법으로 처리하지 못할 때 사용하는 다음 방법동적 설정의 JS 로딩 경로
열린 맵보이는 키 외에 다른 키도 있을 수 있다고 취급하는 표전개 연산자를 처리하는 FieldMap
모듈 보강기존 모듈의 타입 정의에 필드를 추가하는 TypeScript 방식Vite UserConfig 확장
패키지 별칭의존성 이름과 실제 가져올 패키지 이름을 다르게 연결하기vite → Vite+ 코어
잠금 파일선택된 의존성 버전을 기록해 다시 같은 조합을 쓰도록 돕는 파일Cargo.lock, pnpm-lock.yaml
종료 코드프로세스가 끝나면서 남기는 성공·실패 신호검사 결과와 자식 프로세스 상태
프로세스운영체제가 독립된 실행 단위로 관리하는 프로그램글로벌 Rust CLI, Node.js, 도구 실행
멱등성같은 요청을 반복해도 최종 상태가 더 달라지지 않는 성질이미 같은 Git 설정이면 쓰지 않기
경합여러 실행이 같은 자원을 동시에 쓰려는 상황worktree 간 config.lock 충돌
지수형 재시도실패 후 기다리는 시간을 배수로 늘리며 다시 시도하기50 → 100 → 200 → 400 → 800ms
작업 그래프어떤 작업이 끝나야 다음 작업을 할지 연결한 그림run.tasks.*.dependsOn
캐시입력이 같을 때 이전 결과를 재사용하는 저장소·규칙Vite Task의 작업 결과 재사용
PTY프로그램이 실제 터미널처럼 느끼게 해주는 가상 터미널vp_cli_snapshots의 상호작용 테스트
shim / trampoline사용자 명령을 실제 실행 파일로 연결하는 작은 중간 프로그램Node.js·패키지 관리자 연결과 Windows 보조 실행기
타깃 트리플빌드 결과가 맞아야 할 CPU·운영체제·환경 조합8개 네이티브 배포 대상
다단계 빌드도구가 많은 빌드 단계와 작은 제품 실행 단계를 분리하기docs/guide/docker.md

영어 식별자를 정확히 기억하는 것도 중요하지만, 코드 리뷰에서는 동작을 문장으로 풀어 말해 보세요. “fallback을 쓴다”보다 “정적으로 값을 읽지 못하면 Node.js에서 설정을 로딩한다”가 이 설계를 더 잘 설명합니다.

11참고 링크

제품 사용법과 구현 근거를 다른 링크로 찾아가기

공식 저장소와 사용 문서

커밋에 고정한 핵심 구현

관련 기술을 더 깊게 공부할 때

현재 상태 (2026-10-23 확인)

항목상태기준
저장소voidzero-dev/vite-plus공식 GitHub
트렌딩 선정GitHub Trending Rust Weekly #102026-10-23 선정 기록
스타 / 포크6,458 / 2962026-10-23 GitHub 기준
주 언어RustGitHub 언어 분류
생성일2025-03-06GitHub 저장소 정보
최근 push2026-10-23GitHub 저장소 정보
최신 릴리스v1.4.0 · 2026-10-23기준일의 GitHub 릴리스 정보
패키지 버전CLI·코어·글로벌 CLI 모두 1.4.0각 package.json, crates/vp_global_cli/Cargo.toml
최근 커밋d3d9f57 · 2026-10-23공유 Git 설정 잠금을 첫 실행에서 기다리도록 수정 · PR #3199
추적 경로 수4,949git ls-files 기준
주요 코드 규모Rust 약 10.4만 줄 / TypeScript 약 9.5만 줄5절의 확장자별 물리적 줄 수 집계
네이티브 배포 대상8개 타깃packages/cli/package.json 선언
라이선스MIT루트 LICENSE, package.json, Cargo 워크스페이스
쓰기 전에 알아둘 한계
날짜, 의존성 경계, 실행 조건을 함께 기억하기

스타·포크·트렌딩·최신 릴리스는 2026-10-23의 스냅샷입니다. 구현 설명은 커밋 d3d9f57 기준이며, 실시간 공식 문서와 이후 버전은 달라질 수 있습니다. 특정 기능이 순위를 올렸다는 인과관계까지 뜻하지는 않습니다.

코드 규모에는 테스트·예제·생성 파일이 포함되며, 외부 Vite·Rolldown·Vite Task의 전체 소스는 빠져 있습니다. 정적 추출기 README의 반환형과 현재 구현이 다르므로, 동작을 판단할 때는 해당 커밋의 함수와 호출자를 함께 확인해야 합니다.

플랫폼 목록과 의존성 버전은 지원 선언·잠금 기록입니다. 모든 플러그인과 프로젝트의 설치 성공이나 성능을 보증하지 않습니다. 캐시는 입력과 환경에 따라 달라지고, 타입 검사는 설정에 따라 활성화됩니다. RAM·디스크 예시는 공식 최소값이 아닙니다.

실습의 짧은 코드와 의사 코드는 설명 범위를 줄인 예시입니다. 실제 도입 시에는 프로젝트의 기존 스크립트, 패키지 별칭, Node.js 버전, 작업 부작용을 확인하고 마이그레이션 결과를 검토하세요.