# 세션 핸드오프 — 2026-08-14

> **다음 세션은 이 문서부터 읽을 것.** 오늘 한 일, 운영 현재 상태, 즉시 할 일, 미결이 여기 다 있다.

> 🔴 **다음 세션에서 구현한다.** 기존 워크플로우대로 — 지침서 → 서브에이전트 구현 → 리뷰 → 판정 반복.
> 구현이 끝나면 **§9 체크리스트로 자체 검증한 뒤 그 결과를 사용자와 논의**한다.
> 설계는 오늘로 끝났으니 새 설계를 벌이지 말고 §4 순서대로 실행할 것.

---

## 1. 지금 운영 상태

### 1.1 DDL — v2.8 + v2.9 적용 완료

**배포 중 장애가 났고 원인은 v2.8 DDL 미적용이었다.** 수수료 개편 코드는 배포됐는데 스키마가 v2.7이라 `p2p_matches` 엔티티 SELECT 가 통째로 깨졌다(Axim `IXRepository` 가 엔티티 필드로 컬럼 목록을 만든다).

```
scheduler   Unknown column 'fee_rate' in 'field list'   → 스케줄 작업 전량 실패
```

적용한 것 (전부 운영 반영 완료 · 검증 통과)

```
v2.8   p2p_matches         fee_rate · rebate_rate · withdraw_fee_rate
       p2p_deposit_orders  rebate_rate
       partners            p2p_parent_fee_rate · p2p_withdraw_parent_fee_rate · p2p_rebate_rate

v2.9   partners            root_partner_id · p2p_cross_group_matching · lp_provider
                           idx_root_partner
                           백필: root_partner_id = COALESCE(parent_partner_id, id)  — 53건, 검증 0건
       torq_trades         lp_provider (1,317건 전량 TORQ)
                           idx_escrow_id → uk_lp_escrow_id (lp_provider, escrow_id) UNIQUE
```

이미 들어가 있던 것 확인: `settlement_daily_fees.fee_role`, `withdrawals.uk_partner_idem`.

### 1.2 요율

```
최상위 36곳   p2p_parent_fee_rate = 0.2      → 입금자 부담 0.2%
하위          0 (P023744 데모4 만 0.2 — 컬럼 생성 직후 콘솔에서 저장된 것으로 보임)
전 파트너     p2p_withdraw_parent_fee_rate = 0 · p2p_rebate_rate = 0
```

> **P2P 유동성이 들어오기 전까지 요율 0은 무해하다.** TORQ·PARTNER 레그는 코드에서 수수료를 `ZERO` 로 박고(TORQ 는 견적 환율에 내장, PARTNER 는 파트너 자기 거래), **활성 출금 주문이 0건이라 P2P 레그가 생기지 않는다.** 최근 24시간 레그 36건이 전부 TORQ.
>
> ⚠️ 출금 주문이 하나라도 들어오면 그 시점 요율로 매칭되고 **소급 정정이 안 된다.** P2P 를 열기 전에 값을 확정할 것.

### 1.3 커밋 — 푸시 대기

```
cryptoments        ed7b03c   하위 파트너 P2P 요율 조회·수정 누락 수정
                   47365bc   설계 문서 6종 + DDL 드리프트 정정
                   0567c55   그룹 범위 + LP 라우팅 (v2.9)
cryptoments-admin  5d044f9   P2P 요율 입력 검증 + 파트너 콘솔 노출
                   ac4cdca   파트너 상세 그룹 범위 카드
```

**전부 로컬 커밋 상태. push 안 함.** 배포 잡은 `when: manual` ▶ 이다.

---

## 2. 오늘 고친 것

| | 내용 |
|---|---|
| **장애** | v2.8 DDL 미적용 → 컬럼 7개 추가로 해소. 오류 멎음 확인 |
| **admin-ui** | 파트너 등록 P2P 요율 3필드가 `type="number"` 라 Zod `Expected string, received number` 로 등록 불가 → `inputmode="decimal"` |
| **partner-api** | 하위 파트너 P2P 요율이 조회·수정에서 누락. 생성만 되고 상세에 안 보이고 수정 불가였다 |
| **partner-ui** | 하위 파트너 상세에 P2P 요율 표시·수정 블록 추가 |

`UpdateSubPartnerRequest.feeSettings` 에 `@Valid` 가 없어 중첩 제약이 무시될 구조였던 것도 같이 고쳤다(기존 두 필드엔 제약이 없어 회귀 없음).

---

## 3. 설계 문서 6종 — 전부 구현 대기

운영 DB 실측으로 P2P 전 구간 정합성을 점검하고 재설계안을 정리했다. **구현은 0.**

| 문서 | 핵심 |
|---|---|
| `P2P_ASYNC_MATCHING_DESIGN.md` | 매칭을 사용자 요청 경로에서 분리. **주문 상태가 곧 큐**(별도 큐 테이블 없음). 지금은 외부 TORQ 호출이 비관적 잠금을 쥔 채 트랜잭션 안에 있다 |
| `P2P_MATCHES_INTEGRITY.md` | 증거 무결성 결함 7종. **A~F 는 사후 추적 문제, G 만 자금이 실제로 어긋났다** |
| `P2P_DISPUTE_PROCESS_DESIGN.md` | 분쟁을 매칭 컬럼 12개에서 `dispute_events` 로. **TORQ 위탁 원칙** — 강제 청산이 만드는 무담보 크레딧 제거. 반려 시 구매자의 명시적 "고객센터 문의" → plumdesk |
| `P2P_SETTLEMENT_RESTRUCTURE.md` | `p2p_settlements` 제거. 오입금 금액 변동 규격. **사고는 거래의 종결** |
| `P2P_WITHDRAW_LIQUIDITY_DESIGN.md` | 출금 = 회원을 LP 로 만드는 장치. 누적 컬럼 3종 → append-only 원장 |
| `ONCHAIN_NOTARY_SERVICE_NOTE.md` | 내부거래 온체인 근거용 외부 서비스(A/B 마스터 순환, BSC). 준비 노트 |

### 3.1 관통하는 원칙

여섯 문서가 결국 **같은 결함의 여섯 얼굴**이다.

```
계산된 상태를 컬럼에 담아두고 여러 곳에서 갱신한다
  → 경합 · 두 번째 진실 · 되돌릴 수 없음
```

```
매칭   분쟁 12컬럼      → dispute_events        이력은 이벤트로
정산   p2p_settlements  → 제거                   역할이 이관됐으면 껍데기를 남기지 않는다
확인   매칭 컬럼 3개     → p2p_bank_confirmations 1급 개체여야 UNIQUE 가 성립
출금   누적 컬럼 3종     → p2p_withdraw_entries   append-only 가 경합을 없앤다
```

### 3.2 신설 예정 테이블 4개 (+ 공증 서비스)

```
p2p_disputes             분쟁 헤더 — 기한 감시 대상이라 인덱스 필요
p2p_incidents            사고 기록 — 거래의 종결
p2p_bank_confirmations   입금 확인 — UNIQUE(bank_account_id, bank_transfer_ref)
p2p_withdraw_entries     출금 원장 — CHARGE / LOCK / UNLOCK / RELEASE
```

**아직 `CRYPTOMENTS_V2_DDL.sql` 에 반영 안 됨.** 문서에만 있다.

---

## 3.5 매칭 시스템 — 문서 지도

**오늘 새로 쓴 6종만으로는 매칭을 못 건드린다.** 기존 지침서가 정본인 영역이 있고, 그룹 범위는 이번에 **구현·커밋까지 끝났는데** 위 표에 없다.

### 정본 (먼저 읽을 것)

| 문서 | 무엇 | 상태 |
|---|---|---|
| `P2P_MATCHING_ARCHITECTURE.md` | 크로스사이트 출금↔입금 자동 매칭 전체 (v0.6) | 기존 |
| `P2P_TORQ_UNIFIED_MATCHING_DESIGN.md` | **통합 매칭** — P2P + TORQ + 파트너 폭포 구조 (v0.7) | 기존 · 현행 |
| `P2P_MATCHING_RULES_ASIS.md` | 코드 기준 현행 룰 정리 (2026-06-15) | 기존 |
| `P2P_MATCHING_GROUP_SCOPE_GUIDE.md` | **그룹 범위 + LP 라우팅 (v2.9)** | **이번에 구현·배포 대기** |
| `P2P_MANUAL_CONFIRM_DISPUTE_RULES.md` | **타임 규칙 확정 스펙** — 30분/10분/6h. Phase 1 = 자동 판정 없음 | 기존 · **정본** |
| `P2P_ASYNC_MATCHING_DESIGN.md` | 매칭 워커 분리 · 주문이 큐 | 오늘 · 설계 |

> ⚠️ 기한을 다시 설계하지 말 것. `P2P_MANUAL_CONFIRM_DISPUTE_RULES.md` §1 이 정본이다.
> 오늘 이것을 못 찾고 "기한이 없다"고 결함으로 잡았다가 정정했다.

### 현행 매칭 구조 (한눈에)

```
입금 주문 생성 → 같은 트랜잭션에서 즉시 매칭 (동기)

  ① P2P 레그      최대 2 (p2p.max_p2p_legs)   그룹 범위 적용 · 출금자 USDT 잠금
  ② 잔여 슬롯 1    TORQ → PARTNER 순서로 폴백
                   TORQ    외부 createTrade (잠금 보유 중 HTTP)
                   PARTNER 파트너 서비스계좌 · 잠금 없음 · 최종 안전망

잔여 미충족 → failUnifiedOrder → 만든 레그를 CANCELLED 로 되돌림
```

**레그별 성격이 다르다는 것이 이 시스템의 핵심**이다. 상대방 링크·자금 잠금·판정 주체·수수료가 전부 갈린다.

```
          상대방 링크              잠금   판정      수수료
P2P       withdraw_order_id        있음   우리      요율 적용
TORQ      torq_escrow_id ↔ 역링크  없음   TORQ      0 (견적 환율 내장)
PARTNER   없음 (읽을 때 재해석)     없음   우리      0 (파트너 자기 거래)
```

### 관련 (필요 시)

```
P2P_LOCK_HARDENING_AND_SELLER_GATE_GUIDE.md   잠금 강화 R1~R4 + 셀러 게이트
P2P_MATCHING_IMPROVEMENTS_GUIDE.md            네트워크 무관 매칭 · 재매칭 · 만료
P2P_REMAINDER_RESOLUTION_GUIDE.md             출금 잔여 처리 이력
P2P_DUST_MATCHING_FIX_GUIDE.md                더스트 방지
P2P_PARTNER_LEG_NO_STANDING_REFACTOR.md       파트너 레그 가짜 출금주문 제거
```

---

## 3.6 TORQ 연동 — 이미 요청이 나가 있다

오늘 "TORQ 페이로드 보강 3건은 별건"이라고 적었으나, **이미 요청과 규격이 존재한다.**

```
TORQ_HANDOFF_2026_08_14.md            연동 개선 요청 5건 (08-14 01:59)
TORQ_HANDOFF_REPLY_2026_08_14.md      회신 (13:36)
TORQ_AMOUNT_CORRECTION_INTERFACE.md   금액 정정 인터페이스 규격 — TORQ 확인 대기 (13:36)
```

요청 항목이 오늘 논의와 정확히 겹친다.

```
1  거래 금액 정정 — 제자리(in-place) 방식        신규 개발   ← 오입금 증액 보정의 전제
2  분쟁 제기 웹훅에 사유 싣기                     페이로드
3  분쟁 해소 사유 싣기                            페이로드
A  X-Internal-Token 발급                         운영
B  웹훅 서명 시크릿 합의                          운영 · 보안
```

**`p2p_settlements` 제거·오입금 설계는 1번 진행 상황과 물린다.** 착수 전에 회신 문서를 확인할 것.


---

## 4. 다음 세션 즉시 할 일

### 4.1 배포 마무리

```
① git push (양쪽 repo)
② spring:deploy-production ▶ · partner-ui ▶ · admin-ui ▶
③ 검증 — P2P 매칭 정상(기본값이 전량 크로스 허용이라 결과가 배포 전과 같아야 함)
        파트너 콘솔 P2P 풀 현황이 매칭기와 같은 결과
        TORQ 웹훅 수신 ((lp_provider, escrow_id) 조회로 바뀜)
```

### 4.2 DDL 파일에 신설 테이블 반영

문서에 흩어진 스키마를 `CRYPTOMENTS_V2_DDL.sql` 로 모은다. **그래야 구현 지시서의 소스 오브 트루스가 하나가 된다.**

### 4.3 구현 순서표

문서 여섯이 같은 테이블을 건드린다. **순서를 틀리면 데이터가 사라진다.**

```
분쟁   이벤트 적재 복구 → 과거 42건 백필 → 매칭 컬럼 제거
       (먼저 지우면 분쟁 이력이 통째로 사라진다 — dispute_events 는 현재 3행뿐)

정산   원장 참조 통일(95행) → reference_type 개명 → 테이블 제거
       (먼저 지우면 변환 근거가 사라진다)

확인   p2p_matches.bank_account_id 신설 → 확인 테이블 → UNIQUE
```

---

## 5. 착수 전 정해야 할 것

| 항목 | 내용 |
|---|---|
| **위젯 대기 UX** | 비동기 매칭 전환 시 짧은 동기 대기(2초) 후 폴링 vs 전면 비동기 |
| **사고 단위** | 매칭 단위 기록 + `order_code` 병기로 제안했으나 확정 필요 |
| **P2P 상한** | 현재 가드가 전역 `p2p.max_fee_rate`(3%) 뿐. `p2pParentFeeRate` 는 하위가 상위에게 올리는 몫이라 총판은 올릴 유인만 있는데 `maxFeeCap` 같은 파트너 단위 제한이 없다 |
| **하위 방향 검증** | 중간 노드 요율을 올리면 **모든 자손의 실효 부담률이 오르는데** `validateP2pRates` 는 상향 체인만 본다. 자손이 조용히 상한 초과 가능 (어드민 경로도 동일 갭) |

---

## 6. 미결 (구현을 막지는 않음)

```
Phase 2 활성화        6h 분쟁 기한 자동 처리를 언제 켤지 (현재 Phase 1 = 전건 관리자 수동)
증빙 보관             app-01 로컬 디스크(/opt/cryptoments/uploads/evidence) 유지 여부·백업
relock_failed 거취    분쟁 표식이나 내용은 원장 성격. 0건이라 판단할 실물 없음
partner_reference     UNIQUE(partner_id, partner_reference) + 종결 주문 키 재사용 정책
잔액 캐시             출금 원장 SUM 이 후보 조회 병목이 되면 검토 (지금 물량에선 SUM 안전)
자동 만료             출금 주문 자동 만료 없음(2026-06-11 정책). 유동성 관점 재검토할지
과거 5건 실지급 검증   출금 원장 재구성 시 드러나는 차이가 데이터 오류인지 실제 이중지급인지
데모4 요율            P023744 하위인데 0.2 — 의도인지 테스트 중 저장인지
```

**범위 밖으로 뺀 것**

```
위젯 오입금 방지 장치
구매자 신원 정본 부재   105명이 1,316번 주문, 이름·전화·계좌가 주문마다 전량 복사
계좌번호 평문 저장
findByTxHash 다건화     LP 배치 전송 대비 (tx_hash 는 의도적으로 NON-unique)
TORQ 페이로드 보강      분쟁 사유 · 완료 해소 사유 · 가격정정 인터페이스 — TORQ 쪽 작업
```

---

## 7. 함정 — 다음 세션이 밟기 쉬운 것

**DDL 을 코드보다 먼저 넣어라.** 오늘 장애가 그것이다. Axim `IXRepository` 는 엔티티 필드로 SELECT 컬럼을 만들기 때문에, 컬럼 하나가 없으면 **그 테이블을 읽는 모든 쿼리가 실패**한다.

**`type="number"` + Zod `z.string()` 조합.** Vue `v-model` 이 숫자로 변환해 `Expected string, received number` 가 난다. 기존 필드가 어떻게 하는지 먼저 보고 맞출 것.

**MyBatis `<script>` 내 `<`·`<=`·`<>` 금지.** 컴파일은 통과하고 기동 시점에 SAXParseException 으로 전 서비스가 죽는다(2026-06-11 실장애).

**`LIMIT` 이 Java 필터보다 먼저 적용된다.** 후보 조회에 조건을 Java 후처리로 넣으면 상위 N건이 전부 걸러져 기아 상태가 된다. 조건은 SQL 에.

**푸시 ≠ 배포.** 모든 배포 잡이 `when: manual` ▶ 이고 `allow_failure:true` 라 파이프라인이 green 이어도 배포가 안 됐을 수 있다.

**실측 표본을 좁게 잡지 말 것.** 오늘 최상위 5곳만 보고 "요율 대부분 0"이라 단정했다가 정정했다(실제로는 0.2가 14곳으로 최다). 분포를 먼저 볼 것.

---

## 8. 참고 — 오늘 확인한 주요 실측

```
분쟁 42건        TORQ 37 · P2P 4 · PARTNER 1
                 사유 있는 것 8건 · 증빙 3건 (TORQ 웹훅이 사유를 안 보냄)
                 dispute_events 3행 / 40건 미기록

정산 45건        INNER 44 · ONCHAIN 1 · 재시도 0
                 TORQ 레그 1,247건은 정산 행 없음
                 원장 P2P_SETTLEMENT 1,258행 → 정산 참조 95 · 매칭 참조 1,163

출금 주문 35건   COMPLETED 34 · CANCELLED 1 · 활성 0
                 파트너 3곳 · 회원 17명

계좌 참조        p2p_withdraw_orders 33건 중 dangling 9건
                 정산 완료 P2P 레그 11건이 삭제된 계좌를 가리킴

증빙 파일        7개 중 매칭에 연결된 것 3개 (4개 고아)
```

---

## 9. 검증 체크리스트

**구현을 마친 뒤 이 순서로 자체 검증하고, 결과를 사용자와 논의한다.**

각 항목은 근거(파일:라인 또는 실측 쿼리 결과)를 붙여 판정한다. **애매하면 합격시키지 말고 질문으로 남길 것.** 오늘 하루에만 내가 세 번 틀렸다 — PARTNER 판정 주체, 기한 부재, 요율 분포. 전부 확인 없이 단정한 것이었다.

### 9.1 양방향 DDL 정합성

```
DDL 에 있는데 코드에 없는 것
코드에 있는데 DDL 에 없는 것 (팬텀 컬럼) ← 이쪽을 집요하게
```

**신설 테이블 4개가 `CRYPTOMENTS_V2_DDL.sql` 에 반영됐는지 먼저 본다.** 문서에만 있으면 소스 오브 트루스가 둘이 된다.

```sql
-- 운영 스키마 vs 엔티티 대조 (테이블별)
SELECT COLUMN_NAME FROM information_schema.COLUMNS
 WHERE TABLE_SCHEMA='cryptoments_db' AND TABLE_NAME='<대상>' ORDER BY ORDINAL_POSITION;
```

### 9.2 오늘 잡은 함정의 재발

| 확인 | 방법 |
|---|---|
| MyBatis `<script>` 내 `<`·`<=`·`<>` | `git diff` 에서 `@Select` 블록 전수. **하나라도 있으면 즉시 반려** (기동 시점 전 서비스 다운) |
| `type="number"` + Zod `z.string()` | `grep 'type="number"' <화면>.vue` 와 스키마 대조 |
| `LIMIT` 전에 Java 필터 | 후보 조회 조건이 SQL 안에 있는지. Java 후처리면 기아 상태 |
| `ORDER BY`/`LIMIT`/`COUNT` 직접 작성 | `XResultInterceptor` 가 자동 처리 — 직접 쓰면 페이지네이션 깨짐 |
| DDL 선행 | 배포 잡 ▶ 전에 스키마 적용 + 검증 완료됐는지 |

### 9.3 마이그레이션 순서 준수 — **되돌릴 수 없는 것들**

```
분쟁   ① dispute_events 적재 복구  ② 과거 42건 백필  ③ 매칭 컬럼 제거
       ③ 을 먼저 하면 분쟁 이력이 통째로 사라진다 (현재 dispute_events 3행)

정산   ① 원장 참조 통일(95행)  ② reference_type 개명  ③ p2p_settlements 제거
       ③ 을 먼저 하면 변환 근거가 사라진다

출금   원장 재구성 시 ④ RELEASE 단계에서 과거 오류가 노출된다.
       음수 잔액이 나오는 건은 개별 검증 후 사고 기록으로
```

검증 쿼리

```sql
-- 원장 참조가 전량 매칭으로 해소되는가 (0 이어야 함)
SELECT COUNT(*) FROM ledger_entries le WHERE le.reference_type='P2P_MATCH'
   AND NOT EXISTS (SELECT 1 FROM p2p_matches m WHERE m.id = le.reference_id);

-- 출금 원장 잔액이 음수인 주문 (개별 검증 대상)
SELECT withdraw_order_id, SUM(amount_krw) bal FROM p2p_withdraw_entries
 GROUP BY 1 HAVING bal < 0;

-- 종결 주문의 잔액은 0
SELECT o.id, SUM(e.amount_krw) bal FROM p2p_withdraw_orders o
  JOIN p2p_withdraw_entries e ON e.withdraw_order_id=o.id
 WHERE o.status IN ('COMPLETED','CANCELLED','EXPIRED') GROUP BY o.id HAVING bal != 0;

-- 입금 확인 중복 (UNIQUE 적용 후 0 이어야 함)
SELECT bank_account_id, bank_transfer_ref, COUNT(*) c
  FROM p2p_bank_confirmations GROUP BY 1,2 HAVING c > 1;
```

### 9.4 회귀 — 기본값에서 동작 불변

```
p2p_cross_group_matching 전량 TRUE  → 매칭 결과가 변경 전과 동일해야
lp_provider 전량 NULL               → TORQ 기본 · 자격증명 불변
비동기 매칭 스위치 false            → 배포만으로는 아무것도 안 바뀌어야
```

### 9.5 게이트 3곳 일치

`findAvailableWithdrawOrders`(라우팅) · `findMatchableWithdrawOrdersForUpdate`(매칭) · `PartnerP2pPoolMapper`(화면)가 **같은 결과 집합**을 내는지. 하나라도 어긋나면 라우터가 P2P 를 고르고 매칭이 실패하거나, 화면 수치와 실제가 다르다.

### 9.6 빌드

```bash
./gradlew :common:compileJava :core:compileJava :admin-api:compileJava \
          :partner-api:compileJava :open-api:compileJava :scheduler:compileJava
cd ../cryptoments-admin/admin-ui   && npm run build
cd ../cryptoments-admin/partner-ui && npm run build
```

> 샌드박스는 Java 11 이라 컴파일이 안 된다. **Desktop Commander 로 로컬 Mac 에서** 돌릴 것.

### 9.7 판정

**합격 / 조건부 합격 / 반려** 중 하나로 명확히. 문제는 심각도별로.

```
P0   서비스 다운 · 자금 오차 · 조용한 오동작
P1   배포 후 곧 문제
P2   개선 제안
```
