Skip to content

종목 부위·기능 분류 완결 — 미분류 267종목과 두-주동근 몰빵에서 부위 % 지분 집계까지 (2026-08-24)

  • 기간: 2026-08-24 (세션 1, 오너: "리포트에 부위별·기능별 훈련 비중이 있는데 종목에 이 필드가 없어서 집계가 잘 안되는 것 같다 — 종목마다 데이터 추가, 너가 하나씩 보면서 판단, 애매한 애들은 %로 나눠도 됨")
  • 랜딩: PR #643 (a52cdd0b — 스쿼시 제목의 "570000"은 재번호 전 표기, 실제 마이그레이션은 20260821580000, PR 코멘트로 정정) — 클라이언트 변경은 데스크톱 부위 렌즈 kg 반올림 1줄 + deploymentManifest 상수 동기(Vercel 자동 배포)
  • 설계서: 없음(단일 PR 트랙) — 판정표 artifact b661d26b-063f-4be5-b3e5-65c3960bc496(오너 검토용 전 행 표) · 요약 리포트 artifact 3a361485-2ffb-4b66-9e31-29e9f253d8c0(전→후 6각형 오버레이)
  • 정본: exercises.region_shares(정수 % 지분, 합 100)·exercise_region_shares_valid_v1·get_volume_overview_annual_v3(지분 가중), 판정 원칙은 마이그레이션 헤더 주석
  • 도구: scratchpad/gen_data_sections.py(판정표 → SQL 직렬화, slug 대조 검증, 레포 밖) · mgmt_query.py(Production 실측)
  • 게이트: supabase/tests/database/report_taxonomy_region_shares.test.sql(pgTAP 12) + 기존 exercises_region_shares_check 제약
  • 버그리포트: 없음(기능 결손 보수, 결함 아님)
  • 계약: 없음(페이로드 형태 불변 — taxonomy_volumes 2×6 벡터 그대로)

1. 배경

리포트(모바일·친구 리포트·데스크톱 볼륨)의 "부위별/기능별 훈련 비중"은 get_volume_overview_annual_v3가 기간마다 싣는 taxonomy_volumes 2×6 벡터가 원천이고, 분류는 exercises.primary_region(7부위)·movement_pattern(6기능) 단일 값이었다. 클라이언트 매퍼는 서버 벡터를 우선 쓰고, 두 벡터의 분류 합이 어긋나면 통째로 버린다(barbelicMappers.compactReportTaxonomyProjection). 오너가 "종목에 필드가 없어 집계가 잘 안되는 것 같다"고 보고했다.

2. 문제 제기

외부(WodUp) 164·유저 커스텀 103종목의 분류가 전부 NULL — 그 볼륨은 차트에서 조용히 사라진다

Production 실측(2026-08-24): origin별 NULL 수 external 164/164, user 103/103, system 0/784. 집계 함수는 두 축 모두 non-NULL인 종목만 세므로 미분류 볼륨은 어느 축에도 안 실린다(오너 연간 볼륨의 2%, E2E 계정 96%, 성근 체조 드릴 다수). create_custom_exercise도 분류 없이 행을 만들어 앞으로 계속 새는 구멍이었다.

두-주동근 종목의 부위 몰빵 — 전 유저 볼륨 1·2위가 왜곡의 최대 사례

평행봉 딥스 392,494kg(전 유저 연간 합) 전부 "팔", 컨벤셔널 데드리프트 304,921kg 전부 "등". 오너 개인으로도 최다 볼륨 종목이 딥스(74,409kg)라 부위 차트의 팔이 실제보다 크게 나왔다.

3. 해결 방안

원칙 (오너 지시, 2026-08-24)

D1 "종목마다 이 데이터 추가" — 전 종목 분류 완결. D2 "너가 하나씩 보면서 판단" — 자동 추론·정규식 분류 금지, 전수 수동 판정(중간 메시지로 재확인: "기계적으로 하지 말고 하나씩", "정확도가 중요"). D3 "애매한 애들은 %로 나눠도 됨" — 부위 지분 도입 승인.

접근

대안판정
미분류만 단일 값으로 채우기기각 — 딥스·데드 몰빵(문제 2)이 그대로 남고 D3를 안 씀
모든 종목을 근활성 %로 분해기각 — 숫자 근거가 흐려지고 판정이 자의적이 됨
두-주동근 종목만 지분 분할, 나머지 단일 유지채택 — 딥스=가슴/팔, 데드=하체/등처럼 단일 라벨이 실제로 오해를 만드는 곳만 나눔
기능(movement) 축도 지분 분할기각 — 혼합 동작은 "전신협응" 축이 원래 흡수, 분할 실익 없음

4. 적용한 내용

Phase 1 — 스키마·집계·파생 (#643, 580000)

  • exercises.region_shares jsonb + exercise_region_shares_valid_v1(정수 %·합 100·부위 키 2~7개) CHECK, 지분 존재 시 두 분류 축 필수(벡터 합 동치 불변식 보호).
  • get_volume_overview_annual_v3 전체 재선언: 부위 벡터를 volume × 지분/100 가중 합으로. numeric 무반올림이라 부위 합 == 기능 합이 정확히 유지 — 클라이언트 변경 0.
  • create_custom_exercise 전체 재선언: primary_part(가슴/등/어깨/팔/하체/코어)에서 region·pattern 파생, 매핑 불가 → full_body/full_body_coordination.

Phase 2 — 데이터 전수 판정 (#643 동일 마이그레이션)

  • 시스템 지분 132행(가족 24계열: 데드 4형·풀업/풀다운·친업·풀오버·딥스 3형·클로즈그립·머슬업·하이풀·역도 풀·업라이트/페이스풀·Y레이즈·백익스텐션·저크·푸쉬프레스·스내치 밸런스·레니게이드·제르처·인치웜 등), 우세 부위 변경 6행은 primary_region 동기(컨벤셔널 데드 계열 등→하체).
  • 단일 라벨 교정 5행: 덤벨 클린 앤 프레스→전신, 오스트레일리안 풀업→수평 당기기, 와이퍼→코어, 크랩워크→전신, 스캐풀라 푸쉬업→어깨(2차 정밀 패스에서 발견).
  • 외부/유저 실명 176행 slug 명시 분류, 잔여 91행은 전부 E2E 잔재(CASE-00x 등) 확인 후 전신 스윕.

주요 결정과 그 근거

  • 분할 문턱 = "서로 다른 부위의 주동근 둘 이상": 벤치(가슴)·로우(등)·스쿼트(하체)·스트릭트 프레스(어깨)는 단일 유지. 문턱이 없으면 전 종목이 분해되어 D2(판정 근거)가 무너진다.
  • 지분은 정수 %, 합 100 강제: 클라이언트가 두 벡터의 분류 합 동치를 검사해 어긋나면 벡터를 통째로 버리므로, 스키마 수준에서 동치가 깨질 수 없게 했다.
  • 판정 데이터는 slug 대조 스크립트로 직렬화: 판정은 손으로, SQL 변환만 기계로 — slug 오타가 조용한 no-op이 되는 것을 차단(시스템 행은 count=132 postcheck로 잠금).

작업 중 드러난 것

  • 마이그레이션 번호 하루 3연속 선점: 착수 시점 최신 540000 → 작업 중 550000(#637)·560000(#639)·570000(#640)이 병렬 세션에 순차 선점되어 580000까지 두 번 재번호. push 직전 fetch로 번호 재확인이 필수이고, 조립 스크립트에 번호를 변수로 두면 재번호가 1분 일이 된다.
  • 충돌 상태의 PR은 GitHub Actions가 아예 돌지 않는다(check-suite 자체 미생성, gh pr checks는 "no checks reported"): PR 열기 6초 전 #640이 main에 머지되며 schema.sql·번호가 충돌 → mergeable UNKNOWN → 워크플로 0건. close/reopen도 소용없고, 새 main 위 재빌드 + push(synchronize)로만 풀린다.
  • VALUES 행 뒤 -- 주석을 붙이고 ",\n".join으로 조인하면 콤마가 주석에 삼켜진다(42601). 콤마를 주석 앞에 붙일 것.
  • PowerShell git commit -m @'…'@는 본문 속 큰따옴표에서 인자가 쪼개진다 — 커밋 메시지는 파일로(-F).
  • Out-File -Encoding utf8(PS 5.1)은 BOM+CRLF을 심는다 — 게이트 있는 파일 치환은 python io로.

5. 적용 결과

항목전 → 후
분류 NULL 종목(외부/유저)267 → 0 (Production 적용 후)
부위 지분 보유 종목0 → 132 (시스템) + 외부 드릴 일부
딥스(평행봉) 부위 집계팔 100% → 가슴 50 / 팔 50
컨벤셔널 데드리프트 부위 집계등 100% → 하체 60 / 등 40
새 커스텀 종목 분류NULL(차트 누락) → part 파생(폴백 전신)
pgTAP지분 가중 벡터·동치 불변식·제약 거부 4종·파생 2종 = 12 tests PASS
node 게이트npm run check 전체 통과(1,828 tests) + PR CI 3 잡(scope·verify·migration-smoke) 전부 pass
Production 적용db push 580000 EXIT 0(마이그레이션 내부 postcheck 통과) · remote-schema EXIT 0 · 실측: 미분류 0, 시스템 지분 132행, 외부 지분 47행
오너 실제 리포트 벡터(2026 연간, RPC 실호출)부위 [어깨 18,271 / 팔 61,413 / 가슴 120,148 / 등 118,016 / 하체 136,879 / 코어·전신 13,637], 부위 합 == 기능 합 == 468,364.96 정확 일치(클라이언트 수용 조건) — 전: 팔 82,834·가슴 79,789·등 151,581·하체 117,264 + 미분류 4,320 누락
6각형 끝단 검증(오너 요청, 08-24 사후)오너 실제 Production v4 페이로드(574KB·195기간)를 실제 클라 경로(adaptScreenRpcPayloadbuildVolumePropsFromOverviewbuildMobileReportProjectionUiReportScreen 렌더)에 통과 — exact 벡터 경로 사용 확인, 연간 6각형 2개 SVG 폴리곤 반경 == 라벨 % == 지분, 12/12축 일치. 연간 % 전 → 후: 부위 어깨 4→4·팔 18→13·가슴 17→26·등 33→25·하체 25→29·코어/전신 3→3, 기능은 미분류 편입분(+4,320)만 미세 변동. 월간(8월)도 지분 반영, 주간(8/24 시작)은 볼륨 0 = 정상
오너 판정표 검토미완 — artifact 전달, 지적 행만 지분 UPDATE로 반영 예정

6. 이번 개선으로 향상된 것

리포트 부위별/기능별 차트가 전체 볼륨을 분류한다

인입·커스텀 종목 볼륨이 더는 조용히 빠지지 않고, 두-주동근 종목이 한 부위로 몰리지 않는다.

구조적으로 남는 것

  • region_shares 스키마 + 합 100·동치 불변식이 제약으로 고정 — 이후 지분 수정은 UPDATE 한 줄.
  • create_custom_exercise 분류 파생 — 미분류 재발 경로 차단.
  • 판정 원칙("두-주동근만 분할")이 마이그레이션 헤더와 판정표 문서에 명문화.

남은 것

  • 오너 판정표 검토 후 지분 보정(지적 행만 UPDATE).
  • WodUp 인입이 앞으로 만드는 새 external placeholder는 여전히 수동 분류 필요(인입은 관리자 주도·드묾 — 다음 인입 런북에서 스윕 규칙 재사용).
  • 데스크톱 볼륨 화면의 자체 계산 폴백 fade-out(기존 잔여 트랙)은 이 트랙 범위 밖.