docs: README 전면 업데이트 — music/blog/realestate/agent-office 추가

- 누락된 4개 서비스 (music-lab, blog-lab, realestate-lab, agent-office) 섹션 추가
- agent-office FSM + WebSocket + 텔레그램 양방향 구조 설명
- LLM provider 추상화 (claude/ollama 전환) 반영
- 환경변수 섹션에 Anthropic/Suno/Naver/Telegram/공공데이터 키 추가
- Windows AI 서버 스펙 정정 (RTX 3070 Ti → RTX 5070 Ti 16GB)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 02:56:32 +09:00
parent d11aadce8a
commit 43ee920617

355
README.md
View File

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