Skip to content

관리자 표기 편집 — '숄더프레스' 미검색에서 표기·별칭 구조 관리 화면까지 (2026-08-31)

  • 기간: 2026-08-31 (세션 1, 오너 보고 "'숄더프레스' 검색했는데 바벨 숄더프레스가 왜 안나오지" → 지시 "너가 파악한 원인을 내가 종목 관리자 페이지만 보고도 한눈에 파악할 수 있도록 해당 페이지를 개편해줄래?")
  • 랜딩: PR #970(Phase 1~3) — 마이그레이션 20260831140000(130000은 #923에 같은 날 선점 — 재번호), Vercel 자동 배포
  • 설계서: 없음(이슈 #963 본문의 Phase 계획이 정본, "예상 효과·개선사항" 표 포함)
  • 정본: src/react/exerciseSynonymSearch.ts(diagnoseExerciseSynonymSearch — 검색 판정·근거) · src/react/services/barbelicRepository.ts 표기 편집 5종 · docs/contracts/desktop-screens-props.md 표기 카드·콜백 절
  • 도구: 없음
  • 게이트: tests/react/adminSynonymSearchProbe.test.mjs(진단 6) · adminExerciseSynonymPanel.test.mjs(매퍼·렌더 3) · adminSynonymRepository.test.mjs(저장소 6) · adminUiWriteContract.test.mjs +2 · supabase/tests/database/admin_synonym_editor.test.sql(pgTAP 9)
  • 버그리포트: bug-report/bug-045-20260831.md
  • 계약: desktop-screens-props.md exercise.synonyms 확장(+표기 편집 콜백 6종), exercise-search.md 변경 없음(엔진 규칙 그대로)

Phase 현황

Phase내용상태
Phase 1상세 "표기와 별칭" 구조 개편(표기 카드)✅ PR #970 (7ecaf2b3)
Phase 2검색 확인 상자(엔진 그대로 판정+근거)✅ PR #970 (7ecaf2b3)
Phase 3표기 편집(추가·이름 수정·대표 변경·삭제·별칭 편집·승격)✅ PR #970 (50d23c89)
Phase 4'바벨 숄더프레스' 승격 실전 수행·검색 확인✅ Production 실행·실측 HIT (08-31)

1. 배경

8월 24일 near-duplicate 병합(20260821550000)에서 '바벨 숄더프레스' 종목이 '바벨 오버헤드 프레스'에 흡수되며 그 이름은 교정 별칭으로 강등됐다("검색은 계속 걸리게"가 의도). 다음 날 검색 수리(#746)에서 별칭은 앞부분 일치만 지원하게 바뀌었고 — 각각은 타당했지만 겹치면서 '숄더프레스' 검색에 바벨 변형만 빠지는 구멍이 생겼다. 8월 31일 오너가 이 증상을 보고했고, 원인 분석 후 "이런 원인을 관리자 페이지만 보고 진단할 수 있게 + 이런 수리를 화면에서 직접 할 수 있게" 개편을 지시했다.

2. 문제 제기

관리자 상세가 표기 구조를 납작하게 뭉개 보여줬다

실제 구조는 종목 → 동등 표기 여러 개(대표 1) → 표기별 별칭인데, 화면은 "교정 별칭"(대표 표기 별칭만, 한글/영문 정규식 분리)과 "다른 표기"(비대표 이름만) 두 줄. 비대표 표기의 별칭은 어디에도 안 나왔다. 데이터는 이미 전부 반입되고 있었고(adminExerciseCatalog.ts) 화면 직전 변환기(barbelicViewMappers.ts)가 이름만 남기고 버렸다.

검색 규칙(표기=아무 위치, 별칭=앞부분만)이 화면에 없었다

'바벨 숄더프레스'가 별칭 목록에 보이는데 왜 검색이 안 되는지 화면만으로 알 수 없었다 — 진단에 코드 열람이 필요했다.

표기 수리를 화면에서 할 수 없었다

편집 가능한 것은 대표 표기의 별칭 목록뿐. 별칭→표기 승격은 개발자 마이그레이션(20260821600000 선례)으로만 가능했다.

3. 해결 방안

원칙 (오너 결정, 2026-08-31)

  • D1 "너가 파악한 원인을 내가 종목 관리자 페이지만 보고도 한눈에 파악할 수 있도록 해당 페이지를 개편" — 채택(Phase 1~2).
  • D2 "삭제까지 포함해서 go" — Phase 3에 표기 삭제 포함(참조 중이면 차단).

접근

  • 검색 확인은 규칙 재구현이 아니라 앱 검색 엔진의 실제 필터를 호출하는 진단 함수(diagnoseExerciseSynonymSearch)로 — 재구현이면 엔진과 어긋나는 순간 화면이 거짓말을 한다(재구현-금지 가드 테스트 상주).
  • 편집 쓰기는 관리자 RLS 직접 쓰기(기존 별칭 편집 선례)를 기본으로 하고, 클라이언트가 안전하게 못 하는 두 가지만 서버 함수로: ① 삭제(타 유저 기록 참조는 RLS 밖 + FK가 on delete set null이라 DB도 안 막음) ② 대표 변경(플립 2행+종목명 이양이 원자여야 — 중간에 끊기면 대표 0개로 카탈로그 읽기가 깨짐). 별도 편집 화면 신설은 기각 — 기존 상세 패널 카드에 편집을 붙이는 쪽이 "보는 곳=고치는 곳".

4. 적용한 내용

Phase 1 — 표기 카드 (#970 7ecaf2b3)

barbelicViewMappers.ts가 표기 트리 전체(id·대표 여부·표기별 별칭)를 전달, DesktopAdminExercises.tsx 상세가 표기 단위 카드 + 매칭 규칙 안내로 교체(전체 종목 화면도 같은 패널 재사용). desktop-screens-props.md 갱신.

Phase 2 — 검색 확인 (#970 7ecaf2b3)

exerciseSynonymSearch.tsdiagnoseExerciseSynonymSearch 신설: 실제 filterExerciseSynonymRows 결과로 나온다/안 나온다 + 어느 표기/별칭 덕인지, 안 나오면 "별칭에 포함되지만 앞부분 일치가 아니라 탈락"인 별칭을 승격 안내와 함께 보고.

Phase 3 — 표기 편집 (#970 50d23c89, 20260831140000)

  • 서버: admin_delete_exercise_synonym_v1(42501 게이트, 대표 22023 거부, 참조 시 synonym-in-use:sessions=N,plans=M 23503 거부) · admin_set_default_exercise_synonym_v1(플립+exercises.name_ko/name_en 이양 — 이름 동기화 트리거는 no-op이 되는 순서).
  • 클라: repository 5종(추가·수정·승격·삭제·대표) → catalogDomain → barbelicApi → adminController 6 핸들러 → 표기 카드 버튼(전부 확인 팝업). 승격 의미론 = 20260821600000 마이그레이션과 동일(같은 이름 행 생성/재사용 + 전 표기 행 별칭에서 대소문자 무시 제거). 삭제 차단 사유는 "이 표기로 저장된 기록 N건·계획 M건이 있어 지울 수 없습니다"로 번역.

Phase 4 — 승격 실전 수행 (Production, 2026-08-31)

바벨 오버헤드 프레스에 표기 '바벨 숄더프레스'(Barbell Shoulder Press) 신설, 대표 표기 별칭에서 해당 이름 3형(한/영/슬러그)을 이관·소거 — 화면 "표기로↑" 버튼과 같은 의미론의 트랜잭션(postcheck 포함)으로 실행(관리자 화면은 세션 로그인 장벽으로 직접 조작 불가). read-back 후 실데이터를 앱 검색 엔진에 넣어 '숄더프레스' → HIT(표기 '바벨 숄더프레스') 실측.

주요 결정과 그 근거

  • 별칭의 앞부분-일치 규칙 자체는 손대지 않음 — 08-25 오너가 확정한 소음 방지 계약(#746). 구멍은 규칙 변경이 아니라 데이터(승격)로 메운다.
  • 대표 표기의 이름 수정은 카드에서 막고 종목 수정(연필)으로 안내 — 이름 동기화 트리거(exercises→default synonym)와 이중 정본이 되는 것을 방지.

작업 중 드러난 것

  • session_exercises.synonym_id·planned_sets.synonym_id FK는 on delete set null — 표기 삭제를 DB가 막아주지 않고 기록의 표기 선택이 조용히 사라진다. 참조 검사는 서버 함수 몫.
  • schema.sql은 손 append 금지 — 계약이 "trim 후 \n\n join"이라 마이그레이션 정렬 concat으로 재생성해야 통과.
  • 신규 data-lg-hook 마커는 designContract.ts 등록, 마이그레이션 추가는 deploymentManifest.tsEXPECTED_LATEST_MIGRATION(ADM-003) 동반 상승 — 둘 다 전체 스위트가 적발해줬다.
  • 같은 날 번호 경합이 또 발생(130000을 #923이 선점) — Production 장부 재실측 후 140000으로 손 재번호·CI 재실행. 직후 랜딩된 직렬화 절차(#971, process/migration-landing.md)가 이 반복의 종결책 — 다음 마이그레이션 랜딩부터는 그 절차를 따른다.

5. 적용 결과

항목결과
비대표 표기의 별칭 노출0건 → 전체(표기 카드별 소속 표시)
검색 미노출 진단코드 열람 필요 → 화면 검색 확인 상자가 원인 문구까지 표시 (렌더·진단 테스트로 검증)
별칭→표기 승격 소요마이그레이션 1건(개발자) → 화면 클릭 2회(관리자)
표기 삭제 안전성DB 무방비(set null) → 서버 참조 검사·개수 사유 거부 (pgTAP 9건)
테스트전체 2,195건 통과(main 병합 후 기준), 신규 17건 + pgTAP 9건
Production 반영20260831140000 db push 적용·함수 2종 존재 실측 완료
'숄더프레스' 검색에 바벨 변형0건 → 1건('바벨 숄더프레스' 표기) — Production 실데이터×앱 엔진 판정 HIT 실측
관리자 화면 실물 렌더렌더 계약 테스트(renderToStaticMarkup)로 검증 — 실물 화면은 시각 정보 항목(§14, 게이트 아님)

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

관리자 화면이 검색 구조의 진단·수리 창구가 됐다

"왜 이 검색어로 안 나오지"를 화면 단독으로 판정하고(검색 확인 상자), 그 자리에서 승격·편집으로 고친다. 구조적으로 남는 것: 표기 편집 서버 함수 2종 + 진단 함수(엔진과 어긋날 수 없는 구조) + 표기 카드 계약.

병합·표기 정리가 마이그레이션 없이 돌아간다

20260821600000 같은 승격 마이그레이션이 필요했던 일이 관리자 작업이 됐다 — 이후 유사 정리는 전부 화면에서.

남은 것

  • 없음 — 전 Phase 종결(이슈 #963 [반영완료] 닫음). 잠재 동종 사례(병합 흡수명 3건: 트라이셉스 푸쉬다운·머신 카프 레이즈·고블렛 스쿼트)는 증상 보고가 오면 검색 확인 상자로 점검 후 같은 절차로 처리 — 정보성 기록.