그룹 보드 복합종목 격하 수리 — 왕복 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 Productiondb push완료(release contract clean), Vercel 청크 3곳 마커 실측, 오너 실기기 확인 08-29 종결 - 설계서: 없음(단일 트랙 — 분석·Phase 계획은 이슈 #888 쓰레드)
- 정본:
group_validated_board_exercises_v1(schema.sql 20260829120000 절) ·groupStore.tssaveBoard/toBoard ·group_boards테이블 코멘트(스냅샷 계약) - 도구: Management API read-only(remote-schema release contract 실측) · Production 청크 마커 grep
- 게이트:
groupBoardCompositeRoundtrip.test.mjs6건(#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 4 | bug-034 + 작업 기록 + 등록 2곳 | ✅ 이 문서 |
1. 배경
그룹 화이트보드 컴포저는 v78(#831)에서 계획 편집기(WfRecord + 픽커)를 재사용하게 됐고, 그때부터 보드에 복합종목(한 세트 안에서 이어서 하는 2~5개 종목)을 올릴 수 있게 됐다. 그런데 저장 후 다시 열면 복합 구성이 사라지고 첫 구성 종목 하나짜리 일반 종목만 남았다(오너 보고 2026-08-28). 같은 편집기 재사용이 낳은 왕복 소실은 이번이 세 번째다 — 자유 기록 kind(#831), % 기준 pct_base(#837), 그리고 composite(#888).
2. 문제 제기
4층 전부가 구멍이었다
이슈 초안은 서버 검증기·스토어 2층을 지목했지만, 착수 검증에서 수리 지점이 더 드러났다:
- 서버 검증기 —
group_validated_board_exercises_v1은 화이트리스트 재구성 방식이라composite·scheme키를 통째로 떨군다. 부수로, 복합 표시 이름(구성 이름의 연결)이 일반 종목 name 상한 60자에 걸리면 저장 자체가 22023으로 거부된다. - 컴포저 draft out —
GroupComposer.save()가 composite를 직렬화하지 않아, 스토어에 닿기 전에 이미 소실. - 프리필 하드 리셋 —
onGroupBoardStart가 모든 항목에composite: null을 고정 — 서버 왕복과 무관하게 "이 운동으로 시작"에서도 격하. - 세트 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 동반+길이 일치 시만) ·toBoardcamelCase 복원. - 프리필: 구성 전원이 카탈로그 매칭(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는 보드 스냅샷 계약에 아직 없음) — 별도 트랙.