Files
2026-10-26 12:28:48 +09:00

82 lines
3.8 KiB
TypeScript

import { fileURLToPath, URL } from 'node:url'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vueDevTools from 'vite-plugin-vue-devtools'
/** 루트 .env 에 값이 없을 때만 쓰는 기본 포트 (.env.*.example 과 같은 값) */
const DEFAULT_FRONTEND_PORT = 5173
const DEFAULT_BACKEND_PORT = 3000
/**
* 환경변수를 양의 정수로 파싱한다.
* 값이 없거나(undefined·빈 문자열) 숫자가 아니거나 0 이하면 기본값을 반환한다.
* (compose 는 정의되지 않은 변수를 빈 문자열로 주입한다 — `Number('') === 0` 방어.
* backend 의 `shared/utils/env.ts` 와 같은 규칙)
*/
function parsePositiveInt(value: string | undefined, fallback: number): number {
if (value === undefined || value.trim() === '') return fallback
const parsed = Number(value)
if (!Number.isInteger(parsed) || parsed <= 0) return fallback
return parsed
}
// https://vite.dev/config/
export default defineConfig(({ mode }) => {
// 포트·주소의 SSOT 는 루트 `.env.{mode}` 다 (.claude/rules/env-structure.md).
// 프론트엔드 디렉터리 밖이라 vite 가 자동으로 읽지 않으므로 여기서 직접 로드한다.
// 접두어 ''(전체)로 읽지만, 읽은 값은 **dev 서버 설정에만** 쓰고 클라이언트 번들에는 넣지 않는다
// (VITE_ 접두어가 없는 값을 번들에 넣으면 시크릿이 노출된다 — .claude/rules/security.md).
// 파일이 없으면(클론 직후 등) process.env 만 반영되고 아래 기본값으로 떨어진다.
const rootEnv = loadEnv(mode, fileURLToPath(new URL('..', import.meta.url)), '')
const frontendPort = parsePositiveInt(rootEnv.FRONTEND_PORT, DEFAULT_FRONTEND_PORT)
const backendPort = parsePositiveInt(rootEnv.BACKEND_PORT, DEFAULT_BACKEND_PORT)
// 컨테이너(바인드 마운트) 환경에서만 true — docker-compose.dev.yml 이 주입한다.
// 호스트에서 직접 `npm run dev` 로 띄울 때는 OS 파일 감시 이벤트가 정상 동작하므로
// 폴링을 켜면 CPU 만 쓰고 기동이 느려진다.
const usePolling = rootEnv.CHOKIDAR_USEPOLLING === 'true'
return {
plugins: [
vue(),
vueJsx(),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
server: {
// ngrok 등 외부 도메인 접근 허용 (DNS Rebinding 보호 비활성화)
allowedHosts: true,
// 루트 .env 의 FRONTEND_PORT 를 따른다.
// 컨테이너로 띄울 때는 compose 의 `--port`(컨테이너 내부 고정 포트)가 이 값을 덮어쓴다.
port: frontendPort,
// 포트가 이미 사용 중이면 조용히 다음 포트로 옮기지 않고 실패한다.
// 루트 .env 의 CORS_ORIGIN 과 compose 포트 매핑이 이 포트에 묶여 있어,
// 말없이 5174 로 옮겨가면 API 요청이 원인 모르게 막힌다 (fail-fast).
strictPort: true,
// 도커 바인드 마운트에서는 inotify 가 호스트 쪽 변경을 잡지 못해 폴링이 필요하다
watch: usePolling ? { usePolling: true } : undefined,
// 개발 서버 API 프록시.
// useApi 의 baseURL 은 '/api'(상대경로)라 프록시가 없으면 vite 가 SPA 폴백으로
// index.html 을 200 으로 돌려준다 — 요청이 조용히 HTML 을 받아 실패한다.
//
// 기본 대상은 호스트에서 띄울 때 기준이고(백엔드 컨테이너가 BACKEND_PORT 를 호스트에 노출한다),
// 프론트까지 docker 로 띄울 때는 compose 가 VITE_DEV_API_PROXY=http://backend:${BACKEND_PORT} 를 주입한다.
proxy: {
'/api': {
target: rootEnv.VITE_DEV_API_PROXY || `http://localhost:${backendPort}`,
changeOrigin: true,
},
},
},
}
})