화이트보드 완료 인원·날짜 이동 수리 — 사람 단위 집계와 스켈레톤 전환 (2026-08-27)
- 기간: 2026-08-27 (1세션, 오너 보고 원문: "완료한사람 2명인데 3명이 완료했다고 뜸 … 날짜 움직였는데 완료 메세지는 그대로 … 날짜이동이 너무 느려" + 추가 지시 "로딩 스켈레톤 적용, 넘기면 빨리빨리, 데이터는 도착하는대로 표시")
- 랜딩: PR #876 (Phase 1~3,
c83acd8a) — Vercel Production success 실측(GroupScreen-NVceIi6C.js에 스켈레톤 마커 "화이트보드 불러오는 중"·gb-skel 각 1회) - 설계서: 없음 — 계획서 = 이슈 #875(예상 효과·개선사항 표 포함, 오너 승인 08-27)
- 정본:
src/react/controllers/groupStore.ts(loadBoard 소거→독립 도착·openBoard swr·날짜 캐시) ·src/react/ui/mobile/screens/GroupScreen.tsx(gpDoneByMember·loading 스켈레톤) ·docs/contracts/group-props.md§4 - 도구: Management API read-only 프로브(원인 실측 — Production 세션 원자료)
- 게이트:
tests/react/groupBoardDateNav.test.mjs(신설 6) ·groupScreens.test.mjs사람 단위·스켈레톤 앵커 ·profileHandle.test.mjs서명 캐시 앵커 - 버그리포트: BUG-033
- 계약: group-props.md §4 개정(완료 행 = 사람 단위 표시·loading 스켈레톤) ·
src/react/ui/CHANGE-NOTES.md재싱크 규칙 등재
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 1 | 완료 행·카운트 사람 단위(gpDoneByMember, 오너 D1=a) | ✅ PR #876 |
| Phase 2 | 날짜 전환 즉시 스켈레톤 + RPC 독립 도착 표시 + 재진입 swr(오너 D2=a) | ✅ PR #876 |
| Phase 3 | 아바타 서명 캐시 + (그룹,날짜) 보드 캐시 | ✅ PR #876 |
| Phase 4 | 서버 병합 RPC(contract v3) | ⬜ 보류 — Phase 3까지로 부족할 때만 |
1. 배경
그룹 v2(#847)가 보드 아래 "완료한 멤버" 행과 완료 인원 배지를 신설했다. 완료 행의 소스는 get_group_board_day_sessions_v1 — 그날 그룹 멤버들의 완료 세션 목록이다. 날짜 이동은 v1(#822)부터 매번 RPC 2개를 새로 부르는 구조였다.
2. 문제 제기
오너 보고 3건(08-27). ① 완료 2명이 3명으로, 그것도 운동 한참 전 시각과 함께 표시. ② 날짜를 옮겨도 하단 완료 행이 그대로. ③ 이동이 너무 느림. Production 프로브로 ①을 실측 확정: 마블오후반 08-26에 acetuna가 완료 세션 2개(아침 러닝 08:30 완료 + 오후 스트렝스)·chewie_park 1개 = 3행 2명 — 행·배지가 세션 단위였다. ②는 코드로 2경로 확정: 전환 중 이전 날짜 데이터 유지(로딩 표시 전무·실패 시 무기한) + 재진입 시 재로드 생략. ③은 이동 1회당 직렬 2홉(RPC 2 병렬 + 아바타 재서명 최대 2회)·캐시 전무.
3. 해결 방안
원칙 (오너 결정, 2026-08-27)
- D1 = a안: 완료 행·카운트는 사람 단위 1줄, 시각은 그 멤버의 최신 완료 세션.
- D2 = a안 + 강화: 전환 즉시 스켈레톤, 보드·완료 세션 RPC는 도착하는 대로 개별 표시.
접근
| 대안 | 판단 |
|---|---|
| 표시층 dedupe(gpDoneByMember) | 채택 — 서버 계약 불변, 종목 기록 뷰는 원본 전 세션 유지 |
| 서버 RPC 멤버 단위 개편 | 기각 — 종목 기록 뷰 데이터 소실·계약 개정 비용 |
| 이전 데이터 유지 + 로딩 바 | 기각 — 날짜 불일치 잔존 |
| 캐시 2층(서명 경로→URL·(그룹,날짜) 보드 swr) | 채택 — 왕복 절감, 정합은 백그라운드 재검증 |
| 서버 병합 RPC(contract v3) | 보류(Phase 4) |
4. 적용한 내용
Phase 1 — 사람 단위 (#876)
gpDoneByMember(GroupScreen export): member.id당 최신 completedAt 1건, completedAt 오름차순. 완료 행·배지가 이걸 쓰고, exerciseRecords 집계는 원본 daySessions 전 세션 그대로. 계약문 §4에 "표시는 사람 단위" 명시.
Phase 2 — 전환 즉시 반응 (#876)
loadBoard: 전환 즉시 보드·완료 행·댓글 소거(달력 워시 마크는 같은 그룹이면 유지) →loading상태. 같은 그룹·날짜가 ready면 소거 없이 재검증(swr).- 보드 RPC·완료 세션 RPC를 Promise.all에서 분리 — 각자 도착 즉시 set(완료 세션이 먼저 오면 로딩 중에도 먼저 그림). 늦은 응답은 단조 토큰으로 폐기.
openBoard= 항상 오늘로 재검증. 부수 행동 변경: 과거 날짜를 보다 나갔다 재진입하면 오늘 보드로 복귀.- 화면:
loadingprop 신설 — 보드 자리gb-skel3행 셔머 + 완료 행 fd-cmrow 셔머 1줄(기존 primitives 문법 재사용), 빈 상태 문구("화이트보드가 없어요")는 로드 완료 후에만, 날짜 내비는 스켈레톤 중에도 연타 가능.
Phase 3 — 캐시 (#876)
signProfileImagePaths모듈 캐시: 경로→URL, 유효 7일 중 6일 재사용, 실패 경로 미캐시(다음 로드 재시도). 사진 교체는 avatar_path 자체가 바뀌므로 경로 키가 낡은 사진을 서빙할 일 없음. 피드·친구·그룹 공용 효과.- groupStore (그룹,날짜) 캐시 30건: 재방문 = 캐시 즉시 ready + 백그라운드 재검증 교체. 보드 저장 = 키 무효화, 댓글·좋아요 = 캐시 동기 갱신, 오너 전환 = clear.
주요 결정과 그 근거
- dedupe는 서버가 아니라 표시층 — 같은 payload를 종목 기록 뷰(전 세션 필요)와 완료 행(사람 단위)이 나눠 쓰므로, 접는 쪽이 소비자다.
- 재진입은 무조건 오늘 — "운동하러 가기" 진입 의미와 일치, 낡은 날짜 고정 구멍을 재검증 생략 케이스 자체를 없애 봉쇄.
작업 중 드러난 것
- 화면 카운트 결함은 실데이터의 중복 케이스로만 드러난다 — 기존 테스트 픽스처가 전부 1인 1세션이라 세션/사람 구분 결함이 통과해 왔다. 신규 앵커는 실사례(3세션 2명)를 그대로 고정.
sessions에 완료 시각 컬럼이 없다 — RPCcompleted_at=created_at근사(라이브 플로우는 저장 = 완료라 근사 성립, 수정 재저장·인입에서 어긋날 수 있음). 이번 범위 밖, 필요 시 별도 트랙.- Production 프로브(Management API read-only)가 "3명"의 정체를 쿼리 한 번으로 확정 — 증상 재현 대신 원자료 실측이 빨랐다.
5. 적용 결과
| 항목 | 전 | 후 |
|---|---|---|
| 마블오후반 08-26 완료 표시 | 3명(행 3줄, 아침 시각 노출) | 2명(사람 단위, 최신 시각) — 테스트 고정 |
| 전환 직후 이전 날짜 완료 행 노출 | 응답까지 수 초(실패 시 무기한) | 0초(즉시 스켈레톤) — 스토어 스냅샷 테스트 고정 |
| 재진입 최신성 | 재로드 생략(낡은 데이터 고정) | 항상 재검증(swr) |
| 이동 1회 직렬 왕복 | 2홉·요청 4개 | 1홉(서명 캐시 적중)·재방문 날짜 0홉(캐시 즉시 표시) |
| 완료 세션 표시 시점 | 보드 응답까지 동반 대기 | 도착 즉시 개별 표시 |
- 미검증: 오너 실기기 체감(완료 3→2·이동 속도).
6. 이번 개선으로 향상된 것
- 완료 인원이 사람 수와 일치 — 그룹 표면의 신뢰 회복.
- 날짜 이동이 즉답(스켈레톤) + 도착 순 표시 — 연타 이동 가능, 날짜 불일치 잔상 소멸.
- 아바타 서명 캐시는 그룹 밖(피드·친구)에도 왕복을 줄이는 구조적 이득.
남은 것
- 오너 실기기 확인(완료 인원·이동 체감) → 이슈 #875 종결.
- (보류) Phase 4 서버 병합 RPC — Phase 3까지로 부족할 때만.
- (범위 밖 관찰) sessions 완료 시각 컬럼 부재 — completed_at 근사가 어긋나는 사례가 보고되면 별도 트랙.