bc93b7df3c
## 요약 - STRATEGIC_ROADMAP_2026_08_18.md: 4주 병렬화 계획 - EXECUTION_STRATEGY_19PRINCIPLES.md: 19가지 원칙 실행 전략 - WEEK1_EXECUTION_CHECKLIST.md: Day-by-day 실행 체크리스트 ## 내용 - 현황 분석: COMPLETED 40+, IN_PROGRESS 25, BLOCKED 9 - 목표: 의존성 제거 3개, FE 병렬 8개 시작, 현장감 검증 - 일정: 2026-08-18 ~ 08-25 (8일) ## 19가지 원칙 매핑 ✅ SOLID, 정규화, 패턴화, 표준화, 구조화, 재현성, 정공법 ⏳ 코드리팩토링, 데이터정합, 과유불급, 역정규화, 프로세스 ⏳ 바이브코딩, 홀루시네이션방지, 현장감, 이력성, 안정성, 고도화, 컴포넌트화, 기술부채 Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1299 lines
35 KiB
Markdown
1299 lines
35 KiB
Markdown
# 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<ShadowRunCommand>
|
|
{
|
|
// 책임: 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/<feature>/
|
|
├─ pages/<Screen>.vue (페이지)
|
|
├─ composables/use<Feature>.ts (로직)
|
|
├─ stores/<feature>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: <Feature>CommandHandler, <Feature>QueryHandler
|
|
✅ Policy: <Feature>Policy
|
|
✅ Sql: <Feature>Sql
|
|
✅ Test: <Feature><Method><Scenario>
|
|
|
|
TypeScript 명명:
|
|
✅ Component: <Feature>.vue (PascalCase)
|
|
✅ Composable: use<Feature>.ts
|
|
✅ Store: <feature>Store.ts
|
|
✅ Test: *.spec.ts
|
|
|
|
Commit 메시지:
|
|
✅ feat(module): <description> (WBS_ID)
|
|
✅ fix(module): <description>
|
|
✅ refactor(module): <description>
|
|
✅ test(module): <description>
|
|
|
|
일관된 형식 → 코드 리뷰 25% 빨라짐
|
|
```
|
|
|
|
**검증:**
|
|
```bash
|
|
# 1. 명명 규칙 검증
|
|
dotnet format --verify-no-changes # C# 포맷 자동 검증
|
|
cd frontend && npm run lint # ESLint + Prettier 자동 검증
|
|
|
|
# 2. Commit 메시지 검증
|
|
git log --oneline | head -20 # 모두 <type>(<scope>): 형식
|
|
|
|
# 3. 파일명 검증
|
|
find src -name "*Endpoint.cs" | wc -l # 모두 <Feature>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<ShadowRunCommand>
|
|
{
|
|
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<ShadowRunCommand>
|
|
{
|
|
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 <pid>
|
|
|
|
# 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/<feature>/ (독립: 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)
|