用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/puk0806/gugbab-claude --skill cra-to-vite-migration命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
DDD(Domain-Driven Design) 아키텍처 핵심 패턴 - 유비쿼터스 언어, 서브도메인, 바운디드 컨텍스트, Aggregate, Entity/VO, 도메인 서비스/이벤트, 레이어드 아키텍처
대규모 React/Next.js 프로젝트를 layer-first(types/·utils/·hooks/·api/·components/ 밑에 도메인이 반복되는 구조)에서 domain-first(feature/도메인 우선) 구조로 전환하는 설계 기준과 절차. Feature-Sliced Design 2.1 정본(layers 6종·slices·segments·import 규칙·@x 크로스임포트·public API), FSD를 쓰지 않는 경량 대안(features + shared 2~3계층 + ESLint import/no-restricted-paths), Next.js App Router 공존 전략(route group `()`·private folder `_`·colocation), Turborepo/Nx 모노레포에서 폴더↔패키지 승격 기준, colocation과 배럴 파일 성능 트레이드오프, 도메인 경계 역추출(import 그래프·change coupling·용어 클러스터), 전환 실패 패턴(shared 비대화·entities 남용·순환 의존·도메인=라우트 착각·조기 추상화). 도메인 개념 자체(바운디드 컨텍스트·유비쿼터스 언어)는 `architecture/ddd` 스킬을 참조한다.
소스 파일 수천 개 규모 프론트엔드 코드베이스를 멈추지 않고 점진 재구조화하는 실행 전략 - Strangler Fig / Branch by Abstraction / Parallel Change, ts-morph·jscodeshift codemod, PR 분할·검증 게이트·되돌리기, 테스트 없는 코드의 안전망, 작업 순서 설계와 위반 수 기반 진행 추적
正在显示 SKILL.md
| name | cra-to-vite-migration |
| description | Create React App(CRA)에서 Vite로 마이그레이션하는 단계별 절차. 환경 변수·index.html·SVG·Jest→Vitest·tsconfig 변경 패턴 포함 |
소스: https://vitejs.dev/guide/ | https://react.dev/blog/2025/02/14/sunsetting-create-react-app | https://vitest.dev/config/ 검증일: 2026-08-11 (최초 작성 2026-04-20, WebSearch 재검증 완료 — Vite 8/Rolldown 전환은 이 스킬의 rollupOptions/manualChunks 미사용으로 영향 없음, vite-plugin-svgr 최신 v5.2.0 확인·
?react쿼리 방식은 v4~v5 동일 유효)
배경: CRA(Create React App)는 2025년 2월 공식 deprecated. 신규 프로젝트는 Vite, 기존 프로젝트는 이 가이드로 전환한다.
1. 패키지 교체 (react-scripts 제거, vite 설치)
2. index.html 이동 (public/ → 루트)
3. vite.config.ts 생성
4. package.json scripts 수정
5. 환경 변수 전환 (REACT_APP_ → VITE_)
6. SVG import 수정 (vite-plugin-svgr)
7. tsconfig 업데이트
8. Jest → Vitest 전환
9. 빌드·실행 검증
# 제거
npm uninstall react-scripts
# 설치
npm install -D vite @vitejs/plugin-react-swc
# 또는 Babel 기반이면: @vitejs/plugin-react
주의:
@vitejs/plugin-react-swc는 SWC로 트랜스파일해 더 빠름. Babel 플러그인(styled-components 등)이 있으면@vitejs/plugin-react유지.
Before (CRA):
public/
index.html ← %PUBLIC_URL% 사용
After (Vite):
index.html ← 루트에 위치, %PUBLIC_URL% 제거
<!-- Before (CRA) -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<div id="root"></div>
<!-- After (Vite) -->
<link rel="icon" href="/favicon.ico" />
<div id="root"></div>
<!-- body 닫기 전 추가 (CRA는 자동 주입, Vite는 명시 필요) -->
<script type="module" src="/src/main.tsx"></script>
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import path from 'path'
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 3000,
// CRA의 setupProxy.js → server.proxy로 이전
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
build: {
outDir: 'build', // CRA 기본값 유지하려면 'build', Vite 기본은 'dist'
sourcemap: true,
},
})
tsconfig의 paths를 Vite에서 자동 인식하려면:
npm install -D vite-tsconfig-paths
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [react(), tsconfigPaths()],
// resolve.alias 없어도 tsconfig paths 자동 반영
})
{
"scripts": {
"start": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "vitest"
}
}
# Before (CRA)
REACT_APP_API_URL=https://api.example.com
REACT_APP_FEATURE_FLAG=true
# After (Vite)
VITE_API_URL=https://api.example.com
VITE_FEATURE_FLAG=true
// Before (CRA)
const apiUrl = process.env.REACT_APP_API_URL
const flag = process.env.REACT_APP_FEATURE_FLAG === 'true'
// After (Vite)
const apiUrl = import.meta.env.VITE_API_URL
const flag = import.meta.env.VITE_FEATURE_FLAG === 'true'
| CRA | Vite |
|---|---|
process.env.NODE_ENV | import.meta.env.MODE |
process.env.PUBLIC_URL | import.meta.env.BASE_URL |
process.env.REACT_APP_* | import.meta.env.VITE_* |
주의:
VITE_접두사 없는 변수는 클라이언트에 노출되지 않음. 의도적으로 숨기는 서버 전용 값은 접두사 없이 유지.
npm install -D vite-plugin-svgr
// vite.config.ts
import svgr from 'vite-plugin-svgr'
export default defineConfig({
plugins: [react(), svgr()],
})
// src/vite-env.d.ts (또는 vite-plugin-svgr/client 추가)
/// <reference types="vite/client" />
/// <reference types="vite-plugin-svgr/client" />
// Before (CRA — @svgr/webpack 내장)
import { ReactComponent as Logo } from './logo.svg'
// After (Vite + vite-plugin-svgr v4)
import Logo from './logo.svg?react'
// 사용은 동일
function App() {
return <Logo className="logo" />
}
주의:
?react쿼리가 없으면 URL 문자열로 import됨. 모든 SVG 컴포넌트 import에?react추가 필수.
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"types": ["vite/client"],
"paths": {
CRA tsconfig에서 주요 변경점:
| 항목 | CRA 기본값 | Vite 권장값 |
|---|---|---|
moduleResolution | node | bundler |
types | ["react-scripts"] | ["vite/client"] |
noEmit | 없음 | true |
allowImportingTsExtensions | 없음 | true |
# Jest 제거
npm uninstall jest jest-environment-jsdom babel-jest @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
# Vitest 설치
npm install -D vitest @vitest/coverage-v8 jsdom @testing-library/jest-dom
/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
export default defineConfig({
plugins: [react()],
test: {
globals: true, // describe/it/expect 전역 사용
environment: 'jsdom', // 브라우저 DOM 시뮬레이션
setupFiles: './src/setupTests.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'lcov'],
},
},
})
// src/setupTests.ts (CRA와 동일하게 유지)
import '@testing-library/jest-dom'
Vitest는 vite.config.ts의 test 블록으로 통합. jest.config.js 삭제.
// Before (Jest)
import { jest } from '@jest/globals'
jest.fn()
jest.mock('./module')
jest.spyOn(obj, 'method')
// After (Vitest — API 동일, 네이밍만 변경)
import { vi } from 'vitest'
vi.fn()
vi.mock('./module')
vi.spyOn(obj, 'method')
<!-- ❌ Vite에서 %PUBLIC_URL%은 빈 문자열로 처리되지 않음 -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- ✅ 절대 경로로 수정 -->
<link rel="icon" href="/favicon.ico" />
// ❌ Vite에서 process.env.REACT_APP_* undefined 반환
const url = process.env.REACT_APP_API_URL
// ✅
const url = import.meta.env.VITE_API_URL
// ❌ 문자열 URL이 반환됨 (컴포넌트 아님)
import Logo from './logo.svg'
return <Logo /> // 런타임 에러
// ✅
import Logo from './logo.svg?react'
<!-- ❌ CRA는 자동 주입, Vite는 직접 명시 필요 -->
<body>
<div id="root"></div>
<!-- 여기에 script 없으면 앱이 로드 안 됨 -->
</body>
<!-- ✅ -->
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
# ❌ 아래 변수는 import.meta.env에서 undefined
SECRET_KEY=abc123
# ✅ 클라이언트에서 접근하려면 VITE_ 접두사 필수
VITE_SECRET_KEY=abc123
# (단, 진짜 시크릿 값은 VITE_ 붙이면 브라우저에 노출됨 — 주의)
// ❌ jest.config.js 남겨두면 충돌 가능
// ✅ jest.config.js 삭제 + vite.config.ts test 블록 단일화
# ❌ @types/jest가 남으면 jest/vitest 타입 충돌
npm uninstall @types/jest
# ✅ vitest globals 타입은 tsconfig에 추가
# "types": ["vite/client", "vitest/globals"]