Skip to content

출시 전후 정리 묶음 — 감사 확정 중·저위험 결함 8건 일괄 수리 (2026-09-01)

  • 기간: 2026-09-01 (1세션, 오너: "#928좀 진행해줄래?")
  • 랜딩: PR #1049(Phase 1~4 한 묶음, 7fd716e6) — 마이그레이션 0, 클라이언트 전용(Vercel 자동 배포)
  • 설계서: 없음(수리 묶음 — 분석·Phase 계획·예상 효과는 이슈 #928 스레드)
  • 정본: groupStore.ts(목록 오류 재시도·월 마크 병합·좋아요 스탬프) · SetComposer(복합 동작별 편집) · pendingWorkoutSavesStore.ts(플러시 통계 재확인) · profileController.ts(계정 삭제 로컬 정리)
  • 도구: 없음(코드 읽기 + 스토어 행동 테스트로 재현)
  • 게이트: tests/react/issue928Cleanup.test.mjs 10건(스토어 행동·렌더 계약·배선 앵커) + 기존 대기 큐 테스트 2파일 보강
  • 버그리포트: 없음 — 감사(워크플로 wf_e7702b27-b0d, 2026-08-30) 발견 묶음이라 이슈 #928 표가 정본
  • 계약: designContract.ts 액션 마커 group.list.retry 등재

한 줄 요약 — 출시 전 전면 감사가 확정한 중·저위험 결함 11건을 전수 재조사해, 이후 트랙이 이미 수리한 3건(①⑥⑨ — #975·#950)을 제외한 8건을 한 PR로 수리했다. 서버 변경 0.

Phase 현황

Phase내용상태
Phase 011건 전수 재조사 — 기수리 3건 판별, 유효 8건 원인 지목✅ 이슈 스레드
Phase 1그룹 탭 ③④⑧ (목록 오류·달력 마크·좋아요 경합)✅ PR #1049
Phase 2기록·세션 상세 ②⑦⑩ (시간 버튼·복합 수정·드래그 취소)✅ PR #1049
Phase 3동기화·계정 ⑤⑪ (플러시 통계 재확인·계정 삭제 로컬 정리)✅ PR #1049
Phase 4검증·랜딩 (신규 테스트 10건, 로컬 게이트 전 체인, CI 1회)✅ PR #1049

1. 배경

출시 전 전면 감사(2026-08-30, 워크플로 wf_e7702b27-b0d)가 개별 트랙을 열 규모는 아니지만 사용자가 실제로 겪는 오동작 11건을 이슈 #928로 묶어 두었다. 오너 go(09-01)로 착수. 재조사 결과 3건은 그 사이 #975(입력 검증 정비)·#950(입력 상한 정비)이 이미 수리해 두었다 — 감사 목록은 착수 시점에 반드시 최신 코드로 전수 재확인해야 한다는 것이 이 트랙의 첫 결론.

2. 문제 제기

③ 그룹 목록 첫 로드 실패가 가짜 "아직 그룹이 없어요"로 영구 고정

실패해도 화면은 오류 표시 없이 빈 목록을 그렸고(mobileApp.tsx — error 상태 분기 없음), 탭 재진입도 "이미 로드했다" 기록(loadedOwner 래치) 때문에 재요청하지 않았다.

④ 미니 달력에서 월을 넘기면 보드 표시(워시 마크)가 전부 소실

마크 원천(board_dates)을 서버가 "보고 있는 날짜의 달" 창으로만 보내는데, 클라이언트가 다른 달을 가져오는 경로가 없었다. 서버 함수는 이미 기간 인자(p_from/p_to, ≤62일)를 받고 있었다.

⑧ 좋아요가 눌렀다 되돌아간 것처럼 보임

보드 재진입 시 백그라운드 새로고침(swr) 응답(누르기 전 값)이 방금 누른 좋아요를 무조건 덮었다(groupStore.ts loadBoard 성공 핸들러).

② 타인 기록에도 "시간 수정" 버튼 노출 — 저장해도 무동작

버튼 노출 조건(SessionSheetBody.tsx)에 "내 세션인가" 판정이 없었다.

⑦ 복합종목 세트 수정이 조용히 버려짐

세트 수정 모달이 복합종목에도 합계 횟수 한 칸만 열었는데, 저장·표시는 동작별 값(scheme)을 우선하므로 고친 합계가 어디에도 반영되지 않았다.

⑩ 취소된 드래그 뒤 리스너 누수 + 엉뚱한 세트 추가 창

세트 순서 드래그가 window 리스너를 pointerup에서만 제거해, 브라우저가 드래그를 강제 취소(pointercancel)하면 리스너가 남아 다음 탭이 엉뚱한 위치에 컴포저를 열었다.

⑤ 오프라인 대기 기록 동기화 직후 하루 통계가 옛값 (#902 동종)

온라인 저장·삭제 경로는 저장 영수증으로 통계 반영 여부를 재확인(reconcileCompletedWorkoutReadModels)하는데, 오프라인 큐를 올리는 동기화(flush) 경로에만 이 호출이 빠져 있었다.

⑪ 계정 삭제 후 운동 기록이 기기에 무기한 잔존 (개인정보)

삭제 시 로컬 정리가 화면 스냅샷 1종만 지웠다. 진행중 운동 초안(봉투)·보관함(#887)·오프라인 대기 큐는 IndexedDB에 남았다.

3. 해결 방안

원칙 (오너 결정)

  • D1 (2026-09-01, go에 포함): 계정 삭제 시 이 기기의 로컬 운동 기록(진행중 초안·보관함·오프라인 대기 큐)을 함께 삭제한다. 로그아웃은 종전대로 보존(재로그인 시 이어짐).

접근

  • 한 묶음 PR·CI 1회(전역 규칙) — 전부 클라이언트 수정이라 마이그레이션 잠금(§15) 불요.
  • ④는 서버 RPC 신설 대신 기존 기간 인자 활용(서버 변경 0)을 채택. 전용 마크 RPC 신설은 기각 — 월 이동은 드문 조작이라 보드 payload 재사용으로 충분.
  • ⑧은 재검증 자체를 취소(tokens.board 상승)하는 안을 기각 — 보드·댓글 갱신까지 버리게 된다. likes만 스탬프로 보호.

4. 적용한 내용

Phase 1 — 그룹 탭 (③④⑧)

  • groupStore.ts: activateForGroupTab이 error 상태면 재진입마다 재요청 / loadBoardMonthMarks(year, monthIndex) 신설(월당 1회, 월 창 단위 병합 mergeBoardDatesForMonth — 날짜 이동·캐시 히트가 타 달 마크를 지우지 않음) / likeStamp+likePending으로 swr 응답의 likes 덮어쓰기 차단.
  • GroupScreen.tsx: 목록 오류 화면(문구+group.list.retry 버튼, 목록이 있으면 오류 화면 미전환) / GpCalSheetonMonth 콜백.
  • mobileApp.tsx 배선, group.css 재시도 버튼, designContract.ts 마커 등록.

Phase 2 — 기록·세션 상세 (②⑦⑩)

  • SessionSheetBody.tsx: 시간 수정 버튼 노출 조건에 onUpdateSession && !(profile.self === false).
  • WorkoutRecordParts.tsx SetComposer: scheme 복합 세트는 동작별 횟수 칸(빠른 추가 컴포저와 같은 문법)으로 {scheme, repsParts, reps=합} 커밋.
  • WorkoutRecord.tsx: 드래그 리스너에 pointercancel 추가 — 정리만 하고 컴포저 미발동.

Phase 3 — 동기화·계정 (⑤⑪)

  • pendingWorkoutSavesStore/-Controller + appController.tsx: 플러시 성공 시 업로드 영수증마다 reconcileCompletedWorkoutReadModels(receipt, [date]) 호출. 통계 복구 컨트롤러 조립을 대기 저장 컨트롤러 앞으로 이동(내용 불변).
  • workoutDraftCache.ts purgeWorkoutDraftDataForUser(봉투+보관함, 소유자 스코프) · pendingWorkoutSaves.ts purgePendingWorkoutSavesForUser 신설, profileController.deleteAccount에서만 호출(로그아웃 경로 미접촉).

주요 결정과 그 근거

  • 마크는 상태 신설 없이 기존 boardDates에 병합 — 화면 계약 불변, 소비처 수정 0.
  • 계정 삭제 정리는 서버 삭제 성공 뒤 실행하고 실패해도 삭제 성공을 되돌리지 않음(#663 계약 유지).

작업 중 드러난 것

  • 감사 목록의 3건은 착수 시점에 이미 수리돼 있었다(#975 Phase 3-1·3-4, #950) — 감사·백로그 묶음은 반드시 현재 코드로 전수 재확인 후 착수.
  • tests/audit/pending-changes.json명부(manifest)에 없는 신규 테스트 파일을 선언하면 오히려 게이트가 깨진다 — 선언은 명부 등재 파일 전용.
  • PowerShell로 한글 포함 파일을 Get-ContentSet-Content로 고치면 인코딩이 깨진다(CP949 오독+BOM) — 다중행·한글 파일 수정은 Edit 도구로만.

5. 적용 결과

항목전 → 후
그룹 목록 로드 실패 시가짜 "그룹 없음" 영구 고정 → 오류 문구+재시도 버튼, 재진입 자동 재요청 (행동 테스트)
달력 이전/다음 달 보드 마크0개 표시 → 그 달 실제 보드 날짜 표시·월당 1회 로드 (행동 테스트)
보드 재진입 직후 좋아요재검증 응답이 덮어 깜빡 복귀 → 유지, 서버 집계로 화해 (경합 2경로 테스트)
타인 세션 시간 수정 버튼노출(저장 무동작) → 비노출 (렌더 계약 테스트)
복합 세트 수정 모달합계 1칸(저장 시 버려짐) → 동작별 칸(scheme 커밋) (렌더 계약 테스트)
pointercancel 후 다음 탭엉뚱한 컴포저+리스너 누수 → 정리·미발동 (배선 앵커)
동기화 직후 하루 통계재확인 없음(옛값 고착 가능) → 영수증마다 재검증 호출 (행동 테스트)
계정 삭제 후 로컬 잔존초안·보관함·대기 큐 잔존 → 소유자 것 전량 삭제, 타 소유자 보존 (행동 테스트)
게이트로컬 check 전 체인 + 전체 테스트 2,281건 green, CI 1회 green
미검증⑦ 모달의 시각 품질, ⑧ 실기기 체감 — 자동 증거 범위 밖(§14에 따라 게이트 아님)

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

통신이 나쁜 곳에서도 그룹 탭이 스스로 복구된다

실패가 보이고(문구), 사용자가 고칠 수 있고(재시도), 재진입만 해도 다시 시도한다.

복합종목 수정이 "한 일이 사라지는" 부류에서 벗어났다

수정 모달에서 고친 값이 실제 저장·표시에 반영된다 — 조용한 무반영은 데이터 불신으로 직결되는 부류였다.

구조적으로 남는 것

  • 오프라인 큐 동기화가 온라인 저장 경로와 같은 통계 재확인 문법을 공유 — 경로별 편차 1건 해소.
  • 계정 삭제의 로컬 정리 범위가 "소유자 데이터 전부"로 정의되고 서비스 함수 2종으로 남음.
  • 달력 마크 병합 규칙(월 창 단위)과 좋아요 스탬프가 회귀 테스트로 상주.

남은 것

  • 이종 복합(동작별 원자, parts형) 세트의 수정 모달은 이번 범위 밖 — 같은 모양의 문제가 있는지 별도 확인 가치(감사 항목은 scheme형만 지목).