troubleshooting2025-02-14·9 min·207/348

Vite 개발 서버 설정 문제: 핫 리로딩 및 프록시 구성

Vite 개발 서버 설정 시 발생하는 문제와 핫 리로딩, 프록시 설정 방법을 설명합니다.

Vite 개발 서버 설정 문제

Introduction

Vite는 빠르고 경량적인 프론트엔드 개발 서버로, 네이티브 ES 모듈을 활용하여 즉시 시작되는 개발 경험을 제공합니다. 하지만 프로젝트 규모가 커지면서 핫 모듈 리플레이스먼트(HMR), 프록시 설정, 환경 변수 등에서 다양한 문제에 직면할 수 있습니다. 이 글에서는 Vite 개발 서버 설정 문제 해결 방법을 다루겠습니다.

Environment

// package.json
{
  "name": "my-vite-app",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.2.0",
    "vite": "^5.0.0",
    "typescript": "^5.3.0"
  }
}
npx vite --version
# vite/5.0.11

Problem

Vite 개발 서버 설정 시 발생하는 문제들:

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

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    proxy: '/api'
  }
});

// 문제점:
// 1. HMR이 작동하지 않음
// 2. 프록시 설정 오류
// 3. 환경 변수 로딩 실패
# 개발 서버 실행 시 에러
npm run dev

# 에러:
# Port 3000 is already in use
# [vite] server error: Cannot use 'in' operator to search for 'VITE_' in undefined
// 환경 변수 접근 오류
const apiUrl = import.meta.env.VITE_API_URL;
console.log(apiUrl);  // undefined

// 환경 변수 파일 로딩 실패
// .env 파일에 VITE_API_URL 정의했으나 접근 불가

Analysis

Vite 설정 구조를 분석했습니다:

// vite.config.ts 구조 분석
import { defineConfig } from 'vite';

export default defineConfig({
  // 플러그인 설정
  plugins: [],
  
  // 개발 서버 설정
  server: {
    port: 3000,
    strictPort: false,
    host: 'localhost',
    open: true,
    proxy: {}
  },
  
  // 빌드 설정
  build: {
    outDir: 'dist',
    sourcemap: true,
    minify: 'terser'
  },
  
  // 환경 변수 접두사
  envPrefix: 'VITE_',
  
  // 미리보기 설정
  preview: {
    port: 4173
  }
});
# Vite 환경 변수 파일 우선순위
# .env                    # 모든 경우
# .env.local              # 모든 경우, git 무시
# .env.[mode]             # 특정 모드
# .env.[mode].local       # 특정 모드, git 무시

Solution

1단계: 기본 설정 구성

// vite.config.ts
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig(({ mode }) => {
  // 환경 변수 로드
  const env = loadEnv(mode, process.cwd(), '');
  
  return {
    plugins: [react()],
    
    resolve: {
      alias: {
        '@': path.resolve(__dirname, './src'),
        '@components': path.resolve(__dirname, './src/components'),
        '@utils': path.resolve(__dirname, './src/utils')
      }
    },
    
    server: {
      port: 3000,
      strictPort: false,
      host: 'localhost',
      open: true,
      cors: true,
      
      // 에러 오버레이 설정
      appType: 'spa'
    },
    
    build: {
      outDir: 'dist',
      sourcemap: true,
      minify: 'terser',
      
      // 번들 분석
      reportCompressedSize: true,
      chunkSizeWarningLimit: 1000,
      
      // 코드 분할
      rollupOptions: {
        output: {
          manualChunks: {
            vendor: ['react', 'react-dom'],
            utils: ['lodash']
          }
        }
      }
    },
    
    envPrefix: 'VITE_',
    
    css: {
      devSourcemap: true
    }
  };
});

2단계: 프록시 설정

// vite.config.ts (프록시 설정)
export default defineConfig({
  server: {
    port: 3000,
    proxy: {
      // 기본 API 프록시
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        secure: false,
        rewrite: (path) => path.replace(/^\/api/, '')
      },
      
      // WebSocket 프록시
      '/ws': {
        target: 'ws://localhost:8080',
        ws: true,
        changeOrigin: true
      },
      
      // GraphQL 프록시
      '/graphql': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        secure: false
      },
      
      // 정적 파일 프록시
      '/uploads': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    },
    
    // 프록시 커스터마이징
    configure: (proxy) => {
      proxy.on('error', (err) => {
        console.log('proxy error', err);
      });
      proxy.on('proxyReq', (proxyReq, req, res) => {
        console.log('Proxying:', req.method, req.url);
      });
      proxy.on('proxyRes', (proxyRes, req, res) => {
        console.log('Response from proxy:', proxyRes.statusCode);
      });
    }
  }
});

3단계: HMR 최적화

// vite.config.ts (HMR 설정)
export default defineConfig({
  server: {
    hmr: {
      // HMR 연결 설정
      host: 'localhost',
      port: 3000,
      
      // WebSocket 프로토콜
      protocol: 'ws',
      
      // HMR 업데이트 필터링
      overlay: {
        errors: true,
        warnings: false
      }
    },
    
    watch: {
      // 파일 감시 설정
      usePolling: false,
      interval: 100,
      
      // 무시할 디렉토리
      ignored: ['**/node_modules/**', '**/dist/**', '**/.git/**']
    }
  },
  
  // 환경 변수 정의
  define: {
    __APP_VERSION__: JSON.stringify(process.env.npm_package_version),
    __DEV_MODE__: JSON.stringify(process.env.NODE_ENV)
  }
});

4단계: 환경 변수 관리

# .env.development
VITE_API_URL=http://localhost:8080
VITE_APP_TITLE=My App (Dev)
VITE_LOG_LEVEL=debug

# .env.production
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=My App
VITE_LOG_LEVEL=error

# .env.local (git 무시)
VITE_API_KEY=your-secret-key
// src/config/env.ts
interface EnvConfig {
  API_URL: string;
  APP_TITLE: string;
  LOG_LEVEL: string;
}

export const env: EnvConfig = {
  API_URL: import.meta.env.VITE_API_URL || 'http://localhost:3000',
  APP_TITLE: import.meta.env.VITE_APP_TITLE || 'My App',
  LOG_LEVEL: import.meta.env.VITE_LOG_LEVEL || 'info'
};

// 타입 안전한 환경 변수 사용
export function getEnv(key: keyof EnvConfig): string {
  const value = env[key];
  if (!value) {
    throw new Error(`Environment variable ${key} is not defined`);
  }
  return value;
}

Lessons Learned

  1. HMR 설정: HMR이 작동하지 않으면 server.watch 옵션과 파일 감시 설정을 확인해야 합니다
  2. 프록시 설정: CORS 문제 해결을 위해 changeOrigin: true 옵션을 반드시 설정해야 합니다
  3. 환경 변수 접두사: Vite 환경 변수는 VITE_ 접두사가 붙어야 클라이언트에서 접근 가능합니다
  4. 타입 안전성: import.meta.env 타입을 정의하여 환경 변수의 타입 안전성을 확보해야 합니다
  5. 빌드 최적화: 코드 분할과 청크 크기 경고를 설정하여 빌드 결과를 최적화해야 합니다

이 블로그는 외부 스폰서십, 제휴 마케팅 또는 광고 수익을 받지 않습니다.