2026-03-11 08:30:47 +09:00

web-backend

Synology NAS 기반 개인 웹 플랫폼 백엔드 모노레포. 로또 분석, 주식 포트폴리오, 여행 앨범, 블로그, 투두리스트를 하나의 서비스로 운영한다.


서비스 구성

┌─────────────────────────────────────────────────────────────┐
│  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-backend 18000 로또·블로그·투두 API
stock-lab 18500 주식 뉴스·포트폴리오·자산 추적
travel-proxy 19000 여행 사진 API + 썸네일 생성
lotto-frontend 8080 SPA 서빙 + 리버스 프록시
webpage-deployer 19010 Gitea Webhook → 자동 배포

디렉토리 구조

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     # 전체 서비스 헬스 체크
│
├── docker-compose.yml
├── .env.example
└── CLAUDE.md

빠른 시작 (로컬 개발)

# 1. 환경변수 설정
cp .env.example .env

# 2. 컨테이너 실행 (.env 기본값으로 즉시 실행 가능)
docker compose up -d

# 3. 확인
curl http://localhost:18000/health
curl http://localhost:18500/health
서비스 로컬 URL
Frontend + API http://localhost:8080
lotto-backend http://localhost:18000
stock-lab http://localhost:18500
travel-proxy http://localhost:19000

API 목록

lotto-backend (/api/)

로또

메서드 경로 설명
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 당첨번호 수동 동기화

추천 이력

메서드 경로 설명
GET /api/history 목록 (limit, offset, favorite, tag, sort)
PATCH /api/history/{id} 즐겨찾기·메모·태그 수정
DELETE /api/history/{id} 삭제

투두리스트

메서드 경로 설명
GET /api/todos 전체 목록
POST /api/todos 생성 (status: todo|in_progress|done)
PUT /api/todos/{id} 수정
DELETE /api/todos/done 완료 항목 일괄 삭제
DELETE /api/todos/{id} 개별 삭제

⚠️ /done 라우트는 반드시 /{id} 보다 먼저 등록해야 함

블로그

메서드 경로 설명
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 }


stock-lab (/api/stock/, /api/trade/, /api/portfolio)

뉴스 & 지표

메서드 경로 설명
GET /api/stock/news 뉴스 목록 (limit, category)
GET /api/stock/indices 주요 지표 (KOSPI 등)
POST /api/stock/scrap 뉴스 수동 스크랩

실계좌 (Windows AI 서버 프록시)

메서드 경로 설명
GET /api/trade/balance 실계좌 잔고 조회
POST /api/trade/order 주문 (BUY|SELL, price=0이면 시장가)

포트폴리오

메서드 경로 설명
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: 전체)

travel-proxy (/api/travel/)

메서드 경로 설명
GET /api/travel/regions 지역 GeoJSON
GET /api/travel/photos 사진 목록 (region, page, size)
POST /api/travel/reload 캐시 초기화
  • 썸네일: /media/travel/.thumb/{album}/{file} (nginx 직접 서빙, 30일 캐시)
  • 원본: /media/travel/{album}/{file} (nginx 직접 서빙, 7일 캐시)

핵심 로직

몬테카를로 시뮬레이션 (lotto-backend)

역대 당첨번호 분석 → 번호별 가중치 산출
→ 가중 확률 샘플링으로 후보 20,000개 생성
→ 5가지 기법으로 각 조합 점수화
→ 상위 100개 DB 저장 → best_picks 20개 교체

5가지 채점 기법:

기법 가중치 내용
빈도 Z-score 25% 번호 출현 빈도의 표준편차
조합 지문 30% 합계 정규분포 + 홀짝 비율 + 구간분포
갭 분석 20% 마지막 출현 이후 경과 회차
공동 출현 15% 번호 쌍 동시 출현 빈도
다양성 10% 연속번호·범위·구간 커버리지

스케줄: 매일 0, 4, 8, 12, 16, 20시 (하루 6회, 각 5분)

총 자산 스냅샷 (stock-lab)

평일 15:40 자동 실행 → holidays.json으로 공휴일 스킵
→ 포트폴리오 현재가 조회 → total_eval
→ 예수금 합계 → total_cash
→ asset_snapshots upsert (date UNIQUE, 같은 날 중복 시 덮어씀)

현재가 조회 (stock-lab)

  • 네이버 모바일 API 우선 (m.stock.naver.com/api/stock/{ticker}/basic)
  • 실패 시 네이버 금융 HTML 파싱 폴백
  • 3분 TTL 메모리 캐시

여행 사진 썸네일 (travel-proxy)

  • 480×480 리사이징 (Pillow), 확장자 유지 (JPEG/PNG/WEBP)
  • 온디맨드 생성 후 /data/thumbs/ 영구 캐시
  • 원자성 보장: tmp 파일 작성 후 rename

자동 배포

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에 수동 업로드


데이터베이스

lotto.db (/app/data/lotto.db)

테이블 설명
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)

환경변수

# 경로 설정
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

인프라

항목
장비 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 기반 이미지)
DB SQLite (볼륨 마운트로 영속 저장)

주의사항

  • .env 파일 — 절대 커밋 금지. .env.example만 레포에 포함
  • Nginx trailing slash/api/portfolio는 두 location 블록으로 처리 (trailing slash 유무 모두 매칭)
  • 라우트 순서/api/todos/done/api/todos/{id} 보다 먼저 등록 필수
  • 캐시 전략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 고정 예약)
Description
웹페이지 백엔드 저장소
Readme 3.6 MiB
Languages
Python 98.7%
Shell 0.8%
Dockerfile 0.4%
Jinja 0.1%