프레임워크 코드 (/core)
데모/테스트 코드 (/jsx-demo/*.jsx)
.jsx → .js 트랜스파일 MyReact.createElement MyReact.Fragment jsxInject별칭(alias)
@core/*, @/* 등이 VSCode와 Vite 양쪽에서 정상 작동개발 서버(root)
/jsx-demo /src (프레임워크 코드)와 /jsx-demo 모두 파일 시스템에서 서빙 허용{
"compilerOptions": {
"allowJs": true,
"jsx": "preserve", // JSX를 보존만 – 변환은 ESBuild에 위임
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"baseUrl": "src",
"paths": {
"@/*": ["*"],
"@shared/*": ["shared/*"],
"@core/*": ["core/*"] // bare "@core" 매핑 누락
},
"moduleResolution": "bundler",
"noEmit": true,
"strict": true,
"types": ["vitest"]
},
"include": ["src", "jsx-demo"]
}
import { defineConfig } from "vite";
import path from "path";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
// ▶ 개발 서버 루트: 데모 폴더만 호스팅
root: path.resolve(__dirname, "jsx-demo"),
plugins: [
// ▶ 최상위 tsconfig.json 경로 직접 지정
tsconfigPaths({
projects: [path.resolve(__dirname, "tsconfig.json")]
}),
/*
// Babel 기반 React 플러그인 대신 ESBuild 사용
react({
jsxRuntime: "classic",
}),
*/
],
esbuild: {
// ▶ Classic JSX 런타임 지정
jsxFactory: "MyReact.createElement",
jsxFragment: "MyReact.Fragment",
jsxInject: `import MyReact from '@/core';`,
// ▶ include 패턴 누락으로 초기 .jsx 처리 실패
// include: [/\.jsx$/],
},
server: {
fs: {
// ▶ /src + /jsx-demo 모두 서빙 허용
allow: [
path.resolve(__dirname, "src"),
path.resolve(__dirname, "jsx-demo"),
],
},
port: 3000,
open: true,
},
build: {
outDir: "jsx-demo/dist",
sourcemap: true,
},
});
⸻

// vite.config.ts
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
root: path.resolve(__dirname, "jsx-demo"),
plugins: [
tsconfigPaths({
// 프로젝트 최상위의 tsconfig.json을 직접 지정
projects: [ path.resolve(__dirname, "tsconfig.json") ]
}),
// (plugin-react는 잠시 주석 처리)
],
// …그 외 설정…
});
vite-tsconfig-paths가 올바른 paths 설정을 읽어와, 데모 환경에서도 별칭이 적용됨.
root: path.resolve(__dirname, "jsx-demo"),
<!-- bundle.js 대신 ES 모듈 직통 로드 -->
<script type="module" src="app.jsx"></script>
GET /dist/bundle.js 404
… outside of Vite serving allow list
server: {
fs: {
allow: [
path.resolve(__dirname, "src"), // 기존 프레임워크 코드
path.resolve(__dirname, "jsx-demo") // 테스트 데모 코드
]
}
}
Pre-transform error: Failed to load url /app.jsx
esbuild: {
loader: "jsx",
include: [/\.jsx$/], // .jsx로 끝나는 모든 파일을 처리
jsxFactory: "MyReact.createElement",
jsxFragment: "MyReact.Fragment",
jsxInject: `import MyReact from '@core';`,
}
Failed to resolve import "@core" from "jsx-demo/app.jsx"
// vite.config.json
esbuild: {
// 오직 데모 폴더의 .jsx 만 처리
// include: [/jsx-demo\/.*\.jsx$/],
// Classic 런타임 프래그마 지정
jsxFactory: "MyReact.createElement",
jsxFragment: "MyReact.Fragment",
//별칭 경로 수정
jsxInject: `import MyReact from '@/core';`,
},
Identifier 'MyReact' has already been declared
Unexpected token '{' at createElement.ts
-> 근데 vite.config 파일에 root 경로를 테스트 환경인 jsx-demo 를 지정했는데 읽어 올 수 없었을까?
root: path.resolve(__dirname, "jsx-demo")
이건 “Vite가 어느 폴더를 웹 루트(= / URL)로 삼을지”를 결정해 준다.
server.fs.allow: [ … ]
이건 “Vite 개발 서버가 파일 시스템 상에서 어느 폴더까지 읽어와도 안전한지”를 화이트리스트로 지정해 주는 보안 설정이다.
그래서 allow 리스트에 jsx-demo 경로를 명시적으로 추가해야만,
정리하자면:
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"@/*": ["*"],
"@shared/*":["shared/*"],
"@core/*": ["core/*"],
"@core": ["core/index.ts"]
},
"jsx": "preserve",
/* …strict, bundler 모드 옵션… */
},
"include": ["src", "jsx-demo"]
}
import { defineConfig } from "vite";
import path from "path";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
root: path.resolve(__dirname, "jsx-demo"),
plugins: [
tsconfigPaths({ projects: [ path.resolve(__dirname, "tsconfig.json") ] })
],
esbuild: {
include: [/\.jsx$/],
jsxFactory: "MyReact.createElement",
jsxFragment: "MyReact.Fragment",
jsxInject: `import MyReact from '@core';`,
},
server: {
fs: {
allow: [
path.resolve(__dirname, "src"),
path.resolve(__dirname, "jsx-demo")
]
},
port: 3000,
open: true
},
build: {
outDir: "jsx-demo/dist",
sourcemap: true
}
});
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>JSX Demo</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/app.jsx"></script>
</body>
</html>
function App() {
return (
<div>
<h1>JSX Demo</h1>
<p>이것은 JSX 데모입니다.</p>
</div>
);
}
const root = document.getElementById("root");
MyReact.render(<App />, root);
⸻
tsconfig.json
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"@/*": ["*"],
"@shared/*": ["shared/*"],
"@core/*": ["core/*"],
"@core": ["core/index.ts"] // bare import 매핑 추가
},
"jsx": "preserve", // JSX 보존
"moduleResolution": "bundler",
"strict": true,
"noEmit": true,
"types": ["vitest"]
},
"include": ["src", "jsx-demo"]
}
vite.config.ts
import { defineConfig } from "vite";
import path from "path";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
root: path.resolve(__dirname, "jsx-demo"),
plugins: [
tsconfigPaths({
projects: [path.resolve(__dirname, "tsconfig.json")]
}),
],
esbuild: {
include: [/\.jsx$/], // 모든 .jsx 처리
jsxFactory: "MyReact.createElement", // Classic runtime
jsxFragment: "MyReact.Fragment",
jsxInject: `import MyReact from '@core';`
},
server: {
fs: {
allow: [
path.resolve(__dirname, "src"),
path.resolve(__dirname, "jsx-demo")
]
},
port: 3000,
open: true,
},
build: {
outDir: "jsx-demo/dist",
sourcemap: true,
},
});
⸻