개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

Vite — 빠른 개발 서버와 번들링을 제공하는 빌드 도구

2026년 6월 15일
빌드도구번들링HMRVite

기존 번들러(Webpack 등)는 개발 서버를 시작할 때 애플리케이션 전체를 번들링합니다. 프로젝트 규모가 커질수록 서버 시작과 파일 변경 반영에 수십 초가 걸리는 문제가 생깁니다.

Vite는 이 문제를 다른 방식으로 해결합니다. 개발 서버에서는 번들링을 하지 않습니다. 대신 브라우저가 지원하는 ES 모듈(ESM)을 그대로 활용하여 요청된 파일만 필요할 때 변환합니다. 서버 시작 시간이 거의 없고, 파일 변경은 밀리초 단위로 반영됩니다.

프로덕션 빌드는 Rollup을 사용합니다. 트리쉐이킹, 코드 분할, 청크 최적화 등 프로덕션에 필요한 최적화를 모두 적용합니다.

설치 및 프로젝트 생성

npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev

또는 기존 프로젝트에 직접 추가합니다.

npm install -D vite @vitejs/plugin-react

1. Dev Server

Vite 개발 서버의 핵심은 번들링을 하지 않는다는 것입니다. 브라우저가 import 구문을 직접 해석하고 필요한 모듈을 서버에 요청합니다. 서버는 요청받은 파일만 변환(TypeScript → JavaScript, JSX → JS)하여 응답합니다.

  Webpack 방식
  +---------+    전체 번들    +--------+    요청    +----------+
  | 소스    | ------------> | bundle | ---------> | 브라우저 |
  | 파일들  |    (느림)      |  .js   |            |          |
  +---------+               +--------+            +----------+

  Vite 방식
  +---------+    파일 요청   +--------+  ESM 응답  +----------+
  | 소스    | <------------ | Vite   | ---------> | 브라우저 |
  | 파일들  |    필요한 것만 | Server |            |  (ESM)   |
  +---------+               +--------+            +----------+
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    host: true,           // 네트워크에서 접근 허용
    open: true,           // 브라우저 자동 열기
    proxy: {
      "/api": {
        target: "http://localhost:8080",
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ""),
      },
    },
  },
});

2. HMR (Hot Module Replacement)

HMR은 파일이 변경되면 해당 모듈만 교체합니다. 페이지를 새로고침하지 않으므로 React 컴포넌트의 상태가 유지됩니다. 폼에 입력한 값이나 열려 있는 모달이 사라지지 않고 변경 사항만 반영됩니다.

  파일 변경 감지 (fs.watch)
         |
         v
  변경된 모듈과 의존 모듈 분석
         |
         v
  WebSocket으로 브라우저에 알림
         |
         v
  브라우저: 변경된 모듈만 교체
         |
         v
  React 상태 유지 (Fast Refresh)

@vitejs/plugin-react는 React Fast Refresh를 통합하여 컴포넌트 단위의 HMR을 지원합니다. 컴포넌트 코드가 변경되면 해당 컴포넌트만 교체되고, hooks 상태는 가능한 경우 보존됩니다.

// 이 컴포넌트를 수정하면:
// - 페이지 새로고침 없이 변경 반영
// - count 상태 유지
// - 렌더링 결과만 즉시 업데이트

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

  return (
    <button onClick={() => setCount(c => c + 1)}>
      {/* 이 텍스트를 바꿔도 count는 유지됨 */}
      클릭 횟수: {count}
    </button>
  );
}

3. Build

npm run build 명령은 Rollup을 사용하여 최적화된 번들을 생성합니다. 트리쉐이킹으로 사용하지 않는 코드를 제거하고, 코드 분할로 청크를 나눕니다.

npm run build
# dist/ 디렉터리에 결과물 생성
// vite.config.ts — 빌드 설정
export default defineConfig({
  build: {
    outDir: "dist",
    sourcemap: true,          // 소스맵 생성 (프로덕션 디버깅용)
    minify: "esbuild",        // "terser" | "esbuild" | false
    target: "es2015",         // 지원 브라우저 타겟
    rollupOptions: {
      output: {
        // 수동 청크 분할 — vendor 청크를 분리하여 캐싱 효율 향상
        manualChunks: {
          vendor: ["react", "react-dom"],
          router: ["react-router-dom"],
        },
      },
    },
    // 청크 크기 경고 임계값 (바이트)
    chunkSizeWarningLimit: 500,
  },
});

빌드 결과물 분석은 vite-bundle-visualizer 플러그인으로 시각화할 수 있습니다.


4. Plugin

Plugin: Vite의 변환 파이프라인에 기능을 추가하거나 수정하는 확장 시스템

Vite 플러그인은 Rollup 플러그인 API와 호환됩니다. 대부분의 Rollup 플러그인을 Vite에서 그대로 사용할 수 있습니다.

npm install -D @vitejs/plugin-react vite-plugin-svgr vite-pwa
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";
import { VitePWA } from "vite-pwa";

export default defineConfig({
  plugins: [
    // React JSX 변환 + Fast Refresh
    react(),

    // SVG를 React 컴포넌트로 임포트
    svgr(),

    // PWA 설정
    VitePWA({
      registerType: "autoUpdate",
      manifest: {
        name: "My App",
        icons: [{ src: "/icon-192.png", sizes: "192x192" }],
      },
    }),
  ],
});
// svgr 플러그인 사용 예시
import LogoIcon from "./logo.svg?react";

function Header() {
  return <LogoIcon className="w-8 h-8" />;
}

5. Alias

Alias: 긴 상대 경로 대신 짧은 별칭을 사용할 수 있도록 경로를 매핑하는 설정

깊은 디렉터리 구조에서 ../../../components/Button처럼 긴 상대 경로를 사용하면 유지보수가 어렵습니다. Alias로 절대 경로 별칭을 설정합니다.

// vite.config.ts
import { defineConfig } from "vite";
import path from "path";

export default defineConfig({
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
      "@components": path.resolve(__dirname, "./src/components"),
      "@hooks": path.resolve(__dirname, "./src/hooks"),
      "@utils": path.resolve(__dirname, "./src/utils"),
      "@assets": path.resolve(__dirname, "./src/assets"),
    },
  },
});

TypeScript에서 경로를 인식하려면 tsconfig.json에도 동일하게 추가합니다.

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@hooks/*": ["src/hooks/*"]
    }
  }
}
// 사용 전
import { Button } from "../../../components/ui/Button";
import { useAuth } from "../../hooks/useAuth";

// 사용 후
import { Button } from "@components/ui/Button";
import { useAuth } from "@hooks/useAuth";

6. Env

Vite는 .env 파일에서 환경 변수를 읽습니다. VITE_ 접두사가 붙은 변수만 클라이언트 코드에서 접근할 수 있습니다. 접두사가 없는 변수는 서버(Node.js) 전용이므로 브라우저에 노출되지 않습니다.

# .env — 모든 환경에서 적용
VITE_APP_TITLE=My App
VITE_API_URL=https://api.example.com

# .env.development — 개발 환경에서 적용
VITE_API_URL=http://localhost:8080

# .env.production — 프로덕션 환경에서 적용
VITE_API_URL=https://api.example.com

# 비공개 변수 — 브라우저에서 접근 불가
DB_PASSWORD=secret   # VITE_ 접두사 없음
// 클라이언트 코드에서 접근
const apiUrl = import.meta.env.VITE_API_URL;
const isProd = import.meta.env.PROD;      // boolean
const isDev = import.meta.env.DEV;        // boolean
const mode = import.meta.env.MODE;        // "development" | "production"

// TypeScript 타입 선언 (선택)
// src/vite-env.d.ts
interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_TITLE: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

.env 파일은 .gitignore에 추가해야 합니다. 민감한 값은 .env.local에 저장하고 git에서 제외합니다.


7. Asset

Vite에서는 정적 파일을 JavaScript 모듈처럼 임포트합니다. 임포트 결과는 기본적으로 빌드된 파일의 URL입니다.

// 이미지 — URL 반환
import logoUrl from "./assets/logo.png";
// logoUrl: "/assets/logo-Bq3sFpPB.png" (해시 포함)

// ?url 접미사 — 명시적으로 URL 요청
import iconUrl from "./icon.svg?url";

// ?raw 접미사 — 파일 내용을 문자열로 가져옴
import svgContent from "./icon.svg?raw";
// svgContent: "<svg>...</svg>"

// ?inline 접미사 — base64 Data URL로 인라인 처리
import smallIcon from "./icon.png?inline";
// 사용 예시
function App() {
  return (
    <div>
      {/* URL로 사용 */}
      <img src={logoUrl} alt="로고" />

      {/* dangerouslySetInnerHTML으로 SVG 인라인 삽입 */}
      <div dangerouslySetInnerHTML={{ __html: svgContent }} />
    </div>
  );
}

public/ 디렉터리에 있는 파일은 변환 없이 그대로 복사됩니다. 코드에서 참조하지 않아도 되는 파일(favicon, robots.txt 등)을 여기에 둡니다.

  public/
    favicon.ico      → /favicon.ico
    robots.txt       → /robots.txt
    og-image.png     → /og-image.png

8. SSR

SSR: 서버에서 React 컴포넌트를 HTML로 렌더링하여 전송하는 서버 사이드 렌더링 지원 모드

Vite는 SSR 개발을 위한 API를 제공합니다. Next.js, Nuxt처럼 프레임워크가 아닌 도구이므로, SSR 서버 구현은 직접 해야 합니다. 또는 Vite를 사용하는 SSR 프레임워크(Remix, SvelteKit)를 활용합니다.

// vite.config.ts — SSR 빌드 설정
export default defineConfig({
  build: {
    ssr: true,                     // SSR 모드 활성화
    rollupOptions: {
      input: "src/entry-server.tsx",  // 서버 진입점
    },
  },
});
// src/entry-server.tsx
import { renderToString } from "react-dom/server";
import App from "./App";

export function render(url: string) {
  const html = renderToString(<App />);
  return { html };
}
// server.ts — Express 서버 예시
import express from "express";
import { createServer as createViteServer } from "vite";

const app = express();
const vite = await createViteServer({ server: { middlewareMode: true } });

app.use(vite.middlewares);

app.use("*", async (req, res) => {
  // Vite가 변환한 SSR 모듈 로드
  const { render } = await vite.ssrLoadModule("/src/entry-server.tsx");
  const { html: appHtml } = await render(req.originalUrl);

  const template = await vite.transformIndexHtml(
    req.originalUrl,
    await fs.readFile("index.html", "utf-8")
  );

  const html = template.replace("<!--ssr-outlet-->", appHtml);
  res.send(html);
});

실제 프로덕션에서 SSR이 필요하다면 Vite 기반의 Remix나 TanStack Start 같은 프레임워크를 사용하는 것이 권장됩니다.


정리

Vite는 개발 경험과 프로덕션 출력 품질을 동시에 잡습니다.

개념역할
Dev Server번들링 없는 ESM 기반 빠른 개발 서버
HMR변경 모듈만 교체, 페이지 상태 유지
BuildRollup 기반 트리쉐이킹과 코드 분할
Plugin기능 확장 (React, SVG, PWA 등)
Alias경로 별칭으로 가독성 향상
Env.env 파일로 환경 변수 관리
Asset정적 파일을 모듈처럼 임포트
SSR서버 사이드 렌더링 지원 API

특별한 이유 없이 새 프로젝트를 시작한다면 Vite가 현재 가장 합리적인 선택입니다. Create React App은 더 이상 유지보수되지 않으며, Webpack 기반 설정보다 훨씬 빠른 개발 경험을 제공합니다.

← 이전 글TanStack Query — 서버 상태를 캐시하고 동기화하는 라이브러리
다음 글 →Vitest — Vite 기반 테스트 프레임워크