| name | cra-to-vite-migration |
| description | Create React App(CRA)에서 Vite로 마이그레이션하는 단계별 절차. 환경 변수·index.html·SVG·Jest→Vitest·tsconfig 변경 패턴 포함 |
CRA → Vite 마이그레이션
소스: 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. 빌드·실행 검증
1단계: 패키지 교체
npm uninstall react-scripts
npm install -D vite @vitejs/plugin-react-swc
주의: @vitejs/plugin-react-swc는 SWC로 트랜스파일해 더 빠름. Babel 플러그인(styled-components 등)이 있으면 @vitejs/plugin-react 유지.
2단계: index.html 이동
Before (CRA):
public/
index.html ← %PUBLIC_URL% 사용
After (Vite):
index.html ← 루트에 위치, %PUBLIC_URL% 제거
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<div id="root"></div>
<link rel="icon" href="/favicon.ico" />
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
3단계: 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,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
build: {
outDir: 'build',
sourcemap: true,
},
})
path alias + vite-tsconfig-paths (선택)
tsconfig의 paths를 Vite에서 자동 인식하려면:
npm install -D vite-tsconfig-paths
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [react(), tsconfigPaths()],
})
4단계: package.json scripts 수정
{
"scripts": {
"start": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "vitest"
}
}
5단계: 환경 변수 전환
.env 파일 수정
REACT_APP_API_URL=https://api.example.com
REACT_APP_FEATURE_FLAG=true
VITE_API_URL=https://api.example.com
VITE_FEATURE_FLAG=true
코드 수정
const apiUrl = process.env.REACT_APP_API_URL
const flag = process.env.REACT_APP_FEATURE_FLAG === 'true'
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_ 접두사 없는 변수는 클라이언트에 노출되지 않음. 의도적으로 숨기는 서버 전용 값은 접두사 없이 유지.
6단계: SVG import 수정
npm install -D vite-plugin-svgr
import svgr from 'vite-plugin-svgr'
export default defineConfig({
plugins: [react(), svgr()],
})
import { ReactComponent as Logo } from './logo.svg'
import Logo from './logo.svg?react'
function App() {
return <Logo className="logo" />
}
주의: ?react 쿼리가 없으면 URL 문자열로 import됨. 모든 SVG 컴포넌트 import에 ?react 추가 필수.
7단계: tsconfig 업데이트
{
"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 |
8단계: Jest → Vitest 전환
npm uninstall jest jest-environment-jsdom babel-jest @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
npm install -D vitest @vitest/coverage-v8 jsdom @testing-library/jest-dom
vite.config.ts에 test 블록 추가
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/setupTests.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'lcov'],
},
},
})
setupTests.ts — 수정 불필요
import '@testing-library/jest-dom'
jest.config.js 삭제
Vitest는 vite.config.ts의 test 블록으로 통합. jest.config.js 삭제.
코드 변경 (vi = jest API 호환)
import { jest } from '@jest/globals'
jest.fn()
jest.mock('./module')
jest.spyOn(obj, 'method')
import { vi } from 'vitest'
vi.fn()
vi.mock('./module')
vi.spyOn(obj, 'method')
흔한 실수 패턴
1. %PUBLIC_URL% 제거 누락
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="/favicon.ico" />
2. process.env 잔존
const url = process.env.REACT_APP_API_URL
const url = import.meta.env.VITE_API_URL
3. SVG ?react 쿼리 누락
import Logo from './logo.svg'
return <Logo />
import Logo from './logo.svg?react'
4. script 태그 미추가
<body>
<div id="root"></div>
</body>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
5. VITE_ 없는 변수 클라이언트 노출 불가
SECRET_KEY=abc123
VITE_SECRET_KEY=abc123
6. jest.config.js와 vitest test 블록 중복 존재
7. @types/jest 잔존
npm uninstall @types/jest