docs: archive superseded roadmap/WBS docs, fix broken install command, retire stale files
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 18s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 26s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 12s
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 12s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 9s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped
Frontend CI Pipeline / ci-frontend-8-steps (push) Failing after 1m56s

Archives 6 more superseded planning docs to docs/archive/ (ROADMAP_WBS.md,
MODERNIZATION_ROADMAP_VISUAL.md, MODERNIZATION_STRATEGY_ROADMAP_2026-2027.md,
ROADMAP_ENTERPRISE_TEMPLATES_WBS.md, EXECUTION_PLAN_PHASE0_CLOSEOUT_AND_PHASE1_KICKOFF.md,
CICD_ROADMAP.md), each replaced by a current source of truth (CLAUDE.md,
docs/MIGRATION_STATUS.md, or the OMS·WMS·ERP spec/playbook), and repoints
4 files that linked to the pre-archive path.

Fixes README's top-of-file install instructions and package.json's
"ops:dev" script, both of which pointed at core_satellite_collector.js -
a file that has never existed anywhere in this repo's git history. The
real, working entry point (tools/run_kis_data_collection_v1.py /
npm run ops:data-collect) was already correctly documented further
down the same README.

Also finishes retiring pre-existing stale state that predates this
session: removes the superseded deploy-prod.yml.backup, completes the
already-in-progress removal of the old src/client/ Vue+AG-Grid
prototype (superseded by src/frontend/), and untracks
test-results/.last-run.json (Playwright's own run-metadata file,
regenerated every test run - shouldn't be version controlled).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 11:50:25 +09:00
parent 477bd693c1
commit 88e3d58664
17 changed files with 18 additions and 801 deletions
+301
View File
@@ -0,0 +1,301 @@
# QuantEngine Gitea Actions CI/CD 개선 로드맵
**최종 목표**: 신뢰성 높은 자동화된 배포 파이프라인 구축
---
## ✅ Phase 1 완료 (2026-07-11 커밋: 0d8e3a6)
### 1.0 근본적 아키텍처 개선: SSH 제거 → 로컬 배포
- **문제점 (이전)**: Gitea Actions이 로컬 서버에서 실행되는데 같은 서버로 SSH 배포 ❌
- **해결책**: SSH 제거, 로컬 파일 시스템에 직접 배포 ✅
- **효과**:
- 배포 시간 1-2분 단축
- 네트워크 장애 영향 제거
- 코드 복잡도 60줄 감소
- 신뢰성 향상
**기술 변경**:
```bash
# 이전 (SSH)
ssh user@host "tar -xzf ... && systemctl restart"
# 현재 (로컬)
tar -xzf ...
ln -sfn /deployments/new /active
systemctl restart quantengine
```
### 1.1 타임아웃 확대 (15분 → 30분)
- **효과**: 네트워크 지연 및 재시도 시 안정성 향상
- **변경**: `.gitea/workflows/deploy-prod.yml` line 28
### 1.2 자동 롤백 구현
- **효과**: 배포 실패 시 이전 버전으로 자동 복구
- **구현**:
```bash
# 헬스체크 3회 연속 실패 → 이전 버전으로 자동 복구
if [ $health_check_passed -eq 0 ]; then
PREV_DEPLOY=$(ls -dt /home/kjh2064/deployments/quantengine_* | head -2 | tail -1)
ln -sfn ${PREV_DEPLOY} /home/kjh2064/quantengine_active
sudo systemctl restart quantengine
fi
```
- **장점**:
- 배포 실패 대응 자동화
- 수동 개입 최소화
- Telegram 알림 자동 발송
### 1.3 헬스체크 강화
- **데이터베이스 연결 검증** 추가
- **서비스 상태 확인** 강화
- **Favicon 검증** 경고로 변경 (선택사항)
### 1.4 배포 이력 추적
- **로그 파일**: `/home/kjh2064/.config/quantengine_deploy_history.log`
- **기록 내용**:
```
TIMESTAMP=20260711_175640
COMMIT=96cc7fc
DEPLOY_PATH=/home/kjh2064/deployments/quantengine_20260711_175640
PREV_VERSION=20260711_170421
STATUS=success
DEPLOYED_AT=2026-07-11T17:56:40Z
```
- **용도**: 배포 이력 추적, 빠른 롤백 결정
---
## 📋 Phase 2 계획 (빌드/배포 분리)
### 2.1 별도 빌드 워크플로우 생성 (**새로운 파일**: `.gitea/workflows/build.yml`)
**특징**:
- 빌드 결과를 Gitea Releases로 발행
- 빌드 메타데이터 (커밋, 타임스탐프) 포함
- 배포 시점에 빌드 재사용
**효과**:
```
이전 (현재):
push → 빌드 → 테스트 → 배포 (한 번에)
개선 후:
push → 빌드 (별도) → 배포 (독립적)
└─ 같은 빌드를 여러 번 배포 가능
└─ 빌드 아티팩트 재사용 → 속도 ↑
```
### 2.2 `appsettings.Production.json` 전략 변경
**현재 문제점**:
```yaml
# 현재 (deploy-prod.yml)
- name: Publish Release Package
run: dotnet publish ... -o ./publish
- name: Prepare & Validate DB Env # 배포 시점에 생성
run: |
cat > ./publish/appsettings.Production.json << EOF
{
"ConnectionStrings": {
"DefaultConnection": "Host=127.0.0.1;Database=${{ env.QUANTENGINE_DB_NAME }};..."
}
}
EOF
```
**문제**: 빌드와 배포 사이에 설정이 동적으로 변경됨
**개선 방향**:
```yaml
# 개선 후 (build.yml)
- name: Generate Configuration Template
run: |
cat > ./publish/appsettings.Production.json.template << EOF
{
"ConnectionStrings": {
"DefaultConnection": "Host={DB_HOST};Database={DB_NAME};Username={DB_USER};..."
}
}
EOF
# 배포 시점에 (deploy-prod.yml)
- name: Inject Secrets at Deploy Time
run: |
envsubst < appsettings.Production.json.template > appsettings.Production.json
```
**효과**:
- ✅ 빌드 시점 고정 (재현 가능)
- ✅ 배포 시점에만 secrets 주입
- ✅ "같은 빌드 → 같은 배포" 보장
### 2.3 배포 워크플로우 개선
**변경 사항**:
```yaml
# 현재 (deploy-prod.yml)
- name: Setup .NET
... (시간 낭비)
- name: Build Release
... (빌드 반복)
# 개선 후
- name: Download Build Artifact
run: |
curl -L -o quantengine.tar.gz \
https://gitea.taxbaik.com/api/v1/repos/.../releases/download/build-${COMMIT}/quantengine-${COMMIT}.tar.gz
```
**효과**:
- 빌드 시간 제거 (5-10분 단축)
- 배포 속도 ↑↑
---
## 🎯 Phase 3 계획 (E2E 검증 강화)
### 3.1 로그인 기능 E2E 테스트 추가
```bash
# deploy-prod.yml에 추가
- name: E2E Login Test
run: |
# 1. 로그인 시도
LOGIN_RESULT=$(curl -s -c /tmp/cookies.txt \
-X POST "https://quant.taxbaik.com/Account/Login" \
-d "username=${{ secrets.ADMIN_USERNAME }}" \
-d "password=${{ secrets.ADMIN_PASSWORD }}" \
-o /dev/null -w "%{http_code}")
# 2. 성공 확인
if [ "$LOGIN_RESULT" = "302" ] || [ "$LOGIN_RESULT" = "200" ]; then
echo "✓ Login test passed"
else
echo "❌ Login test failed: $LOGIN_RESULT"
exit 1
fi
# 3. 인증 상태 확인
DASHBOARD=$(curl -s -b /tmp/cookies.txt \
"https://quant.taxbaik.com/Admin/Dashboard" \
-o /dev/null -w "%{http_code}")
if [ "$DASHBOARD" = "200" ]; then
echo "✓ Dashboard accessible"
else
echo "❌ Dashboard access failed: $DASHBOARD"
exit 1
fi
```
### 3.2 API 기능 테스트 추가
```bash
- name: E2E API Test
run: |
# Collection API 상태 확인
API_RESULT=$(curl -s -b /tmp/cookies.txt \
"https://quant.taxbaik.com/api/collection/state" \
-H "Content-Type: application/json" \
-o /dev/null -w "%{http_code}")
if [ "$API_RESULT" = "200" ]; then
echo "✓ API endpoint responding"
else
echo "❌ API test failed: $API_RESULT"
exit 1
fi
```
---
## 📊 구현 우선순위 및 영향도
| 우선 | Phase | 항목 | 난이도 | 효과 | 예상 소요 |
|------|-------|------|--------|------|----------|
| 1️⃣ | 1 | 타임아웃 확대 | ⭐ | 즉시 안정성 ↑ | 5분 |
| 2️⃣ | 1 | 자동 롤백 | ⭐⭐ | 배포 실패 대응 | 30분 |
| 3️⃣ | 1 | 헬스체크 강화 | ⭐⭐ | 검증 확실성 | 20분 |
| 4️⃣ | 1 | 배포 이력 추적 | ⭐⭐ | 운영 가시성 | 15분 |
| 5️⃣ | 2 | 빌드 분리 | ⭐⭐⭐ | 속도 ↑↑ + 일관성 | 2시간 |
| 6️⃣ | 3 | 로그인 E2E | ⭐⭐⭐ | 기능 검증 | 1시간 |
---
## 🔍 모니터링 및 추적
### 배포 이력 조회 (원격 서버)
```bash
ssh kjh2064@178.104.200.7
cat ~/.config/quantengine_deploy_history.log | tail -20
```
### 최근 배포 정보
```bash
ls -lt /home/kjh2064/deployments/ | head -5
readlink -f /home/kjh2064/quantengine_active
```
### 서비스 상태 확인
```bash
sudo systemctl status quantengine
sudo journalctl -u quantengine -f
```
---
## ✨ 기대 효과
### 배포 신뢰성 향상
- **이전**: 배포 실패 시 수동 대응 (15-30분 소요)
- **현재**: 자동 롤백 + 알림 (1-2분)
### 배포 속도 개선 (Phase 2)
- **이전**: 빌드 5-10분 + 배포 2-3분 = 7-13분
- **현재**: 빌드 분리 + 아티팩트 재사용 = 2-3분
### 운영 가시성 향상
- **배포 이력 추적**: 언제, 어떤 버전, 누가 배포했는지
- **빠른 롤백**: 이전 버전으로 즉시 복구 가능
- **근본 원인 분석**: 로그를 통한 배포 실패 원인 파악
---
## 다음 액션 (사용자)
### Phase 2 적용하기
1. `.gitea/workflows/build.yml` 파일 검토 및 조정
2. `deploy-prod.yml` 수정하여 빌드 아티팩트 다운로드 로직 추가
3. GitHub Releases API 대신 Gitea Releases API 사용하도록 변경
### 테스트
```bash
# 수동 배포 트리거
curl -X POST https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/workflows/deploy-prod.yml/dispatches \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"ref":"main", "inputs":{"release_tag":"build-96cc7fc"}}'
```
### 모니터링
- Telegram 알림 확인
- 배포 이력 로그 검증
- 이전 버전 롤백 테스트 (스테이징 환경)
---
## 참고 자료
- **분석 문서**: [gitea_cicd_analysis.md](https://claude.ai/code/artifact/9b62fb29-6438-4cd3-80a4-3593c7057eb5)
- **현재 워크플로우**:
- `.gitea/workflows/deploy-prod.yml` (개선됨)
- `.gitea/workflows/ci.yml` (기존 Python 검증)
- **배포 스크립트**: `tools/deploy_quantengine.sh` (개선됨)
---
**작성일**: 2026-07-11
**상태**: Phase 1 ✅ 완료, Phase 2 📋 계획 중, Phase 3 📋 계획 중
@@ -0,0 +1,968 @@
# QuantEngine 현대화 실행 계획
**Phase 0 마무리 + Phase 1 준비** (2026-07-24 ~ 2026-09-30)
---
## Executive Overview
**현재 상태**: Phase 0 ✅ 기술적 기초 완료
- CI/CD 파이프라인 리팩토링 (9-job parallel, ~15-20min) ✅
- CLAUDE.md 종합 문서화 ✅
- 현대화 로드맵 수립 ✅
**목표**: Phase 0 운영 검증 + Phase 1 (데이터 아키텍처 고도화) 착수
**기간**: 2026-07-24 ~ 2026-09-30 (9주)
**리소스**: 1 FTE (클로드 코드) + 팀 지원
---
## Part 1: Phase 0 운영 검증 (Jul 24 - Aug 31) — 4주
### 목표
현대화 로드맵의 기초가 견고한지 검증
### 1.1 CI/CD 파이프라인 안정성 검증
#### Task 1.1.1: 실제 워크플로우 성능 측정
**목표**: 예상 15-20분이 실제 달성되는지 확인
**구체적 작업**:
```yaml
Week 1 (Jul 24-31):
- Commit 3-5개 추가 (다양한 변경 유형)
* C# 코드 변경
* Python 스크립트 변경
* 데이터베이스 마이그레이션 추가
* YAML 워크플로우 변경
- 각 CI 실행 로그 분석:
├─ core job 시간 (DB 마이그레이션 포함)
├─ 병렬 job 시간 (wbs-audit, dotnet-contracts, ui-storage, etc.)
├─ notify-results 시간
└─ 총 벽시간 (wall clock time)
- 병목 지점 식별:
* 만약 core > 10분: DB 마이그레이션 최적화 필요
* 만약 any parallel > 8분: 해당 job 분할 검토
* 만약 total > 25분: 추가 병렬화 또는 검증 제거 검토
Expected output: "CI Performance Baseline 2026-07-31.json"
```
**SOLID 원칙 적용**:
- **Single Responsibility**: 각 job은 하나의 검증만 담당
- **Dependency Inversion**: 모든 job이 동등하게 core에만 의존 (필요시)
#### Task 1.1.2: 워크플로우 재현성 검증
**목표**: 같은 커밋에서 CI 실행 결과가 항상 동일한지 확인
**구체적 작업**:
```python
# tools/verify_ci_reproducibility_v1.py
class CIReproducibilityValidator:
def test_same_commit_same_result(self, commit_sha):
"""
같은 커밋을 2번 이상 재실행하여 결과 비교
- All jobs: PASS or FAIL 결과 동일
- Test output: 정확히 일치
- Build artifacts: 바이너리 동일 (deterministic build)
"""
results = []
for run in range(3):
result = self.trigger_ci(commit_sha)
results.append(result)
assert all(r == results[0] for r in results), \
"CI results not reproducible!"
return True
def test_no_hidden_state(self):
"""
CI가 외부 상태에 의존하지 않는지 확인
- 시간에 따른 결과 변화 없음 (timestamp-independent)
- 환경변수 없어도 성공 (except secrets)
- 테스트 데이터 일관성 (seed 고정)
"""
pass
# CI에 추가할 Step
ci.yml:
- name: "Verify CI Reproducibility"
run: python3 tools/verify_ci_reproducibility_v1.py
```
**목표 지표**:
- ✅ 3회 연속 재실행 성공률: 100%
- ✅ 결과 일관성: 100% (no flaky tests)
- ✅ Deterministic build: 바이너리 hash 일치
---
### 1.2 데이터 일관성 기초 다지기
#### Task 1.2.1: PostgreSQL 이력 테이블 설계 및 구현
**목표**: 모든 데이터 변경의 감시 추적(audit trail) 기초 마련
**구체적 작업**:
```sql
-- src/dotnet/QuantEngine.Infrastructure/Migrations/V003_add_audit_trail.sql
-- 이력 테이블 템플릿
CREATE TABLE kis_collection_runs_audit (
id BIGSERIAL PRIMARY KEY,
run_id UUID NOT NULL, -- 원본 테이블의 FK
action VARCHAR(10) NOT NULL, -- INSERT, UPDATE, DELETE
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
changed_by VARCHAR(256), -- 누가? (user ID 또는 "scheduler")
change_reason TEXT, -- 왜? (migration, manual edit, etc.)
-- 변경 전/후 스냅샷
old_values JSONB, -- 변경 전 전체 row
new_values JSONB, -- 변경 후 전체 row
INDEX (run_id, changed_at DESC),
INDEX (changed_by, changed_at DESC)
);
-- kis_collection_snapshots_audit 유사 구조
CREATE TABLE kis_collection_snapshots_audit (
id BIGSERIAL PRIMARY KEY,
snapshot_id UUID NOT NULL,
action VARCHAR(10) NOT NULL,
changed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
changed_by VARCHAR(256),
change_reason TEXT,
old_values JSONB,
new_values JSONB,
INDEX (snapshot_id, changed_at DESC)
);
-- Trigger: kis_collection_snapshots 변경 시 자동 기록
CREATE OR REPLACE FUNCTION kis_collection_snapshots_audit_trigger()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO kis_collection_snapshots_audit (snapshot_id, action, changed_by, new_values)
VALUES (NEW.id, 'INSERT', CURRENT_USER, row_to_json(NEW));
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO kis_collection_snapshots_audit (snapshot_id, action, old_values, new_values)
VALUES (NEW.id, 'UPDATE', row_to_json(OLD), row_to_json(NEW));
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER kis_collection_snapshots_after_change
AFTER INSERT OR UPDATE ON kis_collection_snapshots
FOR EACH ROW
EXECUTE FUNCTION kis_collection_snapshots_audit_trigger();
```
**C# Repository 패턴 (Wrapper)**:
```csharp
public class AuditedSnapshotRepository : ISnapshotRepository
{
private readonly ISnapshotRepository _inner;
private readonly IAuditLogger _audit;
public async Task SaveSnapshotAsync(SnapshotDto snapshot, string changedBy, string reason)
{
// 변경 전 상태 저장
var before = await _inner.GetAsync(snapshot.Id);
// 실제 저장
await _inner.SaveAsync(snapshot);
// 감시 추적 기록
await _audit.LogChangeAsync(new AuditEntry
{
EntityId = snapshot.Id,
EntityType = "Snapshot",
Action = "UPDATE",
ChangedBy = changedBy,
ChangeReason = reason,
OldValues = before,
NewValues = snapshot,
ChangedAt = DateTime.UtcNow
});
}
}
```
**성과지표**:
- ✅ 모든 kis_* 테이블에 이력 추적 활성화
- ✅ 이력 조회 API 구현 (`/api/audit/logs?entity=snapshot&id=...`)
- ✅ 수동 개입 추적: who, when, why 100% 기록
#### Task 1.2.2: 데이터 정합성 검증 자동화
**목표**: 매일 자동으로 데이터 품질 점검
**구체적 작업**:
```python
# tools/validate_data_consistency_daily_v1.py
class DailyDataConsistencyValidator:
def validate_kis_snapshots(self):
"""
kis_collection_snapshots 데이터 품질 검사
"""
issues = []
# 1. 완전성 (Completeness)
total = self.db.query("SELECT COUNT(*) FROM kis_collection_snapshots")
nulls = self.db.query("SELECT COUNT(*) FROM kis_collection_snapshots WHERE price IS NULL")
completeness = (total - nulls) / total * 100
if completeness < 95:
issues.append(f"Completeness low: {completeness:.1f}%")
# 2. 신선도 (Freshness)
latest = self.db.query("SELECT MAX(created_at) FROM kis_collection_snapshots")
age_hours = (now() - latest).total_seconds() / 3600
if age_hours > 25:
issues.append(f"Data stale: {age_hours:.1f} hours old")
# 3. 정합성 (Consistency) — bid <= mid <= ask
invalid = self.db.query("""
SELECT COUNT(*) FROM kis_collection_snapshots
WHERE NOT (bid <= price AND price <= ask)
""")
if invalid > 0:
issues.append(f"Bid-mid-ask consistency violated: {invalid} rows")
# 4. 이상값 (Outliers) — 3-sigma rule
stats = self.db.query("""
SELECT
AVG(price) as mean,
STDDEV(price) as std
FROM kis_collection_snapshots
WHERE created_at > NOW() - INTERVAL 30 DAY
""")
outliers = self.db.query("""
SELECT COUNT(*) FROM kis_collection_snapshots
WHERE ABS(price - %s) > 3 * %s
""", stats.mean, stats.std)
outlier_pct = outliers / total * 100
if outlier_pct > 5:
issues.append(f"Outliers detected: {outlier_pct:.1f}%")
# 5. 중복 검사 (Duplicates)
duplicates = self.db.query("""
SELECT COUNT(*) - COUNT(DISTINCT ticker, created_at)
FROM kis_collection_snapshots
WHERE created_at > NOW() - INTERVAL 1 DAY
""")
if duplicates > 0:
issues.append(f"Duplicates found: {duplicates} rows")
return {
"timestamp": now(),
"completeness_pct": completeness,
"freshness_hours": age_hours,
"consistency_violations": invalid,
"outliers_pct": outlier_pct,
"duplicates": duplicates,
"status": "PASS" if not issues else "FAIL",
"issues": issues
}
# 매일 cron으로 실행 (kis_data_collection.yml 확장)
# Slack 알림: completeness < 95% 또는 freshness > 25h
```
**CI 게이트로 추가**:
```yaml
# .gitea/workflows/kis_data_collection.yml (기존) → 확장
- name: "Validate Daily Data Consistency"
run: python3 tools/validate_data_consistency_daily_v1.py --mode strict
# strict mode: 모든 게이트 PASS 필요
```
**성과지표**:
- ✅ 자동 데이터 품질 점검 일일 1회
- ✅ 신선도, 완전성, 정합성, 이상값 추적
- ✅ 수동 개입 필요 시 → Slack 알림 자동화
---
### 1.3 운영 안정성 검증
#### Task 1.3.1: 배포 프로세스 엔드-투-엔드 테스트
**목표**: 실제 배포까지 자동화 검증
**구체적 작업**:
```bash
# 시나리오 1: 정상 배포
1. Local build (Release) → 성공
2. E2E 테스트 → 성공
3. Admin 페이지 모두 200 응답
4. git push main
5. CI 모든 job 통과
6. prepare-release.yml 수동 실행
→ Gitea Release 생성 (v0.1.20260731.0.abc1234)
7. deploy-prod.yml 수동 실행
→ SSH 배포 + 6점 health check
8. 검증:
- Login 페이지 로드 ✓
- CSS/JS 로드 ✓
- Service active ✓
- DB 연결 ✓
- Release tag 일치 ✓
# 시나리오 2: 배포 실패 및 롤백
1. Deploy 중단 (health check 실패)
2. 이전 버전 확인: ln -sfn quantengine_20260718_abc1234
3. systemctl restart quantengine
4. Health check 재실행 → 통과
# 시나리오 3: 데이터베이스 마이그레이션
1. V003_add_audit_trail.sql 배포
2. 기존 데이터 호환성 확인
- SELECT COUNT(*) FROM kis_collection_runs (레코드 동일)
- Audit 트리거 작동 확인
3. Rollback 계획 검증
- DROP TRIGGER / DROP TABLE 스크립트 준비
- 테스트 환경에서 실행
```
**체크리스트 작성**:
```markdown
# docs/DEPLOYMENT_VERIFICATION_CHECKLIST.md
## Pre-Deployment
- [ ] Local build: 0 errors, 0 warnings
- [ ] E2E tests: all pass
- [ ] Admin pages: /Dashboard, /Users, /Collection → 200
- [ ] git status: clean (no uncommitted changes)
- [ ] git log: all commits pushed to origin
## Release Creation (prepare-release.yml)
- [ ] Workflow status: SUCCESS
- [ ] Gitea Release created (v0.1.YYYYMMDD.N.hash)
- [ ] Artifact downloaded locally (for manual verification)
- [ ] Checksum validated: `sha256sum -c artifact.sha256`
## Production Deployment (deploy-prod.yml)
- [ ] SSH connection: successful
- [ ] Artifact uploaded: confirmed on server
- [ ] Extract & symlink: verified
- [ ] Service restart: active
## Health Checks (6-point)
- [ ] HTTP 200: GET /Account/Login
- [ ] Login page content: contains "login" or "로그인"
- [ ] CSS: GET /css/admin.css → 200
- [ ] Service: systemctl is-active quantengine → active
- [ ] Release tag: matches deployed version
- [ ] DB auth: journalctl -u quantengine (no 28P01 errors)
## Post-Deployment Verification
- [ ] Live app accessible: https://quant.taxbaik.com/
- [ ] Admin pages load: /Admin/Dashboard → 200
- [ ] API responds: /api/collection/state → 200
- [ ] Monitoring active: Prometheus/Grafana (if enabled)
```
**성과지표**:
- ✅ 3회 연속 배포 성공 (prepare-release + deploy-prod)
- ✅ 배포 실패 시 자동 롤백 검증
- ✅ 배포 시간 추적: <30분 total
---
## Part 2: Phase 1 준비 (Sep 1-30) — 5주
### 목표
데이터 정규화 설계 완료 및 첫 마이그레이션 준비
### 2.1 데이터 정규화 설계
#### Task 2.1.1: 3NF 스키마 설계 및 검증
**목표**: 현재 비정규 kis_collection_snapshots → 3NF로 재설계
**구체적 작업**:
```sql
-- Current (비정규화) — kis_collection_snapshots
-- 100+ columns: ticker, price, volume, bid1-5, ask1-5, pe_ratio, eps, ...
-- Target (3NF) — 테이블 분리
CREATE TABLE stocks (
id UUID PRIMARY KEY,
ticker VARCHAR(10) NOT NULL UNIQUE,
name VARCHAR(256),
market VARCHAR(20), -- KOSPI, KOSDAQ, KONEX
created_at TIMESTAMPTZ,
INDEX (ticker)
);
CREATE TABLE quotes (
id UUID PRIMARY KEY,
stock_id UUID NOT NULL REFERENCES stocks(id),
timestamp TIMESTAMPTZ NOT NULL,
price DECIMAL(15,2) NOT NULL,
volume BIGINT,
source VARCHAR(50), -- KIS, Naver, Yahoo
created_at TIMESTAMPTZ,
FOREIGN KEY (stock_id) REFERENCES stocks(id),
INDEX (stock_id, timestamp DESC),
INDEX (timestamp)
);
CREATE TABLE order_book (
id UUID PRIMARY KEY,
quote_id UUID NOT NULL REFERENCES quotes(id),
bid_prices DECIMAL(15,2)[] NOT NULL, -- [bid1, bid2, ..., bid5]
bid_sizes BIGINT[] NOT NULL,
ask_prices DECIMAL(15,2)[] NOT NULL,
ask_sizes BIGINT[] NOT NULL,
FOREIGN KEY (quote_id) REFERENCES quotes(id),
INDEX (quote_id)
);
CREATE TABLE fundamentals (
id UUID PRIMARY KEY,
stock_id UUID NOT NULL REFERENCES stocks(id),
as_of_date DATE NOT NULL,
eps DECIMAL(15,4),
pe_ratio DECIMAL(15,2),
dividend DECIMAL(15,2),
book_value DECIMAL(15,2),
FOREIGN KEY (stock_id) REFERENCES stocks(id),
UNIQUE (stock_id, as_of_date),
INDEX (stock_id)
);
```
**정규화 검증**:
```python
# tools/validate_schema_normalization_v1.py
class NormalizationValidator:
def validate_3nf(self):
"""
3NF 검증:
1. 1NF: 모든 테이블이 atomic values만 포함
2. 2NF: 비키 속성이 전체 키에 의존 (partial dependency 없음)
3. 3NF: 비키 속성이 다른 비키 속성에 의존하지 않음 (transitive dependency 없음)
"""
issues = []
# 1NF: 배열/객체 타입 확인 (JSON 제외 대부분)
for table in self.db.tables:
for col in table.columns:
if col.type in ['array', 'object']:
if col.name not in ['bid_prices', 'ask_prices', 'bid_sizes', 'ask_sizes']:
issues.append(f"1NF violation: {table}.{col} is {col.type}")
# 2NF: Foreign Key 의존성 확인
for table in self.db.tables:
for col in table.columns:
if col.is_foreign_key:
# 비키 속성이 전체 키에만 의존하는지 확인
if not self._depends_on_full_key(table, col):
issues.append(f"2NF violation: {table}.{col} partial dependency")
# 3NF: 비키 속성 간 의존성 확인
for table in self.db.tables:
for col in table.columns:
if not col.is_key and not col.is_foreign_key:
for other_col in table.columns:
if not other_col.is_key and col != other_col:
if self._functionally_dependent(col, other_col):
issues.append(f"3NF violation: {table}.{col} depends on {other_col}")
return {
"status": "PASS" if not issues else "FAIL",
"issues": issues,
"tables_checked": len(self.db.tables)
}
```
**과유불급(YAGNI) 원칙 적용**:
- ✅ 필요한 분리만: 100+ columns → 5개 주요 테이블
- ✅ 과도한 정규화 금지: 과도한 조인 피함
- ❌ 조회 성능 향상 위해 의도적 역정규화는 나중 (벤치마크 후)
**성과지표**:
- ✅ 3NF 검증 통과 (1NF, 2NF, 3NF 모두)
- ✅ 데이터 무결성 제약 정의 (FK, CHECK, UNIQUE)
- ✅ 스토리지 절감 예상: 40% (column 중복 제거)
#### Task 2.1.2: 마이그레이션 전략 수립 (Blue-Green Deployment)
**목표**: 무중단 데이터 마이그레이션 계획
**구체적 작업**:
```markdown
# 마이그레이션 전략: Blue-Green (Parallel Run)
## Phase 1: Prepare (1주)
1. 새 테이블 생성 (stocks, quotes, order_book, fundamentals)
2. 데이터 변환 로직 구현
- kis_snapshots → stocks/quotes/order_book 변환
- 데이터 검증 (row count, aggregates)
3. 테스트 환경에서 전체 마이그레이션 실행 및 검증
## Phase 2: Dual Write (1주)
1. 애플리케이션 수정: 새 테이블에도 INSERT/UPDATE
```csharp
await _legacyRepository.SaveAsync(snapshot); // 기존
await _normalizedRepository.SaveAsync(snapshot); // 신규
```
2. 두 테이블 데이터 정합성 비교
- SELECT COUNT(*) 일치 확인
- Aggregates (SUM, AVG) 일치 확인
3. 한 주일 운영: 모든 쿼리가 일관된 결과 반환하는지 확인
## Phase 3: Read Cutover (1주)
1. 읽기(SELECT) 쿼리를 새 테이블에서 수행 시작
```csharp
// Before
var snapshot = await _legacyRepository.GetAsync(id);
// After
var snapshot = await _normalizedRepository.GetAsync(id);
```
2. API 응답이 동일한지 검증
3. 성능 비교: 새 테이블 쿼리가 더 빠른지 확인
## Phase 4: Write Cutover (1주)
1. 쓰기(INSERT/UPDATE) 쿼리도 새 테이블만 사용
2. 기존 테이블은 읽기 전용으로 전환
3. Dual write 제거
## Phase 5: Cleanup (1주)
1. 기존 테이블 백업: kis_snapshots_archived_20260930
2. 모니터링: 일주일 후에도 안정적인지 확인
3. 필요시 기존 테이블 제거
```
**Adapter Pattern으로 호환성 유지**:
```csharp
public class LegacySnapshotAdapter : ISnapshotRepository
{
private readonly IQuoteRepository _newQuotes;
public async Task<SnapshotDto> GetAsync(string ticker)
{
// 새 테이블에서 읽음
var quote = await _newQuotes.GetLatestAsync(ticker);
// 기존 SnapshotDto 형식으로 변환
return new SnapshotDto
{
Ticker = quote.Stock.Ticker,
Price = quote.Price,
Volume = quote.Volume,
Bid = quote.OrderBook.BidPrices[0],
Ask = quote.OrderBook.AskPrices[0],
// ... 나머지 100+ 필드들도 매핑
};
}
}
// 사용처: API, Controller는 변경 없음
public class CollectionApiEndpoints
{
public async Task GetSnapshot(string ticker)
{
var snapshot = await _repository.GetAsync(ticker); // 자동으로 새 테이블 사용
return Ok(snapshot);
}
}
```
**성과지표**:
- ✅ 마이그레이션 계획 상세 정의
- ✅ Rollback 프로세스 테스트
- ✅ 예상 다운타임: 0분 (무중단)
---
### 2.2 SOLID 원칙 적용 설계
#### Task 2.2.1: Repository 인터페이스 분리 (Interface Segregation)
**목표**: 비대한 ICollectionRepository → 작은 책임의 인터페이스로 분리
**구체적 작업**:
```csharp
// BEFORE (ISP 위반)
public interface ICollectionRepository
{
Task<SnapshotDto> GetSnapshotAsync(string ticker);
Task<RunDto> GetRunAsync(Guid runId);
Task<ErrorDto> GetErrorAsync(Guid errorId);
Task SaveSnapshotAsync(SnapshotDto snapshot);
Task SaveRunAsync(RunDto run);
Task DeleteErrorAsync(Guid errorId);
}
// AFTER (ISP 준수)
public interface IQuoteRepository
{
Task<QuoteDto> GetLatestAsync(string ticker);
Task<IEnumerable<QuoteDto>> GetHistoryAsync(string ticker, DateRange range);
Task SaveAsync(QuoteDto quote);
}
public interface ICollectionRunRepository
{
Task<RunDto> GetAsync(Guid runId);
Task<IEnumerable<RunDto>> GetRecentAsync(int limit);
Task SaveAsync(RunDto run);
}
public interface ICollectionErrorRepository
{
Task<ErrorDto> GetAsync(Guid errorId);
Task<IEnumerable<ErrorDto>> GetByRunAsync(Guid runId);
Task SaveAsync(ErrorDto error);
}
public interface IStockRepository
{
Task<StockDto> GetByTickerAsync(string ticker);
Task<IEnumerable<StockDto>> GetAllAsync();
}
// 사용처
public class CollectionService
{
private readonly IQuoteRepository _quotes;
private readonly ICollectionRunRepository _runs;
private readonly ICollectionErrorRepository _errors;
public CollectionService(
IQuoteRepository quotes,
ICollectionRunRepository runs,
ICollectionErrorRepository errors)
{
_quotes = quotes;
_runs = runs;
_errors = errors;
}
// 각 메서드는 필요한 인터페이스만 사용
}
```
**성과지표**:
- ✅ 불필요한 메서드 의존성 제거
- ✅ 테스트 편의성: Mock 주입 간단
- ✅ 변경 영향도 최소화
#### Task 2.2.2: Dependency Inversion 구현 (DI Container)
**목표**: 고수준 모듈이 저수준 모듈에 의존하지 않기
**구체적 작업**:
```csharp
// Program.cs (DI 설정)
services
// Repository abstraction
.AddScoped<IQuoteRepository>(sp =>
new AuditedQuoteRepository(
new QuoteRepository(sp.GetRequiredService<DbContext>()),
sp.GetRequiredService<IAuditLogger>()))
// Data source abstraction (Strategy pattern)
.AddScoped<IDataSourceFactory>(sp =>
new DataSourceFactory(
sp.GetRequiredService<IKisApiClient>(),
sp.GetRequiredService<INaverFinanceClient>(),
sp.GetRequiredService<IYahooFinanceClient>()))
// Fallback chain
.AddScoped<IQuotationService>(sp =>
new FallbackQuotationService(
new KisQuotationService(sp.GetRequiredService<IKisApiClient>()),
new NaverQuotationService(sp.GetRequiredService<INaverFinanceClient>()),
new YahooQuotationService(sp.GetRequiredService<IYahooFinanceClient>())))
// Validation
.AddScoped<IDataQualityValidator>(sp =>
new DataQualityValidator(sp.GetRequiredService<DbContext>()))
.AddScoped<CollectionService>();
// CollectionService (고수준)는 세부 구현을 모름
public class CollectionService
{
private readonly IQuotationService _quotation; // 추상화만 의존
private readonly IQuoteRepository _repository; // 추상화만 의존
public async Task RunAsync()
{
// 구체적 구현은 DI container가 주입
var quote = await _quotation.GetAsync("005930");
await _repository.SaveAsync(quote);
}
}
```
**성과지표**:
- ✅ 느슨한 결합 (Loose coupling)
- ✅ 런타임 구성 가능 (Strategy switching)
- ✅ 테스트 용이 (Mock 쉽게 주입)
---
### 2.3 패턴 및 표준 정립
#### Task 2.3.1: Architecture Decision Records (ADR) 작성
**목표**: 왜 이런 선택을 했는가? 의사결정 기록
**구체적 작업**:
```markdown
# docs/adr/0003-3nf-normalization.md
## Status
ACCEPTED
## Context
현재 kis_collection_snapshots 테이블이 비정규화되어 있음:
- 100+ columns (price, bid1-5, ask1-5, eps, pe_ratio, ...)
- 데이터 중복 (ticker는 매번 저장)
- 업데이트 이상 (fundamentals 변경 시 모든 행 수정)
- 스토리지 비효율 (같은 데이터 반복)
## Decision
PostgreSQL 스키마를 3NF로 정규화:
- stocks: 종목 마스터 (ticker, name, market)
- quotes: 시세 (stock_id, timestamp, price, volume)
- order_book: 호가 (quote_id, bid/ask arrays)
- fundamentals: 재무 (stock_id, eps, pe_ratio, ...)
## Consequences
**Positive**:
- 스토리지 40% 감소
- 데이터 무결성 자동 보장 (FK 제약)
- 업데이트 이상 제거
- 명확한 데이터 의미 (각 테이블이 하나의 개념 표현)
**Negative**:
- JOIN 증가 (성능 영향, 인덱싱으로 완화)
- 마이그레이션 복잡도 증가 (blue-green 필요)
## Alternatives Considered
1. 비정규화 유지 + 인덱싱만 개선 (rejected: 장기 유지 어려움)
2. 부분 정규화 (1NF만) (rejected: 불완전)
## Implementation
- Phase 1a (Sep): 새 테이블 생성 + 검증
- Phase 1b (Oct): Blue-green 마이그레이션
- Phase 1c (Nov): 기존 테이블 아카이빙
```
**추가 ADR들**:
```
docs/adr/
├── 0001-razor-pages-over-wasm.md
├── 0002-dapper-orm-not-ef.md
├── 0003-3nf-normalization.md
├── 0004-game-theoretic-portfolio.md
├── 0005-audit-trail-every-change.md
└── 0006-fallback-data-sources.md
```
**성과지표**:
- ✅ 5개 이상의 ADR 작성
- ✅ 팀 검토 및 승인
- ✅ CLAUDE.md에 ADR 참조 추가
#### Task 2.3.2: Code Style Guide 작성
**목표**: "이 프로젝트에서는 이렇게 코딩한다"
**구체적 작업**:
```markdown
# CODING_STANDARDS.md
## C# Guidelines
### Repository Pattern
```csharp
// DO
public interface IQuoteRepository
{
Task<QuoteDto> GetByTickerAsync(string ticker);
Task SaveAsync(QuoteDto quote);
}
// DON'T
public interface IRepository
{
T Get<T>(object id);
void Save<T>(T entity);
}
```
### Error Handling
```csharp
// DO: Validate at boundary (API input)
[HttpPost]
public async Task CreateSnapshot(SaveSnapshotRequest request)
{
var validation = new SaveSnapshotValidator().Validate(request);
if (!validation.IsValid) return BadRequest(validation.Errors);
// ...
}
// DO: Trust internal guarantees
public class QuoteRepository
{
public async Task SaveAsync(QuoteDto quote)
{
// quote가 null이 아님을 가정 (caller가 검증함)
await _db.SaveAsync(quote);
}
}
// DON'T: Unnecessary defensive checks
if (quote != null && !quote.IsEmpty()) // 불필요
{
// ...
}
```
### Comments
```csharp
// DON'T: 무엇을 하는지 설명 (코드가 이미 말함)
// 가격을 저장한다
await _repository.SaveAsync(quote);
// DO: 왜 이렇게 하는지 설명
// KIS API는 대체로 가격을 30분 지연해서 보고하므로,
// 최신 3시간 데이터만 보관하여 조회 성능 향상
const int RETENTION_HOURS = 3;
```
## Python Guidelines
### Data Validation
```python
# DO: 파이프라인 입구에서만 검증
def collect_quotes(raw_data: List[Dict]):
"""raw_data는 이미 스키마 검증됨"""
quotes = [Quote(**item) for item in raw_data]
return quotes
# DON'T: 모든 곳에서 검증
def process_quote(q: Quote):
if q is None: # 불필요
return
if q.price < 0: # 불필요 (Quote 생성 시 이미 검증)
return
```
### Test Data
```python
# DO: seed 고정 (재현성)
np.random.seed(42)
test_data = np.random.normal(100, 15, 1000)
# DON'T: 시간에 따른 변화
test_timestamp = datetime.now() # ❌ 매번 다름
```
## SQL Guidelines
```sql
-- DO: 매개변수화된 쿼리
SELECT * FROM quotes WHERE ticker = @ticker AND date > @startDate
-- DON'T: 문자열 연결 (SQL injection 위험)
SELECT * FROM quotes WHERE ticker = '" + ticker + "'"
-- DO: 명확한 의도
CREATE INDEX idx_quotes_lookup ON quotes(stock_id, timestamp DESC);
-- 인덱스 이름이 쿼리 의도를 반영 (stock_id로 최신부터)
-- DO: 트랜잭션 명시
BEGIN TRANSACTION;
INSERT INTO quotes (...) VALUES (...);
INSERT INTO quotes_audit (...) VALUES (...);
COMMIT;
```
## Naming Conventions
| 대상 | 규칙 | 예 |
|------|------|-----|
| 클래스 | PascalCase | `QuoteRepository`, `DailyDataValidator` |
| 메서드 | PascalCase (verb-noun) | `GetQuoteAsync`, `ValidateDataAsync` |
| 속성 | PascalCase | `StockId`, `CollectedAt` |
| 지역변수 | camelCase | `quoteList`, `isValid` |
| 상수 | UPPER_SNAKE_CASE | `MAX_RETRIES`, `DEFAULT_TIMEOUT` |
| 인터페이스 | I + PascalCase | `IQuoteRepository`, `IDataValidator` |
| DB 테이블 | snake_case (단수) | `kis_quote`, `collection_run` |
| DB 컬럼 | snake_case | `created_at`, `stock_id` |
```
**성과지표**:
- ✅ Code style guide 작성 및 승인
- ✅ Pre-commit hook 추가 (자동 스타일 체크)
- ✅ 팀 리뷰 시간 30% 단축 (기준 명확)
---
## Part 3: 통합 성과 추적
### 주간 진행도 추적표 (2026-07-24 ~ 2026-09-30)
```
Week Phase Task Status Owner Target Date
─────────────────────────────────────────────────────────────────────────────
1 P0.V CI performance measurement 🔄 Team 2026-07-31
2 P0.V Reproducibility validation 🔄 Team 2026-08-07
3 P0.V Data consistency audit table ▶ Team 2026-08-14
4 P0.V Deployment e2e test ▶ Team 2026-08-21
5 P0.V Daily data quality check ▶ Team 2026-08-28
6 P0.V Phase 0 validation complete 🔲 Team 2026-08-31
7 P1.D Schema normalization design 🔲 Claude 2026-09-07
8 P1.D 3NF validation tool 🔲 Claude 2026-09-14
9 P1.D Blue-green migration plan 🔲 Claude 2026-09-21
10 P1.P Repository interface design 🔲 Claude 2026-09-28
11 P1.P ADR & style guide 🔲 Team 2026-09-30
```
### 리스크 추적
| 리스크 | 영향 | 확률 | 완화 계획 | 담당 |
|-------|------|------|---------|------|
| CI 성능 개선 못 함 | 높음 | 낮음 | 병렬화 추가 검토 | Team |
| 데이터 마이그레이션 실패 | 매우높음 | 중간 | Blue-green test 철저 | Claude |
| 팀 역량 부족 | 중간 | 중간 | Phase 우선순위 조정 | Owner |
| KIS API 변경 | 중간 | 낮음 | Adapter + fallback 활성 | Team |
---
## 최종 성공 기준 (2026-09-30)
```
✅ Phase 0 운영 검증 완료
- CI: 실제 15-20분 달성 확인
- 배포: 3회 연속 성공 + 롤백 검증
- 재현성: 3회 연속 CI 같은 결과
✅ Phase 1 설계 및 준비 완료
- 3NF 스키마: 설계 + 검증 완료
- 마이그레이션 계획: 상세 blue-green 전략 수립
- SOLID 설계: Repository 분리 + DI 설계 완료
- 표준화: ADR 5개 + Style guide 승인
✅ 팀 준비 완료
- Phase 1 리소스 할당 확정
- 마이그레이션 리스크 공유 및 대응 계획 수립
- CLAUDE.md Phase 1 업데이트
🚀 Phase 1 시작 준비: 2026-10-01
```
---
**Document Version**: 1.0
**Status**: Ready for Execution
**Next Review**: Weekly (every Monday)
**Emergency Contact**: Claude Code (@claude)
@@ -0,0 +1,344 @@
# QuantEngine 현대화 로드맵 (시각화)
## 1. 전체 진행도 (Gantt Chart)
```
2026 2027
Jul Aug Sep Oct Nov Dec Jan Feb Mar Apr May Jun
|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|
PHASE 0: Foundation ✅
█████
CI/CD + Data Consistency
PHASE 1: Data Architecture
███████████████
Normalization + Components + Quality
PHASE 2: Quant Engine
█████████████████
Game Theory + Scheduler + Transparency
PHASE 3: Patterns
██████████
Simplification + Standards
PHASE 4: Optimization
██████████████
Performance + Reliability
```
---
## 2. 각 Phase의 핵심 산출물
### PHASE 0: Foundation (Jul-Aug) ✅
```
INPUT PROCESS OUTPUT
Current State: - ci.yml 리팩토링 ✅ 9-job parallel CI
- 1 job CI (40min) - 워크플로우 검증 ✅ (~15-20min)
- No audit trail - CLAUDE.md 작성 ✅ Comprehensive docs
- Manual deployments Auto-validating CI
Ready for Phase 1
```
### PHASE 1: Data Architecture (Sep-Nov)
```
INPUT PROCESS OUTPUT
Legacy Schema: - Table 정규화 3NF Schema
- kis_snapshots(100+ cols) - Repository분리 Normalized tables
- Scattered data - Quality metrics Component APIs
- Data lineage Data quality gates
Backward compatible
```
**구체적 변화**:
```
Before: After:
kis_snapshots ────────────┐ stocks ──────┐
│ ticker │ │ id │
│ price │ │ ticker │
│ volume │──→│ name │
│ bid │ │ market │
│ ask │ └────────────┘
│ bidSize1-5 │
│ askSize1-5 │ quotes ─────────────┐
│ pe_ratio │ │ id │
│ eps │──→│ stock_id │
│ dividend │ │ timestamp │
│ ... x80+ more │ │ price │
└────────────────────────┘ │ volume │
│ source │
└───────────────────┘
order_book ────────┐
│ id │
│ quote_id │
│ bid_levels (json) │
│ ask_levels (json) │
└───────────────────┘
```
### PHASE 2: Quant Engine (Dec-Feb)
```
INPUT PROCESS OUTPUT
Normalized Data: - Nash equilibrium Optimal portfolio
- Clean data feeds - Adaptive scheduler Dynamic scheduling
- Multi-source capability - Decision logging Transparent decisions
- Event detection Audit trail
Reproducible logic
```
**의사결정 투명성 예시**:
```
수집 START (2026-12-15 00:30 KST)
├─ Factor 1: Sharpe ratio ✓ (1.45 > 1.0)
├─ Factor 2: Correlation ✓ (< 0.7)
├─ Factor 3: Nash allocation ✓ (computed)
├─ Data quality ✓ (98.5%)
└─ APPROVED: Rebalance to [005930: 40%, 035720: 35%, 051910: 25%]
→ 의사결정 로그: dec_20261215_001.json
→ 언제든 재현 가능: reproduce() → 동일 결과 보장
```
### PHASE 3: Patterns (Mar-Apr)
```
INPUT PROCESS OUTPUT
Scattered patterns: - 패턴 카탈로그화 Pattern library
- Ad-hoc solutions - ADR 작성 Architecture decisions
- Knowledge in heads - Style guide Development guidelines
- Code cleanup Lean codebase
YAGNI applied
```
### PHASE 4: Optimization (May-Jun)
```
INPUT PROCESS OUTPUT
Stable architecture: - Performance tuning Optimized system
- Sound design - Reliability hardening 99.9% availability
- Functional system - Automation setup 87.5% ops automated
- Monitoring/alerting Production ready
```
---
## 3. 핵심 지표 진행도
```
현재(Jul) Phase 1(Nov) Phase 2(Feb) Phase 4(Jun) 목표
CI 시간 ~40min ~20min ~18min ~12min <15min ✓
테스트 커버리지 ~60% ~70% ~78% ~85% >80% ✓
기술부채 점수 ~60% ~45% ~30% ~15% <20% ✓
포트폴리오 1.0x 1.1x 1.15x 1.2x +20% ✓
Sharpe ratio
API 응답시간 500ms 350ms 250ms 200ms <200ms ✓
수집 시간 15min 10min 8min 6min <6min ✓
시스템 가용성 98% 98.5% 99% 99.9% >99.9% ✓
수동 운영 시간 40h/week 30h/week 15h/week 5h/week <5h ✓
```
---
## 4. 핵심 의존성 & 선결 조건
```
PHASE 0 ✅
└─ CI/CD foundations DONE
└─ PHASE 1 (Sep)
├─ DB normalization
├─ Component APIs
└─ Quality metrics
└─ PHASE 2 (Dec)
├─ Game theory engine
├─ Adaptive scheduler
└─ Decision logging
└─ PHASE 3 (Mar)
├─ Pattern library
├─ Style guide
└─ Code cleanup
└─ PHASE 4 (May)
├─ Performance
├─ Reliability
└─ Automation ✓
```
---
## 5. 리스크 히트맵
```
Impact x Likelihood = Priority
Data migration HIGH(9) x MEDIUM(5) = 45 (HIGH)
(Mitigation: Parallel run + automatic rollback)
Performance HIGH(8) x MEDIUM(5) = 40 (HIGH)
regression
(Mitigation: Before/after benchmarking)
KIS API changes HIGH(7) x LOW(2) = 14 (LOW)
(Mitigation: Adapter pattern + fallbacks)
Team capacity MEDIUM(6) x HIGH(7) = 42 (HIGH)
constraint
(Mitigation: Prioritize P0 > P1 > P2)
Schema drift MEDIUM(6) x MEDIUM(5) = 30 (MEDIUM)
(Mitigation: Automated validation in CI)
```
---
## 6. 관계자별 책임
| 역할 | Phase 0 | Phase 1 | Phase 2 | Phase 3 | Phase 4 |
|------|---------|---------|---------|---------|---------|
| **설계** | Claude ✓ | Claude | Claude | Team | Team |
| **구현** | Claude ✓ | Team | Team | Team | Team |
| **검증** | Claude ✓ | Claude+QA | Claude+QA | QA | QA |
| **배포** | DevOps ✓ | DevOps | DevOps | DevOps | DevOps |
| **승인** | Owner ✓ | Owner | Owner | Owner | Owner |
---
## 7. Go/No-Go 게이트 체크리스트
### 🟢 PHASE 0 (Jul-Aug) ✅ APPROVED
- [x] CI 9 job 병렬화 완료 (40min → 15min)
- [x] 워크플로우 검증 자동화
- [x] CLAUDE.md 종합 문서화
- [x] 데이터 이력 테이블 설계
**진행 상태**: 100% | **승인**: 2026-07-24
---
### 🟡 PHASE 1 (Sep-Nov) PENDING
**Go 조건** (Sep 30):
- [ ] DB 정규화 70% 완료
- [ ] IQuoteRepository, IRunRepository 구현
- [ ] Data quality validator 작동
- [ ] 기존 API 호환성 유지 (Adapter pattern)
- [ ] 데이터 마이그레이션 테스트 통과
**의존성**: Phase 0 완료 ✓
---
### 🟡 PHASE 2 (Dec-Feb) PENDING
**Go 조건** (Feb 28):
- [ ] Nash equilibrium 알고리즘 구현
- [ ] 동적 스케줄러 운영 중
- [ ] 의사결정 로그 100% 추적
- [ ] 재현성 검증 완료
- [ ] 백테스트 통과 (Sharpe ratio +15%)
**의존성**: Phase 1 완료
---
### 🟡 PHASE 3 (Mar-Apr) PENDING
**Go 조건** (Apr 30):
- [ ] 패턴 카탈로그 완성
- [ ] ADR 5개 이상 작성
- [ ] 불필요한 코드 20% 제거
- [ ] Style guide 승인
- [ ] 온보딩 시간 50% 단축 검증
**의존성**: Phase 2 완료
---
### 🟡 PHASE 4 (May-Jun) PENDING
**Go 조건** (Jun 30):
- [ ] 99.9% 가용성 달성 (1개월 운영 증명)
- [ ] 성능 목표 달성 (API <200ms, 수집 <6min)
- [ ] 운영 자동화 87.5% 달성
- [ ] RTO/RPO 테스트 통과
- [ ] 최종 감사 승인
**의존성**: Phase 3 완료 + 프로덕션 안정성 입증
---
## 8. 투자 대비 효과 (ROI 분석)
### 비용 (한 명의 개발자 기준)
```
Phase 0: 2주 (CI/CD)
Phase 1: 8주 (Data architecture)
Phase 2: 12주 (Quant engine)
Phase 3: 4주 (Patterns)
Phase 4: 8주 (Optimization)
─────────────
Total: 34주 = 8.5개월 = 1 FTE
연간 운영 절감: 30시간/주 × 50주 = 1,500시간 절감
투자 대비 효과: 1,500시간 절감 / (34주 × 40시간 = 1,360시간 투자) = 1.1배
추가 효과: 포트폴리오 성과 20% 향상, 시스템 안정성 99.9% 달성
```
### 정성적 효과
- 👥 **팀 생산성**: 온보딩 50% 단축 (신입 개발자)
- 🛡️ **리스크 감소**: 데이터 손실 0%, 감시 추적 100%
- 📊 **의사결정 품질**: 투명성 100%, 재현성 100%
-**정보 반영 속도**: 24시간 → 1시간 이내
---
## 9. 실패 사례 방지
```
❌ 실패 사례 ✅ 우리의 접근법
────────────────────────────────────────────────────
"Big bang" 전환 작은 단위 iterative 개선
(all or nothing) (각 phase별 go/no-go)
마이그레이션 중 장애 Parallel run + 자동 롤백
(데이터 손실) (backward compatibility)
성능 회귀 미발견 Before/after 벤치마킹
+ 자동화된 성능 게이트
기술 선택 이유 불명확 ADR (Architecture Decision Records)
(누가, 언제, 왜?) (투명한 의사결정)
팀 역량 부족 Phase 우선순위 명확화
(너무 빨리 너무 많이) (P0 > P1 > P2)
```
---
## 10. 마일스톤 & 주요 이벤트
```
🟢 2026-07-24 PHASE 0 완료 ✓ CI 9-job, CLAUDE.md updated
🟡 2026-08-31 PHASE 0 검증 데이터 일관성 검증 완료
🟡 2026-09-30 PHASE 1 시작 DB 정규화 첫 배포
🟡 2026-11-30 PHASE 1 완료 검증 Component API 운영
🟡 2026-12-15 PHASE 2 시작 Game theory engine 첫 결정
🟡 2027-02-28 PHASE 2 완료 검증 의사결정 투명성 100%
🟡 2027-03-31 PHASE 3 시작 Pattern library 공개
🟡 2027-04-30 PHASE 3 완료 검증 Style guide 승인
🟡 2027-05-31 PHASE 4 시작 성능 최적화
🟡 2027-06-30 PHASE 4 완료 ✓ 최종 프로덕션 안정화 완료
```
---
## 11. 승인 서명
| 역할 | 이름 | 서명 | 날짜 |
|------|------|------|------|
| Project Owner | [TBD] | _____ | |
| Technical Lead | Claude + Team | _____ | 2026-07-24 |
| QA Lead | [TBD] | _____ | |
| DevOps Lead | [TBD] | _____ | |
---
**Document Version**: 1.0
**Status**: Phase 0 ✅ Approved
**Next Review**: 2026-08-31
@@ -0,0 +1,607 @@
# QuantEngine 데이터 기반 고도화 로드맵
**2026-07-24 ~ 2027-06-30**
---
## Executive Summary
**현상**: Python 레거시 기반 + .NET 신규 웹 UI의 하이브리드 구조
**목표**: Solid 원칙 + 데이터 정합성 + 게임이론 기반 퀀트 최적화 엔진 구축
**기대효과**:
- 코드 품질: 기술부채 80% 감소
- 성능: 데이터 수집 시간 60% 단축
- 신뢰성: 감시 추적 가능성 100% (audit trail)
- 의사결정: 재현성 100% + 현장감(explainability) 개선
---
## Phase 0: Foundation (2026-07 ~ 2026-08) — 현재 진행 중
### 목표: 아키텍처 기초 다지기
#### P0.1: CI/CD 파이프라인 최적화 ✅ (완료: 2026-07-24)
- [x] ci.yml 리팩토링: 1 job → 9 parallel jobs
- [x] 성능: ~40min → ~15-20min (2.5배 가속)
- [x] 워크플로우 검증 자동화
- [x] CLAUDE.md 종합 문서화
**성과지표**:
- CI 리드 타임 단축 ✅
- 병렬 job 의존성 명확화 ✅
- 개발자 온보딩 시간 50% 단축 예상
#### P0.2: 데이터 정합성 기초 구축 (2026-08)
**목표**: 모든 데이터 흐름의 버전 추적 + 감시 추적
**추진 과제**:
1. **PostgreSQL 이력 스키마 도입**
- kis_collection_runs: 실행 시간, 성공/실패, 건수 추적
- kis_collection_snapshots: 각 snapshot의 출처, 변환 이력
- kis_collection_errors: 오류 분류 + 재현 로그
2. **데이터 정합성 검증기 개발**
```
validate_data_consistency_v1.py:
- Row count 변화 추적
- Schema drift 감지
- Null/duplicate 통계
- Data lineage (출처 명시)
```
3. **Snapshot 변경 관리**
- GatherTradingData.json → DB 마이그레이션 추적
- 변경 이력: who, when, what, why (4W)
- Rollback 능력 확보
**성과지표**:
- 모든 수집 run의 재현성 100%
- 데이터 변경 추적률 100%
- 자동화된 감시 추적 구현
---
## Phase 1: Data Architecture Refactoring (2026-09 ~ 2026-11)
### 목표: 정규화 + 컴포넌트화 + 패턴화
#### P1.1: 데이터 모델 정규화 (9월)
**현황**: KIS snapshot → 1개 JSON 구조
**목표**: 3NF (Third Normal Form) 기반 관계형 설계
**추진 과제**:
1. **Table 리팩토링**
```sql
Current (비정규화):
kis_collection_snapshots: {ticker, price, volume, bid, ask, ...100+ columns}
Target (3NF):
stocks: {id, ticker, name, market}
quotes: {id, stock_id, timestamp, price, volume, source}
order_book: {id, quote_id, bid_levels, ask_levels}
fundamental: {id, stock_id, eps, pe_ratio, ...}
```
2. **마이그레이션 전략**
- Phase 1a: 새 테이블 생성 (parallel)
- Phase 1b: 데이터 변환 + 검증 (with fallback)
- Phase 1c: 쿼리 리포인팅 (gradual cutover)
- Phase 1d: 기존 테이블 아카이빙
3. **Backward Compatibility**
```csharp
// Adapter pattern: 기존 API는 유지, 내부적으로 새 테이블 사용
public class LegacySnapshotAdapter : ICollectionSnapshot
{
private readonly IQuoteRepository _newQuotes;
public LegacySnapshotAdapter(IQuoteRepository repo) => _newQuotes = repo;
public SnapshotDto Get(string ticker)
=> SnapshotDto.FromNormalizedTables(_newQuotes.GetBy(ticker));
}
```
**성과지표**:
- 스토리지 용량 40% 감소
- 쿼리 복잡도 50% 감소
- 데이터 무결성 제약 자동 적용
#### P1.2: 컴포넌트화 + 인터페이스 분리 (10월)
**목표**: Dependency Inversion 원칙 적용
**추진 과제**:
1. **Repository 분리**
```csharp
Current (단일 ICollectionRepository):
- GetSnapshots()
- GetRuns()
- GetErrors()
- SaveSnapshot()
Target (SOLID ISP):
- IQuoteRepository: 가격/호가 데이터
- IRunRepository: 수집 메타데이터
- IErrorRepository: 오류 로그
- IFundamentalRepository: 기본정보
```
2. **팩토리 패턴 도입**
```csharp
public interface IDataSourceFactory
{
IDataSource CreateKisSource();
IDataSource CreateNaverFallback();
IDataSource CreateYahooFallback();
}
// 주입: 런타임에 데이터 소스 전환 가능
```
3. **전략 패턴: 데이터 변환**
```csharp
public interface IDataTransformStrategy
{
SnapshotDto Transform(RawApiResponse response);
}
// 구현: Kis변환, Naver변환, Yahoo변환 등
// 각 소스별 정규화 로직 캡슐화
```
**성과지표**:
- 모듈 간 의존성 명확화 (순환 의존성 0)
- 테스트 용이성 (Mock 주입 가능)
- 런타임 구성 가능 (dynamic strategy switching)
#### P1.3: 데이터 팩터 고도화 (11월)
**목표**: 데이터 품질 + 이상 탐지 자동화
**추진 과제**:
1. **Data Quality Metrics**
```python
class DataFactorValidator:
def check_completeness(self, snapshot):
"""누락값 검사: null/missing ratio"""
return snapshot.fillna_ratio >= 0.95
def check_freshness(self, snapshot):
"""신선도 검사: 수집 후 경과 시간"""
age_hours = (now() - snapshot.created_at).hours
return age_hours < 24
def check_consistency(self, snapshot):
"""정합성 검사: bid <= mid <= ask"""
return snapshot.bid <= snapshot.mid <= snapshot.ask
def check_outliers(self, snapshot):
"""이상값 검사: 볼린저 밴드 벗어남"""
z_score = (snapshot.price - mean) / std
return abs(z_score) < 3 # 3-sigma rule
```
2. **자동 보정 규칙**
```
Error Rule 1: 빠진 데이터 → 직전 값 사용 (forward fill)
Error Rule 2: 이상값 → 같은 날짜 유사 종목 중앙값 사용
Error Rule 3: 불가능한 값 → 폴백 소스(Naver/Yahoo) 호출
```
3. **CI 게이트 추가**
```
validate_data_factors_v1.py:
- 완전성 (Completeness) ≥ 95%
- 신선도 (Freshness) < 24h
- 정합성 (Consistency) 100%
- 이상값 (Outliers) < 5%
```
**성과지표**:
- 자동 데이터 품질 검사 자동화
- 수동 개입 필요 비율 <5%
- 데이터 품질 스코어 98% 이상
---
## Phase 2: Quant Engine 고도화 (2026-12 ~ 2027-02)
### 목표: 게임이론 + 최적화 알고리즘 + 의사결정 엔진
#### P2.1: 게임이론 기반 포트폴리오 선택 (12월)
**목표**: 단순 수익률 최대화 → Nash Equilibrium 기반 균형점 추구
**추진 과제**:
1. **다중 플레이어 게임 모델**
```
Players: 시장 참가자들 (기관, 개인, AI)
Strategy space: 매도/보유/매수 + 비중 결정
Payoff: 포트폴리오 return + risk-adjusted Sharpe ratio
Goal: 내 포트폴리오 최적화 + 시장 균형 고려
```
2. **알고리즘**
```python
class GameTheoreticPortfolio:
def compute_nash_equilibrium(self, market_state):
"""
각 자산의 최적 비중을 계산
- Covariance matrix (상관성)
- Expected return (기대수익률)
- Risk aversion parameter (위험회피도)
결과: 다른 플레이어가 이탈할 유인이 없는 균형점
"""
# Linear Programming or Lemke-Howson algorithm
return optimal_allocation
def backtest_nash(self, historical_data):
"""과거 데이터로 Nash 균형 전략 검증"""
# 매년 Nash 균형점 계산 + 연 수익률 추적
```
3. **구현 체크리스트**
- [x] 기본 Markowitz 포트폴리오 (현재)
- [ ] Nash Equilibrium 계산 (12월)
- [ ] 백테스트 (12월)
- [ ] CI 게이트 추가 (1월)
**성과지표**:
- 샤프 지수 개선 20% 이상
- 최대손실률(MDD) 감소 15% 이상
- 시장 급변 시 안정성 입증
#### P2.2: 스케줄러 고도화 (1월)
**목표**: 정적 시간표 → 동적 이벤트 기반 수집
**현황**:
```
현재: cron "00:30 KST" 매일 수집
문제: 시장 급변시 대응 불가, 정보 지연
```
**목표**:
```
개선:
1. 정규 수집: 매일 00:30 KST (기존)
2. 긴급 수집: 시장 변동성 급증 시 즉시 (Volatility-triggered)
3. 이벤트 수집: 공시 발표 시점 수집 (OpenDART-triggered)
4. 포트폴리오 리밸런싱 시점 + 1시간 이내 수집
```
**추진 과제**:
1. **이벤트 감지 엔진**
```csharp
public interface IMarketEventDetector
{
// 변동성 급증: VIX 또는 종목별 일일 등락률 > 5%
IAsyncEnumerable<VolatilityEvent> DetectVolatilitySpike();
// 공시 발표: OpenDART API
IAsyncEnumerable<DisclosureEvent> DetectNewDisclosure();
// 리밸런싱: 내부 신호
IAsyncEnumerable<RebalancingEvent> DetectRebalancingTrigger();
}
```
2. **스케줄링 엔진**
```csharp
public class AdaptiveScheduler
{
public async Task ScheduleCollectionAsync(MarketEvent evt)
{
// 기존: 매일 00:30
// 신규: 이벤트별 즉시 or 정해진 시간 후
var delay = evt switch
{
VolatilityEvent => TimeSpan.Zero, // 즉시
DisclosureEvent => TimeSpan.FromHours(1), // 1시간 후
RebalancingEvent => TimeSpan.FromHours(0.5), // 30분 후
_ => TimeSpan.FromHours(24) // 일반: 매일
};
await _collectionService.QueueAsync(delay);
}
}
```
3. **Backpressure & Rate Limiting**
- KIS API 호출량 제한 준수 (초당 10회)
- 동시 수집 작업 제한 (최대 3개)
- 폴백 소스 자동 선택
**성과지표**:
- 정보 반영 시간: 매일 정시 → 최대 1시간 이내
- KIS API 호출 효율성: 불필요한 호출 80% 감소
- 시장 기회 포착율 30% 증가
#### P2.3: 의사결정 엔진 (의사결정 투명성) (2월)
**목표**: "왜 이 종목을 선택했는가?" → 완벽한 감시 추적
**추진 과제**:
1. **의사결정 로그 (Decision Log)**
```json
{
"decision_id": "dec_20260701_001",
"timestamp": "2026-07-01T00:30:00Z",
"decision_type": "portfolio_rebalance",
"rationale": [
{
"factor": "sharpe_ratio",
"value": 1.45,
"threshold": 1.0,
"status": "pass",
"evidence": "stock_005930_sharpe_ratio.json"
},
{
"factor": "game_theoretic_allocation",
"value": 0.25,
"computation": "nash_equilibrium_20260701.json",
"status": "pass"
}
],
"selected_portfolio": ["005930", "035720", "051910"],
"weights": [0.40, 0.35, 0.25],
"expected_return": 0.085,
"risk_level": "medium",
"data_quality_score": 0.98,
"approval_status": "auto_approved"
}
```
2. **재현 가능한 계산**
```python
class ReproducibleDecision:
def __init__(self, decision_log: Dict):
self.log = decision_log
def reproduce(self) -> PortfolioAllocation:
"""저장된 로그를 기반으로 동일한 의사결정 재현"""
data = self._load_data_from_sources(self.log["data_references"])
allocation = self._compute_nash_equilibrium(data)
assert allocation == self.log["selected_weights"]
return allocation
```
3. **감시 추적 대시보드**
- 의사결정 이력 조회 (date range, factor, status)
- 의사결정 재현 (선택한 의사결정 ID 입력 → 동일 과정 재실행)
- 팩터별 영향도 분석 (이 팩터가 의사결정에 기여한 %?)
- 백테스트 vs 실적 비교
**성과지표**:
- 의사결정 투명성 100% (모든 이유 기록)
- 감시 추적 가능성 100% (언제든 재현 가능)
- 내부 감시 및 컴플라이언스 자동화
---
## Phase 3: Process Simplification & Patterns (2027-03 ~ 2027-04)
### 목표: 프로세스 단순화 + 표준화 + 패턴화
#### P3.1: 과유불급(YAGNI) 원칙 적용 (3월)
**현황**: 불필요한 기능, 미사용 코드, 과도한 추상화
**추진 과제**:
1. **코드 정리**
- [x] 사용되지 않는 .NET method 제거
- [x] 미사용 Python 스크립트 아카이빙
- [ ] 과도한 추상화 단순화 (3계층 이상의 인터페이스 → 2계층으로)
- [ ] 설정값 하드코딩 (config file complexity 감소)
2. **테스트 단순화**
- 현재: 30+ 검증 (ci.yml)
- 목표: 핵심 15개로 정리 (나머지는 수동 또는 주간 검증으로 이동)
3. **배포 프로세스 단순화**
- 현재: prepare-release.yml → deploy-prod.yml (2단계)
- 목표: CI pass → 자동 staging → 수동 1-click deploy to prod
**성과지표**:
- 코드 라인 20% 감소
- CI 시간 추가 10% 단축 (~12-15분)
- 개발자 인지 부담 30% 감소
#### P3.2: 표준 패턴화 + 아키텍처 스타일 가이드 (4월)
**목표**: "언제 어떤 패턴을 쓸까?" 규칙 정립
**추진 과제**:
1. **패턴 카탈로그**
```
[패턴] Repository
- 언제: DB 접근이 필요할 때
- 구현: Dapper + raw SQL
- 예: IQuoteRepository.GetByTickerAsync()
[패턴] Strategy
- 언제: 런타임에 알고리즘 전환이 필요할 때
- 구현: interface IDataTransformStrategy
- 예: KisTransformStrategy, NaverTransformStrategy
[패턴] Factory
- 언제: 복잡한 객체 생성 로직
- 구현: IDataSourceFactory
- 예: CreateKisSource(), CreateNaverFallback()
[패턴] Adapter
- 언제: 레거시 인터페이스 호환성 필요
- 구현: LegacySnapshotAdapter wraps IQuoteRepository
- 예: 기존 SnapshotDto API 유지 while using new DB schema
```
2. **아키텍처 결정 기록 (ADR)**
- adr/0001-razor-pages-over-wasm.md
- adr/0002-dapper-orm-not-ef.md
- adr/0003-postgresql-single-source-of-truth.md
- adr/0004-game-theoretic-portfolio-selection.md
3. **코드 스타일 가이드 (CLAUDE.md 강화)**
- C#: "3 similar lines → extract method"
- Python: "3 similar lines → extract function"
- SQL: "Always use parameterized queries"
- JSON: "Always validate against schema"
**성과지표**:
- 새 기능 개발 시간 40% 단축 (패턴 재사용)
- 코드 리뷰 시간 30% 단축 (명확한 표준)
- 온보딩 시간 50% 단축 (패턴 이해)
---
## Phase 4: Optimization & Maturity (2027-05 ~ 2027-06)
### 목표: 성능 최적화 + 안정성 입증 + 운영 자동화
#### P4.1: 성능 최적화 (5월)
**목표**: 응답 시간 50% 단축, 데이터 수집 시간 60% 단축
**추진 과제**:
1. **데이터베이스 최적화**
- 인덱싱: kis_collection_snapshots(ticker, created_at)
- 쿼리 최적화: N+1 query 문제 제거
- 연결 풀링: Npgsql pool size 최적화
2. **캐싱 전략**
```csharp
// 단기 캐시: 시장 공휴일, 종목 기본정보 (1주일)
IMemoryCache.Set("holidays_2026", holidays, TimeSpan.FromDays(7));
// 중기 캐시: 일일 수집 결과 (1주일)
IDistributedCache.SetAsync("quote_20260701", quote, TimeSpan.FromDays(7));
// 긴기 캐시: 연간 통계 (1년)
IDistributedCache.SetAsync("annual_stats_2026", stats, TimeSpan.FromDays(365));
```
3. **병렬화**
- KIS API: 최대 10개 종목 동시 요청
- 데이터 변환: Parallel.ForEach() 사용
- 검증: 30+ 게이트를 8개 job으로 병렬화 (이미 완료)
**성과지표**:
- API 응답 시간: 500ms → 200ms (60% 단축)
- 수집 시간: 15분 → 6분 (60% 단축)
- DB 쿼리 평균 시간: 50ms → 10ms (80% 단축)
#### P4.2: 안정성 & 신뢰성 (5월)
**목표**: 99.9% 가용성, 데이터 손실 0%
**추진 과제**:
1. **재해 복구 (Disaster Recovery)**
```
RTO (Recovery Time Objective): 1시간 이내
RPO (Recovery Point Objective): 1시간 이내 (6시간 간격 백업)
절차:
1. 매 6시간마다 PostgreSQL 풀 백업
2. 백업: S3 또는 별도 스토리지에 저장
3. 복구 테스트: 월 1회
```
2. **데이터 무결성**
- Foreign key 제약 활성화
- Check constraints: bid <= mid <= ask
- Trigger: 변경 감시 추적 자동 기록
3. **Failover**
- 단일 PostgreSQL → 이중화 (Primary + Replica)
- KIS API 실패 → Naver → Yahoo 자동 폴백
**성과지표**:
- 시스템 가용성: 99.9% 달성
- 데이터 손실: 0% (100% 백업)
- RTO/RPO 달성률: 100%
#### P4.3: 운영 자동화 (6월)
**목표**: 수동 운영 작업 80% 자동화
**추진 과제**:
1. **모니터링 & 알림**
```
Alert 1: 수집 실패 → Slack 알림 + 자동 재시도
Alert 2: 데이터 품질 저하 → 이메일 + 관리자 대시보드
Alert 3: API 할당량 초과 → 수집 일시 중단 + 폴백 활성화
Alert 4: DB 연결 풀 고갈 → 자동 스케일링 또는 모니터링
```
2. **자동 복구**
- 수집 실패: 자동 재시도 (지수 백오프)
- 데이터 이상값: 자동 보정 (또는 폴백 소스 호출)
- 연결 타임아웃: 자동 재연결
3. **운영 리포트 자동화**
- 일일 보고: 수집 건수, 오류율, 데이터 품질 스코어
- 주간 보고: 포트폴리오 성과, 리스크 메트릭
- 월간 보고: 감사 로그, 컴플라이언스 체크
**성과지표**:
- 수동 운영 시간: 8시간/주 → 1시간/주 (87.5% 자동화)
- 평균 대응 시간: 30분 → 5분 (85% 개선)
- 운영 오류율: 5% → <0.1% (98% 개선)
---
## Timeline Overview
```
Q3 2026 (July-Aug): Phase 0 ✅ CI/CD + Data Consistency Foundation
Q4 2026 (Sep-Nov): Phase 1 Data Architecture + Components + Quality Metrics
Q1 2027 (Dec-Feb): Phase 2 Game Theory + Adaptive Scheduler + Transparency
Q2 2027 (Mar-Apr): Phase 3 Simplification + Patterns + Standards
Q2 2027 (May-Jun): Phase 4 Performance + Reliability + Automation
```
---
## Risk Management & Mitigation
| Risk | Impact | Likelihood | Mitigation |
|------|--------|-----------|-----------|
| Data migration breaks production | Critical | Medium | Parallel run (old + new) for 2 weeks, automatic rollback |
| Performance regression | High | Medium | Before/after benchmarking, rollback triggers |
| KIS API changes | High | Low | Adapter pattern, fallback sources active |
| Team capacity constraints | Medium | High | Prioritize P0 > P1 > P2 (vertical slicing) |
| Schema drift during refactor | Medium | Medium | Automated schema validation in CI |
---
## Success Criteria & Metrics
### By End of Phase 4 (2027-06-30):
**Code Quality**:
- ✅ Technical debt score: < 20% (from current ~60%)
- ✅ Code coverage: > 80% (from current ~60%)
- ✅ Cyclomatic complexity: avg 5 (from current ~12)
**Performance**:
- ✅ API response time: < 200ms (p95)
- ✅ Data collection time: < 6 minutes
- ✅ Database query time: < 10ms (avg)
**Reliability**:
- ✅ System availability: 99.9%
- ✅ Data loss: 0% (100% recovery capability)
- ✅ Manual intervention rate: < 1% (99% automated)
**Quant**:
- ✅ Portfolio Sharpe ratio: +20% improvement
- ✅ Decision transparency: 100% (all decisions logged + reproducible)
- ✅ Information latency: < 1 hour (from 24 hours)
---
## Governance & Approval
**Executive Sponsor**: Project Owner
**Technical Lead**: Claude Code + Team
**Review Cadence**: Bi-weekly (every 2 weeks)
**Go/No-Go Gates**:
- End of Phase 0 ✅ (Approved)
- End of Phase 1 (September 30, 2026)
- End of Phase 2 (February 28, 2027)
- End of Phase 3 (April 30, 2027)
- End of Phase 4 (June 30, 2027)
---
**Document Version**: 1.0
**Last Updated**: 2026-07-24
**Next Review**: 2026-08-31
@@ -0,0 +1,87 @@
# OMS·WMS·ERP 입력 컴포넌트 & 공통 CRUD 템플릿 & 상용화 제안 마스터 WBS (WBS-MASTER-2026)
## 0. 개요 및 3대 명세 통합 권위
본 문서는 아래 3대 핵심 상용화 명세를 완벽히 아우르는 마스터 작업분해구조(WBS)와 일정 스케줄, 성공판단 데이터를 정의한다.
1. **OMS·WMS·ERP CRUD 화면 및 입력 컴포넌트 상용화 제안** (22개 섹션 & 10대 계율)
2. **OMS·WMS·ERP 공통 CRUD 화면 템플릿 상세 명세** (11대 표준 템플릿 `TPL-LIST-01` ~ `TPL-HISTORY-01` & 25개 공통 규격)
3. **OMS·WMS·ERP 입력 컴포넌트 상세 명세** (Primitive → Typed Field → Domain Field → Business Composite 4계층 아키텍처 & 52개 세부 규격)
### 0.1 기본 하네스 4대 완수 조건
1. **YAML/MD 계약**: `docs/ENTERPRISE_CRUD_DESIGN_SPECIFICATION.md` & `docs/ROADMAP_ENTERPRISE_TEMPLATES_WBS.md`
2. **코드 구현**: `src/frontend/src/components/` (4계층 컴포넌트), `src/frontend/src/views/templates/` (11대 템플릿) & `src/frontend/src/types/enterpriseTemplateContracts.ts`
3. **데이터 실체**: `Temp/enterprise_crud_validation_report_v1.json`, `Temp/enterprise_crud_validation_report_v1.md`
4. **검증 증빙**: `python tools/validate_enterprise_crud_specification_v1.py` & `npx playwright test`
---
## 1. [트랙 A] 상용화 제안 10대 계율 & 헌법 WBS
| WBS ID | 상용화 설계 원칙 | 주요 이행 사항 | 상태 | 성공판단 데이터 (Acceptance Criteria) |
| :--- | :--- | :--- | :---: | :--- |
| `WBS-GOV-01` | 업무 트랜잭션 정의 | 완료된 거래 물리 삭제/덮어쓰기 금지 | `완료` | `TPL-CANCEL-01` 역트랜잭션 생성 및 audit log 100% 보존 |
| `WBS-GOV-02` | 5계층 아키텍처 | Primitive ~ Process 계층 분리 | `완료` | 4계층 컴포넌트 디렉터리 분리 및 SRP 단일책임 보장 |
| `WBS-GOV-03` | 공통 FieldContract | `FieldStatus` 13가지 & `ValueSource` 8가지 | `완료` | `readonly` vs `disabled` vs `blocked` 3대 상태 명확 분리 |
| `WBS-GOV-04` | 4계층 검증 경계 | UI(1차) → Schema(2차) → Server(3차) → DB(4차) | `완료` | 422 서버 검증 오류 수신 시 최초 필드 자동 이동 |
| `WBS-GOV-05` | 정규화 / 역정규화 | 마스터 정규화 및 Read Model 역정규화 | `완료` | 시점 스냅샷(주문 당시 품목명, 단가, 세율) 보존 |
| `WBS-GOV-06` | 현장 작업 (WMS) | 스캔, 100ms 단결, 오프라인 큐 | `구현완료` | BarcodeInput 연속 스캔 및 음향/진동 피드백 |
| `WBS-GOV-07` | AX (AI Experience) | 초안/추천 국한 & R0~R4 위험 등급 | `구현완료` | AISuggestedField 추천근거 뷰어 및 결정론 수식 AI 위임 차단 |
| `WBS-GOV-08` | 바이브코딩 통제 | 품질 게이트 & 자동 검증 하네스 | `완료` | `validate_enterprise_crud_specification_v1.py` 100% PASS |
| `WBS-GOV-09` | 성능 목표 | 입력 < 100ms, 스캔 < 100ms, P95 < 2s | `완료` | 10,000건 Grid 가상화 sizeColumnsToFit 자동 폭 확장 |
| `WBS-GOV-10` | 접근성 & 보안 | WCAG 2.2 AA / WAI-ARIA & RBAC/ABAC | `완료` | 키보드 전용 조작 및 스크린리더 `aria-describedby` 바인딩 |
---
## 2. [트랙 B] 11대 표준 업무 템플릿 WBS (25개 규격 기반)
| WBS ID | 템플릿 ID | 화면 유형 | 대표 업무 | 구현 상태 | 성공판단 데이터 |
| :--- | :--- | :--- | :--- | :---: | :--- |
| `WBS-TPL-01` | `TPL-LIST-01` | 목록·검색 | OMS 주문목록, WMS 재고현황 | `구현완료` | Summary Strip, URL Query 동기화, `QuantDataGrid` |
| `WBS-TPL-02` | `TPL-CREATE-01` | 단일 등록 | 마스터(거래처/품목) 등록 | `구현완료` | Idempotency Key 생성, 저장 후 계속 등록 모드 |
| `WBS-TPL-03` | `TPL-CREATE-02` | 헤더·라인 등록 | OMS 주문, WMS 입고예정 | `Sprint 3` | 헤더 변경 시 라인 재계산 토스트 및 저장/확정 분리 |
| `WBS-TPL-04` | `TPL-CREATE-03` | 단계형 등록 | 복합 주문, 반품, 계약 | `Sprint 3` | Step별 유효성 검증 및 임시저장 세션 복구 |
| `WBS-TPL-05` | `TPL-DETAIL-01` | 상세 조회 | 주문 상세, 입고 상세 | `구현완료` | Status Timeline 뱃지 및 관련 문서 릴레이션 노드 표출 |
| `WBS-TPL-06` | `TPL-EDIT-01` | 일반 수정 | 마스터 및 주문 수정 | `Sprint 3` | 409 Conflict 발생 시 서버 최신값 vs 내 변경값 3-Way Diff |
| `WBS-TPL-07` | `TPL-BULK-01` | 일괄 수정 | 담당자/예정일 일괄 변경 | `Sprint 4` | 예상 영향건수 미리보기 및 100건 초과 시 비동기 Job ID |
| `WBS-TPL-08` | `TPL-DELETE-01` | 삭제 | 미사용 마스터 삭제 | `Sprint 4` | 참조 데이터 존재 시 삭제 차단 및 확인 코드 재입력 Modal |
| `WBS-TPL-09` | `TPL-CANCEL-01` | 취소·역처리 | 주문 취소, 전표 역분개 | `구현완료` | Cancellation Preview Token & 역트랜잭션 생성 (물리 삭제 0건) |
| `WBS-TPL-10` | `TPL-APPROVAL-01`| 승인·반려 | 발주 승인, 전표 승인 | `Sprint 4` | 작성자-승인자 직무분리(SoD) 승인 버튼 차단 |
| `WBS-TPL-11` | `TPL-HISTORY-01` | 변경 이력 | Audit Event, 이력 감사 | `Sprint 4` | AuditEvent 스키마 기반 필드 변경 차이(Diff) 뷰어 |
---
## 3. [트랙 C] 입력 컴포넌트 4계층 WBS (52개 섹션 기반)
### Phase 1: Primitive Layer (`components/primitives/`)
- `WBS-COMP-1.1`: `TextInput.vue` (완료) - IME 조합유지, aria-invalid
- `WBS-COMP-1.2`: `SelectInput.vue` (완료) - 방향키/Enter/Escape 제어
- `WBS-COMP-1.3`: `DialogModal.vue` (완료) - 포커스 트랩 및 ESC 닫기
### Phase 2: Typed Field Layer (`components/fields/`)
- `WBS-COMP-2.1`: `StringField.vue` (완료) - 공백 제거, 대문자 정규화
- `WBS-COMP-2.2`: `NumberField.vue` (구현완료) - Decimal 정밀도, 천단위 쉼표
- `WBS-COMP-2.3`: `DateField.vue` (구현완료) - ISO YYYY-MM-DD 날짜 및 '오늘' 버튼
- `WBS-COMP-2.4`: `CodeField.vue` (완료) - Debounce 300ms 중복 검사
### Phase 3: Domain Field Layer (`components/domain-fields/`)
- `WBS-COMP-3.1`: `QuantityField.vue` (완료) - 단위 환산 및 가용재고 표출
- `WBS-COMP-3.2`: `MoneyField.vue` (구현완료) - 부동소수점 금지 및 통화 선택
- `WBS-COMP-3.3`: `BarcodeInput.vue` (구현완료) - 100ms 연속 스캔 및 피드백
- `WBS-COMP-3.4`: `LotField.vue` (구현완료) - FEFO/FIFO 추천 및 로트 검증
### Phase 4: Business Composite Layer (`components/business-composites/`)
- `WBS-COMP-4.1`: `AddressEditor.vue` (완료) - 주소 및 우편번호 편집기
- `WBS-COMP-4.2`: `AISuggestedField.vue` (구현완료) - R0~R4 위험 등급 및 AI 추천
- `WBS-COMP-4.3`: `OrderLineEditor.vue` (진행중) - 주문 라인 가상화 편집기
---
## 4. 종합 이행 스케줄 (Master Schedule)
```text
[Sprint 1: 3대 규격 프레임워크 구축] ───▶ 52개 명세, 11대 템플릿, 하네스 CLI v3.0 구축 (완료)
[Sprint 2: 1차 핵심 컴포넌트 & 템플릿] ──▶ Number, Date, Money, Barcode, AI, TPL-LIST-01 (완료)
[Sprint 3: 2차 템플릿 & 컴포넌트 확충] ──▶ TPL-CREATE-01, TPL-DETAIL-01, TPL-CANCEL-01 (완료)
[Sprint 4: 3차 역처리/안전성 템플릿] ──▶ TPL-EDIT-01, TPL-APPROVAL-01, TPL-BULK-01 (진행 중)
```
File diff suppressed because it is too large Load Diff