- 라우팅 react-router v7, 서버상태 TanStack Query - orval 자동 생성 API 클라이언트(src/api/generated) - 견적/상품/협력사/견적설정 페이지, shadcn UI, 토큰(tokens.css) - 실행: 루트 docker compose (front :3001), README 그에 맞게 정리 - AI Studio 스캐폴드 잔재 제거(metadata.json, 중복 DESIGN_TOKENS.md), 타이틀 NegoData, vite.config 죽은코드 제거 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
112 lines
4.5 KiB
Markdown
112 lines
4.5 KiB
Markdown
# negodata 프론트엔드
|
|
|
|
negosium/negodata 협상 플랫폼의 웹 프론트엔드.
|
|
|
|
## 스택
|
|
|
|
- **빌드 / 런타임**: Vite 6 + React 19 + TypeScript
|
|
- **라우팅**: react-router v7 (`createBrowserRouter`)
|
|
- **서버 상태**: TanStack Query (React Query)
|
|
- **UI**: Tailwind CSS v4 + shadcn/ui, lucide-react, sonner
|
|
- **폼 / 검증**: react-hook-form + zod
|
|
- **전역 상태**: zustand
|
|
- **API 클라이언트**: orval (백엔드 OpenAPI → 타입·React Query 훅 자동생성)
|
|
|
|
## 실행
|
|
|
|
이 프론트는 단독으로 띄우지 않는다. **최상위 `negosium` 폴더(저장소 루트)의 docker compose** 로 DB·백엔드와 함께 기동한다.
|
|
|
|
```bash
|
|
# 저장소 루트(o2o-negosium)에서 — 전체 스택(DB + 백엔드 2개 + 프론트)
|
|
docker compose up -d --build
|
|
```
|
|
|
|
- 프론트: **http://localhost:3001** (compose 가 컨테이너 `:3000` → 호스트 `:3001` 로 매핑)
|
|
- 소스를 바인드마운트하므로 코드 수정은 HMR 로 자동 반영된다.
|
|
|
|
### 프론트만 단독 개발 (선택)
|
|
|
|
도커 없이 이 폴더만 띄울 때:
|
|
|
|
```bash
|
|
npm install
|
|
cp .env.example .env # 환경변수 (아래 표)
|
|
npm run dev # http://localhost:3000
|
|
```
|
|
|
|
## 환경변수 (.env)
|
|
|
|
`.env.example` 을 복사해서 만든다. 실제 `.env` 는 git 에 안 올라간다(`.gitignore`).
|
|
|
|
| 변수 | 기본값 | 설명 |
|
|
| --- | --- | --- |
|
|
| `VITE_API_BASE_URL` | `http://localhost:9400` | 백엔드 API 주소. 빌드 시 정적으로 구워진다(운영 빌드는 build arg 로도 전달). |
|
|
|
|
> `ORVAL_INPUT` 은 `.env` 가 아니라 orval 실행 시 쓰는 셸 환경변수다 (아래 참고).
|
|
|
|
## 스크립트
|
|
|
|
| 명령 | 설명 |
|
|
| --- | --- |
|
|
| `npm run dev` | 개발 서버 (vite, :3000) |
|
|
| `npm run build` | 프로덕션 빌드 |
|
|
| `npm run preview` | 빌드 결과 미리보기 |
|
|
| `npm run lint` | 타입 체크 (`tsc --noEmit`) |
|
|
| `npm run orval` | 백엔드 OpenAPI 로 API 클라이언트 재생성 (아래) |
|
|
|
|
## API 클라이언트 생성 (orval)
|
|
|
|
백엔드 FastAPI 의 OpenAPI 스펙을 읽어 `src/api/generated` 에 React Query 훅 + 타입을 생성한다. 설정은 [orval.config.ts](orval.config.ts).
|
|
|
|
```bash
|
|
# 기본: 백엔드를 :9400 으로 띄운 뒤 라이브 스펙에서 생성
|
|
npm run orval
|
|
|
|
# 백엔드 없이: 저장된 openapi.json 파일을 가리켜 생성
|
|
ORVAL_INPUT=../backend/openapi.json npm run orval
|
|
```
|
|
|
|
- 입력: `http://localhost:9400/openapi.json` (또는 `ORVAL_INPUT` 으로 지정한 파일)
|
|
- 출력: `src/api/generated/**` (tags-split, 모델은 `generated/model`)
|
|
- 산출물은 **자동생성물 — 직접 수정 금지.** 백엔드 API 가 바뀌면 다시 실행한다.
|
|
- 모든 생성 호출은 공통 mutator [src/api/mutator/custom-fetch.ts](src/api/mutator/custom-fetch.ts) 를 통과한다 (baseURL·토큰·에러 처리). baseURL 은 `VITE_API_BASE_URL` 을 쓴다.
|
|
- FastAPI operationId(`list_items_v1_item_list_get`)는 `_v1_` 앞부분만 취해 camelCase 로 정리 → `listItems` / `useListItems` 형태가 된다.
|
|
|
|
## 디렉토리 구조
|
|
|
|
```
|
|
src/
|
|
app/ # 엔트리(main), router, provider
|
|
api/
|
|
generated/ # orval 자동생성 (직접 수정 금지)
|
|
mutator/ # custom-fetch (요청 공통 로직: baseURL·토큰·에러)
|
|
features/ # 도메인별: auth, products(상품), partners(협력사), quotations(견적), cards
|
|
components/ # ui(shadcn), layout
|
|
pages/ # 화면
|
|
stores/ # zustand 스토어
|
|
lib/ # 공용 유틸
|
|
```
|
|
|
|
## 코딩 컨벤션
|
|
|
|
### 에러 처리 규칙
|
|
|
|
에러는 **던지는 곳(service/hook)** 과 **잡는 곳(UI)** 을 분리한다.
|
|
|
|
- **service / hook 계층** — 에러를 처리하지 않는다. 실패하면 `throw` 로 위로 전파만 한다.
|
|
화면을 모르므로 "에러를 어떻게 보여줄지" 를 결정하지 않는다.
|
|
- 단, 사람이 읽을 수 있는 메시지로 **가공한 뒤 다시 throw** 하는 건 허용
|
|
(예: `features/auth/service.ts` 의 `callAuth` — `ApiError` → `Error(error_message)`).
|
|
- **UI 계층(페이지/컴포넌트)** — `try/catch` 로 잡아서 화면에 반영한다
|
|
(예: `pages/login.tsx` 의 `handleSubmit` → `setError(...)`).
|
|
|
|
예외: 에러를 **무시해도 되는 동작은 그 자리에서 삼킨다**.
|
|
예) `service.ts` 의 `logout` — 서버 호출이 실패해도 로컬 토큰만 정리하면 되므로 UI까지 올리지 않는다.
|
|
|
|
| 계층 | 에러 태도 |
|
|
| --- | --- |
|
|
| orval / API 호출 | 실패 발생 (raw) |
|
|
| `callAuth` 같은 래퍼 | 잡아서 **다듬고 다시 throw** |
|
|
| service / hook | **그냥 통과** (안 잡음) |
|
|
| UI (페이지) | **catch → 화면 표시** |
|