|
|
|
|
@@ -0,0 +1,402 @@
|
|
|
|
|
# Lotto 구매 연동 + 전략 진화 시스템 설계
|
|
|
|
|
|
|
|
|
|
> 작성일: 2026-04-05
|
|
|
|
|
> 상태: 승인 대기
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 1. 목표
|
|
|
|
|
|
|
|
|
|
로또 번호 추천 기능을 고도화하여:
|
|
|
|
|
1. **동행복권 실 구매 연동** — 추천 번호를 클립보드 복사 + 동행복권 바로가기로 실제 구매 지원
|
|
|
|
|
2. **가상 구매 모드** — 돈을 쓰지 않고 "이 번호로 구매한다"를 등록, 결과 발표 후 자동 가상 수익률 계산
|
|
|
|
|
3. **전략 진화 시스템** — 구매 이력 기반으로 각 추천 전략(combined, simulation, heatmap, manual, custom)의 성과를 추적하고, EMA + Softmax로 가중치를 자동 조정하는 메타 전략
|
|
|
|
|
4. **통합 구매 이력** — 실제/가상 구매를 하나의 리스트에서 관리하되, 실 구매는 시각적으로 강조
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 2. 접근 방식
|
|
|
|
|
|
|
|
|
|
**방식 1 (단일 확장) 채택**: 기존 `lotto-backend`(backend/) 서비스 내부에 모듈 추가.
|
|
|
|
|
- NAS Celeron J4025 환경에서 새 컨테이너 추가는 리소스 부담
|
|
|
|
|
- 기존 checker/recommender/DB와 자연스러운 연동 가능
|
|
|
|
|
- 파일 수준 모듈 분리로 유지보수성 확보
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 3. 데이터 모델
|
|
|
|
|
|
|
|
|
|
### 3.1 기존 `purchase_history` 테이블 마이그레이션
|
|
|
|
|
|
|
|
|
|
현재 스키마:
|
|
|
|
|
```sql
|
|
|
|
|
CREATE TABLE purchase_history (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
draw_no INTEGER NOT NULL,
|
|
|
|
|
amount INTEGER NOT NULL,
|
|
|
|
|
sets INTEGER NOT NULL DEFAULT 1,
|
|
|
|
|
prize INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
note TEXT NOT NULL DEFAULT '',
|
|
|
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
|
|
|
);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
마이그레이션 전략: **ALTER TABLE로 컬럼 추가** (기존 데이터 보존)
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
ALTER TABLE purchase_history ADD COLUMN numbers TEXT NOT NULL DEFAULT '[]';
|
|
|
|
|
ALTER TABLE purchase_history ADD COLUMN is_real INTEGER NOT NULL DEFAULT 1;
|
|
|
|
|
ALTER TABLE purchase_history ADD COLUMN source_strategy TEXT NOT NULL DEFAULT 'manual';
|
|
|
|
|
ALTER TABLE purchase_history ADD COLUMN source_detail TEXT NOT NULL DEFAULT '{}';
|
|
|
|
|
ALTER TABLE purchase_history ADD COLUMN checked INTEGER NOT NULL DEFAULT 0;
|
|
|
|
|
ALTER TABLE purchase_history ADD COLUMN results TEXT NOT NULL DEFAULT '[]';
|
|
|
|
|
ALTER TABLE purchase_history ADD COLUMN total_prize INTEGER NOT NULL DEFAULT 0;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
- 기존 레코드: `is_real=1`, `source_strategy='manual'`, `checked=0` (기본값)
|
|
|
|
|
- 기존 `prize` 컬럼은 하위호환용으로 유지. 신규 로직은 `total_prize` + `results` 사용
|
|
|
|
|
- 기존 `sets` 컬럼은 하위호환용으로 유지. 신규 로직은 `numbers` JSON 배열 길이로 세트 수 산출
|
|
|
|
|
|
|
|
|
|
### 3.2 신규 `strategy_performance` 테이블
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
CREATE TABLE IF NOT EXISTS strategy_performance (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
strategy TEXT NOT NULL,
|
|
|
|
|
draw_no INTEGER NOT NULL,
|
|
|
|
|
sets_count INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
total_correct INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
max_correct INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
prize_total INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
avg_score REAL NOT NULL DEFAULT 0.0,
|
|
|
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
|
|
|
UNIQUE(strategy, draw_no)
|
|
|
|
|
);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3.3 신규 `strategy_weights` 테이블
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
CREATE TABLE IF NOT EXISTS strategy_weights (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
strategy TEXT NOT NULL UNIQUE,
|
|
|
|
|
weight REAL NOT NULL DEFAULT 0.2,
|
|
|
|
|
ema_score REAL NOT NULL DEFAULT 0.15,
|
|
|
|
|
total_sets INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
total_hits_3plus INTEGER NOT NULL DEFAULT 0,
|
|
|
|
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
|
|
|
|
);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
초기 가중치 (첫 실행 시 seed):
|
|
|
|
|
|
|
|
|
|
| strategy | weight | ema_score |
|
|
|
|
|
|-----------|--------|-----------|
|
|
|
|
|
| combined | 0.30 | 0.15 |
|
|
|
|
|
| simulation | 0.25 | 0.15 |
|
|
|
|
|
| heatmap | 0.20 | 0.15 |
|
|
|
|
|
| manual | 0.15 | 0.15 |
|
|
|
|
|
| custom | 0.10 | 0.15 |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 4. API 설계
|
|
|
|
|
|
|
|
|
|
### 4.1 구매 API (기존 경로 확장)
|
|
|
|
|
|
|
|
|
|
| 메서드 | 경로 | 변경 사항 |
|
|
|
|
|
|--------|------|----------|
|
|
|
|
|
| `POST` | `/api/lotto/purchase` | 요청 바디 확장 (numbers, is_real, source_strategy, source_detail 추가) |
|
|
|
|
|
| `GET` | `/api/lotto/purchase` | 필터 추가: `is_real`, `strategy`, `checked` |
|
|
|
|
|
| `GET` | `/api/lotto/purchase/stats` | 응답 확장: total/real/virtual + by_strategy 섹션 |
|
|
|
|
|
| `PUT` | `/api/lotto/purchase/{id}` | 기존 그대로 (allowed 필드 확장) |
|
|
|
|
|
| `DELETE` | `/api/lotto/purchase/{id}` | 기존 그대로 |
|
|
|
|
|
|
|
|
|
|
**POST 요청 바디:**
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"draw_no": 1125,
|
|
|
|
|
"numbers": [[3, 12, 23, 34, 38, 45], [7, 14, 21, 29, 36, 42]],
|
|
|
|
|
"is_real": true,
|
|
|
|
|
"amount": 2000,
|
|
|
|
|
"source_strategy": "combined",
|
|
|
|
|
"source_detail": {"recommendation_ids": [451, 452]},
|
|
|
|
|
"note": ""
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
하위호환: `numbers`가 빈 배열이면 기존 방식(sets + amount만)으로 동작. `is_real` 미지정 시 기본값 `true`.
|
|
|
|
|
|
|
|
|
|
**GET /purchase/stats 응답:**
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"total": {"sets": 48, "invested": 48000, "prize": 15000, "roi": -68.75, "win_rate": 12.5},
|
|
|
|
|
"real": {"sets": 20, "invested": 20000, "prize": 10000, "roi": -50.0, "win_rate": 15.0},
|
|
|
|
|
"virtual": {"sets": 28, "invested": 28000, "prize": 5000, "roi": -82.14, "win_rate": 10.7},
|
|
|
|
|
"by_strategy": {
|
|
|
|
|
"combined": {"sets": 15, "avg_correct": 1.8, "hits_3plus": 3, "roi": -45.0},
|
|
|
|
|
"simulation": {"sets": 12, "avg_correct": 2.1, "hits_3plus": 4, "roi": -30.0}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4.2 전략 진화 API (신규)
|
|
|
|
|
|
|
|
|
|
| 메서드 | 경로 | 설명 |
|
|
|
|
|
|--------|------|------|
|
|
|
|
|
| `GET` | `/api/lotto/strategy/weights` | 현재 전략별 가중치 + 성과 요약 + trend |
|
|
|
|
|
| `GET` | `/api/lotto/strategy/performance` | 전략별 회차 성과 이력 (차트용, `days` 파라미터) |
|
|
|
|
|
| `POST` | `/api/lotto/strategy/evolve` | 수동 가중치 재계산 트리거 |
|
|
|
|
|
|
|
|
|
|
**GET /strategy/weights 응답:**
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"weights": [
|
|
|
|
|
{"strategy": "combined", "weight": 0.32, "ema_score": 0.285, "total_sets": 15, "hits_3plus": 3, "trend": "up"},
|
|
|
|
|
{"strategy": "simulation", "weight": 0.28, "ema_score": 0.312, "total_sets": 12, "hits_3plus": 4, "trend": "up"},
|
|
|
|
|
{"strategy": "heatmap", "weight": 0.18, "ema_score": 0.195, "total_sets": 10, "hits_3plus": 1, "trend": "down"},
|
|
|
|
|
{"strategy": "manual", "weight": 0.14, "ema_score": 0.160, "total_sets": 8, "hits_3plus": 1, "trend": "stable"},
|
|
|
|
|
{"strategy": "custom", "weight": 0.08, "ema_score": 0.105, "total_sets": 3, "hits_3plus": 0, "trend": "stable"}
|
|
|
|
|
],
|
|
|
|
|
"last_evolved": "2026-04-05T09:10:00",
|
|
|
|
|
"min_data_draws": 10,
|
|
|
|
|
"current_data_draws": 32,
|
|
|
|
|
"status": "active"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4.3 스마트 추천 API (신규)
|
|
|
|
|
|
|
|
|
|
| 메서드 | 경로 | 설명 |
|
|
|
|
|
|--------|------|------|
|
|
|
|
|
| `GET` | `/api/lotto/recommend/smart` | 전략 가중치 기반 메타 전략 추천. `sets` 파라미터 (기본 5) |
|
|
|
|
|
|
|
|
|
|
**응답:**
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"sets": [
|
|
|
|
|
{
|
|
|
|
|
"numbers": [3, 12, 23, 34, 38, 45],
|
|
|
|
|
"meta_score": 0.847,
|
|
|
|
|
"source_strategy": "simulation",
|
|
|
|
|
"contribution": {"simulation": 0.42, "combined": 0.31, "heatmap": 0.27},
|
|
|
|
|
"individual_scores": {"frequency": 0.82, "fingerprint": 0.91, "gap": 0.78, "cooccur": 0.85, "diversity": 0.73}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"strategy_weights_used": {"combined": 0.32, "simulation": 0.28, "heatmap": 0.18, "manual": 0.14, "custom": 0.08},
|
|
|
|
|
"learning_status": {"draws_learned": 32, "status": "active", "message": ""}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 5. 전략 진화 알고리즘
|
|
|
|
|
|
|
|
|
|
### 5.1 성과 점수 산출 (회차별, 세트별)
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
set_score = correct_count / 6.0
|
|
|
|
|
|
|
|
|
|
# 당첨 등수별 보너스
|
|
|
|
|
RANK_BONUS = {5: 0.1, 4: 0.3, 3: 0.6, 2: 0.8, 1: 1.0}
|
|
|
|
|
set_score += RANK_BONUS.get(rank, 0)
|
|
|
|
|
|
|
|
|
|
# 한 구매 건의 draw_score = avg(set_scores)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.2 EMA 갱신
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
ALPHA = 0.3 # 최근 3~4회차가 EMA의 ~65% 차지
|
|
|
|
|
new_ema = ALPHA * draw_score + (1 - ALPHA) * old_ema
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.3 가중치 변환 (Softmax)
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
TEMPERATURE = 2.0
|
|
|
|
|
MIN_WEIGHT = 0.05
|
|
|
|
|
|
|
|
|
|
raw = {s: exp(ema / TEMPERATURE) for s, ema in ema_scores.items()}
|
|
|
|
|
total = sum(raw.values())
|
|
|
|
|
weights = {s: max(v / total, MIN_WEIGHT) for s, v in raw.items()}
|
|
|
|
|
# 재정규화하여 합 = 1.0
|
|
|
|
|
remainder = 1.0 - sum(weights.values())
|
|
|
|
|
# ... 비례 배분으로 조정
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 5.4 재계산 타이밍
|
|
|
|
|
|
|
|
|
|
- **자동**: `check_results_for_draw()` → purchases 체크 → strategy_performance 갱신 → weights 재계산
|
|
|
|
|
- **수동**: `POST /api/lotto/strategy/evolve`
|
|
|
|
|
|
|
|
|
|
### 5.5 스마트 추천 흐름
|
|
|
|
|
|
|
|
|
|
1. `strategy_weights` 로드
|
|
|
|
|
2. 각 전략에서 후보 10세트 생성:
|
|
|
|
|
- `combined`: `generate_combined_recommendation()` x 10
|
|
|
|
|
- `simulation`: `get_best_picks()` 상위 10개
|
|
|
|
|
- `heatmap`: `recommend_with_heatmap()` x 10
|
|
|
|
|
- `manual`: `recommend_numbers()` x 10
|
|
|
|
|
- `custom`: 데이터 없으면 skip
|
|
|
|
|
3. `meta_score = original_score x strategy_weight`
|
|
|
|
|
4. 전체 풀에서 중복 제거 후 상위 N세트 선출
|
|
|
|
|
5. 각 세트에 출처 전략 + 기여도 breakdown 첨부
|
|
|
|
|
|
|
|
|
|
### 5.6 콜드 스타트
|
|
|
|
|
|
|
|
|
|
- 구매 이력 0건: 초기 가중치 그대로 사용
|
|
|
|
|
- 특정 전략 구매 0건: 해당 전략 EMA 초기값(0.15) 유지
|
|
|
|
|
- 10회차 미만: 스마트 추천 응답에 `status: "learning"` + 기존 combined 추천 병행
|
|
|
|
|
|
|
|
|
|
### 5.7 Trend 판정
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
recent_delta = current_ema - ema_5_draws_ago
|
|
|
|
|
if recent_delta > 0.02: trend = "up"
|
|
|
|
|
elif recent_delta < -0.02: trend = "down"
|
|
|
|
|
else: trend = "stable"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 6. 체커 연동 (자동 파이프라인)
|
|
|
|
|
|
|
|
|
|
기존 흐름에 purchase 체크를 연결:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Scheduler (09:10 / 21:10)
|
|
|
|
|
→ sync_latest()
|
|
|
|
|
→ 새 회차 감지 시:
|
|
|
|
|
→ check_results_for_draw() # 기존: recommendations 체크
|
|
|
|
|
→ check_purchases_for_draw() # 신규: purchases 체크
|
|
|
|
|
→ 각 세트별 rank/correct/bonus 계산 (checker._calc_rank 재사용)
|
|
|
|
|
→ purchases.results, total_prize, checked=1 갱신
|
|
|
|
|
→ strategy_performance upsert
|
|
|
|
|
→ strategy_evolver.recalculate_weights()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 7. 백엔드 모듈 구조
|
|
|
|
|
|
|
|
|
|
### 7.1 신규 파일
|
|
|
|
|
|
|
|
|
|
| 파일 | 역할 |
|
|
|
|
|
|------|------|
|
|
|
|
|
| `purchase_manager.py` | 구매 이력 관리 + 결과 체크 |
|
|
|
|
|
| `strategy_evolver.py` | EMA 계산 + 가중치 진화 + 스마트 추천 |
|
|
|
|
|
|
|
|
|
|
### 7.2 수정 파일
|
|
|
|
|
|
|
|
|
|
| 파일 | 변경 내용 |
|
|
|
|
|
|------|----------|
|
|
|
|
|
| `db.py` | purchase_history ALTER + 신규 테이블 2개 + CRUD 함수 추가 |
|
|
|
|
|
| `main.py` | 신규 엔드포인트 9개 + Pydantic 모델 + import |
|
|
|
|
|
| `checker.py` | `check_results_for_draw()` 끝에 purchase 체크 호출 추가 |
|
|
|
|
|
|
|
|
|
|
### 7.3 기존 유지 파일 (변경 없음)
|
|
|
|
|
|
|
|
|
|
`recommender.py`, `generator.py`, `analyzer.py`, `collector.py`, `utils.py`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 8. 프론트엔드 변경
|
|
|
|
|
|
|
|
|
|
### 8.1 신규 컴포넌트
|
|
|
|
|
|
|
|
|
|
| 컴포넌트 | 역할 |
|
|
|
|
|
|----------|------|
|
|
|
|
|
| `SmartRecommendPanel.jsx` | 전략 진화 기반 메타 추천 + 구매 버튼 |
|
|
|
|
|
| `PurchaseHub.jsx` | 통합 구매 이력 (기존 PurchasePanel 대체) |
|
|
|
|
|
| `StrategyDashboard.jsx` | 전략 가중치 시각화 + 성과 추이 차트 |
|
|
|
|
|
| `PurchaseButton.jsx` | 공통 구매 버튼 (실구매/가상구매) |
|
|
|
|
|
|
|
|
|
|
### 8.2 수정 컴포넌트
|
|
|
|
|
|
|
|
|
|
| 컴포넌트 | 변경 내용 |
|
|
|
|
|
|----------|----------|
|
|
|
|
|
| `CombinedRecommendPanel.jsx` | 구매 버튼(PurchaseButton) 추가 |
|
|
|
|
|
| `Functions.jsx` | 신규 패널 3개 추가 + import |
|
|
|
|
|
|
|
|
|
|
### 8.3 신규 훅
|
|
|
|
|
|
|
|
|
|
| 훅 | 역할 |
|
|
|
|
|
|----|------|
|
|
|
|
|
| `useStrategyWeights.js` | 전략 가중치/성과 데이터 fetch |
|
|
|
|
|
|
|
|
|
|
### 8.4 수정 훅
|
|
|
|
|
|
|
|
|
|
| 훅 | 변경 내용 |
|
|
|
|
|
|----|----------|
|
|
|
|
|
| `usePurchases.js` | 새 API 스키마 연동 (numbers, is_real, source_strategy 등) |
|
|
|
|
|
|
|
|
|
|
### 8.5 API 헬퍼 추가 (`api.js`)
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 전략
|
|
|
|
|
getStrategyWeights() // GET /api/lotto/strategy/weights
|
|
|
|
|
getStrategyPerformance(days) // GET /api/lotto/strategy/performance
|
|
|
|
|
triggerStrategyEvolve() // POST /api/lotto/strategy/evolve
|
|
|
|
|
|
|
|
|
|
// 스마트 추천
|
|
|
|
|
getSmartRecommend(sets) // GET /api/lotto/recommend/smart
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 8.6 동행복권 바로가기
|
|
|
|
|
|
|
|
|
|
별도 API 없음. 프론트엔드 PurchaseButton에서:
|
|
|
|
|
1. 번호를 클립보드에 복사
|
|
|
|
|
2. `window.open('https://dhlottery.co.kr/gameResult.do?method=byWin')` — 새 탭
|
|
|
|
|
3. 확인 다이얼로그 "구매 완료했나요?" → 예 → `POST /api/lotto/purchase (is_real=1)`
|
|
|
|
|
|
|
|
|
|
### 8.7 UI 시각 구분
|
|
|
|
|
|
|
|
|
|
- 실 구매: 금색/강조 배경 + 지갑 아이콘
|
|
|
|
|
- 가상 구매: 기본 배경 + 게임패드 아이콘
|
|
|
|
|
- 미확인: 시계 아이콘
|
|
|
|
|
- 당첨: 초록 하이라이트 + 체크 아이콘
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 9. 전체 데이터 흐름
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
추천(기존) ──[구매 버튼]──→ POST /purchase
|
|
|
|
|
│
|
|
|
|
|
스마트 추천(신규) ──[구매 버튼]──┘
|
|
|
|
|
↓
|
|
|
|
|
purchase_history 테이블
|
|
|
|
|
│
|
|
|
|
|
매주 토요일 추첨 결과 ──→ sync_latest()
|
|
|
|
|
↓
|
|
|
|
|
check_results_for_draw()
|
|
|
|
|
├── recommendations 체크 (기존)
|
|
|
|
|
└── check_purchases_for_draw() (신규)
|
|
|
|
|
↓
|
|
|
|
|
strategy_performance 갱신
|
|
|
|
|
↓
|
|
|
|
|
recalculate_weights()
|
|
|
|
|
↓
|
|
|
|
|
strategy_weights 갱신
|
|
|
|
|
↓
|
|
|
|
|
다음 스마트 추천에 반영 ──→ 순환
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 10. 비기능 요구사항
|
|
|
|
|
|
|
|
|
|
- **하위호환**: 기존 purchase API 사용자(프론트 PurchasePanel)는 마이그레이션 중에도 동작해야 함
|
|
|
|
|
- **성능**: 스마트 추천은 각 전략 10세트 생성 → 총 50세트 중 상위 N개 선출. 1-2초 내 응답 목표
|
|
|
|
|
- **데이터 안전**: ALTER TABLE은 SQLite 트랜잭션으로 안전하게 실행. 기존 데이터 유실 없음
|
|
|
|
|
- **콜드 스타트**: 구매 데이터 없어도 스마트 추천 동작 (초기 가중치 사용)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 11. 범위 외 (추후 고려)
|
|
|
|
|
|
|
|
|
|
- 동행복권 자동 로그인/자동 구매 (CAPTCHA + 보안 정책으로 불가)
|
|
|
|
|
- 번호 자동 입력 브라우저 확장 프로그램
|
|
|
|
|
- 푸시 알림 (당첨 결과 통보)
|
|
|
|
|
- 다중 사용자 지원
|