Skip to content

디자인 배송 v86 — 메뉴 드로어 재편 · 나의 주요 종목 · 내 1RM 기록 관리(목록·수정·삭제) · 운동 기록 검색 · 커스텀 종목 관리 (2026-09-03)

  • 기간: 2026-09-03 (세션 1개, 오너 지시 원문 "디자인 작업하다가 토큰 끝나서 패키징을 못했는데, 얘네들만가지고 메인좀 반영해줄 수 있어?" + 디자인 환경 원본 zip 2개 바벨릭 (5).zip(docs/contracts)·바벨릭 (6).zip(src/react 전체), base 1f4c103d5692)
  • 랜딩: PR #1187 (Phase 1~4, fb1cf307) — 마이그레이션 없음, 서버 변경 없음, Vercel 배포 O
  • 설계서: 없음(디자인 배송 — 정본은 zip 안 src/react/ui/CHANGE-NOTES.md 맨 위 [전달 대기] 항목). 분석·Phase 계획 = 이슈 #1184 본문
  • 정본: src/react/ui/mobile/shell/MobileShell.tsx(드로어·탭바) · src/react/ui/mobile/screens/PrTools.tsx(나의 주요 종목·내 1RM 기록 관리) · docs/contracts/shell-frames.md §NavDrawer·§BottomNav(2026-09-03) · src/react/services/barbelicRepository.ts listManualPrRecords/deleteManualPr
  • 도구: 델타 추출 = zip 원문 ↔ base 커밋 파일 단위 cmp(배송 manifest가 없어 노트 대신 실측) · 로그인 없는 브라우저 하네스(워크트리 루트 일회용 _v86probe/, 커밋 제외)
  • 게이트: designContract(마커 8건 등록) · mobileCssBoundaries(화면 전용 선택자 공용 CSS 금지 — 이관 1건) · check:dual-key(서버 행 snake 한 형태로 확정) · check-typescript-unused(미소비 onBack 2건) · manualPrRecordsList.test.mjs(신설) · inputRangeGuards·viteServices·mobileExperienceViewMappers 재조준
  • 버그리포트: 없음(오너 보고 결함 아님)
  • 계약: shell-frames.md §NavDrawer onPickReport?(unit)·reportUnit?·activeItem? / §BottomNav menuActive? 신설 · writeContracts.ts DeleteManualPrInput 신설 · MobileManualPrInputid? 동반(수정)

Phase 현황

Phase내용상태
Phase 0델타 추출·반입 감사 — 배송 그대로 넣으면 안 되는 지점 3건, 오너 결정 D1~D3✅ 이슈 #1184 본문
Phase 1배송 파일 11개 반입 + 감사 절충✅ PR #1187
Phase 2컨테이너 배선(타입·mobileApp·카피·마커)✅ PR #1187
Phase 31RM 직접 입력 목록·수정·삭제 클라이언트 경로✅ PR #1187
Phase 4로컬 게이트 + 브라우저 하네스 실측
Phase 5PR 1개·CI 1회·머지 → [스테이징] + 이 기록✅ PR #1187 (fb1cf307)

1. 배경

디자인 환경(Claude Design)이 2026-09-03에 메뉴 드로어와 드로어에서 들어가는 네 화면(나의 주요 종목·1RM 직접 입력·세션 검색·커스텀 종목 관리)을 손봤는데, 토큰이 소진돼 배송 패키지(zip + manifest + PR 초안)를 만들지 못했다. 오너가 디자인 환경의 원본 폴더 두 개를 그대로 넘겨 main 반영을 요청했다.

배송 노트(CHANGE-NOTES.md)에 [전달 대기] 1건이 있었고 base가 main 1f4c103d5692로 적혀 있었다. base 이후 main은 6커밋 전진했지만 배송 파일 11개와 겹치는 상류 변경은 없었다.

2. 문제 제기

2-1. 배송 노트만 믿을 수 없어 델타를 직접 뽑아야 했다

manifest가 없으니 어느 파일이 바뀌었는지 노트의 파일 목록이 유일한 단서였다. zip의 src/react 전체(423파일)를 base 커밋과 파일 단위로 대조하니, 노트에 적힌 파일 외에 ProfileScreen.tsx·profile.css·records.css가 더 바뀌어 있었고, features/**·types/** 6파일은 반대로 디자인 쪽이 main보다 낡은 사본이었다(각각 main의 이전 커밋과 동일 확인). 노트만 따라갔으면 커스텀 종목 화면 변경을 빠뜨리거나, 낡은 사본으로 main 코드를 되돌릴 뻔했다.

2-2. 커스텀 종목 관리 — 배송대로 넣으면 보관 기능이 사라진다

유저 A가 자기가 만든 종목을 더 안 쓰게 되면 목록에서 "보관"을 눌러 숨긴다. 배송은 이 보관/복원 버튼을 "수정" 연필로 바꾸고 보관은 "수정 화면 몫"이라고 적었는데, main에는 커스텀 종목 수정 화면이 없다(onEditCustomExercise가 어디에도 없음). 그대로 넣으면 A는 종목을 보관할 방법을 잃는다. customExerciseArchiveUi.test도 보관 마커와 안내 문구를 잠그고 있었다.

2-3. 1RM 입력 모달 헤더가 그룹 탭 CSS에 기대고 있었다

배송의 1RM 입력 모달은 그룹 모달 헤더 클래스 gpc-phead를 재사용했는데, 그 규칙은 그룹 탭을 열 때만 불러오는 group.css가 갖고 있다. 유저 B가 앱을 켜서 그룹 탭을 거치지 않고 바로 "내 1RM 기록 관리 → 추가하기"를 누르면 제목 아래에 X 버튼이 네모 박스로 따로 내려앉는다(하네스에서 실측). 디자인 환경 프리뷰는 모든 CSS를 한꺼번에 읽어 이 문제가 안 보였다.

2-4. 1RM 직접 입력 목록·삭제는 읽기 경로가 없었다

서버에는 save_manual_pr(id를 주면 수정)·delete_manual_pr RPC와 본인 행만 읽는 RLS 정책이 baseline부터 있었다. 하지만 클라이언트에는 "내가 직접 입력한 1RM 목록"을 읽는 함수도, 삭제 함수도 없었다(#664 D5에서 진입 경로가 없다는 이유로 deleteManualPr를 지운 상태).

2-5. 반복수 종목의 1RM 직접 입력은 서버가 못 받는다

배송은 풀업처럼 반복수로 기록하는 종목을 고르면 값 필드가 "최대 반복 횟수/회"로 바뀌게 했다. 그런데 user_manual_pr_records.unit'kg'만 허용하고 통계 체인도 kg 1RM 전제다. 검색 결과 행에 지표 종류(recordMetric)가 실리지 않아 이 분기는 실제로는 켜지지 않는다.

3. 해결 방안

원칙 (오너 결정 — 이번 트랙에서는 반입 감사 판단으로 대체, D1~D3은 이슈 본문에 대기)

  • 배송의 표현은 그대로, 기능이 사라지는 지점은 main 기능을 유지한다(커스텀 종목 보관/복원).
  • lazy CSS 의존은 자체 규칙으로 끊는다(1RM 모달 헤더).
  • 서버 변경 없이 되는 것만 이번에 한다(1RM 목록·삭제는 기존 RPC·RLS로 충분, 반복수 종목 서버 확장은 D2).

접근

대안판단
배송 파일을 그대로 덮어쓰기기각 — 커스텀 종목 보관 소실·모달 헤더 깨짐·공용 CSS 게이트 위반
커스텀 종목 수정 화면까지 이번에 신설기각 — 배송에 화면이 없고 오너 결정 사항(D1)
파일 대조로 델타 추출 → verbatim 반입 → 감사 절충 3건 → 배선 → 목록·삭제 경로채택
반복수 종목 서버 지원(마이그레이션) 동반기각 — 통계 의미 정의가 필요한 오너 결정(D2), 분기는 잠든 코드로 남김

4. 적용한 내용

Phase 1 — 반입 + 감사 절충

11개 파일(셸·화면 4·CSS 5·아이콘·계약 문서)을 줄 끝 형식만 맞춰 그대로 넣었다. 절충 3건: ① 커스텀 종목 행 액션은 onEditCustomExercise가 공급될 때만 연필, 없으면 보관/복원 버튼과 안내 문구 유지 ② 모달 헤더 gpc-pheadprt-manhead(primitives.css 소유, 그룹 모달 문법 동조) ③ .ss-page 규칙을 primitives.css에서 set-search.css로 이관. 배송의 CHANGE-NOTES.md·아카이브는 이전 배송처럼 그대로 반입.

Phase 2 — 컨테이너 배선

  • 드로어: onPickReport(unit) → 리포트 기간 단위를 그 값으로, 현재 기간(offset 0), 리포트 탭 진입(운동 중이면 다른 드로어 항목과 같은 이탈 경로). reportUnit은 연간·월간만 넘기고 주간/분기는 미공급(연간 기본). activeItem은 기존 recordsTool 상태 그대로.
  • 탭바: menuActive={recordsToolOpen} — 레이어가 열려 있으면 메뉴 탭만 강조.
  • 카피: 운동 기록 검색(구 세션 검색)·나의 주요 종목(구 주요 종목 목록 수정)·내 1RM 기록 관리(구 1RM 직접 입력)·연간/월간 리포트 등재 후 화면의 임시 리터럴을 카피 참조로 되돌림.
  • 마커 8건 등록(pr.manual.save/edit/delete·profile.customExercise.edit·hook 4).

Phase 3 — 1RM 직접 입력 목록·수정·삭제

listManualPrRecords(테이블 직접 select, 본인 행만 — 커스텀 종목 목록과 같은 관례)·deleteManualPr(delete_manual_pr RPC) → workoutDomain → barbelicApi → appController(manualPrRecords 상태: 도구를 열 때 적재, 저장·삭제 뒤 재적재, 토스트 "기록되었습니다"/"삭제하였습니다") → mobileApp(서버 행에 카탈로그 종목명을 붙여 화면 계약 {id, exerciseId, name, value, date}로). DTO는 id가 있을 때만 동반해 서버가 수정으로 받는다.

작업 중 드러난 것

  • 배송 노트의 파일 목록은 불완전했다(3파일 누락) — 델타는 항상 원문 대조로.
  • dual-key 게이트가 새 코드의 exercise_id || exerciseId 이중 키를 잡았다 — 저장소가 넘기는 형태 하나로 확정.
  • mobileCssBoundaries는 주석 안의 .ss-page 문자열까지 잡는다 — 주석에 점 붙은 클래스명을 쓰지 않는다.
  • viteServices.test가 "deleteManualPr는 진입 경로가 없어 제거됐다"를 단언하고 있었다 — 진입 경로가 생겼으니 반대로 재조준.

5. 적용 결과

항목
드로어에서 월간 리포트 진입리포트 → 기간 토글 2탭1탭(하네스: 단위 month, 강조 행 1개)
레이어(나의 주요 종목 등) 열림 중 탭바 강조홈 탭 강조 잔존메뉴 탭만 active
직접 입력한 1RM 수정·삭제경로 없음목록 행에서 수정(프리필)·삭제(확인 카드)
워드마크 행 위치(홈→레이어)지면 패딩 0↔47px 변동네 화면 모두 47px 고정(하네스 실측)
달력 모달 높이달마다 5·6주로 변동6행 고정
1RM 모달 헤더(그룹 탭 미방문)X가 제목 아래 박스제목 행 우측 X
유닛 스위트2,449 pass · 실패 1(tabSwitchLatencyProbe, main 동일 Windows CRLF 건)

미검증: 실기기 시각 품질(라벨 굵기·간격), 운동 플로우 중 드로어에서 연간/월간 진입(하네스는 탭 모드만).

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

  • 리포트 진입이 한 번에 되고, 드로어 강조가 "지금 열린 화면 하나"라는 규칙을 계약으로 얻었다.
  • 유저가 직접 넣은 1RM을 목록으로 보고 고치고 지울 수 있다(서버 변경 없이).
  • 배송 반입 절차에 "manifest 없는 원본은 파일 대조로 델타 추출"과 "lazy CSS 의존 실측"이 추가됐다.

남은 것

  • 오너 결정 D1(커스텀 종목 수정 화면 신설) · D2(반복수 종목 1RM 직접 입력 서버 지원 — unit='reps' 허용 + 통계 의미) · D3(.tab-pane.off * 가드 없는 규칙 13건 감사).
  • 릴리스 v0.15.0(PR #1177, 2026-09-03) 머지로 이슈 #1184 [반영완료] 처리 완료. D2는 이슈 #1194, D3는 이슈 #1195로 분리.