복합세트 세션 상세 횟수 — 표시 수리 — 한 줄 표기 공용 함수와 세션 상세·하루 상세 적용 (2026-09-02)
- 기간: 2026-09-02 (세션 1개 — 오너 보고 "지금 복합세트한거 세션 상세정보에서 횟수가 - 로 뜨는데 한번 확인해줄래?" → 조사·이슈 #1151 분석 게시 → 오너 go "고")
- 랜딩: PR #1153(Phase 1·2,
7f593fbc) — 마이그레이션 0건, 엣지 0건, 앱 화면 코드만. Production 적용 = 다음main → production릴리스 PR([스테이징]) - 설계서: 없음(수리 건 — 분석·Phase 계획은 이슈 #1151 본문, "예상 효과·개선사항" 절 포함)
- 정본: 복합 세트 한 줄 표기·횟수 합 =
compositeSemantics.tsformatCompositeSetLine·compositeSetRepTotalOf·formatSetRunsWithComposite/ 세트 축약 =measureSemantics.tsformatSetRuns(exercise, sets, lineOf?) - 도구: Production 실측은 Management API 쿼리 러너(read_only) — 오너 9/2 "하이 클린 + 저크" 세션의 동작별 행 확인
- 게이트:
tests/react/compositeSemantics.test.mjs(+2) ·tests/react/compositeSessionDetailDisplay.test.mjs(신설 5) · 기존 축약 계약feedSetRunAbbrev.test.mjs무변경 통과 - 버그리포트:
bug-report/bug-062-20260902.md - 계약: 변경 없음(화면 props 불변 — 표기 함수만 추가)
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 1 | 복합 세트 한 줄 표기·횟수 합 공용 함수 + 모바일 세션 상세(세트 줄·종목 합계·세션 요약) 적용 | ✅ PR #1153 (9075d608) |
| Phase 2 | 일지 하루 상세 축약("무게 횟수x세트수")에 복합 표기 주입 — 50kg(3+1)x2 | ✅ PR #1153 (1bc6c999) |
| 랜딩 | PR 1개·CI 1회·머지 → [스테이징] | ✅ 7f593fbc |
1. 배경
복합세트(예: 하이 클린 + 저크)는 서버에 동작별 행 2개로 저장되고, 완료 세션을 읽을 때 앱이 한 종목으로 다시 합친다. 이때 합친 세트의 횟수 칸은 비워 두고 동작별 횟수를 배열(scheme, 이종 복합은 parts)에 담는 것이 오래된 읽기 모델 계약이다. 데스크톱 세션 상세와 운동 중 기록 화면은 이 배열을 읽는 복합 전용 표기를 갖고 있었다.
2. 문제 제기
모바일 세션 상세가 복합세트를 단일 종목처럼 그렸다
유저 A가 하이 클린 + 저크를 50kg에 3회 + 1회로 기록하면 저장은 정상인데, 모바일 세션 상세를 열면 세트 줄이 50kg × —로 보였다. SessionSheetBody.tsx가 복합 여부를 보지 않고 단일 종목 표기 함수만 불러, 비어 있는 횟수 칸을 대시로 찍은 것. 종목 합계 줄(총 볼륨·메인세트 반복)과 세션 요약(총 무게·랩)도 같은 횟수 칸을 더해 복합세트는 0으로 셌다. PC에서 같은 세션은 50kg × (3+1)로 정상.
일지 하루 상세 축약도 같은 경로였다
하루 상세 카드의 세트 축약(DayDetailBody.tsx → formatSetRuns)도 같은 표기 함수를 거쳐 50kg × —. 무게 없는 맨몸 복합은 "무게>0 또는 횟수>0" 판정에 걸려 줄에서 빠졌다. 피드 카드는 서버가 동작별 행을 따로 보내 증상이 없었다.
Production 실측
세션 a380c940-0b2d-4698-ad65-c454c63761b6(2026-09-02): 하이 클린 행 50kg 3회 · 80kg 3회 · 100~120kg 1회, 저크 행 같은 무게로 각 1회 — 데이터는 온전했다.
3. 해결 방안
원칙 (오너 지시, 2026-09-02)
- D1 "고" — 이슈 #1151의 분석·Phase 계획(세션 상세 → 하루 상세, 서버 무변경)대로 착수.
접근
- 읽기 모델 계약(횟수 칸 비움)은 손대지 않는다 — 수정 흐름·통계·테스트 다수가 그 계약 위에 있고, 합계 하나로는
(3+1)표기를 만들 수 없다. - 복합 세트 한 줄 표기를 공용 함수 하나로 두고, 모바일 상세 계열이 그 함수를 소비한다. 데스크톱과 같은
무게 × (3+1)표기를 따른다. - 세트 축약 함수에는 세트 줄 공급자(선택 인자)를 열어 복합 표기를 주입한다 — 단일 종목 결과는 불변(기존 축약 테스트 그대로).
4. 적용한 내용
Phase 1 — 공용 함수 + 세션 상세
compositeSemantics.tsformatCompositeSetLine(exercise, set): 무게 공유 컴플렉스50kg × (3+1)· 맨몸 복합10회 + 10회· 이종 복합(parts)10회 + 30초. 동작에 기록 항목 정보가 없으면(서버에서 읽어 온 완료 세션이 그렇다) 값이 들어 있는 항목만 정본 순서로 표기해 대시를 만들지 않는다. 복합이 아니면null.compositeSetRepTotalOf(exercise, set): 동작별 횟수 합(실패 세트는 읽기 모델이reps에 싣는 실제 수행 합).
SessionSheetBody.tsx: 세트 줄formatCompositeSetLine ?? formatSetMeasurements, 종목 합계·세션 요약의 횟수 읽기를ssReps한 곳으로 모아 복합은 동작별 합.- 주요 결정과 근거: 세션 요약(총 무게·랩)은 분석 단계에서 빠뜨렸던 항목인데 같은 원인이라 Phase 1에 포함했다.
- 작업 중 드러난 것: 이중 키 허용 게이트가
set_resultsnake 폴백 1곳을 잡아 제거(클라 읽기 모델은 camel 확정).
Phase 2 — 하루 상세 축약
measureSemantics.tsformatSetRuns(exercise, sets, lineOf?): 세트 줄 공급자 + 복합 구성(3+1)도 무게·구성이 같은 연속 세트를x세트수로 묶음.compositeSemantics.tsformatSetRunsWithComposite·DayDetailBody.tsx메인세트 판정·축약에 적용 →50kg(3+1)x2.- 작업 중 드러난 것: 축약 결과 타입(
SetRun.reps)에 null 가능성이 섞여 타입 체크가 한 번 실패 — 빈 문자열로 확정.
5. 적용 결과
| 항목 | 전 | 후 | 검증 |
|---|---|---|---|
| 모바일 세션 상세 세트 줄(무게 공유 복합) | 50kg × — | 50kg × (3+1) | 렌더 테스트(Production 실측 형 픽스처) |
| 종목 합계 줄(총 볼륨·메인세트 반복) | 0kg · 0회 | 360kg · 6회 (50×4 + 80×2) | 렌더 테스트 |
| 세션 요약(총 무게·랩) | 0kg · (0랩) | 360kg · (6랩) | 렌더 테스트 |
| 이종 복합(풀업 + 홀드) 세트 줄 | — | 10회 + 30초 | 렌더 테스트 |
| 일지 하루 상세 축약 | 50kg × — | 50kg(3+1)x2 | 렌더 테스트 |
| 단일 종목 표기·축약 | 종전 | 불변 | 기존 테스트 통과 |
| 로컬 게이트 | — | npm run check 단위 2,381건 pass 0 fail · check-typescript-unused 통과 | Phase마다 실행 |
미검증(사람 눈): 세트 줄 50kg × (3+1)의 칸 정렬과 이종 복합 한 덩어리 표기의 폭 — 자동 검증은 문자열까지. 실기기 확인은 요청하지 않는다(§14).
6. 이번 개선으로 향상된 것
- 복합세트를 기록하는 사용자가 모바일 세션 상세와 일지 하루 상세에서 동작별 횟수를 그대로 읽을 수 있다. 모바일·데스크톱 표기가 같아졌다.
- 복합 세트 한 줄 표기·횟수 합의 정본이 공용 모듈 한 곳에 생겨, 앞으로 복합을 보여줄 화면은 이 함수만 쓰면 된다(화면마다 분기를 따로 만들지 않는다).
남은 것
- 릴리스 PR 머지 → [반영완료] + 이슈 닫기.