refactor: backend→lotto 서비스 리네이밍 + lotto.db 레거시 테이블 스키마 제거

- backend/ → lotto/ 디렉토리 이동
- docker-compose: lotto-backend→lotto, lotto-frontend→frontend
- deploy scripts, nginx, agent-office config 네이밍 일괄 반영
- lotto/app/db.py에서 todos·blog_posts CREATE TABLE 제거 (personal로 이관 완료)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-27 17:29:13 +09:00
parent 6c46759848
commit 2a8635e9ed
26 changed files with 18 additions and 56 deletions

View File

@@ -31,7 +31,7 @@ Synology NAS 기반의 개인 웹 플랫폼 백엔드 모노레포.
``` ```
/volume1 /volume1
├── docker/webpage/ # 운영 런타임 (Docker Compose 실행 위치) ├── docker/webpage/ # 운영 런타임 (Docker Compose 실행 위치)
│ ├── backend/ # lotto-backend 소스 (rsync 동기화) │ ├── lotto/ # lotto 소스 (rsync 동기화)
│ ├── stock-lab/ # stock-lab 소스 (rsync 동기화) │ ├── stock-lab/ # stock-lab 소스 (rsync 동기화)
│ ├── travel-proxy/ # travel-proxy 소스 (rsync 동기화) │ ├── travel-proxy/ # travel-proxy 소스 (rsync 동기화)
│ ├── deployer/ # deployer 소스 (rsync 동기화) │ ├── deployer/ # deployer 소스 (rsync 동기화)
@@ -53,7 +53,7 @@ Synology NAS 기반의 개인 웹 플랫폼 백엔드 모노레포.
| 컨테이너 | 포트 | 역할 | | 컨테이너 | 포트 | 역할 |
|---------|------|------| |---------|------|------|
| `lotto-backend` | 18000 | 로또 데이터 수집·분석·추천 API | | `lotto` | 18000 | 로또 데이터 수집·분석·추천 API |
| `stock-lab` | 18500 | 주식 뉴스·AI 분석·KIS API 연동 | | `stock-lab` | 18500 | 주식 뉴스·AI 분석·KIS API 연동 |
| `music-lab` | 18600 | AI 음악 생성·라이브러리 관리 API | | `music-lab` | 18600 | AI 음악 생성·라이브러리 관리 API |
| `blog-lab` | 18700 | 블로그 마케팅 수익화 API | | `blog-lab` | 18700 | 블로그 마케팅 수익화 API |
@@ -61,7 +61,7 @@ Synology NAS 기반의 개인 웹 플랫폼 백엔드 모노레포.
| `agent-office` | 18900 | AI 에이전트 오피스 (실시간 WebSocket + 텔레그램 연동) | | `agent-office` | 18900 | AI 에이전트 오피스 (실시간 WebSocket + 텔레그램 연동) |
| `personal` | 18850 | 개인 서비스 (포트폴리오·블로그·투두 통합) | | `personal` | 18850 | 개인 서비스 (포트폴리오·블로그·투두 통합) |
| `travel-proxy` | 19000 | 여행 사진 API + 썸네일 생성 | | `travel-proxy` | 19000 | 여행 사진 API + 썸네일 생성 |
| `lotto-frontend` (nginx) | 8080 | 정적 SPA 서빙 + API 리버스 프록시 | | `frontend` (nginx) | 8080 | 정적 SPA 서빙 + API 리버스 프록시 |
| `webpage-deployer` | 19010 | Gitea Webhook 수신 → 자동 배포 | | `webpage-deployer` | 19010 | Gitea Webhook 수신 → 자동 배포 |
--- ---
@@ -70,7 +70,7 @@ Synology NAS 기반의 개인 웹 플랫폼 백엔드 모노레포.
| 경로 | 프록시 대상 | 비고 | | 경로 | 프록시 대상 | 비고 |
|------|------------|------| |------|------------|------|
| `/api/` | `lotto-backend:8000` | lotto API (기본) | | `/api/` | `lotto:8000` | lotto API (기본) |
| `/api/travel/` | `travel-proxy:8000` | travel API | | `/api/travel/` | `travel-proxy:8000` | travel API |
| `/api/stock/` | `stock-lab:8000` | stock API | | `/api/stock/` | `stock-lab:8000` | stock API |
| `/api/trade/` | `stock-lab:8000` | KIS 실계좌 API | | `/api/trade/` | `stock-lab:8000` | KIS 실계좌 API |
@@ -139,7 +139,7 @@ docker compose up -d
## 9. 서비스별 핵심 정보 ## 9. 서비스별 핵심 정보
### lotto-lab (backend/) ### lotto-lab (lotto/)
- DB: `/app/data/lotto.db` - DB: `/app/data/lotto.db`
- 데이터 소스: `smok95.github.io/lotto/results/` - 데이터 소스: `smok95.github.io/lotto/results/`
- 파일 구조: `main.py`, `db.py`, `recommender.py`, `collector.py`, `checker.py`, `generator.py`, `analyzer.py`, `utils.py`, `purchase_manager.py`, `strategy_evolver.py` - 파일 구조: `main.py`, `db.py`, `recommender.py`, `collector.py`, `checker.py`, `generator.py`, `analyzer.py`, `utils.py`, `purchase_manager.py`, `strategy_evolver.py`
@@ -454,7 +454,7 @@ docker compose up -d
- `CONVERSATION_MODEL`: 대화 모델 (기본 `claude-haiku-4-5-20251001`) - `CONVERSATION_MODEL`: 대화 모델 (기본 `claude-haiku-4-5-20251001`)
- `CONVERSATION_HISTORY_LIMIT`: 이력 주입 수 (기본 20) - `CONVERSATION_HISTORY_LIMIT`: 이력 주입 수 (기본 20)
- `CONVERSATION_RATE_PER_MIN`: 채팅당 분당 최대 메시지 (기본 6) - `CONVERSATION_RATE_PER_MIN`: 채팅당 분당 최대 메시지 (기본 6)
- `LOTTO_BACKEND_URL`: 기본 `http://lotto-backend:8000` - `LOTTO_BACKEND_URL`: 기본 `http://lotto:8000`
- `LOTTO_CURATOR_MODEL`: 기본 `claude-sonnet-4-5` - `LOTTO_CURATOR_MODEL`: 기본 `claude-sonnet-4-5`
**텔레그램 자연어 대화 (옵션 B)** **텔레그램 자연어 대화 (옵션 B)**

View File

@@ -32,5 +32,5 @@ BREAK_DURATION_MIN = int(os.getenv("BREAK_DURATION_MIN", "60")) # 1 min
BREAK_DURATION_MAX = int(os.getenv("BREAK_DURATION_MAX", "180")) # 3 min BREAK_DURATION_MAX = int(os.getenv("BREAK_DURATION_MAX", "180")) # 3 min
# Lotto Curator # Lotto Curator
LOTTO_BACKEND_URL = os.getenv("LOTTO_BACKEND_URL", "http://lotto-backend:8000") LOTTO_BACKEND_URL = os.getenv("LOTTO_BACKEND_URL", "http://lotto:8000")
LOTTO_CURATOR_MODEL = os.getenv("LOTTO_CURATOR_MODEL", "claude-sonnet-4-5") LOTTO_CURATOR_MODEL = os.getenv("LOTTO_CURATOR_MODEL", "claude-sonnet-4-5")

View File

@@ -1,12 +1,12 @@
name: webpage name: webpage
services: services:
backend: lotto:
build: build:
context: ./backend context: ./lotto
args: args:
APP_VERSION: ${APP_VERSION:-dev} APP_VERSION: ${APP_VERSION:-dev}
container_name: lotto-backend container_name: lotto
restart: unless-stopped restart: unless-stopped
ports: ports:
- "18000:8000" - "18000:8000"
@@ -131,7 +131,7 @@ services:
- TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL:-} - TELEGRAM_WEBHOOK_URL=${TELEGRAM_WEBHOOK_URL:-}
- TELEGRAM_WIFE_CHAT_ID=${TELEGRAM_WIFE_CHAT_ID:-} - TELEGRAM_WIFE_CHAT_ID=${TELEGRAM_WIFE_CHAT_ID:-}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- LOTTO_BACKEND_URL=${LOTTO_BACKEND_URL:-http://lotto-backend:8000} - LOTTO_BACKEND_URL=${LOTTO_BACKEND_URL:-http://lotto:8000}
- LOTTO_CURATOR_MODEL=${LOTTO_CURATOR_MODEL:-claude-sonnet-4-5} - LOTTO_CURATOR_MODEL=${LOTTO_CURATOR_MODEL:-claude-sonnet-4-5}
- CONVERSATION_MODEL=${CONVERSATION_MODEL:-claude-haiku-4-5-20251001} - CONVERSATION_MODEL=${CONVERSATION_MODEL:-claude-haiku-4-5-20251001}
- CONVERSATION_HISTORY_LIMIT=${CONVERSATION_HISTORY_LIMIT:-20} - CONVERSATION_HISTORY_LIMIT=${CONVERSATION_HISTORY_LIMIT:-20}
@@ -193,7 +193,7 @@ services:
frontend: frontend:
image: nginx:alpine image: nginx:alpine
container_name: lotto-frontend container_name: frontend
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
- music-lab - music-lab

View File

@@ -143,44 +143,6 @@ def init_db() -> None:
"ON best_picks(is_active, score_total DESC);" "ON best_picks(is_active, score_total DESC);"
) )
# ── todos 테이블 ───────────────────────────────────────────────────────
conn.execute(
"""
CREATE TABLE IF NOT EXISTS todos (
id TEXT PRIMARY KEY
DEFAULT (lower(hex(randomblob(4))) || '-' || lower(hex(randomblob(2)))),
title TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL DEFAULT 'todo'
CHECK(status IN ('todo','in_progress','done')),
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
);
"""
)
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_todos_created ON todos(created_at DESC);"
)
# ── blog_posts 테이블 ──────────────────────────────────────────────────
conn.execute(
"""
CREATE TABLE IF NOT EXISTS blog_posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
body TEXT NOT NULL DEFAULT '',
excerpt TEXT NOT NULL DEFAULT '',
tags TEXT NOT NULL DEFAULT '[]',
date TEXT NOT NULL DEFAULT (date('now','localtime')),
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
);
"""
)
conn.execute(
"CREATE INDEX IF NOT EXISTS idx_blog_date ON blog_posts(date DESC);"
)
# ── purchase_history 테이블 ──────────────────────────────────────────── # ── purchase_history 테이블 ────────────────────────────────────────────
conn.execute( conn.execute(
""" """

View File

@@ -204,7 +204,7 @@ server {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://backend:8000; proxy_pass http://lotto:8000;
} }
# Fear & Greed Index (CNN 공개 API) # Fear & Greed Index (CNN 공개 API)

View File

@@ -2,7 +2,7 @@
set -euo pipefail set -euo pipefail
# ── 서비스 목록 (한 곳에서만 관리) ── # ── 서비스 목록 (한 곳에서만 관리) ──
SERVICES="backend travel-proxy deployer stock-lab music-lab blog-lab realestate-lab agent-office personal nginx scripts" SERVICES="lotto travel-proxy deployer stock-lab music-lab blog-lab realestate-lab agent-office personal nginx scripts"
# 1. 자동 감지: Docker 컨테이너 내부인가? # 1. 자동 감지: Docker 컨테이너 내부인가?
if [ -d "/repo" ] && [ -d "/runtime" ]; then if [ -d "/repo" ] && [ -d "/runtime" ]; then

View File

@@ -7,11 +7,11 @@ flock -n 200 || { echo "Deploy already running, skipping"; exit 0; }
# ── 서비스 목록 (한 곳에서만 관리) ── # ── 서비스 목록 (한 곳에서만 관리) ──
# docker compose 서비스명 (deployer 제외 — 자기 자신을 재빌드하면 스크립트 중단) # docker compose 서비스명 (deployer 제외 — 자기 자신을 재빌드하면 스크립트 중단)
BUILD_TARGETS="backend travel-proxy stock-lab music-lab blog-lab realestate-lab agent-office personal frontend" BUILD_TARGETS="lotto travel-proxy stock-lab music-lab blog-lab realestate-lab agent-office personal frontend"
# 컨테이너 이름 (고아 정리용) # 컨테이너 이름 (고아 정리용)
CONTAINER_NAMES="lotto-backend stock-lab music-lab blog-lab realestate-lab agent-office personal travel-proxy lotto-frontend" CONTAINER_NAMES="lotto stock-lab music-lab blog-lab realestate-lab agent-office personal travel-proxy frontend"
# 헬스체크 대상 # 헬스체크 대상
HEALTH_ENDPOINTS="backend stock-lab travel-proxy music-lab blog-lab realestate-lab agent-office personal" HEALTH_ENDPOINTS="lotto stock-lab travel-proxy music-lab blog-lab realestate-lab agent-office personal"
# data 디렉토리 # data 디렉토리
DATA_DIRS="music stock blog realestate agent-office personal" DATA_DIRS="music stock blog realestate agent-office personal"
@@ -89,7 +89,7 @@ done
# 3) 재빌드 및 시작 # 3) 재빌드 및 시작
docker compose up -d --build $BUILD_TARGETS docker compose up -d --build $BUILD_TARGETS
docker exec lotto-frontend nginx -s reload 2>/dev/null || true docker exec frontend nginx -s reload 2>/dev/null || true
# ── 배포 후 헬스체크 ── # ── 배포 후 헬스체크 ──
echo "Waiting for services to start..." echo "Waiting for services to start..."