diff --git a/README.md b/README.md index 1b1e7f2..55a22ca 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,40 @@ # web-backend Synology NAS 기반 개인 웹 플랫폼 백엔드 모노레포. -로또 분석, 주식 포트폴리오, 여행 앨범, 블로그, 투두리스트를 하나의 서비스로 운영한다. +로또 분석, 주식 포트폴리오, AI 음악 생성, 블로그 마케팅, 부동산 청약, AI 에이전트 오피스, 여행 앨범을 하나의 Docker Compose 스택으로 운영한다. --- ## 서비스 구성 ``` -┌─────────────────────────────────────────────────────────────┐ -│ lotto-frontend (Nginx:8080) │ -│ ├── 정적 SPA 서빙 (React + Vite) │ -│ └── API 리버스 프록시 │ -│ ├── /api/ → lotto-backend:8000 │ -│ ├── /api/stock/ → stock-lab:8000 │ -│ ├── /api/trade/ → stock-lab:8000 │ -│ ├── /api/portfolio → stock-lab:8000 │ -│ ├── /api/travel/ → travel-proxy:8000 │ -│ └── /webhook → deployer:9000 │ -└─────────────────────────────────────────────────────────────┘ +┌──────────────────────────────────────────────────────────────────────┐ +│ lotto-frontend (Nginx:8080) │ +│ ├── 정적 SPA 서빙 (React + Vite) │ +│ └── API 리버스 프록시 │ +│ ├── /api/ → lotto-backend:8000 (로또·블로그·투두)│ +│ ├── /api/stock/, /trade/ → stock-lab:8000 │ +│ ├── /api/portfolio → stock-lab:8000 │ +│ ├── /api/music/ → music-lab:8000 │ +│ ├── /api/blog-marketing/ → blog-lab:8000 │ +│ ├── /api/realestate/ → realestate-lab:8000 │ +│ ├── /api/agent-office/ → agent-office:8000 (+ WebSocket) │ +│ ├── /api/travel/ → travel-proxy:8000 │ +│ ├── /media/music/… (nginx 직접 서빙, 생성 오디오) │ +│ ├── /media/travel/… (nginx 직접 서빙, 사진/썸네일) │ +│ └── /webhook → deployer:9000 │ +└──────────────────────────────────────────────────────────────────────┘ ``` | 컨테이너 | 포트 | 역할 | |---------|------|------| -| `lotto-backend` | 18000 | 로또·블로그·투두 API | -| `stock-lab` | 18500 | 주식 뉴스·포트폴리오·자산 추적 | -| `travel-proxy` | 19000 | 여행 사진 API + 썸네일 생성 | +| `lotto-backend` | 18000 | 로또 데이터 수집·분석·추천 + 블로그·투두 API | +| `stock-lab` | 18500 | 주식 뉴스·AI 요약·KIS 실계좌·포트폴리오·자산 추적 | +| `music-lab` | 18600 | AI 음악 생성 (Suno + 로컬 MusicGen 듀얼 프로바이더) | +| `blog-lab` | 18700 | 블로그 마케팅 수익화 (키워드→글 생성→리뷰→발행) | +| `realestate-lab` | 18800 | 청약 공고 자동 수집·프로필 매칭 | +| `agent-office` | 18900 | AI 에이전트 가상 오피스 (WebSocket + 텔레그램 봇) | +| `travel-proxy` | 19000 | 여행 사진 API + 온디맨드 썸네일 | | `lotto-frontend` | 8080 | SPA 서빙 + 리버스 프록시 | | `webpage-deployer` | 19010 | Gitea Webhook → 자동 배포 | @@ -35,47 +44,19 @@ Synology NAS 기반 개인 웹 플랫폼 백엔드 모노레포. ``` web-backend/ -├── backend/ # lotto-backend 서비스 (Python/FastAPI) -│ ├── app/ -│ │ ├── main.py # 라우터, 스케줄러 -│ │ ├── db.py # SQLite CRUD (7개 테이블) -│ │ ├── generator.py # 몬테카를로 시뮬레이션 엔진 -│ │ ├── analyzer.py # 5가지 통계 분석 -│ │ ├── checker.py # 당첨 결과 채점 -│ │ ├── collector.py # 로또 데이터 수집 -│ │ ├── recommender.py # 추천 알고리즘 -│ │ └── utils.py # 메트릭 계산 -│ └── Dockerfile -│ -├── stock-lab/ # stock-lab 서비스 (Python/FastAPI) -│ ├── app/ -│ │ ├── main.py # 라우터, 스케줄러 -│ │ ├── db.py # SQLite CRUD (4개 테이블) -│ │ ├── scraper.py # 네이버 금융 뉴스 크롤링 -│ │ ├── price_fetcher.py # 현재가 조회 (3분 캐시) -│ │ └── holidays.json # 한국 주식시장 휴장일 -│ └── Dockerfile -│ -├── travel-proxy/ # travel-proxy 서비스 (Python/FastAPI) -│ ├── app/ -│ │ └── main.py # 사진 API, 썸네일 생성 (Pillow) -│ └── Dockerfile -│ -├── deployer/ # Gitea Webhook 수신 → 자동 배포 -│ ├── app.py # HMAC SHA256 검증 + 배포 트리거 -│ └── Dockerfile -│ -├── nginx/ -│ └── default.conf # 리버스 프록시 + SPA + 캐시 -│ -├── scripts/ -│ ├── deploy.sh # 운영 배포 (git pull → rsync → compose up) -│ ├── deploy-nas.sh # rsync 전용 스크립트 -│ └── healthcheck.sh # 전체 서비스 헬스 체크 -│ +├── backend/ # lotto-backend (로또·블로그·투두) +├── stock-lab/ # 주식·포트폴리오 +├── music-lab/ # AI 음악 생성 +├── blog-lab/ # 블로그 마케팅 파이프라인 +├── realestate-lab/ # 청약 자동 수집·매칭 +├── agent-office/ # AI 에이전트 오피스 (WS + 텔레그램) +├── travel-proxy/ # 여행 사진 + 썸네일 +├── deployer/ # Gitea Webhook 수신 → 자동 배포 +├── nginx/default.conf # 리버스 프록시 + SPA + 캐시 +├── scripts/ # deploy.sh, deploy-nas.sh, healthcheck.sh ├── docker-compose.yml ├── .env.example -└── CLAUDE.md +└── CLAUDE.md # Claude Code 작업용 상세 컨텍스트 ``` --- @@ -83,13 +64,9 @@ web-backend/ ## 빠른 시작 (로컬 개발) ```bash -# 1. 환경변수 설정 cp .env.example .env - -# 2. 컨테이너 실행 (.env 기본값으로 즉시 실행 가능) docker compose up -d -# 3. 확인 curl http://localhost:18000/health curl http://localhost:18500/health ``` @@ -99,106 +76,111 @@ curl http://localhost:18500/health | Frontend + API | http://localhost:8080 | | lotto-backend | http://localhost:18000 | | stock-lab | http://localhost:18500 | +| music-lab | http://localhost:18600 | +| blog-lab | http://localhost:18700 | +| realestate-lab | http://localhost:18800 | +| agent-office | http://localhost:18900 | | travel-proxy | http://localhost:19000 | --- -## API 목록 +## 서비스별 기능 -### lotto-backend (`/api/`) +### 1. lotto-backend (`/api/`) -#### 로또 +로또 당첨번호 수집·통계 분석·몬테카를로 시뮬레이션 기반 추천 + 투두·블로그 CRUD. -| 메서드 | 경로 | 설명 | -|--------|------|------| -| GET | `/api/lotto/latest` | 최신 당첨번호 | -| GET | `/api/lotto/{drw_no}` | 특정 회차 | -| GET | `/api/lotto/stats` | 번호 빈도 통계 | -| GET | `/api/lotto/analysis` | 5가지 통계 분석 리포트 | -| GET | `/api/lotto/best` | 시뮬레이션 최적 번호 (기본 20쌍) | -| GET | `/api/lotto/simulation` | 시뮬레이션 상세 결과 | -| GET | `/api/lotto/recommend` | 통계 기반 추천 | -| GET | `/api/lotto/recommend/heatmap` | 히트맵 기반 추천 | -| GET | `/api/lotto/recommend/batch` | 배치 추천 | -| POST | `/api/admin/simulate` | 시뮬레이션 수동 실행 | -| POST | `/api/admin/sync_latest` | 당첨번호 수동 동기화 | +- **로또**: 당첨번호 조회, 5종 통계 분석, 시뮬레이션 최적 번호(`best_picks` 20쌍), 통계/히트맵/스마트/배치 추천, 전략 가중치(EMA+Softmax), 구매 이력 관리 +- **추천 이력**: 즐겨찾기·태그·메모 관리 +- **투두리스트**: UUID PK, 상태(todo/in_progress/done) +- **블로그**: 일기형 포스트 (tags JSON 배열, date DESC) -#### 추천 이력 +**스케줄러** +- 09:10 / 21:10 — 당첨번호 동기화 + 추천 채점 +- 00:05, 04:05, 08:05, 12:05, 16:05, 20:05 — 몬테카를로 시뮬레이션 (후보 20,000 → 상위 100 → best_picks 20쌍 교체) -| 메서드 | 경로 | 설명 | -|--------|------|------| -| GET | `/api/history` | 목록 (limit, offset, favorite, tag, sort) | -| PATCH | `/api/history/{id}` | 즐겨찾기·메모·태그 수정 | -| DELETE | `/api/history/{id}` | 삭제 | +### 2. stock-lab (`/api/stock/`, `/api/trade/`, `/api/portfolio`) -#### 투두리스트 +주식 뉴스 스크래핑 + LLM 요약 + KIS 실계좌 연동 + 포트폴리오·자산 스냅샷. -| 메서드 | 경로 | 설명 | -|--------|------|------| -| GET | `/api/todos` | 전체 목록 | -| POST | `/api/todos` | 생성 (status: todo\|in_progress\|done) | -| PUT | `/api/todos/{id}` | 수정 | -| DELETE | `/api/todos/done` | 완료 항목 일괄 삭제 | -| DELETE | `/api/todos/{id}` | 개별 삭제 | +- **뉴스**: 네이버 증권 + 해외 사이트 크롤링, LLM 기반 한국어 요약 +- **실계좌**: Windows AI 서버(192.168.45.59:8000) 프록시 → KIS Open API (잔고/주문) +- **포트폴리오**: 종목·예수금·매도 히스토리 관리, 현재가 자동 조회 +- **자산 스냅샷**: 평일 15:40 자동 저장 (KRX 공휴일 판별, `holidays.json` 매년 갱신) -> ⚠️ `/done` 라우트는 반드시 `/{id}` 보다 먼저 등록해야 함 +**LLM provider 전환** — `LLM_PROVIDER` 환경변수 +- `claude` (기본): Anthropic Messages API (`claude-haiku-4-5`) +- `ollama`: Windows AI 서버 Ollama (`qwen3:14b`) -#### 블로그 +**현재가 조회**: 네이버 모바일 API → HTML 파싱 폴백, 3분 TTL 메모리 캐시 -| 메서드 | 경로 | 설명 | -|--------|------|------| -| GET | `/api/blog/posts` | 글 목록 (`{"posts": [...]}`, date DESC) | -| POST | `/api/blog/posts` | 글 생성 (date 미입력 시 오늘 날짜) | -| PUT | `/api/blog/posts/{id}` | 글 수정 | -| DELETE | `/api/blog/posts/{id}` | 글 삭제 | +### 3. music-lab (`/api/music/`) -블로그 포스트 구조: `{ id, title, tags[], body, date, excerpt, created_at, updated_at }` +듀얼 프로바이더 AI 음악 생성. ---- +- **Suno** (`suno`): REST API 연동, 보컬·가사·인스트루멘탈. 1회 요청 시 2개 variation 생성, 곡 연장, 보컬 분리, WAV 변환, 12스템 분리, 뮤직비디오, AI Cover 등 풀 스위트 지원 +- **로컬 MusicGen** (`local`): Windows AI PC(RTX 5070 Ti, 16GB VRAM) 인스트루멘탈 전용 +- **라이브러리**: 생성 파일은 `/app/data/music/`에 저장되고 Nginx가 `/media/music/`으로 직접 서빙 +- **가사 도구**: 저장·편집·타임스탬프 기반 가라오케 동기 -### stock-lab (`/api/stock/`, `/api/trade/`, `/api/portfolio`) +### 4. blog-lab (`/api/blog-marketing/`) -#### 뉴스 & 지표 +블로그 마케팅 수익화 4단계 파이프라인 (`draft → marketed → reviewed → published`). -| 메서드 | 경로 | 설명 | -|--------|------|------| -| GET | `/api/stock/news` | 뉴스 목록 (limit, category) | -| GET | `/api/stock/indices` | 주요 지표 (KOSPI 등) | -| POST | `/api/stock/scrap` | 뉴스 수동 스크랩 | +``` +리서치(Naver Search + 상위 블로그 본문 크롤링) + → 작가(AI 초안 생성) + → 마케터(전환율 강화 + 브랜드 링크 삽입) + → 평가자(6기준×10점, 42/60 통과 시 published) +``` -#### 실계좌 (Windows AI 서버 프록시) +- **AI 엔진**: Claude API (`claude-sonnet-4-20250514`) +- **키워드 분석**: 네이버 검색(블로그+쇼핑) API + 경쟁도/기회 점수 +- **수익 추적**: 포스트별 월간 클릭/구매/수익 기록 +- **프롬프트 템플릿**: DB에 저장 → 코드 배포 없이 수정 가능 -| 메서드 | 경로 | 설명 | -|--------|------|------| -| GET | `/api/trade/balance` | 실계좌 잔고 조회 | -| POST | `/api/trade/order` | 주문 (BUY\|SELL, price=0이면 시장가) | +### 5. realestate-lab (`/api/realestate/`) -#### 포트폴리오 +공공데이터포털 청약홈 API 연동 + 프로필 기반 자동 매칭. -| 메서드 | 경로 | 설명 | -|--------|------|------| -| GET | `/api/portfolio` | 전체 조회 (현재가·손익·예수금 포함) | -| POST | `/api/portfolio` | 종목 추가 | -| PUT | `/api/portfolio/{id}` | 종목 수정 | -| DELETE | `/api/portfolio/{id}` | 종목 삭제 | -| GET | `/api/portfolio/cash` | 예수금 전체 조회 | -| PUT | `/api/portfolio/cash` | 예수금 upsert | -| DELETE | `/api/portfolio/cash/{broker}` | 예수금 삭제 | -| POST | `/api/portfolio/snapshot` | 총 자산 스냅샷 수동 저장 | -| GET | `/api/portfolio/snapshot/history` | 자산 변화 이력 (days=0: 전체) | +- **공고 수집**: 09:00 매일 자동 (`DATA_GO_KR_API_KEY` 필요) +- **상태 갱신 + 재매칭**: 00:00 매일 자동 +- **프로필 매칭**: 지역·주택형·소득·부양가족 등으로 점수화, 신규 매칭 알림 +- **대시보드**: 진행 중 공고수, 신규 매칭수, 다가오는 일정 요약 ---- +### 6. agent-office (`/api/agent-office/`) -### travel-proxy (`/api/travel/`) +AI 에이전트 가상 오피스 — 2D 픽셀아트 사무실에서 에이전트가 실제 작업을 수행한다. -| 메서드 | 경로 | 설명 | -|--------|------|------| -| GET | `/api/travel/regions` | 지역 GeoJSON | -| GET | `/api/travel/photos` | 사진 목록 (region, page, size) | -| POST | `/api/travel/reload` | 캐시 초기화 | +- **아키텍처**: stock-lab / music-lab 기존 API를 서비스 프록시로 호출 (직접 DB 접근 없음) +- **FSM 상태**: `idle → working → waiting(승인 대기) → reporting → break` +- **실시간 동기화**: WebSocket `/api/agent-office/ws` (init, agent_state, task_complete, command_result) +- **텔레그램 연동**: 양방향 알림 + 인라인 키보드 승인 + - 봇이 작업 결과를 텔레그램으로 푸시, 명령은 텔레그램에서 바로 에이전트에 전달 + - Webhook 검증 후 `chat.id` 기준 라우팅 +- **에이전트**: Stock Agent (뉴스 요약), Music Agent (음악 생성) 등 확장 가능 구조 -- 썸네일: `/media/travel/.thumb/{album}/{file}` (nginx 직접 서빙, 30일 캐시) -- 원본: `/media/travel/{album}/{file}` (nginx 직접 서빙, 7일 캐시) +**스케줄러** +- 08:00 매일 — 주식 뉴스 요약 (`stock_news_job`) +- 60초 — 유휴 에이전트 휴식 체크 (`idle_check_job`) + +### 7. travel-proxy (`/api/travel/`) + +여행 사진 API + 온디맨드 썸네일 생성. + +- 원본: `/data/travel/` (RO 마운트) +- 썸네일: 480×480 Pillow 리사이징, `/data/thumbs/` 영구 캐시 (tmp → rename 원자성 보장) +- 메모리 캐시: 앨범 스캔 결과 TTL 300초 +- 메타: `region_map.json`, `regions.geojson` + +### 8. deployer (`/webhook`) + +Gitea Webhook 수신 → NAS 자동 배포. + +- HMAC SHA256 서명 검증 (`compare_digest`, `WEBHOOK_SECRET`) +- 수신 즉시 200 응답 후 BackgroundTask로 배포 +- 배포 스크립트: `git pull` → `.releases/` 백업 → `rsync` → `docker compose up -d --build` → `chown PUID:PGID` +- 타임아웃 10분 --- @@ -213,8 +195,6 @@ curl http://localhost:18500/health → 상위 100개 DB 저장 → best_picks 20개 교체 ``` -**5가지 채점 기법:** - | 기법 | 가중치 | 내용 | |------|--------|------| | 빈도 Z-score | 25% | 번호 출현 빈도의 표준편차 | @@ -223,28 +203,21 @@ curl http://localhost:18500/health | 공동 출현 | 15% | 번호 쌍 동시 출현 빈도 | | 다양성 | 10% | 연속번호·범위·구간 커버리지 | -**스케줄:** 매일 0, 4, 8, 12, 16, 20시 (하루 6회, 각 5분) +### LLM 요약 provider 추상화 (stock-lab) + +`ai_summarizer.py`는 provider 분리 구조. `summarize_news(articles)` 시그니처는 provider와 무관하게 고정. + +- `_summarize_with_claude`: Anthropic Messages API 직접 호출 (httpx, SDK 의존성 없음) +- `_summarize_with_ollama`: Ollama `/api/generate` (타임아웃 180s, qwen3:14b 첫 로드 대응) +- 실패 시 `LLMError` (구 `OllamaError` alias 유지) ### 총 자산 스냅샷 (stock-lab) -``` -평일 15:40 자동 실행 → holidays.json으로 공휴일 스킵 -→ 포트폴리오 현재가 조회 → total_eval -→ 예수금 합계 → total_cash -→ asset_snapshots upsert (date UNIQUE, 같은 날 중복 시 덮어씀) -``` +평일 15:40 자동 실행 → `holidays.json`으로 공휴일 스킵 → 포트폴리오 현재가 조회 + 예수금 합계 → `asset_snapshots` upsert (date UNIQUE). -### 현재가 조회 (stock-lab) +### 에이전트 FSM + WS 동기화 (agent-office) -- 네이버 모바일 API 우선 (`m.stock.naver.com/api/stock/{ticker}/basic`) -- 실패 시 네이버 금융 HTML 파싱 폴백 -- 3분 TTL 메모리 캐시 - -### 여행 사진 썸네일 (travel-proxy) - -- 480×480 리사이징 (Pillow), 확장자 유지 (JPEG/PNG/WEBP) -- 온디맨드 생성 후 `/data/thumbs/` 영구 캐시 -- 원자성 보장: tmp 파일 작성 후 rename +DB에 저장된 에이전트 상태가 바뀔 때마다 `websocket_manager`가 전체 클라이언트에 브로드캐스트. 텔레그램 봇은 `waiting` 상태 작업에 인라인 키보드를 붙여 승인 요청. 승인/거부 결과가 DB → WS → 프론트로 전파. --- @@ -252,7 +225,7 @@ curl http://localhost:18500/health ``` git push → Gitea → X-Gitea-Signature (HMAC SHA256) - → deployer:9000/webhook (서명 검증, compare_digest 사용) + → deployer:9000/webhook (서명 검증, compare_digest) → BackgroundTask: scripts/deploy.sh (10분 타임아웃) 1. git pull 2. .releases/{timestamp}/ 백업 @@ -261,39 +234,29 @@ git push → Gitea → X-Gitea-Signature (HMAC SHA256) 5. chown PUID:PGID ``` -> 프론트엔드는 **자동 배포 안 됨** — 로컬 빌드 후 NAS에 수동 업로드 +> 프론트엔드는 **자동 배포 안 됨** — 로컬 빌드 후 NAS에 수동 업로드 (`scripts/deploy.bat --frontend`) --- ## 데이터베이스 -### lotto.db (`/app/data/lotto.db`) +각 서비스는 독립 SQLite DB를 `/app/data/` 볼륨에 저장. -| 테이블 | 설명 | -|--------|------| -| `draws` | 로또 당첨번호 | -| `recommendations` | 추천 이력 (즐겨찾기·태그·채점 포함) | -| `simulation_runs` | 시뮬레이션 실행 기록 | -| `simulation_candidates` | 시뮬레이션 후보 (점수 5종) | -| `best_picks` | 현재 활성 최적 번호 20개 (is_active 플래그) | -| `todos` | 투두리스트 (UUID PK) | -| `blog_posts` | 블로그 글 (tags: JSON 배열) | - -### stock.db (`/app/data/stock.db`) - -| 테이블 | 설명 | -|--------|------| -| `articles` | 뉴스 기사 (hash UNIQUE, category: domestic\|overseas) | -| `portfolio` | 보유 종목 (broker, ticker, quantity, avg_price) | -| `broker_cash` | 증권사별 예수금 (broker UNIQUE) | -| `asset_snapshots` | 일별 총 자산 스냅샷 (date UNIQUE) | +| DB | 소유 서비스 | 주요 테이블 | +|----|------------|-----------| +| `lotto.db` | lotto-backend | draws, recommendations, simulation_runs/candidates, best_picks, purchase_history, strategy_performance/weights, weekly_reports, todos, blog_posts | +| `stock.db` | stock-lab | articles, portfolio, broker_cash, asset_snapshots, sell_history | +| `music.db` | music-lab | music_tasks, music_library (provider, lyrics, image_url, suno_id, file_hash, cover_images, wav_url, video_url, stem_urls) | +| `blog_marketing.db` | blog-lab | keyword_analyses, blog_posts, brand_links, commissions, generation_tasks, prompt_templates | +| `realestate.db` | realestate-lab | announcements, announcement_models, user_profile, match_results, collect_log | +| `agent_office.db` | agent-office | agent_config, agent_tasks, agent_logs, telegram_state | --- ## 환경변수 ```env -# 경로 설정 +# 경로 RUNTIME_PATH=. REPO_PATH=. FRONTEND_PATH=./frontend/dist @@ -306,6 +269,32 @@ PGID=1000 # 외부 서비스 WINDOWS_AI_SERVER_URL=http://192.168.45.59:8000 WEBHOOK_SECRET=your_secret_here + +# LLM (stock-lab, blog-lab, agent-office 공통) +ANTHROPIC_API_KEY=sk-ant-... +ANTHROPIC_MODEL=claude-haiku-4-5-20251001 +LLM_PROVIDER=claude # claude | ollama +OLLAMA_URL=http://192.168.45.59:11435 +OLLAMA_MODEL=qwen3:14b + +# music-lab +SUNO_API_KEY= +MUSIC_AI_SERVER_URL= +MUSIC_MEDIA_BASE=/media/music + +# blog-lab +NAVER_CLIENT_ID= +NAVER_CLIENT_SECRET= + +# realestate-lab +DATA_GO_KR_API_KEY= + +# agent-office +TELEGRAM_BOT_TOKEN= +TELEGRAM_CHAT_ID= +TELEGRAM_WEBHOOK_URL= +STOCK_LAB_URL=http://stock-lab:8000 +MUSIC_LAB_URL=http://music-lab:8000 ``` --- @@ -316,9 +305,9 @@ WEBHOOK_SECRET=your_secret_here |------|----| | 장비 | Synology NAS (Intel Celeron J4025, 18GB RAM) | | Docker | Synology Container Manager | -| Git 서버 | Gitea (NAS 내부 self-hosted) | -| AI 서버 | Windows PC (192.168.45.59:8000) — RTX 3070 Ti + Ollama | -| Python | 3.12 (`slim` / `alpine` 기반 이미지) | +| Git 서버 | Gitea (NAS 내부 self-hosted, `gahusb.synology.me`) | +| AI 서버 | Windows PC (192.168.45.59) — RTX 5070 Ti (16GB VRAM) + Ollama + MusicGen | +| Python | 3.12 (`slim` 기반 이미지) | | DB | SQLite (볼륨 마운트로 영속 저장) | --- @@ -327,8 +316,18 @@ WEBHOOK_SECRET=your_secret_here - **`.env` 파일** — 절대 커밋 금지. `.env.example`만 레포에 포함 - **Nginx trailing slash** — `/api/portfolio`는 두 location 블록으로 처리 (trailing slash 유무 모두 매칭) -- **라우트 순서** — `/api/todos/done`은 `/api/todos/{id}` 보다 먼저 등록 필수 +- **라우트 순서** — `DELETE /api/todos/done`은 `/api/todos/{id}` 보다 먼저 등록 필수 (FastAPI prefix 매칭) - **캐시 전략** — `index.html`: no-store / `assets/`: 1년 immutable - **PUID/PGID** — travel-proxy는 NAS 파일 권한을 위해 환경변수 주입 필수 -- **공휴일 목록** — `stock-lab/app/holidays.json` 매년 수동 갱신 필요 (KRX 기준) -- **Windows AI 서버** — IP 192.168.45.59 (공유기 DHCP 고정 예약) +- **공휴일 목록** — `stock-lab/app/holidays.json` 매년 수동 갱신 (KRX 기준) +- **Windows AI 서버 IP** — `192.168.45.59` 공유기 DHCP 고정 예약. Synology Tailscale은 userspace 모드라 TCP 불가 → 로컬 IP 사용 +- **Suno CDN** — `cdn1.suno.ai` URL은 임시 만료 → 생성 즉시 로컬 다운로드 필수 +- **LLM provider 롤백** — Claude API 장애 시 `.env`의 `LLM_PROVIDER=ollama`로 전환 후 `docker compose up -d` +- **시뮬레이션 교체 방식** — `best_picks`는 교체형 (`is_active=0` 비활성화 후 신규 입력) + +--- + +## 참고 문서 + +- `CLAUDE.md` — Claude Code 작업용 상세 컨텍스트 (API 전체 목록, 테이블 스키마 등) +- `docs/` — 서비스별 기획·설계 문서