# web-ui — CLAUDE.md 개인 웹페이지 프론트엔드 프로젝트에 대한 컨텍스트 문서입니다. ## 프로젝트 개요 - **스택**: React 18 + Vite + react-router-dom v6 - **목적**: 개인 블로그, 로또 실험, 주식 뉴스/트레이딩, 여행 기록을 한 곳에 모은 개인 웹 UI - **배포 대상**: Synology NAS (`gahusb.synology.me`) Docker 컨테이너 내 nginx ## 페이지 구조 | 경로 | 컴포넌트 | 설명 | |------|----------|------| | `/` | `Home` | 메인 허브 | | `/blog` | `Blog` | 마크다운 기반 블로그 | | `/lotto` | `Lotto` | 로또 추천/통계 | | `/stock` | `Stock` | 주식 뉴스/지수 | | `/stock/trade` | `StockTrade` | 주식 트레이딩 | | `/realestate` | `Subscription` | 청약 자격·일정 관리 | | `/realestate/property` | `RealEstate` | 관심 단지 정보 | | `/travel` | `Travel` | 여행 사진 갤러리 (Dark Room 테마) | | `/lab` | `EffectLab` | UI/UX 실험 허브 | | `/lab/sword-stream` | `SwordStream` | Three.js 파티클 인터랙션 | | `/lab/day-calc` | `DayCalc` | 날짜 계산기 | | `/lab/music` | `MusicStudio` | AI 음악 생성 스튜디오 (Sonic Forge) | | `/todo` | `Todo` | 태스크 보드 | 라우트 정의: `src/routes.jsx` / 라우터 설정: `src/Router.jsx` --- ## API 설정 ### 핵심 원칙 - **항상 상대 경로 사용**: 프로덕션에서 프론트와 백엔드는 nginx 리버스 프록시로 동일 도메인에서 서비스됨 - **절대 URL 사용 금지**: `https://` 절대 URL을 fetch에 직접 사용하면 Mixed Content 오류 발생 - `VITE_API_BASE` 환경변수는 사용하지 않음 ### API 헬퍼 (`src/api.js`) ```js // 모든 API 호출은 이 헬퍼를 통해 사용 import { apiGet, apiPost, apiPut, apiDelete } from './api'; // 예시 apiGet('/api/lotto/latest') apiPost('/api/portfolio', { ... }) ``` 제공 함수: `apiGet`, `apiPost`, `apiPut`, `apiDelete` ### 개발 서버 프록시 (`vite.config.js`) ```js proxy: { '/api': { target: 'https://gahusb.synology.me', changeOrigin: true, secure: true }, '/media': { target: 'https://gahusb.synology.me', changeOrigin: true, secure: true }, // /ext/* — Yahoo Finance, CNN Fear&Greed 등 외부 API 프록시 } ``` - `/api/*` → NAS 백엔드 - `/media/*` → NAS 미디어 파일 (여행 사진 `/media/travel/`, 음악 `/media/music/`) - 개발 서버 포트: **3007** ### API 엔드포인트 목록 | 분류 | 메서드 | 경로 | |------|--------|------| | 로또 기본 | GET | `/api/lotto/latest`, `/api/lotto/stats`, `/api/lotto/recommend` | | 로또 기본 | GET | `/api/lotto/best`, `/api/lotto/analysis` | | 로또 기본 | POST | `/api/admin/simulate` | | 로또 고도화 | GET | `/api/lotto/stats/performance` | | 로또 고도화 | GET | `/api/lotto/report/latest`, `/api/lotto/report/:drw_no`, `/api/lotto/report/history?limit=N` | | 로또 고도화 | GET | `/api/lotto/analysis/personal` | | 로또 구매 | GET | `/api/lotto/purchase?draw_no=N&days=N`, `/api/lotto/purchase/stats` | | 로또 구매 | POST/PUT/DELETE | `/api/lotto/purchase`, `/api/lotto/purchase/:id` | | 히스토리 | GET | `/api/history` | | 히스토리 | DELETE | `/api/history/:id` | | 주식 | GET | `/api/stock/news`, `/api/stock/indices` | | 트레이딩 | GET | `/api/trade/balance` | | 트레이딩 | POST | `/api/trade/order` | | 포트폴리오 | GET/POST | `/api/portfolio` | | 포트폴리오 | PUT/DELETE | `/api/portfolio/:id` | | 예수금 | PUT | `/api/portfolio/cash` — body: `{ broker, cash }` | | 예수금 | DELETE | `/api/portfolio/cash/:broker` | | 자산 스냅샷 | POST | `/api/portfolio/snapshot` — body: `{ total_assets }` 또는 body 없이 서버 계산 | | 자산 스냅샷 | GET | `/api/portfolio/snapshot/history?days=N` — response: `{ history: [{date, total_assets}] }` | | 실현손익 | GET | `/api/portfolio/sell-history?broker=X&days=N` — response: `{ records: [...] }` | | 실현손익 | POST/PUT | `/api/portfolio/sell-history`, `/api/portfolio/sell-history/:id` | | 실현손익 | DELETE | `/api/portfolio/sell-history/:id` | | TODO | GET/POST | `/api/todos` | | TODO | PUT/DELETE | `/api/todos/:id`, `/api/todos/done` | | 블로그 | GET/POST | `/api/blog/posts` | | 블로그 | PUT/DELETE | `/api/blog/posts/:id` | | AI 음악 | POST | `/api/music/generate` — body: `{ title, genre, moods, instruments, duration_sec, bpm, key, scale, prompt }` → `{ task_id }` | | AI 음악 | GET | `/api/music/status/:task_id` → `{ status, progress, message, audio_url?, error?, track? }` | | AI 음악 라이브러리 | GET/POST | `/api/music/library` — response: `{ tracks: [...] }` | | AI 음악 라이브러리 | DELETE | `/api/music/library/:id` | --- ## NAS 배포 설정 ### 배포 경로 | 환경 | 경로 | |------|------| | Windows | `Z:\docker\webpage\frontend\` (NAS 네트워크 드라이브 마운트) | | macOS (SMB) | `/Volumes/gahusb.synology.me/docker/webpage/frontend/` | | macOS (SSH) | `/volume1/docker/webpage/frontend/` | ### 배포 명령어 ```bash # 빌드 + 배포 (권장) npm run release:nas # 빌드만 npm run build # 배포만 (dist 폴더가 이미 있을 때) npm run deploy:nas ``` ### Windows 배포 NAS가 `Z:` 드라이브로 마운트되어 있어야 함. `robocopy`로 `/MIR` 동기화하며 로그는 `robocopy.log`에 저장됨. ### macOS 배포 — SSH 방식 (권장) ```bash # 환경변수 설정 후 배포 NAS_SSH_TARGET=user@gahusb.synology.me NAS_SSH_PORT=22 npm run release:nas ``` `NAS_SSH_TARGET`이 설정되면 `rsync`로 SSH 배포. SMB 마운트 방식보다 안정적. ### macOS 배포 — SMB 마운트 방식 SMB 마운트 후 `ditto`로 복사. `NAS_CLEAN=1` 설정 시 배포 전 기존 파일 전체 삭제. ```bash NAS_CLEAN=1 npm run release:nas ``` ### 배포 스크립트 파일 `scripts/deploy-nas.cjs` — Node.js CJS 모듈, 플랫폼 자동 감지 --- ## 개발 환경 ```bash npm install npm run dev # localhost:3007 에서 개발 서버 실행 npm run build # dist/ 로 프로덕션 빌드 npm run lint # ESLint 검사 npm run preview # 빌드 결과물 미리보기 ``` ## 주요 파일 위치 | 파일 | 역할 | |------|------| | `src/api.js` | API 헬퍼 함수 모음 | | `src/routes.jsx` | 라우트 및 네비게이션 링크 정의 | | `src/Router.jsx` | BrowserRouter 설정 | | `vite.config.js` | 개발 서버 및 프록시 설정 | | `scripts/deploy-nas.cjs` | NAS 배포 스크립트 | | `src/content/blog/` | 블로그 마크다운 파일 | | `public/` | 정적 파일 (로고, API 스펙 등) | --- ## Sonic Forge — AI 음악 생성 스튜디오 (`/lab/music`) ### 파일 구조 | 파일 | 역할 | |------|------| | `src/pages/music/MusicStudio.jsx` | 메인 컴포넌트 | | `src/pages/music/MusicStudio.css` | 스타일 (Bebas Neue · Syne · Courier Prime) | ### 주요 컴포넌트 - **SonicRadar** — 헤더 우측 비주얼. SVG 링·크로스헤어·스윕 라인 + 48개 CSS 방사형 바. `isGenerating` / `accentColor` prop으로 상태 전환 - **WaveformCanvas** — 스테이지 우측 캔버스 오실로스코프 (헤더와 별도) - **AudioPlayer** — 실제 `