1. CRA 기반 프로젝트를 다시 열게 된 계기
작년에 열심히 만들고 손을 놓았던 프로젝트, '멜로디메이트'를 1년 만에 다시 꺼내게 되었습니다.
그 계기는 구독 중이던 [Korean FE Article] 뉴스레터에서 Create React App(CRA)의 공식 지원 중단 소식을 접하면서 시작됐습니다. 소식을 듣고 곧바로 내 프로젝트 중 CRA 기반으로 만들어진 것이 있는지 확인해봤고, 그중 가장 애착이 가던 '멜로디메이트'가 눈에 띄었습니다. 이참에 Vite로 마이그레이션을 해봐야겠다고 생각했고, 주저 없이 실행에 옮겼습니다.
React팀은 2025년 2월, CRA의 공식 지원 종료를 발표했습니다.
이제 더 이상 CRA에 대한 유지보수는 이루어지지 않으며, 새로운 프로젝트는 Vite, Parcel, Rsbuild 같은 빌드 도구나, Next.js와 같은 프레임워크의 사용이 권장되고 있습니다.
👉 공식 문서 보기
2. 왜 Vite로 마이그레이션했는가?
1) CRA의 한계와 지원 종료
'멜로디메이트' 프로젝트는 개발 초기 Create React App(CRA)을 기반으로 시작했습니다. CRA는 별다른 설정 없이도 바로 개발을 시작할 수 있어, 초기 단계에서는 큰 불만 없이 빠르게 서비스를 구성할 수 있었습니다.
하지만 프로젝트 규모가 커지고 유지보수나 리팩터링이 중요해지면서 CRA의 한계가 점차 드러나기 시작했습니다. CRA는 Webpack을 기반으로 하기 때문에 초기 빌드 속도가 느리고, 코드 스플리팅이나 성능 최적화 같은 고급 기능들은 직접 설정해주어야 했습니다.
실제로 CRA 기반이었던 당시에는 주요 기능들이 모두 하나의 번들로 묶여 로딩 성능에 영향을 주었고, 코드 스플리팅이 제대로 이루어지지 않았습니다. 아래는 CRA 환경에서의 번들 구조입니다.
기능 추가에 대한 고민이 계속되던 중, 2025년 2월 React 팀에서 CRA의 공식 지원 종료를 발표했고, 자연스럽게 더 이상 CRA를 유지할 이유가 사라졌습니다. 이 시점을 계기로, 빠른 개발 환경과 기본적인 최적화 기능이 내장된 Vite로 마이그레이션을 진행하게 되었습니다.
Vite로 전환한 이후에는 각 기능 단위로 모듈이 분리되어 코드 스플리팅이 자동으로 적용되었고, 초기 로딩 속도 또한 개선되었습니다. 아래는 Vite 환경에서의 번들 구조입니다.
2) Vite의 장점과 내가 느낀 체감 속도
Vite는 이미 커뮤니티에서 빠른 번들링 속도와 간편한 설정으로 호평받고 있었고, 저 역시 평소 관심이 있던 도구였습니다. 특히 Vite는 기존 Webpack 방식이 아닌 pre-building 기반이라 빌드 속도가 빠릅니다.
설정도 복잡한 커스터마이징 없이 `vite.config.ts`파일 하나로 깔끔하게 관리할 수 있었고, React와 TypeScript와의 궁합도 좋아 도입 장벽이 낮았습니다.
이론적으로는 빠르다고 알고 있었지만, 실제로 Vite로 마이그레이션을 진행하면서 그 차이를 명확히 체감할 수 있었습니다. 로컬 개발 서버 구동 속도는 물론, HMR(Hot Module Replacement)반응성도 CRA와는 비교되지 않을 정도로 쾌적했습니다.
* HMR은 파일을 수정했을 때 전체 페이지를 새로고침하지 않고, 변경된 모듈만 즉시 반영해주는 기능으로, 빠른 UI 피드백이 가능합니다.
또한, `npm run build` 명령어로 프로덕션 번들을 만들 때도 CRA에 비해 빌드 속도가 확연히 빨라졌습니다. Vite는 개발 서버뿐 아니라 실제 배포용 번들링 과정에서도 빠른 성능을 보여주는데, 이는 pre-bundling과 Rollup 기반의 빌드 구조 덕분입니다.
* pre-bundling은 앱 실행 전에 사용하는 라이브러리를 미리 번들링하여 개발 서버 구동 속도를 높이는 기법입니다.
* Rollup은 Vite가 내부적으로 사용하는 모던 번들러로, 사용하지 않는 코드는 빌드에서제거하고(트리 쉐이킹), 각 기능을 나눠서 필요한 코드만 불러올 수 있도록 분리해줍니다(코드 스플리팅). 덕분에 번들 용량이 줄어들고, 초기 로딩 속도도 빨라집니다.
파일을 저장하고 곧바로 결과를 확인할 수 있다는 점만으로도 개발 흐름이 끊기지 않았고, 작은 수정이나 테스트를 반복할 때도 명확한 체감 성능 차이가 있었습니다.
결과적으로 단순한 도구의 교체를 넘어, 개발 환경 전반의 질적 향상을 경험할 수 있었으며, 더 일찍 도입하지 못했던 점이 아쉬울 정도로 만족스러운 선택이었습니다.
3. Vite로 마이그레이션 과정
CRA(Create React App)에서 Vite로의 마이그레이션은 생각보다 간단했습니다. 공식 가이드를 참고해 하나씩 순서대로 옮겨갔으며, 전체 작업 흐름은 다음과 같습니다.
1. Vite 및 React 플러그인 설치
먼저 기존 CRA 프로젝트에 Vite와 React 플러그인을 개발 의존성으로 설치합니다.
npm install vite @vitejs/plugin-react --save-dev
2. CRA 의존성 제거
CRA의 핵심인 `react-scripts`는 더 이상 필요하지 않기 때문에 삭제합니다.
npm uninstall react-scripts
3. package.json 스크립트 수정
기존 CRA 방식의 스크립트를 Vite 방식으로 변경합니다.
"scripts": {
"start": "vite",
"build": "vite build",
"serve": "vite preview"
},
4. JSX / TSX 확장자 변경
Vite는 파일 확장자에 민감하기 때문에, JSX 문법을 사용하는 파일은 .jsx, TypeScript를 사용하는 경우에는 .tsx 확장자로 변경합니다.
mv src/App.js src/App.jsx
mv src/index.js src/index.jsx
5. vite.config.ts 생성 및 설정
프로젝트 루트에 vite.config.ts 파일을 생성한 뒤, Vite용 React 플러그인을 등록합니다.
또한, 기존 CRA처럼 build/ 폴더를 출력 디렉토리로 유지하고 싶다면 아래와 같이 설정할 수 있습니다.
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig(() => {
return {
build: {
outDir: 'build',
},
plugins: [react()],
};
});
6. index.html 위치 이동 및 경로 수정
Vite는 index.html 파일이 public 폴더가 아닌 프로젝트 루트에 위치해 있기를 기대합니다.
따라서 아래 명령어를 통해 index.html을 루트 디렉토리로 이동합니다.
mv public/index.html .
이후, index.html 내부에 있는 CRA 특유의 템플릿 문법인 %PUBLIC_URL% 구문을 Vite에 맞게 직접 수정해주어야 합니다.
이와 같이 %PUBLIC_URL%이 사용된 모든 경로를 절대 경로(/)로 수정하면 됩니다.
<!-- 변경 전 (CRA 방식) -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 변경 후 (Vite 방식) -->
<link rel="icon" href="/favicon.ico" />
7. entry 파일 경로 명시
CRA는 내부적으로 엔트리 파일을 자동으로 인식했지만, Vite에서는 명시적으로 <script> 태그를 통해 불러와야 합니다.
src/index.jsx를 아래와 같이 index.html에 추가합니다.
<body>
<div id="root"></div>
<script type="module" src="/src/index.jsx"></script>
</body>
4. Vite 마이그레이션 후, Tailwind CSS가 적용되지 않는 문제
마이그레이션 자체는 비교적 매끄럽게 진행됐지만, 예상치 못한 문제가 하나 있었습니다. 바로 Tailwind CSS가 전혀 적용되지 않는 것이었습니다. 처음엔 import 경로가 잘못되었거나, vite.config.ts 설정에 문제가 있다고 생각했지만, 결국 원인은 매우 단순하고 명확한 문제였습니다.
CRA(Create React App)은 내부적으로 PostCSS 설정을 자동으로 숨기고 처리해주기 때문에, 별도로 postcss.config.js을 추가하지 않아도 Tailwind가 적용되었습니다.
하지만 Vite는 설정이 투명하게 드러나는 구조이기 때문에, Tailwind CSS를 사용하려면 반드시 직접 postcss.config.js 파일을 생성하고 설정을 명시해주어야 합니다.
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
5. 마무리하며
‘멜로디메이트’를 다시 열면서, 과거의 기술적 선택들을 돌아볼 수 있었습니다. 그때는 미처 인식하지 못했던 아쉬운 부분들이 눈에 들어오기 시작했고, 이를 하나하나 보완해 가며 저 역시 성장할 수 있었습니다.
기술은 빠르게 변화하고, 그만큼 개발자에게는 변화에 민감하게 반응하는 태도가 점점 더 중요해지고 있습니다. 이번 Vite 마이그레이션은 단순히 빌드 도구를 바꾸는 작업을 넘어, 프로젝트를 더 깊이 이해하고, 더 나은 개발 환경을 고민하는 계기가 되었습니다.
혹시 CRA 기반으로 만든 프로젝트가 있다면, 지금이라도 한 번 점검해보시길 권합니다. 실제 전환 과정도 생각보다 간단했고, 막상 해보니 어렵지 않았습니다.
6. 참고자료
- 공식 블로그 – Create React App 지원 종료 안내 (React Team, 2025)
→ CRA 지원 중단 배경과 이후 권장 도구 안내 - 번역글 – Create React App 지원 중단 안내 (by @jiwoochoics)
→ 공식 발표를 한글로 번역한 포스트 - Vite로 마이그레이션하는 방법 정리 (Robin Wieruch)
→ CRA에서 Vite로 전환하는 과정을 단계별로 설명한 블로그 글