README.md / STATUS.md가 blog-lab을 운영 중인 18700 포트 컨테이너로 설명하고 insta-lab/personal/packs-lab을 누락했던 문제 정리. CLAUDE.md를 source of truth로 다음을 갱신: - 컨테이너 표 (11개로 정합화) - 디렉토리 구조 (insta-lab/personal/packs-lab 추가) - 빠른 시작 URL 표 - blog-lab 섹션 → insta-lab 파이프라인 설명 - agent-office 표 (InstaAgent + YouTubeResearcher 반영) - 스케줄러 잡 목록 (09:00 Insta trends, 09:30 Insta extract, 16:30 screener 등) - DB 표 (insta.db + personal.db + Supabase pack_files 추가) - .env 예시 (YOUTUBE_DATA_API_KEY, ADMIN_API_KEY, INSTA_LAB_URL 등) - STATUS 최근 작업: 2026-05-15~17 인스타 + 보안 fix 이력
394 lines
18 KiB
Markdown
394 lines
18 KiB
Markdown
# web-backend
|
||
|
||
Synology NAS 기반 개인 웹 플랫폼 백엔드 모노레포.
|
||
로또 분석, 주식 포트폴리오, AI 음악 생성, 인스타 카드 피드, 부동산 청약, AI 에이전트 오피스, 여행 앨범, 개인 서비스(포트폴리오·블로그·투두), NAS 자료 다운로드 자동화를 하나의 Docker Compose 스택으로 운영한다.
|
||
|
||
---
|
||
|
||
## 서비스 구성
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────────┐
|
||
│ frontend (Nginx:8080) │
|
||
│ ├── 정적 SPA 서빙 (React + Vite) │
|
||
│ └── API 리버스 프록시 │
|
||
│ ├── /api/ → lotto:8000 (로또) │
|
||
│ ├── /api/stock/, /trade/ → stock:8000 │
|
||
│ ├── /api/portfolio → stock:8000 │
|
||
│ ├── /api/music/ → music-lab:8000 │
|
||
│ ├── /api/insta/ → insta-lab:8000 │
|
||
│ ├── /api/realestate/ → realestate-lab:8000 │
|
||
│ ├── /api/agent-office/ → agent-office:8000 (+ WebSocket) │
|
||
│ ├── /api/profile/, /todos, /blog/ → personal:8000 │
|
||
│ ├── /api/packs/ → packs-lab:8000 (HMAC + 5GB upload) │
|
||
│ ├── /api/travel/ → travel-proxy:8000 │
|
||
│ ├── /media/music/, /media/videos/ (nginx 직접 서빙, 미디어) │
|
||
│ ├── /media/travel/… (nginx 직접 서빙, 사진/썸네일) │
|
||
│ └── /webhook → deployer:9000 │
|
||
└──────────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
| 컨테이너 | 포트 | 역할 |
|
||
|---------|------|------|
|
||
| `lotto` | 18000 | 로또 데이터 수집·분석·추천 API |
|
||
| `stock` | 18500 | 주식 뉴스·AI 요약·KIS 실계좌·포트폴리오·자산 추적 |
|
||
| `music-lab` | 18600 | AI 음악 생성 (Suno + 로컬 MusicGen 듀얼 프로바이더) + YouTube 수익화 |
|
||
| `insta-lab` | 18700 | 인스타 카드 피드 자동 생성 (뉴스→키워드→10페이지 카드, Playwright) |
|
||
| `realestate-lab` | 18800 | 청약 공고 자동 수집·5티어 매칭·신규 매칭 push |
|
||
| `agent-office` | 18900 | AI 에이전트 가상 오피스 (WebSocket + 텔레그램 봇) |
|
||
| `personal` | 18850 | 개인 서비스 — 포트폴리오·블로그·투두 통합 |
|
||
| `packs-lab` | 18950 | NAS 자료 다운로드 자동화 (DSM 공유 링크 + 5GB 청크 업로드) |
|
||
| `travel-proxy` | 19000 | 여행 사진 API + 온디맨드 썸네일 |
|
||
| `frontend` | 8080 | SPA 서빙 + 리버스 프록시 |
|
||
| `webpage-deployer` | 19010 | Gitea Webhook → 자동 배포 |
|
||
|
||
---
|
||
|
||
## 디렉토리 구조
|
||
|
||
```
|
||
web-backend/
|
||
├── lotto/ # 로또 추천·통계·시뮬레이션
|
||
├── stock/ # 주식·포트폴리오·KIS 연동
|
||
├── music-lab/ # AI 음악 생성 + YouTube 수익화
|
||
├── insta-lab/ # 인스타 카드 피드 자동 생성 (Playwright)
|
||
├── realestate-lab/ # 청약 자동 수집·5티어 매칭
|
||
├── agent-office/ # AI 에이전트 오피스 (WS + 텔레그램)
|
||
├── personal/ # 포트폴리오·블로그·투두 통합
|
||
├── packs-lab/ # NAS 자료 다운로드 자동화 (HMAC + Supabase)
|
||
├── 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 Code 작업용 상세 컨텍스트
|
||
```
|
||
|
||
---
|
||
|
||
## 빠른 시작 (로컬 개발)
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
docker compose up -d
|
||
|
||
curl http://localhost:18000/health
|
||
curl http://localhost:18500/health
|
||
```
|
||
|
||
| 서비스 | 로컬 URL |
|
||
|--------|----------|
|
||
| Frontend + API | http://localhost:8080 |
|
||
| lotto | http://localhost:18000 |
|
||
| stock | http://localhost:18500 |
|
||
| music-lab | http://localhost:18600 |
|
||
| insta-lab | http://localhost:18700 |
|
||
| realestate-lab | http://localhost:18800 |
|
||
| personal | http://localhost:18850 |
|
||
| agent-office | http://localhost:18900 |
|
||
| packs-lab | http://localhost:18950 |
|
||
| travel-proxy | http://localhost:19000 |
|
||
|
||
---
|
||
|
||
## 서비스별 기능
|
||
|
||
### 1. lotto-backend (`/api/`)
|
||
|
||
로또 당첨번호 수집·통계 분석·몬테카를로 시뮬레이션 기반 추천 + 투두·블로그 CRUD.
|
||
|
||
- **로또**: 당첨번호 조회, 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쌍 교체)
|
||
|
||
### 2. stock (`/api/stock/`, `/api/trade/`, `/api/portfolio`)
|
||
|
||
주식 뉴스 스크래핑 + LLM 요약 + KIS 실계좌 연동 + 포트폴리오·자산 스냅샷.
|
||
|
||
- **뉴스**: 네이버 증권 + 해외 사이트 크롤링, LLM 기반 한국어 요약
|
||
- **실계좌**: Windows AI 서버(192.168.45.59:8000) 프록시 → KIS Open API (잔고/주문)
|
||
- **포트폴리오**: 종목·예수금·매도 히스토리 관리, 현재가 자동 조회
|
||
- **자산 스냅샷**: 평일 15:40 자동 저장 (KRX 공휴일 판별, `holidays.json` 매년 갱신)
|
||
|
||
**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/`)
|
||
|
||
듀얼 프로바이더 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/`으로 직접 서빙
|
||
- **가사 도구**: 저장·편집·타임스탬프 기반 가라오케 동기
|
||
|
||
### 4. insta-lab (`/api/insta/`)
|
||
|
||
인스타그램 카드 피드 자동 생성 — 뉴스 모니터링 → 키워드 추출 → 10페이지 카드 카피·PNG 렌더 → 텔레그램 푸시 → 사용자 수동 업로드.
|
||
|
||
```
|
||
NAVER 뉴스 + YouTube 인기 (외부 트렌드)
|
||
→ 카테고리별 빈도 + Claude Haiku 정제 → 트렌딩 키워드
|
||
→ 사용자가 키워드 선택
|
||
→ Claude Sonnet으로 10페이지 카피 추론 (커버 1 + 본문 8 + CTA 1)
|
||
→ Jinja2 + Playwright 1080×1350 PNG 10장 렌더
|
||
→ 텔레그램 미디어 그룹 + 추천 캡션·해시태그
|
||
```
|
||
|
||
- **AI 엔진**: Claude Sonnet (카피) + Claude Haiku (키워드 분류)
|
||
- **데이터 소스**: NAVER 뉴스 검색 + YouTube Data API v3 mostPopular(KR)
|
||
- **카테고리 가중치**: 사용자가 economy/psychology/celebrity 등 카테고리별 가중치 설정 → 자동 추출 비율에 반영
|
||
- **카드 디자인**: `insta-lab/app/templates/default/card.html.j2` — 사용자가 자유 수정 (Tailwind 등)
|
||
- **프롬프트 템플릿**: DB에 저장 → 코드 배포 없이 수정 가능
|
||
|
||
### 5. realestate-lab (`/api/realestate/`)
|
||
|
||
공공데이터포털 청약홈 API 연동 + 프로필 기반 자동 매칭.
|
||
|
||
- **공고 수집**: 09:00 매일 자동 (`DATA_GO_KR_API_KEY` 필요)
|
||
- **상태 갱신 + 재매칭**: 00:00 매일 자동
|
||
- **프로필 매칭**: 지역·주택형·소득·부양가족 등으로 점수화, 신규 매칭 알림
|
||
- **대시보드**: 진행 중 공고수, 신규 매칭수, 다가오는 일정 요약
|
||
|
||
### 6. agent-office (`/api/agent-office/`)
|
||
|
||
AI 에이전트 가상 오피스 — 2D 픽셀아트 사무실에서 4명의 에이전트가 실제 작업을 수행한다.
|
||
|
||
- **아키텍처**: stock / music-lab / insta-lab / realestate-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`) | 08:00 매일 | — | 뉴스 요약 (LLM) → 텔레그램 아침 브리핑, 종목 알람 등록 |
|
||
| 🎵 **음악 프로듀서** (`music`) | 수동 트리거 | ✅ 작곡 | 프롬프트 수신 → 승인 → Suno API 작곡 → 트랙 푸시 |
|
||
| 🎴 **인스타 큐레이터** (`insta`) | 09:00 / 09:30 매일 | — | 09:00 외부 트렌드(NAVER + YouTube) 수집 → 09:30 가중치 기반 키워드 추출 → 텔레그램 후보 5개씩 카테고리당 인라인 버튼 푸시 → 사용자 선택 시 카드 10장 미디어 그룹 |
|
||
| 🏢 **청약 애널리스트** (`realestate`) | realestate-lab push trigger | — | realestate-lab이 신규 매칭 발견 시 push → 인라인 [북마크] 버튼 포함 텔레그램 알림 |
|
||
| 🎬 **YouTube 리서처** (`youtube`) | 09:00 매일 | — | 한국 YouTube 트렌딩 + Google Trends + Billboard → music-lab market_trends push |
|
||
|
||
#### 에이전트별 명령
|
||
|
||
**Stock** — `fetch_news`, `list_alerts`, `add_alert`, `test_telegram`
|
||
**Music** — `compose` (승인 필요), `credits`
|
||
**Insta** — `extract`, `render <keyword_id>`, `collect_trends`
|
||
**Realestate** — `fetch_matches`, `dashboard`
|
||
**YouTube** — `research {countries: [...]}`
|
||
|
||
#### 스케줄러 잡
|
||
|
||
- 07:00 월요일 — Lotto: AI 큐레이터 브리핑 (5세트 + 내러티브)
|
||
- 07:30 — Stock: 뉴스 요약
|
||
- 08:00 평일 — Stock: AI 뉴스 sentiment 분석
|
||
- 09:00 — YouTube: 한국 트렌딩 수집
|
||
- 09:00 — Insta: 외부 트렌드 수집 (NAVER 인기 + YouTube mostPopular)
|
||
- 09:30 — Insta: 키워드 추출 (가중치 적용) + 텔레그램 후보 푸시
|
||
- 15:40 평일 — Stock: 총 자산 스냅샷
|
||
- 16:30 평일 — Stock: 스크리너 실행
|
||
- 60초 interval — 유휴 에이전트 휴식 체크
|
||
|
||
### 7. travel-proxy (`/api/travel/`)
|
||
|
||
여행 사진 API + SQLite 인덱스 + 온디맨드 썸네일 + 지역 관리.
|
||
|
||
- 원본: `/data/travel/` (RO 마운트)
|
||
- 썸네일: 480×480 Pillow 리사이징, `/data/thumbs/` 영구 캐시 (tmp → rename 원자성 보장)
|
||
- DB: `/data/thumbs/travel.db` (photos, album_covers 테이블)
|
||
- 메타: `region_map.json` (RO) + `region_map_extra.json` (RW 오버라이드) + `regions.geojson`
|
||
- 지역 관리: 앨범 지역 변경, 커스텀 지역 생성, 지도 핀 좌표 지정
|
||
- 데이터 흐름: 수동 sync → 폴더 스캔 → SQLite 인덱싱 + 썸네일 일괄 생성
|
||
|
||
### 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분
|
||
|
||
---
|
||
|
||
## 핵심 로직
|
||
|
||
### 몬테카를로 시뮬레이션 (lotto-backend)
|
||
|
||
```
|
||
역대 당첨번호 분석 → 번호별 가중치 산출
|
||
→ 가중 확률 샘플링으로 후보 20,000개 생성
|
||
→ 5가지 기법으로 각 조합 점수화
|
||
→ 상위 100개 DB 저장 → best_picks 20개 교체
|
||
```
|
||
|
||
| 기법 | 가중치 | 내용 |
|
||
|------|--------|------|
|
||
| 빈도 Z-score | 25% | 번호 출현 빈도의 표준편차 |
|
||
| 조합 지문 | 30% | 합계 정규분포 + 홀짝 비율 + 구간분포 |
|
||
| 갭 분석 | 20% | 마지막 출현 이후 경과 회차 |
|
||
| 공동 출현 | 15% | 번호 쌍 동시 출현 빈도 |
|
||
| 다양성 | 10% | 연속번호·범위·구간 커버리지 |
|
||
|
||
### LLM 요약 provider 추상화 (stock)
|
||
|
||
`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)
|
||
|
||
평일 15:40 자동 실행 → `holidays.json`으로 공휴일 스킵 → 포트폴리오 현재가 조회 + 예수금 합계 → `asset_snapshots` upsert (date UNIQUE).
|
||
|
||
### 에이전트 FSM + WS 동기화 (agent-office)
|
||
|
||
DB에 저장된 에이전트 상태가 바뀔 때마다 `websocket_manager`가 전체 클라이언트에 브로드캐스트. 텔레그램 봇은 `waiting` 상태 작업에 인라인 키보드를 붙여 승인 요청. 승인/거부 결과가 DB → WS → 프론트로 전파.
|
||
|
||
---
|
||
|
||
## 자동 배포
|
||
|
||
```
|
||
git push → Gitea → X-Gitea-Signature (HMAC SHA256)
|
||
→ deployer:9000/webhook (서명 검증, compare_digest)
|
||
→ BackgroundTask: scripts/deploy.sh (10분 타임아웃)
|
||
1. git pull
|
||
2. .releases/{timestamp}/ 백업
|
||
3. rsync (repo → runtime)
|
||
4. docker compose up -d --build
|
||
5. chown PUID:PGID
|
||
```
|
||
|
||
> 프론트엔드는 **자동 배포 안 됨** — 로컬 빌드 후 NAS에 수동 업로드 (`scripts/deploy.bat --frontend`)
|
||
|
||
---
|
||
|
||
## 데이터베이스
|
||
|
||
각 서비스는 독립 SQLite DB를 `/app/data/` 볼륨에 저장.
|
||
|
||
| DB | 소유 서비스 | 주요 테이블 |
|
||
|----|------------|-----------|
|
||
| `lotto.db` | lotto | draws, recommendations, simulation_runs/candidates, best_picks, purchase_history, strategy_performance/weights, weekly_reports, lotto_briefings |
|
||
| `stock.db` | stock | 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), video_projects, revenue_records, market_trends, trend_reports |
|
||
| `insta.db` | insta-lab | news_articles, trending_keywords (source 컬럼), card_slates, card_assets, generation_tasks, prompt_templates, account_preferences |
|
||
| `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, conversation_messages |
|
||
| `personal.db` | personal | profile, careers, projects, skills, introductions, todos, blog_posts |
|
||
| `travel.db` | travel-proxy | photos (album, filename, mtime, has_thumb), album_covers |
|
||
| `pack_files` (외부 Supabase) | packs-lab | filename, host_path, mime, byte_size, sha256, deleted_at |
|
||
|
||
---
|
||
|
||
## 환경변수
|
||
|
||
```env
|
||
# 경로
|
||
RUNTIME_PATH=.
|
||
REPO_PATH=.
|
||
FRONTEND_PATH=./frontend/dist
|
||
PHOTO_PATH=./mock_data/photos
|
||
|
||
# NAS 파일 권한
|
||
PUID=1000
|
||
PGID=1000
|
||
|
||
# 외부 서비스
|
||
WINDOWS_AI_SERVER_URL=http://192.168.45.59:8000
|
||
WEBHOOK_SECRET=your_secret_here
|
||
|
||
# LLM (stock, insta-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
|
||
|
||
# stock admin protection (CODE_REVIEW F2)
|
||
ADMIN_API_KEY=
|
||
ALLOW_UNAUTHENTICATED_ADMIN=false
|
||
|
||
# music-lab
|
||
SUNO_API_KEY=
|
||
MUSIC_AI_SERVER_URL=
|
||
MUSIC_MEDIA_BASE=/media/music
|
||
|
||
# insta-lab + agent-office (NAVER 검색 + YouTube Data API 공유)
|
||
NAVER_CLIENT_ID=
|
||
NAVER_CLIENT_SECRET=
|
||
YOUTUBE_DATA_API_KEY=
|
||
|
||
# realestate-lab
|
||
DATA_GO_KR_API_KEY=
|
||
|
||
# packs-lab (DSM + Supabase)
|
||
DSM_HOST=
|
||
DSM_USER=
|
||
DSM_PASS=
|
||
BACKEND_HMAC_SECRET=
|
||
SUPABASE_URL=
|
||
SUPABASE_SERVICE_KEY=
|
||
PACK_HOST_DIR=/docker/webpage/media/packs # shared folder 시점 (CLAUDE.md F5)
|
||
|
||
# agent-office
|
||
TELEGRAM_BOT_TOKEN=
|
||
TELEGRAM_CHAT_ID=
|
||
TELEGRAM_WEBHOOK_URL=
|
||
STOCK_URL=http://stock:8000
|
||
MUSIC_LAB_URL=http://music-lab:8000
|
||
INSTA_LAB_URL=http://insta-lab:8000
|
||
REALESTATE_LAB_URL=http://realestate-lab:8000
|
||
|
||
# personal (포트폴리오 편집 인증)
|
||
PORTFOLIO_EDIT_PASSWORD=
|
||
```
|
||
|
||
---
|
||
|
||
## 인프라
|
||
|
||
| 항목 | 값 |
|
||
|------|----|
|
||
| 장비 | Synology NAS (Intel Celeron J4025, 18GB RAM) |
|
||
| Docker | Synology Container Manager |
|
||
| 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 (볼륨 마운트로 영속 저장) |
|
||
|
||
---
|
||
|
||
## 주의사항
|
||
|
||
- **`.env` 파일** — 절대 커밋 금지. `.env.example`만 레포에 포함
|
||
- **Nginx trailing slash** — `/api/portfolio`는 두 location 블록으로 처리 (trailing slash 유무 모두 매칭)
|
||
- **라우트 순서** — `DELETE /api/todos/done`은 `/api/todos/{id}` 보다 먼저 등록 필수 (FastAPI prefix 매칭)
|
||
- **캐시 전략** — `index.html`: no-store / `assets/`: 1년 immutable
|
||
- **PUID/PGID** — travel-proxy는 NAS 파일 권한을 위해 환경변수 주입 필수
|
||
- **공휴일 목록** — `stock/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/` — 서비스별 기획·설계 문서
|