🚣♀️
CRA -> VITE로 마이그레이션 하기
Kioschool
2025-06-04
어쩌다 마이그레이션 하게 되었나?
키오스쿨은 CRA 기반으로 초기 설정이 이루어진 프로젝트이다. CRA의 장점은 세팅이 아주 편리하고 귀찮고 어려운 번들 설정을 신경쓰지 않아도 된다는 점이다. 그러나, 2025년 2월 14일 React 팀이 CRA의 종료를 발표하며 유지보수 하지 않겠다는 선언을 했다.
기존 CRA에서 사용 중인 Webpack은 개발 서버 환경에서 번들을 기반으로 작동되어서 전체 파일에 대해 번들링하는데 시간이 소요된다. 게다가 세부적인 Webpack 설정을 하기 위해선 eject해야만 하는데, 이 작업은 한번 하면 돌이킬 수 없게 된다. 이러한 점들이 모이고 모여 React 팀에서도 장려하는 Vite로 마이그레이션 하기로 결정했다. Vite를 사용해야 하는 이유(해당 글을 읽으면 좀 더 명료하다.)

무엇을 해야하나?
우선 Vite를 설치했다.
1yarn add -D vite @vitejs/plugin-react
package.json 파일의 스크립트 부분을 Vite를 사용하도록 변경했다.

CRA에서 사용중이던 관련 패키지 및 파일들을 모두 삭제했다.

vite.config.ts파일을 추가했다.
1/* eslint-disable import/no-extraneous-dependencies */2import { defineConfig } from "vite";3import react from "@vitejs/plugin-react";4import tsconfigPaths from "vite-tsconfig-paths";56export default defineConfig({7 plugins: [react(), tsconfigPaths()],8 server: {9 host: "localhost",10 port: 3000,11 open: true,12 },13 build: {14 outDir: "build",15 },16});
기존 tsconfig.json을 tsconfig.app.json과 tsconfig.node.json 두 파일로 나눠 설정했다.
각각 브라우저(React 앱)와 Node 환경(서버 사이드 코드, 빌드 스크립트, Vite 설정 파일)에서의 컴파일 설정을 담당한다. 해당 설정은 Vite로 초기 설정을 진행한 다른 프로젝트에서 그대로 가져왔다.
1{2 "files": [],3 "references": [4 { "path": "./tsconfig.app.json" },5 { "path": "./tsconfig.node.json" }6 ]7}
1{2 "extends": "./tsconfig.paths.json",3 "compilerOptions": {4 "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",5 "target": "ES2020",6 "useDefineForClassFields": true,7 "lib": ["ES2020", "DOM", "DOM.Iterable"],8 "module": "ESNext",9 "skipLibCheck": true,10 "incremental": true,1112 /* Bundler mode */13 "moduleResolution": "node",14 "isolatedModules": true,15 "noEmit": true,16 "jsx": "react-jsx",17 "esModuleInterop": true,18 "allowSyntheticDefaultImports": true,1920 /* Linting */21 "strict": true,22 "noUnusedLocals": true,23 "noUnusedParameters": true,24 "noFallthroughCasesInSwitch": true25 },26 "include": ["src", "src/**/*.d.ts", "vite.config.ts", "*.d.ts"]27}
1{2 "compilerOptions": {3 "composite": true,4 "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",5 "target": "ES2022",6 "lib": ["ES2022"],7 "module": "ESNext",8 "skipLibCheck": true,910 /* Bundler mode */11 "moduleResolution": "node",12 "isolatedModules": true,13 "moduleDetection": "force",14 "noEmit": true,1516 /* Linting */17 "strict": true,18 "noUnusedParameters": true,19 "noFallthroughCasesInSwitch": true20 },21 "include": ["vite.config.ts"]22}
Eslint 설정 파일인 .eslintrc 설정도 살짝 바꿔줬다.
대표적으로 바뀐 것은 최신 ECMA Script 문법을 지원, SourceType: module 로 ESM을 사용, JSX 문법 사용, 더 다양한 환경 변수 사용 등을 명시해줬다.

index.html의 위치를 /public 폴더에서 프로젝트 루트로 옮겨줬다. 그리고 href 속성 값에 붙어있던 %PUBLIC_URL% 구문을 삭제했다.
CRA에선 index.html 파일이 public 폴더 안에 있었지만, Vite는 프로젝트 루트 위치로 옮겨줘야 한다. 그 이유는 Webpack은 프로젝트의 진입점을 index.ts로 삼지만, Vite는 index.html으로 삼기 때문이다.
그리고 %PUBLIC_URL% 이 구문도 CRA의 잔재이다. CRA에서 해당 변수는 public 폴더를 가리킨다. 그래서 프로젝트가 kiochool.com/home-page 라는 하위 페이지에 배포되어야 하고 %PUBLIC_URL% 변수가 없다면 kioschool.com/ 으로 해당 favicon을 요청하여 제대로 리소스를 불러올 수 없을 것이다.
Vite에선 암묵적으로 public에 있는 리소스는 '/' 경로로 시작한다.

이제 기존과 동일한 start 스크립트를 실행시키면? 정상적으로 실행된다!!

성능 개선 결과
CRA | Vite | |
---|---|---|
Cold Start | 22,071ms | 405ms |
빌드 | 3분 40초 | 1분 48초 |
Cold Start 시간은 98.17%, 빌드하여 배포하는 시간은 50.91% 감소했다. 자세한 시간은 PR에 첨부되어 있다.
마이그레이션 도중 겪은 트러블 슈팅
vite.config.ts 파일에서 오류가 발생해요
PR을 리뷰해준 지인이에게 다음과 같은 에러가 발생했다. 에러 메세지를 분석해보면 지인이 환경에서는 vite.config.ts 파일을 common JS로 인식하고 있다. 그래서 vite-tsconfig-paths라는 플러그인을 불러오기 위해 내부적으로 require()를 호출하려고 했지만, vite-tsconfig-paths는 ESM 방식을 취하기에 에러가 발생하는 것이었다.

이 에러는 package.json에 type: module 한 줄만 추가해주면 된다. 해당 키워드를 통해 Node.js에게 ESM 방식을 취하는 것을 명시해주면 에러가 발생하지 않는다.
1{2 "type": "module"3}
후기
들인 노력에 비해 아웃풋이 좋게 나와서 신기하다. 번들링 툴을 바꾸기만 했을 뿐인데, DX가 이렇게 향상되다니. 압도적인 편의성을 직접 체감하고 나니, 개발의 본질에 대해 다시 생각하게 됐다. 개발이란 단순히 화면에 기능을 그려내는 코드 작성을 넘어, 시스템의 보이지 않는 비효율을 찾아내고 구조적인 문제를 해결하는 과정 그 자체임을 온몸으로 실감할 수 있었다.