React 처음 공부할 때 만들어 둔 작은 프로젝트가 하나 있었는데, 오래된 CRA(create-react-app) 기반이라 계속 마음에 걸렸다.
요즘은 새 프로젝트를 CRA로 시작하는 경우도 거의 없고, 실제로도 Vite를 더 자주 쓰고 있어서
- 예전에 만든 CRA 프로젝트를 한 번 Vite로 옮겨 보고
- 마이그레이션 과정을 내 스타일대로 기록해 두면
나중에 또 비슷한 상황이 왔을 때 그대로 참고하기 좋겠다 싶어서 정리해 둔 글이다.
참고한 글
How to Migrate from create-react-app to Vite using Jest and Browserslist
The React team no longer recommends using create-react-app (CRA) as a bundler to create a new React application. The team and community realized that even though CRA was a jump-starter, it lacked the flexibility needed to configure or manage large an...
www.freecodecamp.org
나는 이 글 흐름을 베이스로 가져가고, 실제로 내가 했던 작업 순서에 맞춰서 정리했다.
0. 시작점 – 기존 CRA 프로젝트 상태
마이그레이션 전에 기존 상태부터 한 번 보고 시작했다.
- create-react-app으로 만든 전형적인 구조
- 타입스크립트 사용
- react-scripts로 dev 서버/빌드 돌리던 상태
이 프로젝트를 그대로 Vite로 옮기는 게 이번 작업 목표였다.

1. Vite 관련 패키지 설치
먼저 Vite와 React 플러그인, 그리고 vite-tsconfig-paths를 추가했다.
패키지 매니저는 pnpm을 쓰고 있어서 그대로 사용.
pnpm add vite @vitejs/plugin-react vite-tsconfig-paths
여기까지는 설치만 하는 단계.
2. vite.config.ts 생성
루트에 vite.config.ts 파일을 만들고, 기본 설정을 넣었다.
touch vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import viteTsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
base: "",
plugins: [react(), viteTsconfigPaths()],
server: {
open: true, // dev 서버 실행 시 브라우저 자동 오픈
port: 3000, // CRA와 맞춰서 3000 포트 사용
},
});
vite-tsconfig-paths는 CRA 시절에 쓰던 paths 설정을 그대로 가져가고 싶어서 같이 넣었다.
3. Vite 타입 정의 파일 추가
Vite 환경에서 타입 지원을 받으려면 타입 정의를 한 번 연결해 두는 게 편하다.
touch src/vite-env.d.ts
/// <reference types="vite/client" />
이 한 줄로 Vite 관련 타입들이 전역에 들어온다.
4. index.html 위치 이동
CRA에서는 public/index.html 기준이지만, Vite는 루트에 index.html이 있어야 한다.
그래서 위치를 이렇게 바꿨다.
mv public/index.html ./index.html
Vite는 index.html을 엔트리로 취급하기 때문에, 이 위치가 꽤 중요하다.
공식 문서에도 관련 설명이 있다.
Vite
Next Generation Frontend Tooling
vite.dev
5. index.html 내부 정리
위치만 옮기고 끝이 아니라 내용도 조금 손을 봤다.
5-1. %PUBLIC_URL% 제거
CRA에서 자동으로 치환해 주던 %PUBLIC_URL% 패턴은 Vite에서는 쓰지 않는다.
그래서 index.html에 남아 있던 %PUBLIC_URL% 관련 부분은 전부 삭제했다.
정적 리소스 경로는
- import로 모듈처럼 가져오거나
- public 아래 파일을 /xxx.png처럼 절대 경로로 접근
이런 식으로 바꿔 가는 게 편하다.
5-2. 엔트리 스크립트 명시
body 끝부분에 Vite 방식으로 엔트리 스크립트를 직접 적어줬다.
<script type="module" src="/src/index.tsx"></script>
CRA에서는 react-scripts가 빌드 과정에서 알아서 스크립트를 주입해 줬는데,
Vite는 이렇게 index.html에서 바로 엔트리 모듈을 지정하는 구조다.
6. CRA 관련 패키지/파일 제거
이제 Vite로 갈 거라서 CRA 관련 것들을 정리했다.
pnpm remove react-scripts
rm src/react-app-env.d.ts
- react-scripts 제거
- CRA가 자동으로 생성해 둔 src/react-app-env.d.ts 삭제
여기까지 하면 프로젝트가 점점 CRA 냄새를 덜 풍기기 시작한다.
7. 설정 파일 정리
7-1. package.json 스크립트 변경
CRA용 스크립트를 Vite 기준으로 바꿨다.
{
"scripts": {
"start": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
}
}
- 개발 서버는 pnpm start
- 빌드는 pnpm build
- 빌드 결과 미리보기는 pnpm preview
로 동작한다.
7-2. tsconfig.json 수정
타입스크립트 설정도 Vite에 맞게 약간 손봤다.
{
"compilerOptions": {
"target": "ESNext",
"types": ["vite/client"],
"lib": ["dom", "dom.iterable", "esnext"],
"isolatedModules": true
}
}
기존 CRA 템플릿과 크게 다르지는 않지만
types에 "vite/client"를 추가한 부분이 중요하다.
7-3. 환경 변수 패턴 변경 (필요 시)
이 프로젝트에서는 환경 변수를 이미 몇 개 쓰고 있어서, 같이 정리했다.
CRA 스타일
process.env.REACT_APP_IMAGE_PREFIX;
Vite 스타일
import.meta.env.VITE_APP_IMAGE_PREFIX;
그리고 .env 파일도
- REACT_APP_ 접두사는
- VITE_ 또는 VITE_APP_ 형태로
같이 바꿔줘야 한다.
8. 실행 + ESM 관련 에러 처리
이제 dev 서버를 한 번 띄워 봤다.
pnpm start
그런데 바로 잘 뜨진 않고, 이런 에러를 맞았다.
[ERROR] "vite-tsconfig-paths" resolved to an ESM file. ESM file cannot be loaded by `require`.
See https://vite.dev/guide/troubleshooting.html#this-package-is-esm-only for more details. [plugin externalize-deps]
요약하면
- vite-tsconfig-paths 패키지가 ESM 전용인데
- 이걸 CommonJS 방식으로 불러오려 해서 문제가 난 상황
공식 트러블슈팅 문서에서 안내하는 해결 방법 중 하나가
가장 가까운 package.json에 "type": "module" 추가
이어서, 루트 package.json에 아래를 추가했다.
{
"type": "module"
}
다시 pnpm start를 실행해 보니 이번에는 dev 서버가 정상적으로 떴다.

브라우저에서도 기존 화면이 잘 뜨는 것을 확인했다.

9. 마무리 메모
이번 CRA → Vite 마이그레이션은 결국 이런 작업들의 묶음이었다.
- Vite 및 관련 플러그인 설치
- vite.config.ts 생성
- index.html 위치 이동 + 내용 Vite 스타일로 수정
- CRA 잔재 정리
(react-scripts, react-app-env.d.ts 제거) - package.json 스크립트 변경
- tsconfig.json과 환경 변수 패턴 정리
- vite-tsconfig-paths ESM 문제 → "type": "module"로 해결
처음 해 볼 때는 막연히 귀찮을 것 같았는데
한 번 해 보고 나니 다음부터는 체크리스트처럼 쭉 따라가면 될 정도였다.
나중에 또 오래된 CRA 프로젝트를 발견하면
이 글을 보고 그대로 따라가면서 Vite로 갈아타면 될 것 같다.