Logo

CRA 대신에 Vite로 React 프로젝트 시작하기

이번 포스팅에서는 차세대 번들러인 Vite 사용하여 React 프로젝트를 생성하고 설정하는 방법에 대해서 알아보겠습니다.

Vite에 대한 기초적인 내용에 대해서는 별도 포스팅에서 자세히 다루고 있으니 참고하세요.

CRA 🆚 Next.js 🆚 Vite

오랫동안 React 프로젝트를 만들기 위해서 Create React App라는 CLI 도구가 사용되었습니다. State of JS 2023 설문 결과를 보시면 Create React App을 사용해봤다는 응답자가 90%가 넘을 정도로 CRA는 오랫동안 React 생태계에서 프로젝트를 생성하는 표준처럼 여겨졌습니다.

state-of-js-2023-libraries

하지만 CRA는 2022년 4월 이후로 업데이트가 전혀 없으며, React 공식 문서에서도 사라지면서 사실 상 폐기(deprecated) 수순을 밟고 있습니다.

위 그래프에서 5위를 차지한 Next.js도 React 프로젝트를 만들기 위해서 많이 사용되는데요. 하지만 Next.js는 프로젝트 생성 뿐만 아니라 라우팅, 데이터 패칭(fetching), Server-Side Rendering(SSR), Static Site Generation(SSG), 이미지 최적화 등 온갖 부가 기능을 지원하는 메타 프레임워크입니다. 따라서 제대로 활용하려면 배워야할 것도 많고 CRA처럼 간단하게 React 프로젝트를 만들어 보는데는 적합한 도구가 아니죠.

이러한 이유로 최근에 Vite가 CRA의 대안 기술로 부상하고 있습니다. Vite를 사용하면 CRA처럼 간편하게 React 프로젝트를 만들 수 있을 뿐만 아니라, CRA보다 훨씬 빠르고 쾌적한 개발 서버를 사용할 수 있습니다.

뿐만 아니라, CRA로 만든 프로젝트는 규모가 커지면 결국 돌이킬 수 없는 eject를 해야한다는 부담이 있었는데, Vite는 그런 걱정없이 소규모 프로젝트부터 대규모 프로젝트까지 범용적으로 쓸 수 있도록 설계되어 있습니다.

React 프로젝트 생성

우선 Vite를 사용하여 React 프로젝트를 생성해보도록 하겠습니다.

Node.js를 사용하신다면 터미널에서 npm 명령어를 사용하여 Vite 프로젝트를 시작할 수 있습니다.

$ npm create vite@latest

차세대 자바스크립트 런타임인 Bun을 사용하신다면 bun 명령어를 사용하시면 됩니다.

$ bun create vite

위 명령어를 실행하면 프로젝트 3가지 질문이 나오는데요. 프로젝트 이름은 vite-react라고 하고, 프레임워크는 React를 선택합니다. 요즘 대부분의 React 프로젝트는 타입스크립트로 개발하는 추세이므로 variant는 TypeScript를 선택합니다.

참고로 TypeScript + SWC를 선택하시면 타입스크립트의 내장 컴파일러인 TSC 대신에 SWC(Speedy Web Compiler)를 사용하게 됩니다. 대형 프로젝트에서는 빌드 속도의 향상을 기대할 수 있지만 소규모 프로젝트에서는 오히려 설정만 복잡해질 수 있습니다.

✔ Project name: … vite-react
✔ Select a framework: › React
✔ Select a variant: › TypeScript

Scaffolding project in /Users/daleseo/Temp/vite-react...

Done. Now run:

  cd vite-react
  bun install
  bun run dev

다양한 자바스크립트 프로젝트 생성법에 대해서는 별도 포스팅에서 자세히 다루고 있으니 참고 바랍니다.

의존 패키지 설치

앞에서 프로젝트를 생성하면 나오는 안내대로 프로젝트 폴더에 들어가서 패키지를 설치하도록 하겠습니다. (Node.js를 쓰신다면 bun install 대신에 npm install을 실행하시면 됩니다.)

$ cd vite-react
$ bun install v1.1.22-canary.96 (df33f2b2)

+ @eslint/js@9.9.0
+ @types/react@18.3.4
+ @types/react-dom@18.3.0
+ @vitejs/plugin-react@4.3.1
+ eslint@9.9.0
+ eslint-plugin-react-hooks@5.1.0-rc-fb9a90fa48-20240614
+ eslint-plugin-react-refresh@0.4.10
+ globals@15.9.0
+ typescript@5.5.4
+ typescript-eslint@8.2.0
+ vite@5.4.2
+ react@18.3.1
+ react-dom@18.3.1

195 packages installed [2.68s]

React 관련 패키지 뿐만 아니라 TypeScript를 선택했기 때문에 관련 패키지가 설치가 됩니다. 또한, 자바스크립트에서 가장 많이 사용되는 린터(linter)인 ESLint 관련 패키지도 설치가 된 것을 볼 수 있습니다.

다음과 같이 전형적인 React 프로젝트의 디렉토리 구조가 잡혀있는 것을 확인할 수 있을 것입니다.

$ tree -L 2 -I node_modules
.
├── README.md
├── bun.lockb
├── eslint.config.js
├── index.html
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

4 directories, 15 files

개발 서버 구동

개발자 입장에서 Vite가 가장 매력적인 이유는 무엇보다 말도 안 되게 빠르고 올라가고 실시간으로 코드 변경을 감시하여 반영해주는 개발 서버입니다.

터미널에서 bun run dev 또는 npm run dev 명령어를 실행하면 개발 서버가 구동됩니다.

$ bun run dev
$ vite

  VITE v5.4.2  ready in 430 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

이제 브라우저에서 http://localhost:5173/을 접속해보시면 Vite와 React 로고, 그 아래 카운터 버튼이 있는 웹사이트 하나가 뜰 것입니다.

Vite의 개발 서버는 프로젝트 내의 소스 코드를 변경되면 눈 깜짝할 사이에 브라우저에 반영을 해줍니다. 이러한 HMR(Hot Module Replacement) 기능은 Webpack이나 Parcel과 같은 기존 번들러도 지원했지만 Vite는 내부적으로 Go 언어로 작성된 esbuild를 사용하기 때문에 속도 체감이 크게 납니다.

간단한 실습을 위해서 src/App.tsx 파일을 아래와 같이 수정한 후 저장해보세요. 코드 편집기에서 파일을 저장하자 마자 수정분이 웹사이트에 반영이 되는 것을 볼 수 있으실 것입니다. 🏎️

src/App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          {/* count is {count} */}          카운트는 {count} 입니다.        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

테스트 설정

개인 프로젝트나 시제품 프로토타이핑(prototyping)하시는 게 아니라면 테스트 코드를 작성하시는 게 바람직하겠죠? 테스팅 프레임워크로 CRA 시절에는 Jest를 많이 썼었는데, Vite로 넘어와서는 Vite와 찰딱 궁합인 Vitest를 많이 사용합니다.

자 그럼 우선 Vitest와 더불어 프론트엔드 테스팅에 필요한 Testing Library를 설치하겠습니다. 두 패키지 모두 애플리케이션 실행에 필요한 의존성이 아니므로 개발 의존성으로 설치해야 합니다.

Node.js를 사용하는 프로젝트에서는 터미널에서 npm 명령어를 사용하여 Vitest를 설치합니다.

$ npm add -D vitest @testing-library/react @testing-library/user-event @testing-library/jest-dom happy-dom

차세대 자바스크립트 런타임인 Bun을 사용하는 프로젝트에서는 bun 명령어를 사용해서 설치합니다.

$ bun add -D vitest @testing-library/react @testing-library/user-event @testing-library/jest-dom happy-dom
bun add v1.1.22-canary.96 (df33f2b2)

installed vitest@2.0.5 with binaries:
 - vitest
installed @testing-library/react@16.0.0
installed @testing-library/user-event@14.5.2
installed @testing-library/jest-dom@6.4.8
installed happy-dom@15.0.0

6 packages installed [568.00ms]

그리고 vite.config.ts을 열고, 테스트 환경으로 happy-dom을 사용하도록 설정해줍니다.

vite.config.ts
/// <reference types="vitest" />import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'happy-dom',  },
})

이제 src 폴더 안에 App.test.tsx 파일을 생성하고 테스트 코드를 작성하겠습니다. 화면에 “Vite + React”라를 제목이 잘 표시되고, 버튼을 클릭하면 카운트가 증가하는지 바뀌는지 검증합니다.

src/App.test.tsx
import { expect, test } from "vitest";
import "@testing-library/jest-dom/vitest";
import { screen, render } from "@testing-library/react";
import { userEvent } from "@testing-library/user-event";
import App from "./App";

test("App", async () => {
  render(<App />);

  expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(
    "Vite + React"
  );

  expect(
    screen.getByRole("button", { name: "카운트는 0 입니다." })
  ).toBeInTheDocument();

  userEvent.click(screen.getByRole("button"));

  expect(
    await screen.findByRole("button", { name: "카운트는 1 입니다." })
  ).toBeInTheDocument();
});

npx 또는 bunx 명령어를 사용하여 Vitest로 테스트를 실행하면 통과할 것입니다.

$ npx vitest

 DEV  v2.0.5 /Users/daleseo/Temp/vite-react

 ✓ src/App.test.tsx (1)
   ✓ App

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  10:04:54
   Duration  368ms (transform 25ms, setup 0ms, collect 137ms, tests 26ms, environment 83ms, prepare 29ms)


 PASS  Waiting for file changes...
       press h to show help, press q to quit
$ bunx vitest

 DEV  v2.0.5 /Users/daleseo/Temp/vite-react

 ✓ src/App.test.tsx (1)
   ✓ App

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  10:05:30
   Duration  309ms (transform 24ms, setup 0ms, collect 96ms, tests 25ms, environment 71ms, prepare 28ms)


 PASS  Waiting for file changes...
       press h to show help, press q to quit

프로덕션 빌드

Vite는 프로덕션(production) 빌드(build) 시에는 Rollup을 사용하여 상용 환경에 최적화된 번들을 생성해줍니다. 때문에 Rollup의 오래 시간 축적된 풍부한 플러그인 생태계를 그대로 누릴 수 있다는 장점이 있습니다.

웹사이트를 빌드하면 우선 타입스크립트로 컴파일(compile)한 후에 프로젝트의 최상위 경로에 dist 폴더가 생기고 그 안에 HTML, CSS, JS 파일이 생기는데요.

Bun을 사용하신다면 터미널에서 bun run build 명령어를 실행하면 됩니다.

$ bun run build
$ tsc -b && vite build
vite v5.4.2 building for production...
✓ 34 modules transformed.
dist/index.html                   0.46 kB │ gzip:  0.30 kB
dist/assets/react-CHdo91hT.svg    4.13 kB │ gzip:  2.05 kB
dist/assets/index-DiwrgTda.css    1.39 kB │ gzip:  0.72 kB
dist/assets/index-4HOKWW29.js   143.20 kB │ gzip: 46.12 kB
✓ built in 334ms

Node.js를 쓰신다면 터미널에서 npm run build 명령어를 실행하면 됩니다.

$ npm run build

> vite-react@0.0.0 build
> tsc -b && vite build

vite v5.4.2 building for production...
✓ 34 modules transformed.
dist/index.html                   0.46 kB │ gzip:  0.30 kB
dist/assets/react-CHdo91hT.svg    4.13 kB │ gzip:  2.05 kB
dist/assets/index-DiwrgTda.css    1.39 kB │ gzip:  0.72 kB
dist/assets/index-4HOKWW29.js   143.20 kB │ gzip: 46.12 kB
✓ built in 347ms

dist 폴더에 생성된 번들 파일들을 그대로 상용 서버에 올리면 웹사이트가 배포가 완료되게 됩니다. 물론 실제 프로젝트에서는 직접 올리지 않고 CI/CD 도구를 통해서 배포를 자동화하겠죠?

프로덕션 프리뷰

Vite를 통해서 개발 서버 기준이 아닌 최종 빌드된 번들 파일들을 기준으로 웹사이트를 돌려볼 수도 있습니다. 배포 하기 전에 상용 환경에서 웹사이트가 어떻게 보일지 로컬 환경에서 확인해보고 싶을 때 유용합니다.

터미널에서 bun run preview 또는 npm run preview 명령어를 실행하면 프로덕션 프리뷰를 할 수 있습니다.

$ bun run preview
$ vite preview
  ➜  Local:   http://localhost:4173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

이제 브라우저에서 http://localhost:4173/을 열어보면 상용 환경에서 웹사이트가 어떻게 보일지 확인할 수 있을 것입니다. 🎉

참고로 아까 전에 개발 서버로 웹사이트를 띄울 때는 5173 포트를 사용했었는데요. 프로덕션 프리뷰를 할 때는 포트 충돌이 나지 않도록 4173을 기본 포트로 사용합니다. 참 세심한 배려죠?

마치면서

지금까지 CRA 대신에 Vite를 사용해서 어떻게 React 프로젝트를 생성하고 설정하는지에 대해서 알아보았는데요. 사실 Vite는 비단 React 뿐만 아니라 Vue, Svelte, Lit, Solid, Qwik 등 다른 프레임워크로 프로젝트를 만들 때도 널리 사용되고 있습니다. 따라서 한 번 익혀두면 여러모로 쓸모가 많은 기술이니 이번 기회에 잘 숙지해두셨으면 좋겠습니다.

Vite 연관된 포스팅은 Vite 태그를 통해서 쉽게 만나보세요!