From 32eb4fafca5a575eca462ac5bc69138e16bf2429 Mon Sep 17 00:00:00 2001 From: ttipo Date: Thu, 17 Sep 2026 20:28:14 +0900 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20dev=20=EC=84=9C=EB=B2=84=20API?= =?UTF-8?q?=20=ED=94=84=EB=A1=9D=EC=8B=9C=20=EC=B6=94=EA=B0=80=20=E2=80=94?= =?UTF-8?q?=20=ED=94=84=EB=A1=A0=ED=8A=B8=EA=B0=80=20=EB=B0=B1=EC=97=94?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=ED=98=B8=EC=B6=9C=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EB=AA=BB=ED=95=98=EB=8D=98=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useApi 의 baseURL 이 '/api' 상대경로인데 vite dev 서버에 프록시가 없어, 브라우저의 API 요청이 백엔드에 닿지 않고 SPA 폴백으로 index.html 을 받고 있었다. 더 나쁜 것은 조용히 실패한다는 점이다 — HTTP 200 에 Content-Type: text/html 이라 인터셉터의 success 판별을 통과하지 못하고 HTML 문자열이 그대로 반환된다. 템플릿의 화면이 아직 API 를 호출하지 않아 드러나지 않았을 뿐이다. - vite.config.ts 에 /api 프록시 추가. 대상은 VITE_DEV_API_PROXY 로 조정 가능하며 docker 개발 환경에서는 compose 가 http://backend:3000 을 주입한다. - DB_HOST 기본값을 'db' 에서 'localhost' 로 변경. compose 는 항상 DB_HOST=db 를 주입하므로 이 기본값은 호스트에서 직접 실행할 때만 쓰인다 — 'db' 는 호스트에서 해석되지 않아 로컬 실행이 불가능했다. - README 의 "Docker 없이 로컬 개발" 절차가 위 두 문제로 동작하지 않았다. 실제로 되는 절차로 교체. 프록시 경유 요청이 JSON 을 반환하고 SPA 라우팅도 유지되는 것을 확인했다. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/skills/vue3/SKILL.md | 3 +++ README.md | 14 +++++++++++--- backend/src/shared/config/database.config.ts | 7 ++++--- docker-compose.dev.yml | 2 ++ frontend/vite.config.ts | 14 +++++++++++++- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/.claude/skills/vue3/SKILL.md b/.claude/skills/vue3/SKILL.md index e474c8a..f9e5508 100644 --- a/.claude/skills/vue3/SKILL.md +++ b/.claude/skills/vue3/SKILL.md @@ -27,6 +27,7 @@ description: Vue 3 프론트엔드(frontend/) 개발 규칙 — Composition API | 공통 컴포넌트 예제 | `src/components/BaseButton.vue` | 재사용 컴포넌트 기준 패턴(타입 Props/Emits·scoped·rem·a11y) | | 라우터 | `src/router/index.ts` | lazy import + `auth.store` 연동 `requiresAuth` 가드 | | env 타입 선언 | `frontend/env.d.ts` | `VITE_API_BASE_URL` | +| dev API 프록시 | `vite.config.ts` 의 `server.proxy` | `/api` → 백엔드. 없으면 vite 가 SPA 폴백으로 **HTML 을 200 으로** 돌려줘 조용히 실패한다 | > **확장 시 따르는 규칙:** > - **로그인 연동**: 실제 `/auth/login` 엔드포인트 연결 → 성공 시 `useAuth` 가 `authStore.markAuthenticated()` 호출 @@ -216,3 +217,5 @@ router.beforeEach((to) => { - [ ] **lint/test**: 커밋 전 `npm run lint` (`--max-warnings 0`) + `npm run type-check` + `npm run test:unit` 통과. 자동 수정은 `npm run lint:fix`. - [ ] **린트로 강제되는 항목**: Options API·인라인 스타일·`console`·`any` 는 빌드가 실패한다. - [ ] **env**: `VITE_API_BASE_URL` 은 빌드 시 루트 `BACKEND_API_URL` 에서 주입된다 — `frontend/.env` 에 별도 작성 불필요. +- [ ] **dev 에서 API 가 안 될 때**: 응답이 JSON 이 아니라 HTML 이면 vite 프록시(`server.proxy`) 문제다. + `baseURL` 이 `/api` 상대경로라 프록시가 없으면 index.html 이 200 으로 돌아온다. diff --git a/README.md b/README.md index 5ce1086..a8a553e 100644 --- a/README.md +++ b/README.md @@ -103,17 +103,25 @@ backend/src/common/errors/error-codes.ts ← 여기만 수정 ## 로컬 개발 (Docker 없이) +DB 는 컨테이너로 띄우고 앱만 호스트에서 실행하는 방식이 가장 간단합니다. + ```bash -# backend +# 1) DB 만 컨테이너로 기동 (127.0.0.1:5432 로 노출된다) +docker compose -f docker-compose.yml -f docker-compose.dev.yml --env-file .env.development up -d db + +# 2) backend — DB_HOST 기본값이 localhost 라 그대로 붙는다 cd backend && npm ci && npm run start:dev -# frontend +# 3) frontend — vite dev 서버가 /api 를 localhost:3000 으로 프록시한다 cd frontend && npm ci && npm run dev -# flutter +# 4) flutter cd flutter && flutter pub get && flutter run ``` +> 프론트엔드의 API 요청은 `/api` 상대경로로 나가고 vite dev 프록시가 백엔드로 넘깁니다. +> 백엔드를 다른 주소에서 띄웠다면 `VITE_DEV_API_PROXY` 로 대상을 바꾸세요. + ### 커밋 전 검사 ```bash diff --git a/backend/src/shared/config/database.config.ts b/backend/src/shared/config/database.config.ts index 9edec8c..85962e5 100644 --- a/backend/src/shared/config/database.config.ts +++ b/backend/src/shared/config/database.config.ts @@ -8,10 +8,11 @@ import { parsePositiveInt } from '@/shared/utils/env'; * 두 곳에 따로 적으면 반드시 어긋나므로 여기서만 정의한다. */ -// 개발 환경 기본값 — compose 가 주입하는 값과 동일해야 한다. -// 운영에서는 validateEnv 가 DB_* 를 필수로 요구하므로 이 값들이 쓰이지 않는다. +// 개발 환경 기본값 — **호스트에서 직접 실행할 때** 기준이다. +// docker 환경에서는 compose 가 DB_HOST=db 등을 항상 주입하므로 이 값들이 쓰이지 않고, +// 운영에서는 validateEnv 가 DB_* 를 필수로 요구한다. const DEV_DB_DEFAULTS = { - host: 'db', + host: 'localhost', port: 5432, username: 'postgres', password: 'postgres', diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6f16017..af7a065 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -14,6 +14,8 @@ services: environment: - CHOKIDAR_USEPOLLING=true - WATCHPACK_POLLING=true + # vite dev 서버가 /api 요청을 백엔드 컨테이너로 프록시한다 (vite.config.ts) + - VITE_DEV_API_PROXY=http://backend:${BACKEND_PORT} volumes: - ./${FRONTEND_DIR}:/app - /app/node_modules diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 96fd454..f4a00fc 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -23,6 +23,18 @@ export default defineConfig({ // 도커/윈도우 환경 파일 핫 리로딩 강제 활성화 watch: { usePolling: true, - } + }, + // 개발 서버 API 프록시. + // useApi 의 baseURL 은 '/api'(상대경로)라 프록시가 없으면 vite 가 SPA 폴백으로 + // index.html 을 200 으로 돌려준다 — 요청이 조용히 HTML 을 받아 실패한다. + // + // target 기본값은 호스트에서 직접 띄울 때 기준이고, + // docker 개발 환경에서는 compose 가 VITE_DEV_API_PROXY=http://backend:3000 을 주입한다. + proxy: { + '/api': { + target: process.env.VITE_DEV_API_PROXY || 'http://localhost:3000', + changeOrigin: true, + }, + }, } })