GET /api/clinics 응답을 표로 보는 dev 전용 페이지 추가. /dev/* 경로 전체를 DevOnly layout route 로 감싸 로컬호스트 (localhost/127.0.0.1/0.0.0.0/::1) 외 도메인에선 자동으로 루트로 리다이렉트. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| public/fonts | ||
| src | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| Dockerfile | ||
| Dockerfile.dev | ||
| README.md | ||
| components.json | ||
| docker-compose.yml | ||
| index.html | ||
| nginx.conf | ||
| orval.config.ts | ||
| package-lock.json | ||
| package.json | ||
| tsconfig.json | ||
| vite.config.ts | ||
README.md
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 | 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)
npm install
npm run dev # localhost:3000
npm run build # 프로덕션 빌드
npm run lint # tsc --noEmit
npm run api:gen # SDK 재생성 (orval)
Docker (개발)
Dockerfile.dev + docker-compose.yml — Vite dev 서버를 컨테이너에서 돌리고 호스트 소스 변경을 HMR로 반영.
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에서 관리. 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 하나에서 관리한다.
커스텀 클래스 / CSS
| 위치 | 내용 |
|---|---|
| src/styles/custom.css | 커스텀 유틸 클래스(.text-gradient, .glass-card, .gradient-bg, .soft-gradient) + @keyframes + 애니메이션 유틸(.animate-blob, .animation-delay-*) |
src/styles/index.css @layer base |
전역 element 스타일 (body, h1~h6 등) |
새 유틸 클래스는 custom.css 에 추가. 컴포넌트 단위 스타일은 Tailwind 유틸리티로 JSX에서 직접 작성하는 게 기본.
폰트 변경
Google Fonts로 가져오는 방식. 두 곳만 수정하면 된다.
- index.html — Google Fonts
<link>의family=파라미터에 원하는 폰트 추가/교체<link href="https://fonts.googleapis.com/css2?family=원하는폰트:wght@400;700&display=swap" rel="stylesheet"> - src/styles/index.css 의
@theme inline에서--font-sans(본문) 또는--font-serif(제목) 의 첫 항목을 새 폰트 이름으로 교체
백엔드 API / orval
OpenAPI 스펙을 가져와 SDK(React Query 훅 포함)를 자동생성한다.
- 입력: orval.config.ts 의
input필드 (예:http://localhost:8001/openapi.json) - 출력:
src/shared/api/generated/(함수 + 훅) +src/shared/api/model/(타입) - HTTP 어댑터: src/shared/api/api.ts — ky 기반
customFetcher. 4xx/5xx도 throw하지 않고{ status, data, headers }로 반환.
사용 예
// 쿼리 훅
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)
재생성
백엔드 스펙이 바뀌면:
npm run api:gen
clean: true라 generated/, model/ 폴더는 매번 비워지고 새로 생성된다. 직접 수정 금지.
API 프록시 (개발)
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 컴포넌트는 패키지가 아니라 소스 복사 방식으로 사용한다. 추가/수정한 컴포넌트는 src/shared/ui/ 에 위치하며, Tailwind 토큰(--background, --primary 등)을 통해 디자인 시스템과 연결된다.
새 컴포넌트 추가:
npx shadcn@latest add button