Skip to content

그룹 보드 복합종목 격하 수리 — 왕복 4층 배선과 동작별 반복(scheme) 보존 (2026-08-29)

  • 기간: 2026-08-28 ~ 2026-08-29 (1세션, 오너 보고: "복합종목 세팅을 해놨는데 자꾸 그냥 종목으로 바뀐다" — 이슈 #888)
  • 랜딩: PR #891(Phase 1, b178d006) · #892(Phase 2·3, adf83bca) · #907(CASE-020 e2e, 7724f24b) — 마이그레이션 20260829120000 Production db push 완료(release contract clean), Vercel 청크 3곳 마커 실측, 오너 실기기 확인 08-29 종결
  • 설계서: 없음(단일 트랙 — 분석·Phase 계획은 이슈 #888 쓰레드)
  • 정본: group_validated_board_exercises_v1(schema.sql 20260829120000 절) · groupStore.ts saveBoard/toBoard · group_boards 테이블 코멘트(스냅샷 계약)
  • 도구: Management API read-only(remote-schema release contract 실측) · Production 청크 마커 grep
  • 게이트: groupBoardCompositeRoundtrip.test.mjs 6건(#831·#837 왕복 테스트와 3부작) · 마이그레이션 내 postcheck
  • 버그리포트: bug-034-20260828.md
  • 계약: 보드 스냅샷에 composite?(구성 2..5)·세트 scheme? 추가, composite 동반 name 상한 200자 — GroupComposer draft out 주석 계약 동반 개정

Phase 현황

Phase내용상태
Phase 1서버 검증기 composite·scheme 수용+에코 (20260829120000)✅ PR #891 (b178d006) · Production 적용
Phase 2클라 4층 배선(컴포저 draft out·스토어 왕복·프리필·표시)✅ PR #892 (adf83bca) · Vercel 실측
Phase 3왕복 계약 테스트 상주✅ PR #892 동반
Phase 4bug-034 + 작업 기록 + 등록 2곳✅ 이 문서

1. 배경

그룹 화이트보드 컴포저는 v78(#831)에서 계획 편집기(WfRecord + 픽커)를 재사용하게 됐고, 그때부터 보드에 복합종목(한 세트 안에서 이어서 하는 2~5개 종목)을 올릴 수 있게 됐다. 그런데 저장 후 다시 열면 복합 구성이 사라지고 첫 구성 종목 하나짜리 일반 종목만 남았다(오너 보고 2026-08-28). 같은 편집기 재사용이 낳은 왕복 소실은 이번이 세 번째다 — 자유 기록 kind(#831), % 기준 pct_base(#837), 그리고 composite(#888).

2. 문제 제기

4층 전부가 구멍이었다

이슈 초안은 서버 검증기·스토어 2층을 지목했지만, 착수 검증에서 수리 지점이 더 드러났다:

  1. 서버 검증기group_validated_board_exercises_v1은 화이트리스트 재구성 방식이라 composite·scheme 키를 통째로 떨군다. 부수로, 복합 표시 이름(구성 이름의 연결)이 일반 종목 name 상한 60자에 걸리면 저장 자체가 22023으로 거부된다.
  2. 컴포저 draft outGroupComposer.save()가 composite를 직렬화하지 않아, 스토어에 닿기 전에 이미 소실.
  3. 프리필 하드 리셋onGroupBoardStart가 모든 항목에 composite: null을 고정 — 서버 왕복과 무관하게 "이 운동으로 시작"에서도 격하.
  4. 세트 scheme 동반 소실 — 복합 세트는 편집기에서 reps(합계)+scheme(동작별 반복)으로 만들어지는데, scheme이 어느 층에도 없어 왕복 후 "10+10"이 "20"으로 뭉개진다.

보드 목록 표시는 복합 이름이 name에 담겨 멀쩡해 보이므로, 격하는 수정 재진입·프리필에서 체감된다 — 오너 보고의 "자꾸 바뀐다"가 정확히 이 지점.

3. 해결 방안

  • 원칙: 오너 착수 지시("이슈 888 후속작업 진행") + 이슈 초안의 수리 방향(= #831·#837 선례 패턴) 그대로 — 직렬화·검증기/에코·표시·재진입(+프리필) 전 층 배선 + 왕복 계약 테스트 상주.
  • 접근: 서버 선행(새 필드 전부 선택 → 구 클라 후방호환), 이어서 클라. 구성 exercise_id 정규화는 기존 정책(uuid 모양만, 슬러그는 이름 매칭 폴백) 동일 적용. 복합 이름은 note와 같은 200자 상한으로 완화.

4. 적용한 내용

Phase 1 — 서버 (PR #891)

20260829120000_group_board_composite_v1.sql: 검증기 재정의 — exercise kind에 composite?(2..5 구성, 각 {name 1..60자/240B, exercise_id? uuid 모양, details? ≤3개·각 1..30자/120B}) 수용+에코, 세트 scheme?(composite 동반·길이=구성 수·정수 0..1000) 수용+에코, composite 동반 name 200자/800B, note kind는 composite 불허. postcheck는 손댄 검증기만(에코·이름 완화·구 payload 불변). 상한 근거 = 복합 빌더 "최대 5개"·디테일 시트 "3개·20자".

Phase 2·3 — 클라 + 게이트 (PR #892)

  • 컴포저 draft out: composite(구성 exerciseId·name·details)·세트 scheme 동반 + 헤더 계약 주석 개정.
  • groupStore: saveBoard 직렬화(구성 exercise_id uuid만 소문자·복합 이름 200자 절단·scheme은 composite 동반+길이 일치 시만) · toBoard camelCase 복원.
  • 프리필: 구성 전원이 카탈로그 매칭(id→정규화 이름)되면 복합 드래프트로 시작 — makeFlowExercise가 비-canonical 구성에서 throw하므로 전원 게이트, 한 구성이라도 실패면 항목 제외(기존 "N개 빼고 시작" 토스트 합류). composite: null 하드 리셋 폐지, 세트 scheme 동반.
  • 표시(GbSeq): scheme 있는 세트는 동작별 반복 "10+10x2" 표기(합계 아님) — 런 묶음 키에 scheme 합류.
  • 게이트: 왕복 테스트 6건 — 읽기·쓰기(정규화 3종: 슬러그 구성 id 떨굼·scheme 길이 게이트·이름 절단)·표시·소스 앵커(컴포저 draft out·프리필).

작업 중 드러난 것

  • 같은 날 마이그레이션 번호 선점 3회 — #890 트랙(20260828100000)·#879 트랙(20260829100000·20260829110000)이 차례로 랜딩·Production 적용되며 본 트랙 마이그레이션이 세 번 재번호됐다(README 규칙 6 절차 — 매회 새 main 위 재구축·remote-schema 선점 확인). 특히 #897(BRID 컷오버, 파괴적)이 머지 직후였을 때는 blanket db push가 남의 미적용 마이그레이션을 대신 집행할 위험을 remote clean 실측으로 배제한 뒤에만 push했다.
  • v81 풀 레인 지뢰 2건(CASE-004 죽은 앵커·기하 게이트 탭바 55 구값)을 #891 첫 레인이 밟아 원인 규명·수리(#894)까지 했으나, #893이 등가 수리를 먼저 랜딩해 #894는 대체 닫음 — 규명·레인 green 실증 역할로 종료.

5. 적용 결과

항목
보드 복합종목 저장→수정 재진입첫 구성 종목 일반 격하복합 유지(구성·디테일 포함) — 계약 테스트 고정
세트 동작별 반복합계만 잔존(10+10 → 20)scheme 왕복 + 보드 "10+10" 표기
보드→세션 프리필무조건 일반 격하(composite: null)구성 전원 매칭 시 복합 드래프트
복합 이름 60자 초과 저장22023 거부(보드 전체 저장 실패)200자 수용(초과분 클라 절단)
왕복 계약 픽스처note·% 2종+composite 6건(3부작)
Production마이그레이션 적용·release contract clean · 청크 3곳 마커(HostFrames-DCovtefD.js·mobileRoot-BjU0BzUu.js·GroupScreen-0rWf4fdp.js)

오너 실기기 확인 2026-08-29 — 종결(이슈 #888 닫음). 후속으로 CASE-020 e2e(PR #907, 오너 지시)가 복합 왕복 전 층을 브라우저 번호 저니로 상주 고정(레인 [19/19] 2연속 green — 신설 시 externalAuthE2eConfig 프로파일 발견 목록 등재 필수를 확인). 소급 불가: 이미 격하 저장된 기존 보드는 원본이 저장 시점에 소실돼 재설정 필요.

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

  • 보드 스냅샷 계약이 세션 저장 경로와 같은 composite 어휘(exerciseId+name+details, scheme)를 공유 — 그룹이 공유하는 "오늘의 운동"에 복합종목이 1급 시민이 됐다.
  • 편집기 재사용 표면의 왕복 계약이 3부작 테스트(note·%·composite)로 상주 — 다음 필드 추가 시 4층 체크리스트가 테스트 패턴으로 남는다.

남은 것

  • 시간·거리 전용 종목의 보드 저장 구멍(#831 미결과 동일 — durationSeconds/distanceMeters는 보드 스냅샷 계약에 아직 없음) — 별도 트랙.