Skip to content

관리자 커스텀 종목 전용 페이지 — ID 8자리 눈대중에서 유저별 목록·이름+@핸들까지 (2026-09-02)

  • 기간: 2026-09-02 (세션 1개, 3feaf604-f4c8-4a2d-a319-21b18878f081). 오너 지시: "커스텀 종목 조회 별도 페이지로 빼주고, 유저 별로 조회할 수 있게 해줘" → "유저표기는 이름 + @핸들로", "비활성종목은 왜 있는거야?" → go
  • 랜딩: PR #1148(Phase 1~2, a423544f). 마이그레이션 20260903020000_admin_search_users_handle_v1(함수 반환 열 추가). Production 반영은 릴리스 PR — 그 전까지 [스테이징]. 관리자 화면 자체는 docs 프로젝트 /admin/으로 즉시 배포
  • 설계서: 이슈 #1130 본문(예상 효과·개선사항 포함) + 결정 코멘트
  • 정본: DesktopAdminCustomExercises.tsx · 탭 custom(desktopAdminTabs.ts) · 유저 대응표 로드(DesktopAdminFeature.tsx adminUserDirectory) · admin_search_users_v1(handle 포함)
  • 도구: 없음(브라우저 실측은 워크트리 일회용 vite 하네스, 커밋 안 함)
  • 게이트: adminCustomExercisesPage.test.mjs 4건 · 대리 인입 대상 검색 기대값(handle) · pgTAP admin_wodup_import_on_behalf(함수 재생성 후에도 통과)
  • 버그리포트: 없음(기능 추가)
  • 계약: 마커 adminCustomUserList·adminCustomExerciseList 등록

Phase 현황

Phase내용상태
Phase 1새 탭 '커스텀 종목' — 소유 유저 목록 → 유저의 커스텀 종목 → 기존 상세 패널. 이름 @핸들 대응, 폴백, 비활성 기본 숨김+토글, 최근 등록순✅ PR #1148
Phase 2admin_search_users_v1에 handle 열 추가(drop 후 재생성)✅ PR #1148 · 릴리스 대기
Phase 3로컬 게이트 → CI 1회 → 랜딩 잠금 verify → 머지✅ (a423544f)

1. 배경

Production에 커스텀 종목 109건(활성 19, 소유자 4명)이 있는데, 관리자가 "유저 A가 만든 종목"을 보려면 전체 종목 표에서 종목 타입을 '유저 커스텀'으로 바꾼 뒤 상세를 하나씩 열어 '소유 9bca67e4…' ID 앞 8자리를 눈으로 대조해야 했다. 이름·이메일은 어디에도 없었다.

2. 문제 제기

유저 단위로 볼 화면이 없었다

전체 종목 표는 종목 단위 목록이라 소유자별 묶음·카운트가 없다.

소유자 이름을 볼 방법이 없었다

profiles 표는 RLS가 본인만 조회라 관리자도 못 읽는다. 관리자 전용 검색 함수는 이메일·표시 이름만 돌려주고 핸들이 없었다.

비활성 커스텀 종목의 정체가 불분명했다

비활성 90건 중 89건은 e2e 테스트 계정이 만든 CASE-005 리프트 … 종목을 08-24에 일괄 비활성 처리한 흔적, 1건은 다른 유저 종목 정리. 앱에는 유저의 카탈로그 삭제 기능이 없어 비활성 = 관리자 '종목 삭제'뿐이다.

3. 해결 방안

원칙 (오너 결정, 2026-09-02)

  • D1 유저 표기 = 이름 + @핸들. 핸들은 서버 함수에 없어 반환 열을 추가(릴리스 전 폴백 = 이름만).
  • D2 비활성 종목 = 기본 숨김 + '비활성 포함' 토글(테스트 흔적 89건이 목록을 덮지 않게).

접근

대안판단
새 탭 3-pane(유저 → 종목 → 상세), 종목 행·상세 패널은 전체 종목 표 부품 재사용채택 — 화면 코드 1파일, 편집·삭제는 기존 콜백 그대로
전체 종목 표에 '소유 유저' 필터 축 추가기각 — 유저별 카운트·이름 대응이 표 한 축으로는 안 보인다
프로필 RLS를 관리자 열람으로 완화기각 — 함수 반환 열 추가가 더 좁은 변경

4. 적용한 내용

Phase 1 — 화면 (#1148)

  • DesktopAdminCustomExercises.tsx: 커스텀(origin=user) 종목을 소유자별로 묶어 유저 목록(이름 @핸들·이메일·활성/전체, 종목 수 많은 순, 검색)·유저의 종목(최근 등록순 createdAt, 비활성 토글, 검색)·AdmExerciseDetail. 이름 대응 실패 시 ID 앞 8자리 + 안내 한 줄.
  • custom(전체 종목 다음), DesktopAdmin 라우팅·카탈로그 게이트 포함, DesktopAdminFeature가 탭 진입 시 searchAdminImportTargets({ query: "", limit: 50 }) 1회.
  • 관리자 카탈로그 select에 created_at(어댑터·매퍼 createdAt), 대리 인입 검색 행에 handle.

Phase 2 — 서버 (#1148)

  • admin_search_users_v1 drop 후 재생성: 반환 열 handle, 검색어도 핸들 대조(앞 @ 제거), 권한·주석 재적용. 기존 호출자 무영향.

주요 결정과 그 근거

  • 유저 대응표를 화면이 아니라 피처가 들고 탭 진입 시 1회 읽는 이유: 화면은 props-in/콜백-out 계약, 실패 폴백도 화면 한 줄로 처리 가능.

작업 중 드러난 것

  • 임시 마이그레이션 번호가 main 꼬리(다른 세션 #1142의 20260903010000)보다 앞이라 migrations:renumber20260903020000으로 옮김. 랜딩 절차대로 잠금 안에서 확정.
  • 멱등 검사는 함수도 create or replace를 요구한다(drop if exists 뒤여도).
  • 새 관리자 화면 파일은 TypeScript 경계 게이트의 "desktop presentation boundary" 목록에 등재해야 한다(형제 화면과 동일).
  • 소스 앵커 게이트는 테스트의 indexOf("문자열")을 소스 앵커로 오인한다 — 렌더 결과 순서 비교는 search(/…/)로.

5. 적용 결과

항목결과
유저별 커스텀 종목 보기ID 8자리 눈대중 대조 → 유저 목록 클릭 1회
소유자 식별없음 → 이름 @핸들(릴리스 후) · 이메일. 릴리스 전엔 이름만
기본 표시 종목 수(최대 소유자)90 → 1(활성만, 토글로 90)
하네스 실측유저 목록 박주희 @joohee 2/3·김비 1, 딥스(08-28)가 푸시업(08-20)보다 위, 토글 켜면 '테스트 흔적' 표시, 김비 클릭 시 'B의 종목' 1개
유닛 테스트2,361 → 2,374 (전부 green)
Production 반영릴리스 PR 머지 시 — [스테이징]

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

커스텀 종목 정리가 유저 단위 작업이 됐다

누가 무엇을 만들었는지 이름으로 보고, 그 자리에서 특성 편집·삭제까지 한다.

구조적으로 남는 것

  • 관리자 화면의 유저 이름 대응 경로 1곳(admin_search_users_v1, handle 포함).
  • 종목 행·상세 패널 부품 재사용 관례(새 관리자 목록 화면은 자체 구현 없이 조립).

남은 것

  • 릴리스 PR 머지 → @핸들 표시·이슈 [반영완료].
  • 사용자 50명 초과 시 소유자별 보강 검색(지금은 4명).