Skip to content

그룹 보드 % 세트 프리필 — 작성자 kg가 그대로 채워지던 것에서 시작하는 사람의 1RM 기준 환산·반올림 설정 공유까지 (2026-09-03)

  • 기간: 2026-09-02 ~ 2026-09-03 (1세션, 오너 보고 "그룹에서 %로 입력된 세트를 공유했는데, 같이한 친구가 자기 1rm의 %로 나오는게 아니라, 내 기록이 kg으로 표시되는 문제가 있었어")
  • 랜딩: PR #1163(Phase 1~4, 1cd74c77) — 마이그레이션 20260904010100(보드 검증 함수 pct_round_kg), Vercel staging 자동 배포 · Production 적용 = 릴리스 PR 대기
  • 설계서: 이슈 #1150 본문(분석 + Phase 계획 + 예상 효과·개선사항) + 코멘트(오너 결정 D1~D3 · Phase 개정)
  • 정본: groupBoardPrefill.ts(프리필 % 환산 규칙) · WorkoutRecord round5 = active.pctRoundKg === 5(반올림 = 종목 설정값) · group_validated_board_exercises_v1(20260904010100pct_round_kg) · group-props.md §4·§5
  • 도구: 없음(Production 실측은 Management API 읽기 쿼리)
  • 게이트: groupBoardPercentPrefill.test.mjs(10) · pgTAP group_board_pct_round_kg.test.sql(8) · 브라우저 e2e CASE-019(개정 — 별도 사용자 보드로 회원 간 차이 단언)
  • 버그리포트: bug-report/bug-065-20260902.md
  • 계약: group-props.md §4(보드 pctRoundKg·프리필 환산 규칙) · §5(컴포저 pctRoundKg 동반)

Phase 현황

Phase내용상태
Phase 1프리필 재환산 — %·기준 종목 동반, kg는 시작자 1RM(실측→추정), 기준 없으면 % 유지·kg 빈 값(D1 a)✅ PR #1163 (1cd74c77)
Phase 25kg 반올림 설정 공유(D2) — 종목 값 pctRoundKg ↔ 서버 pct_round_kg(마이그레이션 1건)✅ PR #1163
Phase 3기준 1RM 없음 즉시 안내(D3) — 종목 설정 창 문구✅ PR #1163
Phase 4e2e CASE-019 개정 — 코치 보드로 회원 시작 → 내 1RM 기준 kg✅ PR #1163
Phase 5버그리포트·작업 기록·계약 문서✅ 이 문서

1. 배경

그룹 화이트보드에는 8/26 #837 D3부터 %로 입력한 세트가 %로 저장·표시된다(load_pct·pct_base). 그때 "실제 운동 진행 때만 kg"를 집행하면서 세션 프리필은 스냅샷의 환산 kg(load)를 그대로 쓰도록 두었다. 9/2 마블오후반 보드(박주희, "바벨 점프 스쿼트 20%" 백스쿼트 기준)로 성근 회원이 운동을 시작했고, 오너가 "친구한테 자기 1RM의 %가 아니라 내 kg가 보인다"고 보고했다.

2. 문제 제기

프리필 한 단계가 %와 기준 종목을 버리고 작성자의 환산 kg만 넘겼다

"이 운동으로 시작" 처리(mobileApp.tsx onGroupBoardStart) 단일·복합 두 분기가 세트를 load: set.load, loadPct: null로 만들고 종목 pctBase도 넣지 않았다. 보드 읽기까지는 값이 살아 있고, 운동 화면은 이미 %를 내 1RM으로 환산할 줄 알았다 — 빠진 층은 이 하나였다.

Production 실측(9/2): 보드 load_pct 20 · load 40 · pct_base 백스쿼트 → 박주희 백스쿼트 기준 200(20% = 40kg) / 성근 135(20% = 27kg) → 성근 세션(source_ref group-board:…) 프리필 40kg, 손으로 20kg 수정. 같은 구조의 %-보드가 8/26 이후 3개 그룹 4개.

CI가 결함을 정답으로 고정하고 있었다

e2e CASE-019의 마지막 단계가 "프리필 세트가 140kg로 읽힌다"를 단언했고, 한 사용자가 쓰고 같은 사용자가 시작해 회원 간 차이가 드러날 수 없었다.

3. 해결 방안

원칙 (오너 결정 D1~D3, 2026-09-03)

  • D1 = (a): "시작하는 사람에게 기준 1RM이 없으면 %는 유지하고 kg는 비운다" — 남의 1RM으로 조용히 계산하지 않는다는 #837 규칙과 같은 방향. 채택. (b) 작성자 kg + 토스트는 기각.
  • D2: "5kg 반올림 같은 설정도 같이 공유되서 적용되게 해줘" — 종목 단위 설정값을 보드 스냅샷에 실어 왕복.
  • D3: "1rm 데이터가 없으면 종목 세팅창에 바로 안내 문구를 어딘가에 표시해주면 좋겠네, 그럼 알아서 kg 데이터로 하게" — 저장 시점 카드(#837 D2)에 더해 설정 창 즉시 안내.

접근

대안판단
A. 프리필에서 시작자 1RM으로 재환산(채택)소실 지점 한 곳 수리, 운동 화면의 기존 % 규칙(기준 우선순위·D2 저장 게이트) 재사용
B. 서버가 회원별로 환산클라가 같은 규칙을 이미 보유, 보드 표시는 %라 쓰일 곳이 프리필뿐 — 중복. 기각
C. 현행 유지 + 안내회원이 매 세트를 손으로 고쳐야 함. 기각

4. 적용한 내용

Phase 1 — 프리필 재환산 (#1163)

  • 새 도우미 groupBoardPrefill.ts: groupBoardPctBaseId(명시 기준 → 복합 1RM 보유 첫 동작 → 자기 종목) · groupBoardPrefillOneRmKg(체중 계수 종목은 기준 없음) · groupBoardPrefillLoad(% 세트 = 1RM × %, 0.5kg 또는 5kg 반올림 / 기준 없음 = {load: "", loadPct} / kg 세트 = 스냅샷 그대로).
  • onGroupBoardStart 단일·복합 분기가 도우미로 환산하고 pctBase·pctRoundKg를 동반. 초안 팩토리(makeFlowExercise)가 pctRoundKg 통과.

Phase 2 — 5kg 반올림 설정 공유 (#1163, 마이그레이션 20260904010100)

  • WorkoutRecord: round5 화면 상태 삭제 → active.pctRoundKg === 5, 체크박스 토글 = onUpdateExercise({pctRoundKg}).
  • 컴포저 gpcExercise·onSave → groupStore pct_round_kg 왕복 → 서버 검증 함수가 종목 pct_round_kg(허용값 5, note 불허, 비숫자·5 외 22023) 수용·에코. 본문은 Production 실정의(20260831200000 재발행본과 동일 대조)에 이 처리만 삽입, postcheck가 load_pct·pct_base·calories·parts 생존을 단언(#945·#1024 교훈).

Phase 3 — 즉시 안내 (#1163)

  • 무게 단위 행 아래 .wfx-pctnote: pctSel && !activePctRm일 때 "1RM 기록이 없어 %를 kg로 바꿀 수 없어요 — 기준 종목을 고르거나 kg로 입력해 주세요".

Phase 4 — e2e CASE-019 개정 (#1163)

  • 재진입 체크포인트 분리(reentry), 새 체크포인트 member-own-1rm-prefill: 회원이 두 번째 그룹을 만들어 코치(일회용 사용자)를 초대 → 코치 수락 → RPC로 보드 저장(73%·pct_round_kg 5·작성자 환산 999) → 회원(1RM 200) 시작 → 라이브 세트 145kg(146의 5kg 반올림), 999 없음. case.json·README·USER-JOURNEY 동기화.

주요 결정과 그 근거

  • 반올림 설정은 종목 단위: 운동 화면의 체크박스가 종목별로 동작하고 보드 종목마다 따로 저장돼야 회원 환산에 그대로 옮겨진다. 세트 단위는 과하고 보드 단위는 종목마다 다른 처방을 못 담는다.
  • 서버 허용값은 5 하나: UI에 5kg 옵션만 있다. 값 자체를 숫자(kg 단위)로 두어 나중에 2.5 등이 생겨도 키 이름을 바꾸지 않아도 되게 했다.
  • e2e는 별도 사용자로: 같은 사용자의 1RM을 바꾸는 방식은 "현재 1RM" 산정 규칙(최신 vs 최대)에 얹혀 불안정하다. 코치가 RPC로 올린 스냅샷 load를 일부러 999로 두면 "스냅샷 kg를 안 쓴다"가 한 숫자로 드러난다.

작업 중 드러난 것

  • Production 쓰기 드라이런(DO 블록 끝 raise 롤백)은 이 세션의 실행 정책이 막았다 — 마이그레이션 검증은 CI pgTAP·postcheck에 맡겼다.
  • e2e가 결함을 정답으로 고정하면 CI는 침묵한다 — 구 CASE-019 제목이 "prefill stays kg"였다. 단언을 %로만 바꿔서는 회원 간 차이가 안 드러나므로 별도 사용자 경로가 필요했다.
  • 검증 함수 재발행은 Production 실정의(pg_get_functiondef)를 기반으로 정확히 1회 일치하는 삽입만 하는 생성 스크립트로 만들었다(20260831200000 본문과 diff 0 확인) — 다중 트랙 되덮기(#945·#1024) 회피.

5. 적용 결과

항목결과
친구가 %-보드로 시작할 때 채워지는 kg작성자 기준(성근 사례 40kg) → 본인 기준(27kg, 반올림 설정 공유 시 25kg) — 단위 테스트로 확인, 실기기 미확인
기준 1RM 없는 회원작성자 kg 채움 → % 유지·kg 빈 값 + 설정 창 즉시 안내 + 저장 시점 카드
5kg 반올림 설정화면 상태(저장 안 됨) → 종목 값으로 보드 왕복(pgTAP 8, CI 대기)
CI 잠금CASE-019 "프리필 140kg 고정" → 별도 사용자 보드 "145kg(내 1RM·공유 반올림), 999 아님" (CI 실행 결과: migration-smoke 통과, run 33659570918)
계약 문서group-props §4·§5 "환산 kg는 프리필용" → "프리필은 시작자 1RM 환산, 스냅샷 kg는 작성자 기준"
검증npm run check·unused 게이트 통과, 단위 15건, 등록 검사 27 케이스, CI verify 통과(run 33659570918, 15분)

미검증: 실기기 시각 확인(안내 문구 위치·줄바꿈), Production 적용(릴리스 PR 대기).

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

%-처방 보드가 회원마다 맞는 무게가 된다

역도반처럼 %로 처방하는 그룹에서 회원이 세트마다 무게를 손으로 고칠 필요가 없다.

작성자의 설정이 회원에게 그대로 전달된다

5kg 반올림처럼 "무게를 어떻게 읽을지"의 설정이 보드에 실려, 코치가 의도한 숫자 모양으로 회원 화면에 나타난다.

구조적으로 남는 것

프리필 환산 규칙이 도우미 1곳 + 단위 테스트에 살고, 보드 %-세트의 수명(입력 → 저장 → 표시 → 재진입 → 프리필) 다섯 층 모두가 % 원본을 지키며, e2e가 "보는 사람 기준" 규칙을 별도 사용자로 매 CI마다 확인한다.

남은 것

  • 릴리스 PR(main → production) 머지 시 Production 적용 → 이슈 [반영완료].
  • 기준 종목이 작성자의 커스텀 종목일 때(회원에게 그 1RM이 없음)는 D1 경로로 간다 — 커스텀 기준 종목의 회원 매칭은 스코프 밖.