MD-26 · 장바구니 품절·미노출 대응 (알림·대체 추천)

장바구니 품절·미노출 대응 (품절 알림 · 대체 상품 추천)

장바구니에 담긴 상품이 품절·미노출로 바뀌면 조회 시점에 감지해 품절 배지 + 대체 상품 추천 칸(동일 그룹 차순위·최저가)을 노출하여 이탈·매출 손실을 막는다.

🟡 준비 후 착수(4순위) 앱(사장님 화면) 매출 손실 방지 MD-17 추천 로직 공유
📱 앱 화면 개선
무엇 — 장바구니에 담긴 상품이 품절·미노출로 바뀌면 조회 시점에 감지해 품절 배지 + 대체 상품 추천 칸(동일 그룹 차순위·최저가)을 노출하여 이탈·매출 손실을 막는다.📍 위치: 사장님 앱 › 장바구니

핵심 결정

감지 시점
장바구니 조회 시점seller_product.sold_out / long_term_sold_out / is_original_displayed 실시간 대조 (가정)
대체 추천 기준
동일 group_id 차순위 중 넷 공급가(net_unit_price) 최저가 상위 3건 — MD-17 폴백 로직 공유
그룹 후보 0건
동일 카테고리 판매중 상품으로 폴백, 그래도 없으면 품절 배지만 표시(추천 칸 숨김)
UX 방향
알림 + 수동 대체 담기(1탭) — 자동 대체(강제 교체)는 미적용 (결정 필요)
알림 과함 방지
세션당 상품별 배너 1회, member_cart_alert 로그로 dedupe·성과 측정
01

화면 — 먼저 보기

이 티켓이 실제로 어떤 화면이 되는지 먼저 봅니다

장바구니에 담아둔 뒤 품절·미노출로 바뀐 상품을 사장님이 모르고 주문 시도하면 결제 단계에서 막혀 이탈로 이어진다. 조회 시점에 감지해 대체 상품(동일 group_id 차순위·넷 공급가 최저)으로 전환을 유도한다.
현행 화면 · 실제 캡처
현행 사장님 앱 「장바구니」(스테이지) — 품절 배지·대체 추천 추가
현행 사장님 앱 「장바구니」(스테이지) — 품절 배지·대체 추천 추가
변경안 · 제안 화면
사장님 앱 › 장바구니기능 추가
담긴 상품 ▾품절 3건 · 대체 보기주문하기
상품(히어로코드)상태수량넷 공급가대체 추천 🆕
한우 등심 1++ 1kg (H-83201)🔴 품절2net 42,000원▷ 한우 등심 1+ 1kg net 38,500원 · 담기
청양고추 500g (H-11720)🟠 판매중지3net 3,200원▷ 꽈리고추 500g net 2,900원 · 담기
대저 토마토 2kg (H-40915)정상1net 8,900원

▲ 사장님 앱 — 바뀌는 컬럼은 🆕 로 표시(예시 데이터)

1
장바구니 진입 — 사장님이 장바구니를 열면 담긴 상품의 hero_product_code를 seller_product와 실시간 대조해 품절·장기품절·미노출을 판정한다.
2
품절·미노출 배지 표시 — 해당 상품 카드에 사유별 배지('품절'/'장기품절'/'판매중지')를 붙이고, 정상 상품과 시각적으로 구분한다.
3
대체 상품 추천 — 동일 group_id 차순위 중 넷 공급가 최저가 상위 3건을 추천 칸에 노출(그룹 0건이면 동일 카테고리로 MD-17 폴백)한다.
4
1탭 대체 담기 — 사장님이 추천 카드를 담으면 member_cart에 insert하고 원상품은 유지(직접 제거)한다. 노출·클릭·전환을 member_cart_alert에 기록.
5
주문 진행 시 선차단 — 주문하기를 누를 때 품절·미노출 상품이 남아있으면 결제 진입 전에 '대체 담기 또는 제거'로 유도한다.
6
어드민 성과 확인 — member_cart_alert 집계로 유형별 노출·대체 클릭·전환율을 확인하고, 대체 추천 기준(그룹/카테고리/최저가)을 튜닝한다.
02

정책 / 정의

용어를 못 박고, 규칙을 명확히

용어 정의
용어정의
히어로코드상품 식별 표준키(hero_product_code). 장바구니·대체 후보 모두 이 키로 대조·연결한다.
그룹코드(group_id)동종 상품을 묶는 seller_product.group_id. 대체 추천의 1순위 후보군 기준이며 MD-17 차순위 폴백과 동일 축을 쓴다.
품절(sold_out)seller_product.sold_out=true. 일시 품절 상태로, 대체 추천 + 품절 배지를 노출한다.
장기품절(long_term_sold_out)seller_product.long_term_sold_out=true. 담기 버튼을 비활성하고 대체 추천을 우선 노출한다.
미노출(is_original_displayed=false)판매는 살아있으나 앱 진열에서 내려간 상태. 사장님 화면에서 노출 불가이므로 대체 추천으로 유도한다.
넷 공급가(net)매입 순공급가(net_unit_price, 소문자 표기). 대체 후보 최저가 정렬의 기준값이자 장바구니 표기가.
대체 추천 칸품절·미노출 상품 바로 아래 붙는 카드. 동일 그룹 차순위·최저가 상품을 1탭으로 담을 수 있게 한다.
정책 (규칙)
#규칙비고
1장바구니 조회(진입·새로고침) 시 담긴 hero_product_code를 seller_product와 실시간 대조해 품절/미노출을 판정한다.실시간 대조 채택. '반영 시점(실시간 vs 배치)'은 확정 필요 — 가정값.
2대체 1순위 후보 = 동일 group_id AND sold_out=false AND long_term_sold_out=false AND is_original_displayed=true AND 원상품 제외.정렬은 net_unit_price 오름차순 상위 3건. MD-17과 동일 후보 쿼리를 공유한다.
3group_id 후보가 0건이면 동일 카테고리 판매중 상품으로 폴백한다.MD-17의 '그룹코드 차순위 폴백'과 로직 공유. 카테고리도 0건이면 추천 칸을 숨긴다.
4미노출 사유별 배지: sold_out=true→'품절', long_term_sold_out=true→'장기품절'(담기 비활성), is_original_displayed=false→'판매중지'.세 경우 모두 대체 추천 칸을 노출하되 배지 문구·CTA만 분기.
5결제/주문하기 진행 시 품절·미노출 상품이 포함되어 있으면 진행을 차단하고 '대체 담기 또는 제거'를 유도한다.가정 — 현행은 결제 단계에서야 막혀 이탈 유발. 조회 시점 선차단으로 개선.
6대체 담기는 1탭으로 member_cart에 insert하며, 원상품은 자동 삭제하지 않고 사장님이 직접 제거한다.자동 강제 교체는 오주문 리스크로 미적용. '알림만 vs 자동 대체'는 결정 필요 항목.
7알림 과함 방지: 동일 상품 배너는 세션(또는 1일)당 1회만, member_cart_alert에 (member_id, hero_product_code, session_key) upsert로 dedupe.재조회마다 반복 배너 노출 금지.
8품절·대체 추천 노출/클릭/전환은 member_cart_alert에 기록해 어드민에서 성과(전환율)를 집계한다.기대효과(이탈 방지·대체 구매 유도) 측정 근거.
9화면 명칭·위치는 스테이지(테스트) 어드민 기준이며, 운영 반영 전 어드민 성과 화면을 운영에서 대조한다.티켓 '운영 확인' 항목.
03

ERD

데이터 구조 · 관계 · DDL

member_cart WRITE
member_idPK varchar(20)
hero_product_codePK/FK varchar(100)
product_qtyinteger
selected_delivery_datedate
product_option_jsonjsonb
created_attimestamp
장바구니. 조회 시 이 행의 hero_product_code로 품절/미노출을 판정하고, 대체 담기 시 신규 행을 insert한다.
seller_product READ
hero_product_codePK varchar(100)
sold_outboolean
long_term_sold_outboolean
is_original_displayedboolean
group_idinteger
net_unit_pricebigint
main_product_namevarchar
상품 마스터. 품절·미노출 판정 소스이자 동일 group_id로 대체 후보를 조회하는 대상.
seller_product_sold_out_log READ
hero_product_codeFK varchar(100)
group_idinteger
change_typevarchar(20)
sold_outboolean
created_attimestamp
품절 전환 이력. '감지 시점' 설계 검증 및 어드민 품절 전환 모니터링에 활용.
member_cart_alert 신규
idPK bigserial
member_idFK varchar(20)
hero_product_codeFK varchar(100)
alert_typevarchar(20)
alt_hero_product_codevarchar(100)
session_keyvarchar(50)
shown_attimestamp
clicked_ynboolean
converted_ynboolean
장바구니 품절·미노출 알림/대체 추천 노출 로그. dedupe(과함 방지) + 성과(노출·클릭·전환) 측정 겸용.
member_cart.hero_product_code → seller_product.hero_product_code (담긴 상품의 품절·미노출 상태 판정)
seller_product(원상품).group_id = seller_product(후보).group_id (동일 그룹 차순위 대체 후보 조회, net_unit_price 오름차순)
member_cart_alert.hero_product_code → seller_product.hero_product_code (감지 원상품)
member_cart_alert.member_id → member.id (알림 대상 식당)
-- 장바구니 품절·미노출 알림/대체 추천 로그 (신규)
-- 용도: 알림 과함 방지(dedupe) + 성과 측정(노출·클릭·전환)
CREATE TABLE public.member_cart_alert (
    id                    bigserial     PRIMARY KEY,                 -- PK
    member_id             varchar(20)   NOT NULL,                    -- 식당(사장님) 회원, → member.id
    hero_product_code     varchar(100)  NOT NULL,                    -- 품절/미노출 감지된 원상품(히어로코드), → seller_product
    alert_type            varchar(20)   NOT NULL,                    -- 감지 유형: SOLD_OUT / LONG_TERM_SOLD_OUT / HIDDEN
    alt_hero_product_code varchar(100),                              -- 추천된 대체 상품(동일 group_id 차순위·넷 공급가 최저)
    session_key           varchar(50)   NOT NULL,                    -- 세션 단위 dedupe 키(세션당 상품별 1회 노출)
    shown_at              timestamp     NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Seoul'),  -- 배너 노출 시각
    clicked_yn            boolean       NOT NULL DEFAULT false,      -- 대체 카드 클릭 여부
    converted_yn          boolean       NOT NULL DEFAULT false,      -- 대체 담기/주문 전환 여부
    CONSTRAINT fk_cart_alert_member  FOREIGN KEY (member_id)         REFERENCES public.member(id),
    CONSTRAINT fk_cart_alert_product FOREIGN KEY (hero_product_code) REFERENCES public.seller_product(hero_product_code)
);

-- 알림 과함 방지: 동일 세션·상품 배너 중복 노출 차단
CREATE UNIQUE INDEX ux_member_cart_alert_dedupe
    ON public.member_cart_alert (member_id, hero_product_code, session_key);

-- 성과 집계용(기간·유형별 전환율)
CREATE INDEX ix_member_cart_alert_stat
    ON public.member_cart_alert (alert_type, shown_at);

-- (참고) 대체 후보 조회 성능: 그룹 차순위·최저가 정렬 커버
CREATE INDEX IF NOT EXISTS ix_seller_product_group_alt
    ON public.seller_product (group_id, sold_out, is_original_displayed, net_unit_price);
04

화면 상세 — 세부 표·규칙

목록 외 품목표·모달·규칙 등 세부 화면

대체 추천 규칙표 (MD-17 로직 공유)
우선순위후보 조건정렬 기준폴백
1동일 group_id · sold_out=false · long_term_sold_out=false · is_original_displayed=true · 원상품 제외net_unit_price 오름차순 상위 3
2(그룹 후보 0건) 동일 카테고리 판매중 상품net_unit_price 오름차순 상위 3MD-17 그룹 차순위 폴백
3(카테고리도 0건)추천 칸 숨김 · 품절 배지만 노출
결정 필요: 대체 기준을 그룹 우선/카테고리/최저가 중 어디까지 열지. MD-17과 동일 쿼리를 공유해 중복 구현을 피한다(가정).
어드민 · 장바구니 품절·대체 성과 (member_cart_alert 집계)
기간감지 유형노출대체 클릭전환(담기/주문)전환율
2026-07품절(SOLD_OUT)1,24047021016.9%
2026-07판매중지(HIDDEN)320883310.3%
2026-07장기품절156614025.6%
스테이지 어드민 기준. 운영 반영 전 이 성과 화면을 운영에서 대조(티켓 '운영 확인'). 전환율로 대체 추천 기준을 튜닝한다.