184 lines
7.7 KiB
Markdown
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
|
|
```
|