본문 바로가기
React

CRA에서 Vite로 마이그레이션: 차세대 툴로 개발 환경 개선하기

by Vintz 2024. 2. 22.
반응형

CRA에서 Vite로 마이그레이션 하기

회사에서 '바쁜 기간 지나면 이건 꼭 해봐야지'했던 것 중에 하나가 바로 빌드 도구 마이그레이션 하기였다.

 

시간이 지남에 따라 프로젝트의 크기가 점점 커지면서, 변경 사항에 대한 소스 코드 갱신 시간이 계속해서 조금씩 증가했다. 그래서 그 잠깐 사이에 개발 흐름이 끊기는 불편함을 너무 개선시키고 싶었다.

Vite를 선택한 이유

내가 현재 프로젝트에 마이그레이션할 빌드 도구로 Vite를 선택한 이유는 다음과 같다:

  • Vite 공식문서의 Vite를 사용해야 하는 이유를 읽고 크게 공감했다.
  • 현재 프로젝트(어드민 서비스)와 잘 맞는 SPA/CSR에 친화적인 빌드 도구이다.(SSR도 지원한다.)
  • 공식문서가 한글 번역으로도 잘 되어 있다.
  • 버전이 5까지 나와서 어느 정도 안정화가 되었다고 생각.
  • 현재 프로젝트가 Webpack에 의존성을 크게 가지고 있지 않다.
  • 마이그레이션에 참고할 자료가 많았다.(이미 해본 사람들이 꽤 있다.)

또한, 예전부터 Vite에 대한 얘기가 꽤 나왔고 궁금했기 때문에 Vite로 마이그레이션을 하기로 결심했다.

프로젝트에 적용할 수 있을까?

다음으로, 현재 프로젝트에 적용할 수 있는지에 대한 여부를 확인했다.

  • Vite 브랜치 생성 & 이동
  • Node.js 버전 체크
  • CRA 템플릿과 Vite 템플릿을 만들어서 다른점 비교해보기(React + TS)

플랜 B를 위해 브랜치를 새로 만들고, 해당 브랜치에서 코드를 작성 했다. 먼저, Vite는 버전 18+ 또는 20+ 이상의 Node.js를 요구한다. 당시 프로젝트의 노드 버전은 지원이 중단된 16.xx.x 버전이었기 때문에, 별 고민 없이 현재 기준의 LTS 버전으로 업데이트했다.

 

다음으로, 다른 라이브러리들과 충돌은 없는지 동작은 잘하는지 확인을 한 후 CRA 템플릿과 Vite 템플릿을 다운 받아 비교해보았다.

  • package.json
    • scripts가 다르다.
    • Vite는 dependencies와 devDependencies가 나누어져 있다.
    • CRA는 수정하면 안될 것 같은 추가적인 설정들이 존재한다.
  • index.html 위치가 다르다.
  • tsconfig.json 설정이 다르다.
    • Vite에는 tsconfig.node.json 추가 설정 파일이 있다.
  • Vite에는 vite(vite.config.ts), eslint(.eslintrc.cjs) 설정 파일이 기본으로 있다.
  • .gitignore 설정이 다르다.
  • src 폴더 내에 assets 폴더가 기본으로 있다.
  • ...등 자잘한 것들

제일 먼저 눈에 띈 것은 Vite 템플릿의 디렉토리 구조가 더 깔끔하다는 것이었다. 후발 주자답게 훨씬 깔끔하고 딱 필요한, 필수적인 요소들만 있는 것 같아서 좋았다. CRA로 프로젝트를 시작할 때는 따로 지워야하는 파일이나 주석들이 많아서 불편했던 것들이 Vite에서는 없었다.

마이그레이션 시작

이제 Webpack에서 Vite로 마이그레이션을 할 때 필수적으로 해야할 것들을 살펴보자.(react-ts 템플릿 기준)

1. Vite 설치

가장 먼저 vite와 리액트 관련 플러그인을 devDependencies에 설치한다.

npm install vite @vitejs/plugin-react --save-dev

2. CRA 의존성 제거

npm uninstall react-scripts

3. package.json scripts 변경

"scripts": {
  "dev": "vite",
  "build": "tsc && vite build",
  "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
  "preview": "vite preview"
},

참고로, Vite는 JSX로 작성한 파일은 확장자를 무조건 .jsx로 명시해줘야 한다.(TS도 마찬가지)

4. vite.config.ts 생성

현재 프로젝트 루트 디렉토리에 vite.config.ts를 생성한다.

touch vite.config.ts

그리고 해당 파일에 세부 설정들을 추가한다. 편의를 위한 서버 옵션 몇 가지, 그리고 기존의 CRA 빌드 출력 디렉토리를 유지하기 위한 옵션도 추가했다.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    host: 'localhost',
    port: 3000, // 포트 설정. 기본값은 5173
    open: true, // 서버 시작 시 브라우저 자동 열기
  },
  build: {
    outDir: 'build',
  },
});

5. tsconfig 수정 및 추가 생성

이번에도 루트 디렉토리에 tsconfig.node.json을 추가로 생성하고, 기존 tsconfig.json을 수정한다.

touch tsconfig.node.json

5-1. tsconfig.node.json

{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true,
    "strict": true
  },
  "include": ["vite.config.ts"]
}

5-2. tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

6. react-app-env.d.ts 제거 vite-env.d.ts 생성

기존의 src/react-app-env.d.ts를 제거하고, vite-env.d.ts를 생성하자.

/// <reference types="vite/client" />

7. index.html 옮기기 %PUBLIC_URL% 제거

index.html 역시 루트 디렉토리로 옮겨야 한다.

mv public/index.html .

이는 의도적으로 위치시킨 것으로, 추가적인 번들링 과정 없이 index.html 파일이 앱의 진입점이 되게끔 하기 위함이다. 따라서, index.html 내에 존재하는 URL에 대해 %PUBLIC_URL%과 같은 Placeholder를 사용할 필요가 없다. %PUBLIC_URL%도 지워주자.

8. index.html에 스크립트 연결

마지막으로, index.tsx까지 연결 시켜주면 기본적인 세팅은 끝이다.

<body>
  <div id="root"></div>
  <script type="module" src="/src/index.tsx"></script>
</body>

만약 npm run dev로 앱이 시작되지 않는다면, node_modules를 제거하고 다시 설치해 보자. 그래도 되지 않는다면 프로젝트에 추가적인 구성이 필요한 것일 수 있다.

트러블슈팅(문제 해결)

마이그레이션 중에 만난 문제들이 있다. 해당 문제에 대한 트러블슈팅 내용이다. 여기서 나오지 않은 여러 트러블슈팅들은 다음 글을 참고하면 좋다.

SVG를 리액트 컴포넌트로 사용하기

어드민 서비스라서 그런지 SVG가 상당히 많다. 이것들을 리액트 컴포넌트로 변환하여 관리하고 있었다.

 

Vite에서도 SVG를 컴포넌트로 변환할 수 있다. vite-plugin-svgr을 devDependencies로 설치한 후, 환경 설정 파일에 다음을 추가하자.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), svgr()],
  server: {
    host: 'localhost',
    port: 3000,
    open: true,
  },
  build: {
    outDir: 'build',
  },
});

v4.0.0 이상부터는 경로 마지막에 ?react 접미사를 붙여서 간단하게 사용할 수 있다.

import DashboardIcon from './assets/dashboard.svg?react';

<DashboardIcon />

TS를 사용하는 경우, src/vite-env.d.ts에 다음을 추가해주어야 한다.

/// <reference types="vite/client" />
/// <reference types="vite-plugin-svgr/client" />

기존의 CRA 환경에서처럼 ReactComponent as..를 사용하고 싶었다. 하지만 다운그레이드를 할 경우 vite의 peer 버전과 맞지 않아서 에러가 난다. 다른 방법이 있을까 싶어 찾아보았는데, 결국 못찾아서 v4.0.0의 방식대로 사용을 했다.

 

다행히 대부분의 SVG들을 파일 한 곳에서 관리하고 있었기 때문에 그렇게 오래 걸리진 않았다.

환경 변수

Vite는 import.meta.env 객체를 이용해 환경 변수에 접근할 수 있고, 이러한 환경 변수는 VITE_라는 접두사를 붙여 선언한다.

VITE_SOME_KEY=123
console.log(import.meta.env.VITE_SOME_KEY) // "123"

자세한 내용은 Vite 공식 문서를 확인하자.

배포 서버 환경에서의 Node.js

이 부분은 정말 생각하지 못한 변수였다. 내가 로컬에서 LTS 버전으로 바꾼 것처럼 쉽게 되는 게 아니었다. 처음엔 단순히 젠킨스에서 저런 로그가 뜨길래 상황을 설명드리고, 버전업을 요청드렸지만 정확한 이유없이 다음에 하자는 말만 돌아왔다.

 

배포 시간이 많이 줄어들긴 했지만, 버전업을 하면 더 개선될 것 같다는 기대감에 바로 포기하긴 싫어서 주변 개발자 지인들에게 여쭤봤다. 확실히 공수가 많이 드는 작업일 수도 있을 것 같았다. 젠킨스 서버의 OS가 오래되어서 18+버전 지원을 안하면 OS 마이그레이션이 필요하다. 그러면 다른 프로젝트에도 영향이 갈 수가 있다는 것이었다. 그리고 서버쪽에서 16 -> 18버전으로 올릴 때 이슈가 꽤 있었다는 의견도 있었다. 그래서 신경쓸 게 확실히 많긴 한 것 같았다. 그럼에도 불구하고 16버전은 지원이 중단되었고, SSL 이슈가 있어서 버전업이 필요하다고 생각했다.

 

이 점을 인지하고 혹시 이런 부분때문인지 정중하게 다시 여쭤봤지만, 결국 현재는 정해진 날짜없이 무기한 연기된 상태다.

얼마나 개선이 되었을까?

배포 소요 시간

Create React App Vite
2 min 23 sec 1 min 26 sec
2 min 22 sec 1 min 23 sec
2 min 28 sec 1 min 28 sec
2 min 27 sec 1 min 25 sec
2 min 31 sec 1 min 24 sec

CRA 환경에서 배포했을 때와 Vite 환경에서 배포했을 때 걸린 시간을 표로 나타내보았다. Vite로 배포 시, CRA에 비해 배포 소요 시간이 약 42%나 줄었다. 정말 만족스러운 결과다.

소스 코드 갱신

개인적으로 정말 만족한 부분이다. 실제로 가장 크게 체감한 부분이기도 하다. 기존에는 빠르면 2초에서 5초까지 걸렸던 것 같은데 지금은 신경쓰이지 않을 만큼 정말 빠르다.

결론

생각보다 정말 빠르게 마이그레이션을 완료했다. 아직까지 문제된 부분도 없고, 잘 사용하고 있다. 들인 노력에 비해 개발 효율성이 높아진 것 같아 아주 만족하고 있다.

참고

Vite - 공식문서
Sanghoon's Blog - create-react-app 기본 번들러에서 Vite로 마이그레이션하기
멍개(네이버 블로그) - [react] CRA로 만든 프로젝트 vite로 마이그레이션 하기
Robin Wieruch - Migrate to Vite from Create React App (CRA)
반응형