BEST 상품 자동 교체
메인 BEST 편성 슬롯의 원본 히어로가 품절·판매중단되면, 같은 그룹 내 최저 매입가(net_price) 상품으로 자동 대체하고 MD 고정 편성은 보존한다.
한눈 결론
- 🎯정기 배치(주/월) + 실시간 품절 이벤트가 BEST 슬롯 원본 상태를 점검 → 동일
group_id내 판매가능 상품을net_price최저가 순으로 차순위 후보 선정 → 대체. - 🔒
swap_mode=MD_FIXED슬롯은 스킵(수동 편성 보존).AUTO슬롯만 자동 대체한다. - 🆕신규 컬럼 4종(
swap_mode·displayed_hero_code·swap_reason·swapped_on) + 신규 테이블main_content_swap_log(감사·원클릭 복원). - 🛟후보 없음 → 슬롯 노출 숨김 + 원본 보존 + MD 알림. 모든 교체는 로그에 남고 원클릭 복원.
- 🔴상태 판정식(
sold_out/long_term_sold_out/use_yn/재고의 AND·OR 결합)은 결정 대기. 카테고리 BEST 자동화는 Phase 2. - 📍대상 테이블
main_content_product는 실재(801행, PK=content_id+hero_product_code). 제안 컬럼 4종은 현재 스키마에 미존재 → 신규 추가 확인.
01 개요
무엇을, 왜.
문제. MD가 손으로 편성한 메인 BEST 슬롯의 히어로 상품이 품절(쇼트)되거나 판매중단되면, 사장님 앱 홈에 빈 자리 또는 구매 불가 상품이 노출된다. 현재는 MD가 수동으로 발견·교체해야 하므로 노출 공백과 전환 손실이 발생한다.
해결. 원본 히어로 상태를 정기·실시간으로 점검하고, 같은 상품군(group_id) 안에서 판매가능한 최저가 상품을 자동으로 끼워 넣는다. 단, MD의 의도적 고정 편성은 절대 건드리지 않는다.
- 범위(In). 메인 BEST 편성 슬롯의 상태 점검 · 자동 대체 · 스킵 · 숨김 · 이력/복원.
- 범위 밖(Out). 카테고리별 BEST 자동화(Phase 2), 후보 상품의 가격/재고 원천 데이터 정합성 보정, 앱 렌더링 변경.
02 정책 · 정의
용어와 규칙을 한 곳에.
핵심 정의
| 용어 | 정의 |
|---|---|
| 히어로 코드 | 슬롯에 노출되는 상품 식별키. 원본은 MD 편성 hero_product_code. |
| 그룹(Group) | seller_product.group_id — 동일 상품군. 대체 후보 풀의 경계. |
| Net Price | seller_product.net_price 매입 원가. 후보 1차 정렬 기준. |
| 쇼트(Short) | sold_out=true. 일시 품절. |
| 판매중단(Stop) | use_yn≠'U' 또는 long_term_sold_out. 장기 이탈. |
swap_mode | AUTO 자동 대체 대상 / MD_FIXED 자동화 제외(수동 보존). |
후보 선정 규칙
- 앵커: 원본과 동일한
group_id상품만 후보. (카테고리 확장 없음 = Phase 2) - 판매가능 필터: 쇼트/중단 아님 ·
use_yn='U'· 재고 > 0. - 정렬:
net_priceASC → (동가)net_unit_priceASC →created_onASC. - 선택: 최상위 1건을
displayed_hero_code로 노출.
충돌·안전망
🔒
MD_FIXED 우선. 슬롯이
MD_FIXED면 원본 상태와 무관하게 자동 대체하지 않는다 — 수동 편성 의도 보존.🛟
후보 없음(NO_TARGET). 동일 그룹에 판매가능 상품이 없으면 슬롯을 노출에서 숨기고, 원본 편성은 보존한 채 MD에게 알림을 보낸다.
↩️
복원. 모든 자동/수동 교체는
main_content_swap_log에 남고, MD가 원클릭으로 원본을 되돌릴 수 있다.실행 트리거
| 트리거 | 주기 | 범위 |
|---|---|---|
| 정기 점검 배치 | 주간(월요일) + 월간(1일) 06:00 시각 가정 | 전체 BEST 슬롯 상태 재평가 |
| 실시간 품절 이벤트 | 이벤트 수신 즉시 | 해당 상품이 걸린 슬롯만 재평가 |
03 데이터 모델
기존 실재 테이블 + 신규 추가. (admin-db 실측 기준)
📍
실측.
main_content_product는 실재(801행). 현재 컬럼: content_id, hero_product_code(PK 2개), badge, badge_color, path, sort_order, usable, created_by/on. 아래 4개 컬럼은 현재 미존재 → 신규 추가.main_content_product — 컬럼 추가
-- 신규 컬럼 (ALTER TABLE) swap_mode varchar(10) -- 'AUTO' | 'MD_FIXED' (기존행 기본값 미확정→AUTO 가정) displayed_hero_code varchar(100) -- 실제 노출 코드. 대체 시 후보코드, 미대체 시 = hero_product_code swap_reason varchar(10) -- 'SHORT' | 'STOP' | 'NO_TARGET' | 'MANUAL' | null swapped_on timestamp -- 최근 대체 시각
※ 원본 앵커 hero_product_code는 PK라 불변 — 복원의 기준. 노출은 displayed_hero_code로 분리한다. 슬롯 메타(badge·sort_order·path)를 대체 상품이 상속할지는 가정(상속으로 구성).
main_content_swap_log — 신규 테이블
id bigserial pk content_id varchar(100) -- 슬롯 original_code varchar(100) -- 원본 hero_product_code replaced_code varchar(100) -- 대체 코드 (NO_TARGET이면 null) group_id integer reason varchar(10) -- SHORT|STOP|NO_TARGET|MANUAL status varchar(10) -- SWAPPED|HIDDEN|RESTORED|FIXED trigger_src varchar(10) -- BATCH|EVENT|MANUAL executed_by varchar(20) -- 'system' | 관리자 id created_on timestamp default now()
04 데이터 · 화면 흐름
점검부터 대체·숨김·복원까지.
판정 → 대체 파이프라인
[트리거] 정기배치(주/월 06:00) 또는 실시간 품절이벤트 │ ▼ 1. 슬롯의 원본 hero_product_code 상태 조회 status = f(sold_out, long_term_sold_out, use_yn, 재고) 🔴 결합식 미확정 │ ├─ 정상 ─────────────► 원본 노출 유지, 종료 │ ▼ 쇼트/중단 2. swap_mode == MD_FIXED ? ├─ YES ─► 스킵. 원본 보존. (로그 status=FIXED 남기지 않음/가정) │ ▼ AUTO 3. 동일 group_id 판매가능 후보 조회 ORDER BY net_price ASC, net_unit_price ASC, created_on ASC │ ├─ 후보 있음 ─► displayed_hero_code = 후보, swap_reason=SHORT|STOP, │ swapped_on=now(), swap_log(SWAPPED) 기록 │ └─ 후보 없음 ─► 슬롯 노출 숨김(usable=false 파생/가정), 원본 보존, swap_reason=NO_TARGET, swap_log(HIDDEN), MD 알림
화면 흐름 (어드민)
- 슬롯 편성 탭 — 슬롯별 원본/현재노출/상태/교체모드 일람. 이상 슬롯 배너.
- 슬롯 상세 모달 — 원본·후보 리스트(net_price 순)·
swap_mode토글·원본 복원. - 교체 이력 탭 —
main_content_swap_log뷰 + 원클릭 복원. - 배치·정책 탭 — 트리거 on/off, 후보 규칙, 상태 판정식(미확정 표기).
05 프로토타입
우측 설명 패널의 번호 핀(①~⑧)이 "여기서 개발할 것"을 가리킨다. 넓은 화면에서 패널이 보인다.
🖥️ ↕ 프레임 안에서 스크롤 · 새 창에서 열기 ↗
라이브 프로토: 좌측 어드민 셸 + 본문 BEST 슬롯 화면 + 우측 anno 패널. 🆕 표시는 신규/변경 개발 지점.
⭐ 미정의 항목
OPEN = 스펙 미결(확정 필요) · 가정 = 프로토를 위해 임의로 정한 판단(확정 아님). 추측을 확정처럼 쓰지 않는다.
1
상태 판정식의 AND/OR 결합 OPEN
sold_out·long_term_sold_out·use_yn·total_available_quantity를 어떤 논리로 묶어 "쇼트/중단"을 판정할지 미확정(스펙상 🔴 결정 대기). 프로토는 상태 UI만 노출, 실제 규칙 미반영.2
기존 801행의
swap_mode 마이그레이션 기본값 가정기존 슬롯을 전부 AUTO로 열지, MD_FIXED로 잠글지 미정. 프로토는 AUTO 가정. MD_FIXED 가정 시 자동교체가 초기엔 거의 발동하지 않아 도입 효과가 지연됨.
3
후보 없음 → 재고 회복 시 원본 자동 복원 여부 OPEN
NO_TARGET로 숨긴 슬롯이 원본 재입고 시 자동으로 다시 노출되는지, MD 수동 복원인지 미정. 자동이라면 이벤트가 원본 회복도 감지해야 함.
4
슬롯 메타(badge·sort_order·path) 상속 여부 가정
대체 상품이 원본 슬롯의 뱃지/정렬/링크를 그대로 이어받는지 미정. 프로토는 슬롯 메타 상속 가정(상품만 교체).
5
MD 알림 채널·수신자 OPEN
NO_TARGET 발생 시 누구에게(담당 MD/전체), 어디로(어드민 알림센터/이메일/슬랙) 통지할지 미정.
6
타이브레이커
net_unit_price의 단위 정합성 가정동일 group 안 상품의 규격(용량/수량)이 달라도
net_unit_price 비교가 유효하다는 전제. 규격 상이 시 "최저가"의 체감이 어긋날 수 있음.7
대체 후보의 순간 품절 재검증 OPEN
선정된 최저가 후보가 노출 직전/직후 다시 품절될 때 2순위로 재대체하는 재시도 깊이·간격 미정.
8
원클릭 복원의 사후 모드 OPEN
복원 시 슬롯을 다시 AUTO로 둘지, 재교체 방지를 위해 MD_FIXED로 잠글지 미정.
9
group_id null 상품 처리 가정seller_product.group_id는 nullable. 원본의 group_id가 null이면 후보 풀이 성립하지 않음 → NO_TARGET로 처리 가정.10
배치 정확 주기·타임존 가정
스펙은 "주/월"만 명시. 프로토는 월요일+월1일 06:00(KST) 가정.
06 다음 단계
- 상태 판정식 확정항목①. MD·개발 합의로 AND/OR 결합 규칙과 재고 임계 정의 → 판정 함수 스펙 픽스.
- 마이그레이션 정책 결정항목②·⑧. 기존 801행 기본 swap_mode, 복원 후 모드 결정.
- 스키마 반영컬럼 4종 ALTER +
main_content_swap_log생성, 백필. - 후보 쿼리·재검증 구현항목⑦. net_price 정렬 쿼리 + 순간 품절 재시도 로직.
- 트리거 연결배치 스케줄러 + 품절 이벤트 구독. 알림 채널(항목⑤) 연결.
- 어드민 화면·복원 UI슬롯 탭·상세 모달·이력 탭·원클릭 복원.
- Phase 2 백로그카테고리 BEST 자동화, 후보 회복 자동복원(항목③).