한 저장소(모노레포) 안에서 web(SvelteKit)과 3d(Babylon)을 분리하고, 변경된 쪽만 빌드해도 실행 결과에 반영되도록 만든 예제입니다.
/
web/ # SvelteKit (adapter-node)
3d/ # Vite + Babylon 번들(dist)
scripts/ # 3d → web 동기화 / 스모크 테스트
.github/
workflows/ci.yml
package.json # npm workspaces + 루트 스크립트
3d는 Vite lib 빌드로 dist/bundle.js(ESM) + chunk들을 생성합니다.3d/src/bundle.ts이며, initBabylon(canvas) 함수를 export 합니다.3d/vite.config.ts:build.lib.entry = 'src/bundle.ts'fileName = 'bundle.js'web/src/routes/+page.svelte에서 아래처럼 런타임 동적 import를 사용합니다./3d/bundle.js를 불러옴여기서 핵심이 복사(sync) 스크립트입니다.
scripts/sync-3d-to-web-static.mjs
3d/dist → web/static/3d로 복사scripts/sync-3d-to-web-build.mjs
3d/dist → web/build/client/3d로 복사web/svelte.config.js는 @sveltejs/adapter-node 사용web/build(node 서버)를 바로 실행할 수 있음npm install
npm run dev:3d는 web을 빌드 산출물(server)로 실행하면서 3D만 dev 코드로 붙이는 모드입니다.web/build가 있어야 합니다:npm run build:all
이 레포는 아래 목표를 만족하도록 구성되어 있습니다.
dev:all: web + 3d 둘 다 “개발중 코드”로 합쳐서 구동dev:3d: web은 “빌드된 환경”, 3d만 “개발중 코드”로 합쳐서 구동dev:web: 3d는 “빌드된 환경”, web만 “개발중 코드”로 합쳐서 구동start:all: web + 3d 둘 다 최신 빌드 산출물로 합쳐서 구동start:web: web만 최신 빌드, 3d는 가장 최근 빌드를 합쳐서 구동start:3d: 3d만 최신 빌드, web은 가장 최근 빌드를 합쳐서 구동http://localhost:5174/http://localhost:5173/ (web에서 3d dev 모듈을 불러오기 위해 CORS/고정 포트 사용)http://127.0.0.1:4173/주의:
dev:*또는start:*프로세스는 포트를 점유합니다. 다른 모드를 실행하기 전에 기존 실행을 종료해야 포트 충돌이 안 납니다.
dev:all)npm run dev:all
dev:3d)npm run dev:3d
dev:web)npm run dev:web
npm run build:all
npm run start:all
/에 접속하면 3D 캔버스가 보입니다./3d/bundle.js를 런타임에 로드해서 Babylon을 초기화합니다.web/src/routes/+page.svelte 텍스트를 조금 바꿈npm run start:web
build:3d만 하면 됨).3d/src/bundle.ts에서 예: 배경색/메시/라이트 등을 변경npm run start:3d
build:3d가 web/build/client/3d 아래 파일을 교체하므로, web 재빌드 없이 3D 변경이 반영됩니다.아래 순서대로 옮기면 됩니다.
이 방식은 경로에 의존합니다. 즉, “폴더 이름/위치”가 바뀌면 아래 경로들도 같이 바꿔야 합니다.
web은 런타임에 항상 /3d/bundle.js 를 import 합니다.dev:web에서 /3d/*는 web/static/3d/* 에서 나옵니다.build:3d는 web/build/client/3d/* 를 교체합니다.그래서 “다른 프로젝트에 적용”할 때는 먼저 아래 중 하나로 폴더 구조를 선택하고, 선택한 구조에 맞춰 스크립트 경로를 수정하세요.
옵션 A (이 레포와 동일, 추천)
/
web/
3d/
scripts/
옵션 B (일반적인 모노레포 스타일)
/
apps/
web/
3d/
scripts/
옵션 B로 바꾸면 scripts/sync-3d-to-web-*.mjs 안의 경로(web, 3d)를 apps/web, apps/3d로 변경해야 합니다.
package.json에:"private": true"workspaces": ["web", "3d"] (폴더명이 다르면 맞춰서 변경)3d/vite.config.ts에 build.lib 설정 추가(번들 파일명을 고정: bundle.js)3d/src/bundle.ts에 export function initBabylon(canvas) 같은 웹이 호출할 엔트리 함수를 제공const bundleUrl = new URL('/3d/bundle.js', window.location.origin).toString();await import(/* @vite-ignore */ bundleUrl);/3d/bundle.js)에 존재해야 함추가로 이 레포는 “하이브리드 dev”를 위해 THREED_BUNDLE_URL 환경변수를 지원합니다.
THREED_BUNDLE_URL=http://localhost:5173/src/bundle.tsweb/src/hooks.server.ts가 window.__THREED_BUNDLE_URL__로 주입하고, 페이지에서 그 값을 우선 사용합니다.scripts/ 2개를 그대로 가져오고, 경로만 프로젝트 구조에 맞게 바꾸면 됩니다.sync-3d-to-web-static.mjs: web 빌드 시 포함되게 하기sync-3d-to-web-build.mjs: 3d-only 빌드로도 이미 빌드된 web에 즉시 반영되게 하기web이 SvelteKit이면 @sveltejs/adapter-node를 적용하면web/build를 node로 실행 가능.github/workflows/ci.yml처럼npm run build:all: 3d 빌드 → web/static/3d로 복사 → web 빌드npm run build:web: web/static/3d로 복사 → web만 빌드npm run build:3d: 3d만 빌드 → web/build/client/3d(+ web/static/3d)로 복사npm run start:all: 최신 web+3d 빌드 후 실행npm run start:web: 최신 web 빌드 후 실행(3d는 최근 빌드 사용)npm run start:3d: 최신 3d 빌드 후 실행(web은 최근 빌드 사용)npm run dev:all: web(dev)+3d(dev) 합쳐서 실행npm run dev:web: web(dev) + 3d(최근 빌드) 합쳐서 실행npm run dev:3d: web(최근 빌드) + 3d(dev) 합쳐서 실행http://localhost:5174/http://localhost:5173/web/build)을 node로 실행. 기본 접속 http://127.0.0.1:4173/명령별로 “합쳐진 화면(web 안에 3D)”을 보려면 아래 URL로 접속하세요.
npm run dev:web
web/static/3d에서 서빙(/3d/bundle.js)http://localhost:5174/npm run dev:3d
THREED_BUNDLE_URL=http://localhost:5173/src/bundle.ts)http://127.0.0.1:4173/http://localhost:5173/로 들어가면 3d 단독(dev) 화면이 보이는 게 정상입니다.npm run dev:all
THREED_BUNDLE_URL=http://localhost:5173/src/bundle.ts)http://localhost:5174/npm run start:all / npm run start:web / npm run start:3d
http://127.0.0.1:4173/주의: 위 서버들은 포트를 점유하므로, 다른 모드를 실행하기 전 기존 프로세스를 종료하지 않으면 포트 충돌이 날 수 있습니다.