리포트 육각형 단위 중량 → 세트수 — 부위 간 kg 격차 왜곡에서 세트 비중 지표까지 (2026-08-29)
- 기간: 2026-08-28 ~ 2026-08-29 (1세션, 오너 지시: "부위마다 들수있는 kg수가 다르니까, 중량보다는 세트수로 하는게 조금 더 의미있는 지표" — 이슈 #890)
- 랜딩: PR #893(Phase 1,
1e36295c) · #895(Phase 2,6aedc1dc) — 마이그레이션 20260828100000 Productiondb push완료, Vercel 배포 success 실측 - 설계서: 없음(단일 트랙 — 분석·Phase 계획은 이슈 #890 쓰레드)
- 정본:
report-props.md"6각 레이더 = 세트 수 전용" 절 ·get_volume_overview_annual_v3(schema.sql 20260828100000 절) ·compactReportTaxonomyProjection(barbelicMappers.ts) - 도구: Production 읽기 전용 프로브(Management API
/database/query, 레포 밖) - 게이트: pgTAP
report_taxonomy_target_muscles.test.sql18→22 · 어댑터 동치/상한 거부(screenRpcContracts) · SSR 무폴백 렌더(mobileAnnualReportRendering) - 버그리포트: 없음(기능 변경 — 별건 게이트 부패 2건은 4절)
- 계약: report-props.md 6각 레이더 절 개정(세트수 전용·중량 폴백 폐지)
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 1 | 서버 taxonomy_set_counts 벡터 + pgTAP | ✅ PR #893 (1e36295c) · Production 반영 |
| Phase 2 | 클라이언트 배선 + 중량 폴백 폐지 | ✅ PR #895 (6aedc1dc) · Vercel success |
| Phase 3 | 작업 기록 + 등록 2곳 | ✅ 이 문서 |
1. 배경
리포트의 육각형(레이더) 그래프 2개 — 부위별(MUSCLE BALANCE)·운동패턴별(MOVEMENT PATTERN) — 는 중량(kg) 비중으로 그려졌다. 하체는 팔보다 훨씬 무겁게 들 수 있어 kg 비중은 훈련 균형을 왜곡한다(실측: 한 계정의 연간 하체 140,534kg vs 어깨 19,926kg — 세트로는 302 vs 95.5). 오너가 세트수 단위 전환을 지시했다(이슈 #890).
2. 문제 제기
UI 절반은 준비돼 있었고, 데이터 절반이 비어 있었다
RpHexa(ReportScreen.tsx)는 v81 반입(#881, 08-27)에서 이미 세트수 우선 렌더로 바뀌어 있었다 — 행에 sets가 있으면 세트수로 그리고, 없으면 중량 share로 폴백. 그런데 Production 데이터 경로는 서버 정밀 벡터(taxonomy_volumes, kg 전용)만 공급했고, 클라이언트 매퍼가 이 경로에서 sets: 0을 하드코딩(mobileReportViewMappers fixedAxisVolumeRows) → 세트수 게이트가 발동하지 못하고 항상 kg 폴백으로 떨어졌다.
커버리지는 이미 완전했다 (Production 프로브)
전 종목 1,072/1,072이 target_muscles·movement_pattern을 보유, 연간 세트 16,498개가 100% 분류 종목에 귀속 — 세트 벡터만 추가하면 즉시 채워지고 백필이 필요 없었다.
3. 해결 방안
원칙 (오너 결정, 08-28)
- D1 — 부위 축 세트 귀속 = target_muscles 지분 가중, 표시는 반올림: "지분가중 적용하되, 결과는 반올림해서 보여줘. 소숫점 세트 없도록". 주동 부위 전량 귀속안은 기각(kg 벡터와 원리가 갈라짐).
- D2 — 스코프 = 모바일 리포트 육각형 2개만: 데스크톱 볼륨 화면 부위 막대는 kg 유지.
- 폴백 금지: "없으면 중량으로 폴백하지마. 세트수가 아니면 의미가 없어" — sets 없으면 빈 그리드.
접근
| 대안 | 판단 |
|---|---|
서버 정밀 세트 벡터 추가(채택) — kg 벡터와 같은 분류 원리로 taxonomy_set_counts 공급 | 상위 N 절단(__other__)과 무관한 전수 정밀치, 동치 검증 재사용 |
| 클라이언트 그룹핑 폴백으로 세트 합산 | 종목 행이 절단되고(11위 이하 기타 묶음) 정규식 근사 분류 — 정밀 벡터를 만든 이유 그대로 기각 |
4. 적용한 내용
Phase 1 — 서버 세트수 벡터 (#893, 20260828100000)
get_volume_overview_annual_v3전체 재선언(SCR-3): 기간별 payload에taxonomy_set_counts= [부위 6, 패턴 6] — 부위 =set_count × 지분/100(numeric 정밀값, 소수 허용), 패턴 = 전량 귀속. 두 축 모두 상대 축 NULL이면 0 → 부위 합 == 패턴 합 동치가 세트에서도 성립.- additive jsonb 키 — contract_version(3·4) 무변경, v4 래퍼·친구 리포트(
get_following_volume_overview_v1) 위임 경로 자동 전파. - pgTAP 18→22 +
EXPECTED_LATEST_MIGRATION동기화 + schema.sql append.
Phase 2 — 클라이언트 배선 + 폴백 폐지 (#895)
compactReportTaxonomyProjection:taxonomy_set_counts파싱 — kg 벡터와 같은 검증(2×6·비음수·동치·총 세트 이하) 통과 시에만 투영. snake 단일 키(dual-key 래칫 260 유지).screenRpcAdapters: 존재 시 같은 동치·상한 계약을 어댑터 층에서 단언.mobileReportViewMappers: 정밀 경로byPart/byFunc행에 세트 벡터 축별 반올림 공급(D1).RpHexa: 중량(share/kg) 폴백 폐지 — vals = sets 전용, sets 없으면 빈 그리드, 서브라인 항상 "N세트".- report-props.md 개정("byFunc sets 보강 필요" 부채 해소), reportFixture byFunc sets 보강.
주요 결정과 그 근거
- 서버는 정밀값, 반올림은 표시층: 서버에서 반올림하면 부위 합 == 패턴 합 동치가 깨져 클라이언트 검증이 벡터를 통째로 버린다.
- 랜딩 순서 강제(#893 → db push → #895): #895는 세트 벡터 없는 payload에서 육각형을 빈 그리드로 두는 게 의도된 동작 — 순서가 뒤집히면 기존 화면이 비어 보인다.
작업 중 드러난 것
- 풀 레인 전용 테스트의 조용한 부패 — v81(#881) 랜딩 잔재 2건: 풀 레인은
supabase/**변경에만 발화해서, ui 전용으로 랜딩한 v81이 깨뜨린 e2e들이 #893 풀 레인에서 처음 밟혔다. ① CASE-004 —daySummaryExercises훅 소멸(3항 재설계) 미반영, 첫 진입·재진입 2곳(1곳만 고치고 한 번 더 실패). ② dock-geometry 기하 게이트 — 탭바 68→55 기대값 미동조(채널 109/55·클리어런스 123/55/69/55로 갱신). 교훈: 같은 파일 안 다중 발생은 첫 수리 때 grep으로 전수 확인, ui 반입이 훅·토큰을 바꾸면 풀 레인 전용 소비자(e2e·기하 게이트)를 같은 턴에 검사. - 새 마이그레이션은
EXPECTED_LATEST_MIGRATION(deploymentManifest.ts) 동기화가 세트 — 첫 CI가 잡았다.
5. 적용 결과
| 항목 | 결과 |
|---|---|
| 육각형 단위 | 중량 kg → 세트수 (라벨 "N,NNN kg" → "N세트", % = 세트 비중) |
| Production payload | taxonomy_set_counts 부재 → 연간 실측 부위 [95.5, 101.3, 163, 210.2, 302, 90] · 패턴 합 962 동치 성립 |
| 폴백 | 중량 share 폴백 → 폐지(sets 없으면 빈 그리드) — 배포 청크 실측: 육각형 코드에 .share·+"kg" 0건, .sets 읽기 존재 |
| 게이트 | pgTAP 18→22 · 어댑터 거부 2계약 · SSR 무폴백 렌더 테스트 |
| CI | #893 풀 레인 green(4차 — 실패 3회는 전부 별건 게이트 부패) · #895 verify green |
| 미검증 | 오너 실기기 화면 확인 대기 — 세트 라벨·% 체감, 워밍업만 있는 날의 빈 그리드 |
6. 이번 개선으로 향상된 것
훈련 균형이 무게에 왜곡되지 않는 지표가 됐다
하체 kg가 압도하던 육각형이 세트 비중으로 그려져, 부위·패턴 간 훈련량 배분이 그대로 보인다(실측 하체 kg 7.1배 차 → 세트 3.2배 차).
구조적으로 남는 것
- 세트 벡터의 부위/패턴 합 동치 계약 — pgTAP·어댑터·매퍼 3층 검증.
- "육각형 = 세트수 전용, 폴백 없음" 단일 규칙(report-props.md 정본).
- 풀 레인 전용 게이트 2종(CASE-004·dock-geometry)이 현행 UI와 재동조.
남은 것
- 오너 실기기 확인(세트 라벨·빈 그리드 동작) → 이슈 #890 종결.
- 데스크톱 볼륨 화면 부위 막대는 kg 유지 — D2로 스코프 밖(필요 시 별도 트랙).