Skip to content

복합 빌더 커스텀 종목 등록 + 커스텀 중복 표시 제거 (2026-08-24)

  • 기간: 2026-08-24 (세션 1, 오너 이슈 #674 "복합종목 작성시 커스텀 종목을 추가할 수 없는 문제" + 4중복 스크린샷)
  • 랜딩: PR #682(bcbe0540) — 마이그레이션·엣지 없음, Vercel 반영 실증(데스크톱·모바일 청크 신규 문자열 확인)
  • 설계서: 없음(단일 PR 소건 — Phase 계획서는 이슈 #674 코멘트로 게시, 오너 승인 후 착수)
  • 정본: DesktopPlanEditorModel.ts dkpMergeCatalogWithCustoms / DesktopPlanEditorPickerParts.tsx DkpMovePick 등록 경로 / WorkoutExtras.tsx 등록 게이트
  • 도구: 없음
  • 게이트: tests/react/compositeBuilderCustomExercise.test.mjs (병합 dedupe·스레딩·정확 일치 게이트·raw concat 재유입 차단)
  • 버그리포트: bug-015-20260824.md
  • 계약: 없음(신규 데이터 마커 0 — workout.picker.addCustom 기등록 액션 재사용)

Phase 현황

Phase내용상태
Phase 0진단 — 표면 특정(데스크톱 복합 빌더)·Production 실측(DB 1행, 표시 버그 확정)
Phase 1데스크톱 복합 빌더 커스텀 등록 경로(생성·수정 양 경로)
Phase 2카탈로그+customs 병합점 id dedupe(데스크톱 2곳·모바일 1곳)
Phase 3모바일 빌더 등록 게이트 "결과 0건" → "정확 일치 없음"
Phase 4검증 — 타입체크·테스트 1,639·빌드·브라우저 스모크·CI·Vercel
Phase 5기록 — bug-015·본 문서·이슈 종결

1. 배경

복합 종목 빌더는 데스크톱(2026-07-17 B안)·모바일(07-24 이식) 모두 "이미 카탈로그에 있는 종목"을 묶는 전제로 만들어졌다. 커스텀 종목 등록은 픽커 본면의 "커스텀 종목 추가하기"에만 있었고, 서버 create_custom_exercise는 BRID 계보(오너+압축 이름 키 = 같은 UUID)로 같은 이름 재등록을 멱등 처리한다.

2. 문제 제기

복합 빌더 안에서 커스텀 종목을 등록할 수 없다

오너가 jerk dip + rack jerk 복합 세트를 작성하다 "rack jerk"가 공식 종목이 아니라는 것을 발견 — 데스크톱 빌더 슬롯 드롭다운에는 등록 수단이 없어 작성을 중단하고 나가야 했다. 모바일 빌더의 등록 버튼도 "검색 결과 0건"일 때만 노출이라 부분 일치("저크")가 있으면 같은 벽에 부딪힌다.

등록하고 돌아오면 같은 종목이 4줄

뒤로 나가 "커스텀 종목 추가하기"로 등록 후 재검색하니 "랙 저크 Rack Jerk"가 4행. Production 실측으로 DB에는 1행뿐(서버 멱등 정상) — 클라이언트가 저장마다 로컬 customs에 무조건 append하고 catalog.concat(customs)를 id dedupe 없이 병합해, 저장 반복·카탈로그 리프레시 횟수만큼 표시 사본이 늘었다.

3. 해결 방안

원칙 (오너 승인, 2026-08-24 — 이슈 #674 계획서)

  • 빌더 안에서 바로 등록: 정확 일치가 없으면 "'검색어' 새 커스텀 종목으로 등록" 노출, 등록 즉시 슬롯 선택.
  • 노출 정책은 양 셸 통일: "결과 0건"이 아니라 "정확 일치 없음".
  • 중복은 표시층에서 해소: 병합점 id dedupe — 서버 멱등 계약(같은 이름=같은 행)은 그대로.

접근

대안판정
빌더 내 등록 버튼 + 병합점 id dedupe(클라 전용)채택 — 서버 계약 무접촉, 동선 단절 해소와 표시 버그를 한 번에
서버가 같은 이름 재등록을 에러로 거부기각 — 멱등 반환이 올바른 계약(재시도 안전), 문제는 표시층
등록 동선 안내 문구만 추가기각 — 동선 단절이 원보고의 핵심 불편

4. 적용한 내용

PR #682

  • DkpMovePick — 정확 일치(이름·영문명, 대소문자 무시) 없는 검색어에 등록 버튼, 성공 시 onPick(m)으로 슬롯 즉시 채움. 기록 항목 기본 무게×횟수(모바일 빌더 등록과 동일 계약).
  • onAddCustom 스레딩 — DkpPicker(복합 생성)·DkpExCard(복합 수정) → DkpCompositeBuilderDkpMovePick. DesktopPlanEditor의 동일 인라인 핸들러 2개는 공용 addCustomExercise로 통합(내 변경으로 3벌이 될 것을 1벌로).
  • dkpMergeCatalogWithCustoms 신설 — 같은 id는 customs 우선 1행("내 종목" 태그 보존). DkpPicker·moveCatalog 병합점 교체, setCustoms는 같은 id 교체.
  • 모바일 — ExercisePicker[...customs, ...catalog] 동일 dedupe, WfCompositeBuilder 등록 게이트 완화 + 버튼 문구에 검색어 병기.

주요 결정과 그 근거

  • dedupe는 customs 우선 — 카탈로그 리프레시 행이 이기면 세션 내 "내 종목" 태그가 사라진다. customs 항목은 서버 반환(persisted)을 병합한 값이라 정보 손실이 없다.
  • 등록 버튼 게이트는 정확 일치 기준 — 0건 기준은 부분 일치가 등록을 막는 원보고 재현 조건. 정확 일치가 생기면 버튼이 사라져 오타 재등록 루프도 자연 차단.
  • 신규 데이터 마커 0 — 등록 버튼은 기등록 workout.picker.addCustom 액션 재사용.

작업 중 드러난 것

  • Production 실측(Management API)으로 "중복 4행"이 DB가 아니라 표시임을 먼저 확정 — 데이터 정리 작업이 통째로 불필요해졌다.
  • 서버 멱등 + 클라 append 조합은 "재시도가 안전해 보일수록 표시 사본이 늘어나는" 역설 구조였다.

5. 적용 결과

항목
복합 빌더 안 커스텀 등록(데스크톱)불가(경로 없음)드롭다운에서 등록 → 슬롯 자동 선택
모바일 빌더 등록 노출결과 0건일 때만정확 일치 없으면 노출
커스텀 등록 후 목록 표시저장 반복+리프레시만큼 중복(오너 실측 4행)항상 1행(id dedupe)
DB 행1행(멱등 정상)1행(변화 없음 — 서버 무접촉)
검증tsc·react 테스트 1,639·dead-CSS·UTF-8·vite build·브라우저 스모크(오너 시나리오 재현)·CI 통과

오너 실사용 확인 완료(2026-08-24) — BUG-015 해결 확정.

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

  • 복합종목 작성이 카탈로그에 없는 종목 때문에 중단되지 않는다(생성·수정 양 경로).
  • 커스텀 등록 직후 목록이 항상 1행 — "몇 번 눌렀는지"가 화면에 남지 않는다.
  • 병합점 id dedupe 규칙·노출 정책(정확 일치 기준)이 계약 테스트로 잠겨 픽커류 신설 시 기준이 된다.

남은 것

  • 없음 — 오너 실사용 확인 완료(2026-08-24), BUG-015 해결 확정으로 트랙 종결.