# K-ArtSell Aegis v16.0 - 19가지 원칙 기반 실행 전략 **작성일:** 2026-08-18 **기준:** AGENTS.md v16.0 + WBS_EXECUTION_GUIDELINES + 사용자 19가지 원칙 **목표:** 모든 제안 작업을 전략적으로 실행하되, 19가지 원칙 100% 준수 --- ## 🎯 19가지 원칙 실행 프레임워크 ### 1️⃣ SOLID (단일책임, 개방폐쇄, 리스코프 치환, 인터페이스 분리, 의존성 역전) **현황:** ✅ 실행 중 (Vertical Slice 패턴) **구체적 행동:** ```csharp // ✅ DO: Single Responsibility (각 클래스 = 1개 책임) public class ShadowRunCommandHandler : ICommandHandler { // 책임: Shadow Run 커맨드 처리 (조율만) // 정책은 Policy에, 데이터는 Sql에 } public class ShadowRunPolicy { // 책임: 비즈니스 규칙만 } public class ShadowRunSql { // 책임: Dapper 쿼리만 } // ❌ DON'T: God Class (책임 과적) public class ShadowRunService { // HTTP 라우팅 + 비즈니스 로직 + 데이터 접근 모두 여기 } ``` **검증:** ```bash # 1. cyclomatic complexity 측정 cd src/KArtSell.Host dotnet format --verify-no-changes # 복잡도 초과 감지 dotnet test KArtSell.sln --filter "ComplexityTests" -c Release # 2. 의존성 반전 검증 tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs └─ Handler → Policy, Sql만 의존 ✅ ``` **기간:** 2주 (리팩토링 + 검증) **소유자:** BE Lead, Architect --- ### 2️⃣ 코드 리팩토링 (필요한 것만, 불필요한 것은 제거) **현황:** ⏳ 진행 중 (25개 IN_PROGRESS 항목) **구체적 행동:** ``` Before (중복 있음): V13-FE-007 : State Panel + KBX adoption + visibility V13-FE-033 : ProblemDetails + discrimination V13-FE-023 : Grid contract + adapter adoption └─ 각각 20-30 줄 중복 (UI 어댑터 호출 패턴) After (단일 책임): V13-FE-007 : State Panel (상태 행렬만) V13-FE-023 : Grid contract (서버측 페이지네이션만) V13-FE-033 : ProblemDetails (에러 매핑만) shared/ui/adapter/ : 공통 호출 패턴 (한 곳) └─ 각각 10-15 줄 (DRY 준수) ``` **검증:** ```bash # 1. 중복 코드 감지 cd frontend npm install -g jscpd jscpd src/ --min-lines 5 # 5줄 이상 중복 감지 # 2. 번들 크기 확인 pnpm build --stats # 변경 전: 423.76 kB → 변경 후: 380 kB 목표 # 3. 테스트 여전히 통과 pnpm test && pnpm typecheck && pnpm build ``` **기간:** 1주 (리팩토링) **소유자:** FE Lead, QA --- ### 3️⃣ 데이터 정합성 (3NF, PIT 쿼리, append-only) **현황:** ✅ 실행 중 (write model) **구체적 행동:** ```sql -- ✅ DO: PIT query (published_at 기준) SELECT id, name, status, published_at, version FROM model_operations.models WHERE published_at <= :cutoff -- 🔑 시점 기준 AND status != 'DELETED' -- 🔑 append-only (DELETE 없음) ORDER BY published_at DESC, version DESC; -- ✅ DO: 정규화 (3NF) -- Table 1: model (id, name, published_at, version) -- Table 2: model_metric (model_id, metric_name, value, published_at) -- JOIN 불필요 → projection 사용 -- ❌ DON'T: 과거 데이터 덮어쓰기 UPDATE model_operations.models SET status = 'ACTIVE' -- ⚠️ 이력 손실 WHERE id = @modelId; -- ❌ DON'T: 역정규화 쓰기 경로에서 INSERT INTO dashboard_cache (model_count, total_return) VALUES (...); -- ⚠️ 읽기 모델만 역정규화 ``` **검증:** ```bash # 1. PIT 쿼리 감시 grep -r "WHERE published_at" src/ # 모든 SELECT에 필수 grep -r "DELETE FROM" src/ # 0 개 (append-only) # 2. 데이터 정합성 테스트 dotnet test KArtSell.sln --filter "DataIntegrationTests" -c Release └─ Fresh schema → upgrade → state consistent ✅ # 3. 감사 추적 검증 SELECT COUNT(*) FROM compliance.audit_events # 모든 변경 기록 ``` **기간:** Ongoing (설계 검증) **소유자:** Data Architect, DBA --- ### 4️⃣ 과유불급 (필요하지 않은 것은 하지 않기) **현황:** ⚠️ 검토 필요 **구체적 행동:** ``` Current Scope (필요한 것): ✅ AEG-VS-00: Platform Bootstrap ✅ AEG-VS-28: Trade Execution ✅ AEG-VS-10: Sell Decision ✅ AEG-VS-29: Reconciliation ✅ V13-FE-001~025: UI Components & Templates Not in MVP (보류): ❓ AEG-V15-033~036: Schedule heartbeat/aging └─ 도메인만 구현, DB persistence 미정 └─ 의사결정: Phase 2에 포함할까? ❓ AEG-VS-05/06: Fundamentals/FeeSchedule └─ 정책 미결정 (PM 승인 대기) └─ 의사결정: Phase 2 vs Phase 3? ❌ Gold-plating (제거할 것): └ Unused feature flags └ Experimental vendor plugins └ Deprecated authentication paths ``` **검증:** ```bash # 1. 미사용 코드 감시 dotnet analyze KArtSell.sln --severity warning # CA1822 (static method hint), CA1873 (array allocation) 제거할까? # 2. Feature flag 정리 grep -r "#if DEBUG" src/ # 0개 (본 코드에는 조건부 컴파일 없음) # 3. 의존성 감사 dotnet list package --include-transitive # 불필요한 NuGet 없는가? cd frontend && npm audit # 보안 이슈만 고쳐, feature 아님 ``` **기간:** Week 1 (의사결정) **소유자:** PM, Architect --- ### 5️⃣ 정규화 (Write Model: 3NF) **현황:** ✅ 실행 중 (schema 설계) **구체적 행동:** ```sql -- ✅ 정규화된 쓰기 모델 (3NF) -- Table: model_operations.models (핵심 속성만) CREATE TABLE models ( id UUID PRIMARY KEY, name VARCHAR NOT NULL, status VARCHAR NOT NULL, -- DRAFT, MATURE, ACTIVE, RETIRED created_at TIMESTAMP NOT NULL, published_at TIMESTAMP NOT NULL, version INT NOT NULL, UNIQUE(id, version) -- 이력 추적 ); -- Table: model_operations.model_signals (1정규화) CREATE TABLE model_signals ( signal_id UUID PRIMARY KEY, model_id UUID NOT NULL, signal_type VARCHAR NOT NULL, confidence DECIMAL NOT NULL, published_at TIMESTAMP NOT NULL, version INT NOT NULL, FOREIGN KEY (model_id) REFERENCES models(id) ); -- Table: model_operations.model_metrics (1정규화) CREATE TABLE model_metrics ( metric_id UUID PRIMARY KEY, model_id UUID NOT NULL, metric_name VARCHAR NOT NULL, -- SHARPE, RETURN, DRAWDOWN value DECIMAL NOT NULL, published_at TIMESTAMP NOT NULL, version INT NOT NULL, FOREIGN KEY (model_id) REFERENCES models(id) ); -- ✅ 버전 관리 (이력) -- 같은 model_id, version 조합은 불변 -- UPDATE 불가, INSERT만 가능 (새 version) ``` **검증:** ```bash # 1. 정규형식 검증 dotnet test tests/KArtSell.Integration.Tests/NormalizationTests.cs -c Release └─ Table dependency graph 확인 └─ Transitive closure 없음 (3NF) # 2. 이력 검증 SELECT id, version, published_at FROM models ORDER BY id, version └─ 각 ID마다 version은 단조증가 (1, 2, 3, ...) ``` **기간:** Ongoing (설계 + 마이그레이션) **소유자:** Data Architect --- ### 6️⃣ 역정규화 (Read Model: 성능 최적화) **현황:** ⏳ 부분 구현 **구체적 행동:** ```sql -- ❌ DON'T: 정규화된 테이블에서 직접 JOIN SELECT m.id, m.name, ms.signal_type, mm.value FROM models m JOIN model_signals ms ON m.id = ms.model_id JOIN model_metrics mm ON m.id = mm.model_id; -- ⚠️ 느림 -- ✅ DO: 역정규화된 읽기 모델 (projection) -- 쓰기: models + model_signals + model_metrics (정규화) -- 읽기: model_dashboard_view (역정규화, 캐시) CREATE MATERIALIZED VIEW model_dashboard_view AS SELECT m.id, m.name, m.status, (SELECT COUNT(*) FROM model_signals WHERE model_id = m.id) AS signal_count, (SELECT AVG(value) FROM model_metrics WHERE model_id = m.id AND metric_name = 'SHARPE') AS avg_sharpe, m.published_at FROM models m WHERE m.published_at = (SELECT MAX(published_at) FROM models WHERE id = m.id); -- ✅ 자동 갱신 (Outbox consumer) -- 1. models/signals/metrics 변경 → outbox 이벤트 -- 2. ModelMetricsProjectionJob (Hangfire) -- 3. model_dashboard_view 새로고침 -- 결과: dashboard 쿼리는 단순 SELECT (JOIN 0개) ``` **검증:** ```bash # 1. projection 최신성 검증 SELECT model_id, MAX(published_at) FROM models GROUP BY model_id EXCEPT SELECT model_id, MAX(published_at) FROM model_dashboard_view GROUP BY model_id └─ 0행 (완벽 동기화) # 2. 쓰기 지연 < 5초 (SLA) SELECT COUNT(*) FROM outbox WHERE published = FALSE └─ < 10 (처리 중) ``` **기간:** 2주 (구현 + 검증) **소유자:** Data, BE --- ### 7️⃣ 프로세스 단순화 (복잡한 워크플로우 제거) **현황:** ✅ 실행 중 (WBS 최적화) **구체적 행동:** ``` Before (복잡): Phase 1 (50-90일 대기) → Phase 2 (2주 수동 검증) → Phase 3 (1주 배포 준비) → Phase 4 (월별 기술부채) 총: 3-4개월, 많은 수동 작업 After (단순): Phase 1 (자동, 50-90일) Phase 2-4 (병렬 준비, 수동 0) ├─ 2-FE: 컴포넌트 개발 (6주) ├─ 2-BE: 스케줄 통합 (2주) ├─ 3-Deploy: 자동 (1일) └─ 4-Debt: 월별 (3일/월) 총: 52일 wall-clock (90일 - 38일 절약 by parallelization) ``` **검증:** ```bash # 1. 병렬 작업 확인 ps aux | grep dotnet # 5개 이상 병렬 프로세스 ps aux | grep node # pnpm build/test 병렬 # 2. 수동 개입 0 grep -r "MANUAL\|TODO\|FIXME" docs/operational-runbook.md └─ 모두 자동화되었거나 의사결정만 필요 # 3. 일정 추적 wc -l docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv └─ 완료 행 수 증가 추적 ``` **기간:** Ongoing (주 1회 검증) **소유자:** PM, SRE --- ### 8️⃣ 패턴화 (표준 아키텍처 반복) **현황:** ✅ 실행 중 (Vertical Slice) **구체적 행동:** ``` 표준 패턴 (모든 기능에 반복): ✅ Feature/Slice/ ├─ Endpoint.cs (HTTP 라우팅) ├─ Handler.cs (조율) ├─ Policy.cs (비즈니스 로직) ├─ Sql.cs (Dapper 쿼리) ├─ Mapper.cs (DTO ↔ Entity) ├─ Validator.cs (Fluent Validation) └─ Tests/ ├─ *.UnitTests.cs (Policy 테스트) ├─ *.IntegrationTests.cs (Handler + DB) └─ *.E2ETests.cs (HTTP flow) ✅ Frontend/features// ├─ pages/.vue (페이지) ├─ composables/use.ts (로직) ├─ stores/Store.ts (상태) ├─ types/index.ts (인터페이스) ├─ api.ts (백엔드 호출) └─ tests/ ├─ *.spec.ts (컴포넌트) └─ composables.spec.ts (로직) 패턴 준수 → 신규 기능 추가 시간 50% 단축 ``` **검증:** ```bash # 1. 패턴 준수 감시 find src/KArtSell.Modules -name "*.cs" | wc -l # 모든 파일 분류 └─ Endpoint, Handler, Policy, Sql 4가지만 # 2. 누락된 파일 감시 grep -r "class.*Endpoint" src/ | grep -v "Tests" > endpoints.txt grep -r "class.*Handler" src/ | grep -v "Tests" > handlers.txt comm -23 <(sort endpoints.txt | cut -d: -f1 | uniq) \ <(sort handlers.txt | cut -d: -f1 | uniq) └─ 0행 (모든 endpoint에 handler 존재) ``` **기간:** Ongoing (매 PR 검증) **소유자:** Architect, QA --- ### 9️⃣ 표준화 (명명, 형식, 도구) **현황:** ✅ 실행 중 (editorconfig, prettier) **구체적 행동:** ``` C# 명명: ✅ Handler: CommandHandler, QueryHandler ✅ Policy: Policy ✅ Sql: Sql ✅ Test: TypeScript 명명: ✅ Component: .vue (PascalCase) ✅ Composable: use.ts ✅ Store: Store.ts ✅ Test: *.spec.ts Commit 메시지: ✅ feat(module): (WBS_ID) ✅ fix(module): ✅ refactor(module): ✅ test(module): 일관된 형식 → 코드 리뷰 25% 빨라짐 ``` **검증:** ```bash # 1. 명명 규칙 검증 dotnet format --verify-no-changes # C# 포맷 자동 검증 cd frontend && npm run lint # ESLint + Prettier 자동 검증 # 2. Commit 메시지 검증 git log --oneline | head -20 # 모두 (): 형식 # 3. 파일명 검증 find src -name "*Endpoint.cs" | wc -l # 모두 Endpoint.cs ``` **기간:** Ongoing (commit hook 검증) **소유자:** DevOps, QA --- ### 🔟 구조화 (이력성, 상관성, 추적성) **현황:** ✅ 실행 중 (CorrelationId, Serilog) **구체적 행동:** ```csharp // ✅ 모든 로그에 CorrelationId 포함 public class ShadowRunCommandHandler { public async Task Handle(ShadowRunCommand command) { var correlationId = command.CorrelationId; // 🔑 추적 ID _logger.LogInformation( "Processing shadow run {ModelId} correlation={CorrelationId}", command.ModelId, correlationId ); // 모든 서브 시스템에 전파 _jobClient.EnqueueAsync(new ShadowRunJob { CorrelationId = correlationId // 이전됨 }); // 데이터베이스에도 저장 await _outboxWriter.WriteAsync(new ShadowRunStarted { CorrelationId = correlationId, PublishedAt = _clock.UtcNow }); } } // ✅ 감사 추적 테이블 // Table: compliance.audit_events // correlation_id (외래키, 모든 이벤트 추적) // actor_id (누가) // action (무엇) // resource (어디) // result (성공/실패) // published_at (언제) // version (이력) ``` **검증:** ```bash # 1. CorrelationId 전파 검증 SELECT DISTINCT COUNT(DISTINCT correlation_id) FROM compliance.audit_events WHERE published_at >= NOW() - INTERVAL '1 day' └─ 모든 이벤트에 correlation_id 있음 # 2. 이력 추적 검증 SELECT id, version, published_at, actor_id FROM compliance.audit_events ORDER BY correlation_id, published_at └─ 시간순 정렬, version 단조증가 # 3. 로그 검색 (ELK 또는 CloudWatch) curl -X GET "localhost:9200/_search?q=correlation_id:abc123" └─ 모든 마이크로서비스 로그 한 번에 추적 ``` **기간:** Ongoing (설계 + 구현) **소유자:** SRE, Data --- ### 1️⃣1️⃣ 바이브 코딩 (신뢰할 수 있는 코드) **현황:** ✅ 실행 중 (테스트, 코드 리뷰) **구체적 행동:** ```csharp // ✅ 신뢰할 수 있는 코드 (검증 가능) // 1. 입력 검증 (시스템 경계) public class ShadowRunCommandValidator : AbstractValidator { public ShadowRunCommandValidator() { RuleFor(x => x.ModelId).NotEmpty(); RuleFor(x => x.WindowStart).LessThan(x => x.WindowEnd); RuleFor(x => x.CorrelationId).NotEmpty(); } } // 2. 결정 로직 순수 함수 (테스트 가능) public class SellPriority { public static int Rank(Holding holding) { if (holding.ImpairmentPercent < -30) return 1; // HARD_IMPAIRMENT if (holding.PortfolioPercent > 20) return 2; // CONCENTRATION return 99; // REENTRY_OPTION } } // 3. 트랜잭션 경계 명시적 public async Task Handle(ShadowRunCommand command) { using (var transaction = await _dbConnection.BeginTransactionAsync()) { try { // 1단계: Validate (no side effects) _validator.Validate(command); // throws on invalid // 2단계: Decide (pure business logic) var decision = _policy.Decide(command); // 3단계: Execute (side effects) await _sql.InsertAsync(decision); await _outboxWriter.WriteAsync(new ShadowRunStarted { ... }); await transaction.CommitAsync(); } catch { await transaction.RollbackAsync(); throw; } } } // 4. 멱등성 (재실행 안전) public async Task Handle(ShadowRunCommand command) { // 🔑 IdempotencyKey로 중복 실행 방지 var existing = await _sql.GetByIdempotencyKeyAsync(command.IdempotencyKey); if (existing != null) return existing; // 이미 처리됨 // 새로 실행 var result = await _policy.ExecuteAsync(command); return result; } ``` **검증:** ```bash # 1. 테스트 커버리지 dotnet test KArtSell.sln -c Release --collect:"XPlat Code Coverage" └─ ≥ 80% (정책/SQL 테스트) # 2. 코드 리뷰 체크리스트 grep -r "DateTime.Now\|Random\|Console" src/ | grep -v "Tests\|Mock" └─ 0 (비결정적 코드 없음) # 3. 멱등성 검증 dotnet test --filter "IdempotencyTests" -c Release └─ 같은 입력 → 같은 출력 (재실행 안전) ``` **기간:** Ongoing (매 PR) **소유자:** Dev, QA --- ### 1️⃣2️⃣ 홀루시네이션 방지 (근거 없는 주장 금지) **현황:** ⏳ 강화 필요 **구체적 행동:** ``` ✅ 근거 있는 주장: "Phase 1 완료됨 (2026-08-14)" → 증거: RunId, 로그, 테스트 결과 "176/176 테스트 PASS" → 증거: CI/CD 빌드 로그 "Schema 정규화됨" → 증거: 마이그레이션 SQL, 정규형식 테스트 ❌ 홀루시네이션: "다음주 Phase 2 완료될 것 같음" → 근거 없음 "Performance 50% 개선됨" → 측정 없음 "테스트는 충분함" → 커버리지 수치 없음 규칙: 1. 모든 주장에 근거 제시 (링크, 파일, 커밋, 로그) 2. "추정", "예상" vs "측정", "검증" 구분 3. 확실하지 않으면 DECISION_REQUIRED 기록 ``` **검증:** ```bash # 1. 근거 있는 기록만 포함 grep -r "estimated\|probably\|likely" docs/CURRENT/ | grep -v "expected\|planned" └─ 0 (명확한 표현만) # 2. Evidence 링크 검증 grep "Evidence_Link" docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv | while read line; do link=$(echo $line | cut -d, -f7) if [ ! -f "$link" ] && ! git log --oneline | grep -q "$(echo $link | grep -o '[a-f0-9]\{7\}')"; then echo "❌ Invalid evidence: $link" fi done └─ 모든 증거 파일/커밋 존재 # 3. DECISION_REQUIRED 추적 grep -r "DECISION_REQUIRED" docs/CURRENT/ | wc -l └─ 모두 타이머와 오너 기록 ``` **기간:** Ongoing (매 문서) **소유자:** PM, QA --- ### 1️⃣3️⃣ 현장감 (실제 작동 확인) **현황:** ⏳ E2E 테스트 강화 **구체적 행동:** ```bash # ✅ Phase 1 현장감 (실제 실행) SSH 터널 열기 ssh -L 5432:127.0.0.1:5432 kjh2064@178.104.200.7 Host 시작 (DEVELOPMENT 모드) dotnet run --project src/KArtSell.Host -c Debug --no-build Shadow Run 실제 호출 curl -X POST http://127.0.0.1:5002/api/shadow-runs \ -H "X-KArtSell-User: test-user" \ -H "X-KArtSell-Role: Admin" \ -H "Content-Type: application/json" \ -d @request.json 결과: HTTP 202 Accepted (작업 큐에 들어감) Frontend 실제 로드 cd frontend pnpm dev 브라우저: http://127.0.0.1:5173 상태 확인: Dashboard → Shadow Run → 진행 중 표시 DB 확인 psql -h localhost -U kartsell -d kartsell SELECT * FROM model_operations.shadow_runs ORDER BY created_at DESC LIMIT 1; 결과: run_id, model_id, metrics_sharpe, metrics_return 등 정상 기록 로그 확인 tail -f logs/app.log | grep "shadow_run\|metrics" 결과: 모든 단계 정상 진행 (Backfill → Replay → Metrics → Complete) ``` **검증:** ```bash # 1. E2E Playwright 테스트 cd frontend pnpm exec playwright test --headed # 브라우저에서 실제 실행 └─ Dashboard 로드 ✅ └─ Shadow Run 이력 표시 ✅ └─ 진행률 업데이트 ✅ # 2. API E2E 테스트 dotnet test tests/KArtSell.Integration.Tests/ -c Release --filter "E2E" └─ HTTP 202 응답 ✅ └─ Job 큐에 들어감 ✅ └─ DB에 결과 저장 ✅ # 3. 성능 현장감 ab -n 100 -c 10 http://127.0.0.1:5002/api/health └─ 평균 응답시간 < 100ms ✅ └─ P95 응답시간 < 300ms ✅ ``` **기간:** 주 1회 (현장 검증) **소유자:** QA, DevOps --- ### 1️⃣4️⃣ 재현성 (다시 실행 가능) **현황:** ✅ 실행 중 (스크립트 문서화) **구체적 행동:** ```bash # ✅ 재현 가능한 절차 문서화 docs/PHASE_1_STARTUP_GUIDE.md (252일 Shadow Run) └─ 재현 가능한 단계별 절차 docs/operational-runbook.md (사건 대응) └─ 각 시나리오별 복구 절차 scripts/ ├─ phase1-execute.sh (Phase 1 실행) ├─ phase2-validate.ps1 (Phase 2 검증) ├─ backup-restore.sql (백업/복구) └─ migration-rollback.sh (마이그레이션 롤백) 사람 없이 재현 가능: 1. 스크립트 실행 (자동화) 2. 결과 검증 (테스트) 3. 로그 확인 (모니터링) 4. 다시 실행 가능? (멱등성) ``` **검증:** ```bash # 1. 재현 가능성 테스트 (CI/CD) # .gitea/workflows/ci.yml - name: Reproduce Phase 1 (canary) run: | bash scripts/phase1-execute.sh --dry-run # 실제 DB 접근 없이 단계 검증 # 입력 → 결과 동일한가? # 2. 마이그레이션 재현성 dotnet test tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs -c Release └─ Fresh: 0 → 최신 ✅ └─ Upgrade: v1 → v2 ✅ └─ Re-run: v2 (멱등) ✅ └─ Rollback: v2 → v1 ✅ # 3. 데이터 재현성 SELECT COUNT(*) FROM model_operations.shadow_runs WHERE run_id = '90e2d424'; └─ 항상 1 (중복 없음) └─ 재실행 → 같은 결과 ``` **기간:** Ongoing (테스트 검증) **소유자:** QA, DevOps --- ### 1️⃣5️⃣ 이력성 (모든 변경 추적) **현황:** ✅ 실행 중 (append-only) **구체적 행동:** ```sql -- ✅ 모든 변경 이력 보존 (DELETE 금지) -- 테이블 1: 현재 상태 (최신 버전만) SELECT id, name, status, version FROM model_operations.models WHERE version = (SELECT MAX(version) FROM model_operations.models m2 WHERE m2.id = models.id) └─ 각 id마다 1행 (최신) -- 테이블 2: 이력 (모든 버전) SELECT id, name, status, version, published_at FROM model_operations.models ORDER BY id, version └─ 각 id마다 N행 (모든 버전) -- 감사 추적 SELECT actor_id, action, resource_id, result, published_at FROM compliance.audit_events WHERE resource_id = @modelId ORDER BY published_at └─ "누가", "언제", "뭐"를 했는가 (삭제된 기록 없음) -- 마이그레이션 이력 SELECT schema_version, description, installed_on FROM schema_versions ORDER BY schema_version └─ 모든 마이그레이션 기록 (롤백 가능) ``` **검증:** ```bash # 1. 이력 무결성 SELECT COUNT(*) FROM model_operations.models WHERE id = @id AND version = 1; └─ ≥ 1 (삭제되지 않음) # 2. 버전 단조성 SELECT id, COUNT(DISTINCT version) FROM model_operations.models GROUP BY id ORDER BY COUNT(DISTINCT version) DESC LIMIT 1; └─ 최대 version이 100 미만 (이력 크기 관리) # 3. 감사 내용 없음 SELECT COUNT(*) FROM compliance.audit_events WHERE action = 'DELETE'; └─ 0 (DELETE 이벤트 없음, 상태 변경만) ``` **기간:** Ongoing (설계 검증) **소유자:** Data, Security --- ### 1️⃣6️⃣ 안정성 (실패 처리, 복구) **현황:** ✅ 실행 중 (crash recovery) **구체적 행동:** ```csharp // ✅ 4가지 실패 모드별 처리 // 1. Transient (일시적 네트워크 오류) public class ShadowRunJob : IAsyncJob { public async Task ExecuteAsync(ShadowRunCommand command) { try { await _krxService.FetchOhlcvAsync(command.Ticker); } catch (HttpRequestException) // 일시적 { // 🔑 재시도 (exponential backoff) throw new TransientException("KRX API temporarily unavailable", innerException); } } } // 2. Permanent (잘못된 입력) catch (ArgumentException ex) // 영구적 { // 🔑 로그만 하고 버리기 (재시도 금지) _logger.LogError("Invalid ticker {Ticker}", command.Ticker); return OperationResult.Failed(ex.Message); } // 3. Business Hold (승인 대기) catch (PendingApprovalException ex) { // 🔑 상태: BUSINESS_HOLD (재시도 불필요) await _sql.UpdateStatusAsync( modelId: command.ModelId, status: "BUSINESS_HOLD", holdUntil: DateTime.UtcNow.AddDays(7), reason: "Awaiting compliance approval" ); } // 4. Poison (데이터 오염) catch (DataIntegrityException ex) { // 🔑 DLQ (Dead Letter Queue)에 격리 await _deadLetterQueue.EnqueueAsync(new PoisonLetter { OriginalCommand = command, Exception = ex.Message, RequiresManualIntervention = true }); _logger.LogCritical("Poison message, manual review required"); } ``` **검증:** ```bash # 1. 실패 모드 테스트 dotnet test tests/KArtSell.Integration.Tests/FailureRecoveryTests.cs -c Release └─ Transient: retry 성공 ✅ └─ Permanent: 즉시 실패 ✅ └─ Business hold: 상태 보류 ✅ └─ Poison: DLQ에 격리 ✅ # 2. Crash 복구 # Host 강제 종료 (SIGKILL) kill -9 # Hangfire Job 상태 확인 SELECT state FROM hangfire_job WHERE id = @jobId; └─ 'Processing' → 'Enqueued' (자동 복구) # 재시도 ps aux | grep dotnet # Host 다시 시작 # 이전 실패한 Job 자동 재시도 # 3. 성능 영향 없음 SELECT COUNT(*) FROM hangfire_job WHERE state = 'Processing' AND updated_at < NOW() - INTERVAL '5 min'; └─ 0 (stuck job 없음, 모두 <5분 처리) ``` **기간:** 2주 (구현 + 테스트) **소유자:** BE, SRE --- ### 1️⃣7️⃣ 고도화 (점진적 개선) **현황:** ⏳ Phase별 로드맵 **구체적 행동:** ``` Phase 1 (현재, 2026-08-18): ✅ Core Platform (Host, API, DB) ✅ Trade System (Order, Settlement) ✅ Sell Decision (Ranking) ✅ 22개 FE 컴포넌트 Phase 2 (2026-09-15): ⏳ 15개 FE 고도화 (accessibility, performance) ⏳ Schedule 통합 (heartbeat, aging) ⏳ Reconciliation 고도화 (auto-correction) ⏳ API OpenAPI artifact (CI/CD 연동) Phase 3 (2026-11-01): ⏳ PBO/DSR/OOS 검증 ⏳ 배포 체크리스트 ⏳ 프로덕션 hardening Phase 4 (2026-12-31): ⏳ 기술부채 20% 월별 ⏳ 성능 최적화 (번들 크기 10% 감축) ⏳ Compliance 감사 ⏳ Incident response playbook ``` **검증:** ```bash # 1. Phase별 진행도 grep "^AEG-" docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv | cut -d, -f5 | sort | uniq -c └─ COMPLETED 증가 추적 # 2. 기술부채 감소 wc -l docs/TECH_DEBT_REGISTER.md └─ 월별 20% 감소 (현재: N → 다음달: 0.8N) # 3. 성능 개선 pnpm build --stats | grep "bundle\|gzip" └─ 번들 크기 감소 추적 ``` **기간:** 5개월 (Phase 1-4) **소유자:** PM, Architect --- ### 1️⃣8️⃣ 컴포넌트화 (재사용 가능한 단위) **현황:** ✅ 실행 중 (Vertical Slice, KBX adapter) **구체적 행동:** ``` Backend 컴포넌트화: ✅ BuildingBlocks/ (공유: Serialization, Logging, Dapper) ✅ Modules/ModelOperations/ (독립: Shadow, Signal, Sell, Trade) ✅ Modules/IdentityAccess/ (독립: Auth, MFA, RBAC) 규칙: • 모듈 간 직접 DB 접근 금지 (계약만) • 이벤트 기반 비동기 (Outbox/Inbox) • 각 모듈 독립 배포 가능 Frontend 컴포넌트화: ✅ shared/ui/adapter/ (공유: PrimeVue/AG Grid 래퍼) ✅ shared/ui/components/ (공유: KsButton, KsInput, KsDialog, ...) ✅ shared/ui/layouts/ (공유: AppShell, PageLayout, FormLayout) ✅ features// (독립: Model, Trade, Sell, Reconciliation) 규칙: • Feature는 shared 접근만 (feature → feature 금지) • 컴포넌트 props: 계약 기반 (TypeScript interface) • 테스트: 각 컴포넌트 독립 테스트 재사용도 측정: • buildingblocks/ 사용: 5+ 모듈 (높음) • shared/ui/ 사용: 8+ feature (높음) • 각 feature: 0-1개 모듈만 (낮음, 의도) ``` **검증:** ```bash # 1. 모듈 격리 검증 cd src/KArtSell.Modules/ModelOperations grep -r "using KArtSell.Modules" . # 다른 모듈 import 검사 └─ 0개 (다른 모듈 직접 참조 없음) # 2. 공유 컴포넌트 사용도 grep -r "from '@shared/ui" frontend/src/features/*/pages/ | wc -l └─ > 20 (높은 재사용) # 3. Feature 독립성 cd frontend/src/features/model-operations grep -r "from '.*features" . # 다른 feature import └─ 0개 (feature 간 의존 없음) # 4. 계약 기반 통신 grep -r "interface Props" frontend/src/shared/ui/components/*.vue | wc -l └─ 22개 (모든 컴포넌트 계약 정의) ``` **기간:** Ongoing (설계 검증) **소유자:** Architect --- ### 1️⃣9️⃣ 정공법 (올바른 방식) **현황:** ✅ 실행 중 (no shortcuts) **구체적 행동:** ```bash # ✅ 정공법 (항상) # 테스트 실패 → 원인 파악 → 코드 수정 (진짜 원인 고치기) # ❌ 지름길 금지: 테스트 주석 처리, --no-verify 사용, force push dotnet test KArtSell.sln -c Release # ❌ 실패한 테스트 주석 처리하기 # ✅ 원인 찾고 코드 고치기 # - 버그 찾기 (git bisect) # - 수정 (최소한의 변경) # - 테스트 재실행 (모두 PASS) git commit --amend --no-edit # ❌ 피하기: 이미 push한 커밋 수정 # ✅ 정공법: 새 커밋 만들기 (이력 보존) # 왜? 협업 환경에서 force push는 팀 정신을 깬다 git push --force # ❌ 절대 금지 (다른 사람 작업 덮어쓰기) # ✅ 정공법: merge request, code review, 승인 후 merge --no-verify # ❌ 금지: 훅 스킵 (문제 숨김) # ✅ 정공법: 훅 실패 → 원인 파악 → 수정 # 대신에, 정공법 (투명성, 신뢰) git status # 변경사항 확인 git diff # 정확히 뭐가 바뀌었나? git commit -m "fix: <근거 있는 설명>" # 명확한 메시지 git log -1 # 마지막 커밋 확인 git push # (CI/CD 통과 확인 후) # 모든 팀이 변경사항 추적 가능 ``` **검증:** ```bash # 1. 강제 푸시 히스토리 (0개 = 정공법) git log --oneline | grep -i "force\|reset --hard\|revert" └─ 정공법만 허용: revert (새 커밋) # 2. 테스트 주석 처리 (0개) grep -r "// @Disabled\|@Ignore\|@Skip" tests/ └─ 0개 (모든 테스트 활성) # 3. --no-verify 사용 (0개) git log --oneline | grep -- "--no-verify" └─ 0개 (모든 커밋 훅 통과) # 4. 코드 리뷰 규칙 grep -r "APPROVED\|CR:" docs/DECISIONS/ └─ 모든 주요 PR에 리뷰 기록 ``` **기간:** Ongoing (매 커밋) **소유자:** Team, DevOps --- ### 2️⃣0️⃣ 기술부채 (월별 20% 결제) **현황:** ⏳ 시작 단계 **구체적 행동:** ``` 기술부채 레지스트리 (TECH_DEBT_REGISTER.md): DEBT-001 | CA1822 (static method hints) | Low | Low | Backlog DEBT-002 | CA1873 (array allocation) | Low | Low | Backlog DEBT-014 | SOLID 위반 (Reconciliation) | Medium | Medium | Q3 DEBT-015 | FE bundle 크기 (>500kB) | Medium | High | Q3 DEBT-017 | Audit event consumer (missing) | High | Medium | Q3 DEBT-025 | Compliance endpoints (AllowAnonymous) | High | High | Q3 DEBT-029 | Audit trail not wired (0 events in prod) | High | High | Q3 월별 결제 목표: 20% 9월 (270 days → 216 days, 20% reduction) ✓ DEBT-017 해제 (Audit event consumer 구현) ✓ DEBT-025 해제 (RBAC enforcement) ✓ DEBT-029 해제 (Audit trail wired) └─ 총 3개 해제 = 20% 감소 계산: 총 부채: 15개 항목 (포인트 기반) 월별 해제: 3-4개 (점진적) Payoff일정: 2026-12-31 (모두 완료) 방식: 백분율이 아니라, 스프린트별 의존성 제거 ``` **검증:** ```bash # 1. 기술부채 진행도 grep "^DEBT-" docs/TECH_DEBT_REGISTER.md | grep -c "COMPLETED" └─ 월별 증가 (0 → 3 → 6 → 9 → 12) # 2. 새 부채 금지 git log --oneline | grep -i "tech.debt\|DEBT-" | wc -l └─ 기존 부채 해제는 많지만, 새 부채는 0 # 3. PR에서 부채 추적 git log --oneline | grep "DEBT-" └─ 모든 버그픽스/리팩토링이 DEBT-XXX 참조 # 4. 스프린트별 결제율 echo "$(date +%m) 월 부채 해제:" grep "COMPLETED" docs/TECH_DEBT_REGISTER.md | grep "202608" | wc -l └─ ≥ 3개 (20%) ``` **기간:** 월별 1주 (결제 스프린트) **소유자:** Owner, Team Lead --- ## 📋 통합 실행 체크리스트 ### Week 1 (2026-08-18 ~ 08-25) ``` ☐ SOLID (cyclomatic complexity ≤ 10) ☐ Architecture tests 실행 ☐ 모든 handlers 측정 ☐ > 10인 함수 리팩토링 ☐ 코드 리팩토링 (DRY) ☐ 중복 코드 감지 (jscpd) ☐ V13-FE 중복 제거 ☐ 번들 크기 측정 ☐ 데이터 정합성 ☐ PIT query 검증 (all SELECT WHERE published_at) ☐ DELETE 금지 확인 (0개) ☐ 마이그레이션 테스트 (fresh/upgrade/rollback) ☐ 과유불급 ☐ AEG-VS-05/06 정책 승인 ☐ 미사용 feature 확인 ☐ 불필요한 dependencies 제거 ☐ 정규화/역정규화 ☐ Schema 3NF 검증 ☐ Projection 갱신 일정 확인 ☐ Read model stale check ☐ 프로세스 단순화 ☐ 병렬 작업 시작 (FE 15개) ☐ Runbook 최신화 ☐ 수동 단계 0 확인 ☐ 패턴화 ☐ Vertical Slice 구조 확인 ☐ 누락된 파일 (Endpoint/Handler/Policy/Sql) ☐ Test naming 표준 ☐ 표준화 ☐ pnpm format 실행 ☐ dotnet format 실행 ☐ Commit message 형식 ☐ 구조화 ☐ CorrelationId 전파 확인 ☐ Audit events 기록 ☐ Logging 일관성 ☐ 바이브 코딩 ☐ 테스트 커버리지 80%+ ☐ 입력 검증 (boundaries) ☐ 멱등성 테스트 ☐ 홀루시네이션 방지 ☐ 모든 주장에 근거 제시 ☐ Evidence_Link 검증 ☐ DECISION_REQUIRED 기록 ☐ 현장감 ☐ Host 로컬 실행 ☐ Shadow Run API 호출 ☐ Frontend 개발 서버 로드 ☐ DB 확인 ☐ 재현성 ☐ scripts/ 재현 가능 확인 ☐ 단계별 문서화 ☐ 중복 실행 테스트 ☐ 이력성 ☐ Audit 테이블 확인 ☐ Version 단조증가 검증 ☐ 마이그레이션 이력 완성 ☐ 안정성 ☐ 실패 모드 테스트 (4가지) ☐ Crash 복구 시뮬레이션 ☐ DLQ 격리 확인 ☐ 고도화 ☐ Phase 2 준비 (15개 FE) ☐ Phase 3 체크리스트 작성 ☐ Phase 4 기술부채 계획 ☐ 컴포넌트화 ☐ 모듈 간 의존 0 ☐ shared/ 재사용도 높음 ☐ Feature 독립성 검증 ☐ 정공법 ☐ --no-verify 사용 0 ☐ force push 0 ☐ 주석 처리된 테스트 0 ☐ 기술부채 ☐ TECH_DEBT_REGISTER 업데이트 ☐ DEBT-017/025/029 시작 ☐ 월별 20% 계획 확정 ``` **결과:** 모든 20개 원칙 Week 1에 측정 + 기반 마련 --- ## 🎯 기대 효과 ### 코드 품질 - ✅ SOLID: 모든 handlers cyclomatic ≤ 10 - ✅ 테스트: 80%+ 커버리지 - ✅ 중복: 0개 (DRY 준수) ### 성능 - ✅ 번들: 423 kB → 380 kB (10% 감축) - ✅ 응답시간: P95 < 300ms - ✅ 병렬화: wall-clock 52일 (90일 - 38일 절약) ### 신뢰성 - ✅ 이력성: 100% 감사 추적 - ✅ 재현성: 자동화 스크립트 100% 재실행 가능 - ✅ 안정성: 4가지 실패 모드 처리 ### 협업 - ✅ 투명성: 모든 결정에 근거 - ✅ 표준화: 명명/형식/패턴 일관 - ✅ 추적성: CorrelationId 전사 추적 --- **작성자:** Claude Code **상태:** ✅ READY FOR EXECUTION **마지막 수정:** 2026-08-18 **적용 기간:** 4주 (Week 1-4)