o2o-infinith-frontend/README.md

184 lines
7.7 KiB
Markdown

# o2o-infinith-frontend
INFINITH 마케팅 분석 플랫폼의 프론트엔드. React 19 + Vite + Tailwind v4 + TanStack Query.
## 기술 스펙
| 영역 | 라이브러리 / 도구 | 비고 |
| --- | --- | --- |
| 언어 | TypeScript ~5.8 | |
| 빌드 | Vite 6 | `@vitejs/plugin-react` |
| UI 프레임워크 | React 19 | |
| 스타일 | Tailwind CSS v4 | CSS-first 방식 (config 파일 없음) |
| 컴포넌트 | [shadcn/ui](https://ui.shadcn.com/) | Radix + Tailwind 조합, `src/shared/ui/` 에 복사해 사용 |
| 아이콘 | lucide-react, 커스텀 (`src/shared/icons/`) | |
| 애니메이션 | motion (Framer Motion 후속), tw-animate-css | |
| 라우팅 | react-router 7 | |
| 데이터 페칭 | TanStack Query 5 | |
| HTTP 클라이언트 | ky | `customFetcher` 의 베이스 |
| API SDK 생성 | orval 7 | OpenAPI → React Query 훅 |
| 클라이언트 상태 | zustand 5 | |
| PDF 출력 | jspdf, html2canvas-pro | |
| 폰트 | Pretendard Variable (self-host), Playfair Display | |
## 시작하기
### 로컬 (Node)
```bash
npm install
npm run dev # localhost:3000
npm run build # 프로덕션 빌드
npm run lint # tsc --noEmit
npm run api:gen # SDK 재생성 (orval)
```
### Docker (개발)
[Dockerfile.dev](Dockerfile.dev) + [docker-compose.yml](docker-compose.yml) — Vite dev 서버를 컨테이너에서 돌리고 호스트 소스 변경을 HMR로 반영.
```bash
docker compose up # 시작
docker compose up --build # 의존성 바뀐 경우
docker compose down # 종료
```
## 프로젝트 구조
```
src/
├── app/ # 라우터 / 프로바이더 / 진입점 셋업
├── config/ # 앱 전역 설정
├── features/ # 도메인별 모듈 (페이지 + 훅 + 컴포넌트 + 데이터)
│ ├── admin/ # 관리자 (API 대시보드 등)
│ ├── auth/ # 인증
│ ├── channels/ # 채널 검증 / enrichment
│ ├── clinics/ # 병원 프로필
│ ├── dev/ # 개발용 페이지
│ ├── distribution/ # 콘텐츠 배포
│ ├── landing/ # 랜딩
│ ├── performance/ # 성과 대시보드
│ ├── plan/ # 마케팅 플랜
│ ├── pricing/ # 요금제
│ ├── report/ # 분석 리포트 (로딩/뷰어)
│ └── studio/ # 콘텐츠 스튜디오
├── shared/
│ ├── api/ # orval-generated SDK + HTTP 어댑터
│ │ ├── api.ts # customFetcher (ky 기반 mutator)
│ │ ├── generated/ # ⚠️ orval 자동생성 (직접 수정 금지)
│ │ └── model/ # ⚠️ orval 자동생성 타입
│ ├── constants/ # 전역 상수
│ ├── hooks/ # 범용 훅
│ ├── icons/ # 커스텀 아이콘
│ ├── layouts/ # 공통 레이아웃
│ ├── lib/ # 유틸 헬퍼
│ ├── types/ # 공통 타입
│ ├── ui/ # shadcn 기반 공용 컴포넌트
│ └── utils/ # 유틸 함수
├── styles/
│ └── index.css # ★ Tailwind + 디자인 토큰 (브랜드 색 / 폰트 / radius)
└── assets/ # 정적 자산 (폰트, 이미지 등)
```
기능 모듈 내부 컨벤션:
```
features/<name>/
├── pages/ # 라우트에 매핑되는 페이지 컴포넌트
├── components/ # 해당 기능 전용 컴포넌트
├── hooks/ # 기능 전용 훅 (use<Feature>.ts)
├── lib/ # transform / parser 등 순수 로직
├── data/ # mock / 시드 상수
├── types/ # 기능 전용 타입
└── routes.tsx # 라우트 정의
```
## 디자인 토큰
전부 [src/styles/index.css](src/styles/index.css)에서 관리. Tailwind v4의 CSS-first 방식이라 별도 `tailwind.config.js` 파일이 없다.
| 블록 | 위치 | 용도 |
| --- | --- | --- |
| `:root { --brand-* }` | 상단 | 브랜드 색 원본 (purple / navy / rose / earth …) |
| `:root { --status-* }` | 중간 | semantic 상태 색 (critical / warning / good / info) |
| `:root { --background, --primary, … }` | shadcn 영역 | shadcn 토큰 → 브랜드 색 매핑 |
| `.dark { … }` | 다크모드 | 다크 모드 오버라이드 |
| `@theme inline { --color-*, --font-* }` | 하단 | Tailwind 유틸리티로 노출 (`bg-brand-purple`, `text-status-critical-text` 등) |
새 색을 만들고 싶다면 `:root``--brand-xxx` 추가 → `@theme inline``--color-brand-xxx: var(--brand-xxx)` 추가 → `bg-brand-xxx` 유틸로 사용.
> **왜 `tailwind.config.js` 가 없나요?**
> Tailwind v4 부터 설정을 JS 파일이 아닌 CSS의 `@theme` 블록에서 한다. 폰트, 색, radius, breakpoint 같은 모든 토큰을 [src/styles/index.css](src/styles/index.css) 하나에서 관리한다.
## 커스텀 클래스 / CSS
| 위치 | 내용 |
| --- | --- |
| [src/styles/custom.css](src/styles/custom.css) | 커스텀 유틸 클래스(`.text-gradient`, `.glass-card`, `.gradient-bg`, `.soft-gradient`) + `@keyframes` + 애니메이션 유틸(`.animate-blob`, `.animation-delay-*`) |
| [src/styles/index.css](src/styles/index.css) `@layer base` | 전역 element 스타일 (`body`, `h1`~`h6` 등) |
새 유틸 클래스는 `custom.css` 에 추가. 컴포넌트 단위 스타일은 Tailwind 유틸리티로 JSX에서 직접 작성하는 게 기본.
## 폰트 변경
Google Fonts로 가져오는 방식. 두 곳만 수정하면 된다.
1. **[index.html](index.html)** — Google Fonts `<link>``family=` 파라미터에 원하는 폰트 추가/교체
```html
<link href="https://fonts.googleapis.com/css2?family=원하는폰트:wght@400;700&display=swap" rel="stylesheet">
```
2. **[src/styles/index.css](src/styles/index.css)** 의 `@theme inline` 에서 `--font-sans` (본문) 또는 `--font-serif` (제목) 의 첫 항목을 새 폰트 이름으로 교체
## 백엔드 API / orval
OpenAPI 스펙을 가져와 SDK(React Query 훅 포함)를 자동생성한다.
- 입력: [orval.config.ts](orval.config.ts) 의 `input` 필드 (예: `http://localhost:8001/openapi.json`)
- 출력: `src/shared/api/generated/` (함수 + 훅) + `src/shared/api/model/` (타입)
- HTTP 어댑터: [src/shared/api/api.ts](src/shared/api/api.ts) — ky 기반 `customFetcher`. 4xx/5xx도 throw하지 않고 `{ status, data, headers }`로 반환.
### 사용 예
```ts
// 쿼리 훅
import { useGetReport } from '@/shared/api/generated/reports/reports'
const { data, isLoading } = useGetReport(id, { query: { enabled: !!id } })
// 뮤테이션 훅
import { useStartAnalysis } from '@/shared/api/generated/analyses/analyses'
const { mutateAsync } = useStartAnalysis()
await mutateAsync({ data: { url: '...' } })
// imperative 호출(폴링 등)
import { getAnalysisStatus } from '@/shared/api/generated/analyses/analyses'
const res = await getAnalysisStatus(runId)
```
### 재생성
백엔드 스펙이 바뀌면:
```bash
npm run api:gen
```
`clean: true``generated/`, `model/` 폴더는 매번 비워지고 새로 생성된다. 직접 수정 금지.
## API 프록시 (개발)
[vite.config.ts](vite.config.ts) 에서 `/api/*` 요청을 백엔드로 프록시한다.
- 기본 타겟: `http://localhost:8001`
- 도커 안에서 dev 서버 돌 때: `http://host.docker.internal:8001` (CHOKIDAR_USEPOLLING=true 일 때 자동)
- 오버라이드: `VITE_API_TARGET=http://40.82.133.44:8001` 환경변수
## shadcn/ui
[ui.shadcn.com](https://ui.shadcn.com/) 컴포넌트는 패키지가 아니라 **소스 복사** 방식으로 사용한다. 추가/수정한 컴포넌트는 [src/shared/ui/](src/shared/ui/) 에 위치하며, Tailwind 토큰(`--background`, `--primary` 등)을 통해 디자인 시스템과 연결된다.
새 컴포넌트 추가:
```bash
npx shadcn@latest add button
```