장바구니 품절·미노출 대응 (앱)

사장님 앱 장바구니에서 담긴 상품이 품절/장기품절/미노출로 바뀌었을 때, 배지로 알리고 같은 그룹·카테고리의 대체 상품을 추천한다. 강제 교체는 하지 않는다.

📌 한눈 결론

범위
장바구니 화면 + 결제 직전. 상품 상세/검색은 범위 밖.
판정 축
sold_out · long_term_sold_out · is_original_displayed
추천 기준
같은 group_id → 없으면 카테고리 폴백, net_unit_price 오름차순 상위 3건
교체 정책
자동 교체 미적용 알림·추천만. 원본은 사용자가 직접 삭제할 때까지 유지.
신규 저장소
member_cart_alert (현재 DB 미존재 → 생성) · 기존 seller_product_sold_out_log 재사용
핵심 미정
반영 타이밍(실시간/배치) 중복 억제 단위 미노출 결제 허용 여부 · 상세는 아래

01개요

왜 필요한가 — 장바구니 담을 때는 판매 중이던 상품이, 결제 시점엔 품절·미노출로 바뀌어 있는 경우가 잦다.

사장님(회원)은 여러 날에 걸쳐 장바구니를 유지·재방문한다. 그 사이 공급사가 상품을 품절 처리하거나(sold_out), 장기 품절로 전환하거나(long_term_sold_out), 노출을 내리면(is_original_displayed=false) 담긴 상품을 그대로 주문할 수 없다. 지금은 이를 결제 단계에서야 알게 되어 이탈·재검색 비용이 크다.

MD-26은 장바구니 진입 시점에 담긴 hero_product_codeseller_product와 대조해 상태를 판정하고, 문제 상품에는 배지를, 그 아래에 같은 그룹/카테고리의 대체 추천을 붙인다. 자동으로 상품을 바꿔치기하면 사장님이 의도치 않은 발주를 낼 위험이 있어, 알림과 추천까지만 하고 담기·삭제는 사용자 행동으로 남긴다.

스코프 경계. 이 티켓은 "이미 장바구니에 담긴 상품"의 사후 대응이다. 상품 상세/검색 화면에서의 품절 표시, 알림톡/푸시로 사전 통지하는 것은 별도 티켓 범위로 본다(가정).

02정책 · 정의

상태별 판정 기준과 사용자 동작 규칙.

상태 정의

상태판정 조건 (seller_product)배지사용자 동작
품절sold_out = true품절수량조절·주문 비활성. 대체 추천 노출. (일시적, 재입고 가능)
장기품절long_term_sold_out = true장기품절담기 불가. 대체 추천 우선 노출("당분간 입고 없음").
미노출is_original_displayed = false미노출판매는 가능하나 앱에서 내려간 상품. 배지+추천. 결제 허용 여부는 미정.
정상위 조건 모두 아님평소대로 주문.

대체 추천 규칙

  • 1순위 그룹: 같은 group_id의 판매가능 상품을 net_unit_price 오름차순으로 상위 3건.
  • 2순위 폴백: group_idNULL이거나 그룹 내 대체재가 없으면 카테고리 기준으로 폴백.
  • 추천 없음: 그룹·카테고리 모두에서 대체재가 없으면 배지만 노출하고 추천 카드는 생략(어드민에서 별도 추적).
  • 담기 동작: 대체 상품 "담기"는 장바구니에 추가만 한다. 원본 품절 상품은 자동 삭제하지 않는다.
불가침 정책 — 자동 강제 교체 금지. 품절 상품을 시스템이 임의로 대체 상품으로 바꿔 담지 않는다. 사장님의 발주는 금액·수량이 그대로 매입/정산에 반영되므로, 오발주를 막기 위해 교체는 반드시 사용자 확인(직접 담기 + 원본 삭제)을 거친다.

결제 전 처리

  • 주문하기 시 장바구니에 품절/장기품절이 남아있으면 바텀시트로 경고하고, 해당 건은 자동 제외 후 나머지만 주문한다.
  • 원본은 삭제하지 않고 장바구니에 남긴다(재입고 시 다시 담을 수 있도록).

03데이터 모델

실측(app-db 개발DB) 기준. 기존 = 이미 존재, 신규 = 이번에 생성.

기존 member_cart — 장바구니 (857행)

컬럼타입비고
member_id PKvarchar(20)FK → member.id
hero_product_code PKvarchar(100)FK → seller_product.hero_product_code ← 판정 조인 키
product_qtyinteger수량
product_option_jsonjsonb옵션
selected_delivery_datedate희망 납기

기존 seller_product — 판정 소스

컬럼타입판정 역할
hero_product_codevarchar(100)조인 키
sold_outboolean NOT NULL품절 판정
long_term_sold_outboolean NOT NULL장기품절 판정
is_original_displayedboolean NULLABLE미노출 판정 — NULL 처리 미정
group_idinteger NULLABLE추천 그룹 — NULL이면 폴백
net_unit_pricebigint NULLABLE추천 정렬(오름차순)

기존 seller_product_sold_out_log — 상태 변경 이력

이미 hero_product_code · group_id · seller_id · change_type · sold_out · created_at를 적재 중. 실시간 반영 시 트리거/캐시 무효화 소스로 재사용 가능.

신규 member_cart_alert — 노출/추천/전환 로그

현재 개발DB에 존재하지 않음 → 신규 생성 필요. 초안 스펙이 참조하는 member_cart_alert는 아직 스키마에 없다. 중복 배너 억제(dedup)와 어드민 성과 튜닝의 근거가 되므로 컬럼·유니크 키를 확정해야 한다(아래 미정의 U-3).
컬럼(제안)타입용도
idbigint PK
member_idvarchar(20)회원
hero_product_codevarchar(100)대상 상품
alert_typevarchar(20)SOLD_OUT / LONG_TERM / HIDDEN
group_idinteger null추천 기준
rec_countinteger추천 노출 건수(0=추천없음)
rec_basisvarchar(12)GROUP / CATEGORY / NONE
convertedboolean대체 담기 전환
session_keyvarchardedup 단위 — 정의 미정
created_attimestamp

04화면 · 데이터 흐름

장바구니 진입 → 판정 → 표시 → (선택) 대체 담기 → 결제 차단.

1장바구니 진입/새로고침
2담긴 hero_product_code
일괄 조회
3seller_product 플래그
판정
4배지 + 대체추천
(group→category)
5member_cart_alert
적재·dedup
6결제 시 품절 제외
경고 시트
반영 타이밍(미정). 위 2~3단계를 장바구니 진입마다 실시간 조회할지, 배치로 미리 계산해 캐시에서 읽을지 결정 필요. 프로토는 "진입 시 온디맨드 실시간"으로 가정했다.

05프로토타입

화면 요소별 개발 항목은 우측 설명 패널(번호 핀)에 있다. 넓은 화면에서 패널이 보인다.

md26 / app / index.html — 클릭 가능한 프로토타입

탭 ①앱 장바구니(판정+추천) · ②결제 전 차단 시트 · ③어드민 대응 로그. 대체 "담기" 버튼과 "주문하기"는 실제로 동작한다.

06⭐ 미정의 항목

스펙에 확정되지 않은 것들. 프로토는 아래 가정으로 구성했으며, 확정 시 프로토/구현이 달라질 수 있다. 추측을 확정으로 읽지 말 것.

U-1 반영 타이밍 — 실시간 vs 배치

장바구니 진입마다 seller_product를 실시간 조회할지, 배치로 계산·캐시할지. 실시간은 정확하지만 조회 부하, 배치는 가볍지만 최신성 지연.

▶ 프로토 가정: 진입 시 온디맨드 실시간 조회. 확정 시 캐시/트리거(seller_product_sold_out_log 활용) 설계 추가.

U-2 is_original_displayed = NULL 판정

컬럼이 nullable이다. NULL을 "미노출"로 볼지 "정상 노출"로 볼지에 따라 배지 노출 대상이 크게 달라진다.

▶ 프로토 가정: NULL = 정상(노출). false일 때만 미노출로 판정. 운영 데이터 분포 확인 필요.

U-3 중복 억제(dedup) 단위 · member_cart_alert 키

같은 품절 상품에 대해 배너/로그를 얼마나 자주 남길지 — 세션당 1회? 하루 1회? 상품×회원당 1회? session_key 정의와 유니크 제약이 여기에 달림.

▶ 프로토 가정: 회원×상품×일자 단위 1회 로깅. 테이블 자체가 신규라 컬럼도 함께 확정 필요.

U-4 미노출 상품의 결제 허용 여부

미노출은 "판매는 가능"하나 앱에서 내려간 상품. 이미 담긴 경우 결제를 허용할지(경고만), 품절처럼 차단할지 미정.

▶ 프로토 가정: 미노출은 경고 후 주문 허용, 품절/장기품절만 자동 제외·차단.

U-5 다중 플래그 시 배지 우선순위

한 상품이 품절+미노출처럼 여러 상태가 동시에 참일 때 어떤 배지를 대표로 보일지.

▶ 프로토 가정: 장기품절 > 미노출 > 품절 순으로 대표 배지 표기.

U-6 대체 추천 세부 — 재고·동일 공급사·tie-break

추천 후보를 "판매가능"만으로 거를지, 최소 재고/납기 가능까지 볼지. net_unit_price 동가일 때 순서, 동일 공급사 우대 여부, net_unit_price=NULL 후보 처리.

▶ 프로토 가정: 판매가능 필터만, net_unit_price 오름차순·NULL은 후순위. 상위 3건 고정.

U-7 카테고리 폴백의 "카테고리" 기준

폴백 시 어떤 카테고리 레벨로 좁힐지(대/중/소). seller_productcategory 연결 컬럼을 실물로 확정해야 한다(이번 실측에서 미확인).

▶ 프로토 가정: 원본 상품의 최하위 카테고리 동일 기준. 연결 컬럼 확인 필요.

DEP MD-15 그룹3 흡수/분리 (선행 미정의)

추천의 1순위 축인 group_id 그룹핑 정책이 MD-15(그룹3 흡수/분리)에서 확정된다. 그룹 정의가 바뀌면 이 티켓의 추천 후보군이 통째로 달라진다.

▶ 의존: MD-15 확정 전까지 현행 group_id 기준으로 구현하되, 그룹 정의 변경에 추천 로직이 종속됨을 명시.

07다음 단계

  1. 미정의 확정 회의 — U-1~U-7 및 MD-15 의존 결정. 특히 U-2(NULL 판정)·U-3(dedup 키)는 스키마에 직결.
  2. member_cart_alert DDL 확정·생성 — 컬럼·유니크 제약(dedup 단위) 반영.
  3. 판정 API — 담긴 hero_product_code 배열 → 상태+대체추천 응답. 반영 타이밍(U-1) 방식 반영.
  4. seller_product ↔ category 연결 실물 확인 — 폴백(U-7) 기준 컬럼 확정.
  5. 앱 장바구니 UI — 배지·추천 카드·비활성 처리·결제 전 시트.
  6. 어드민 대응 로그/KPI — 추천 커버율·전환·"추천없음" 추적(그룹핑 품질 개선 피드백).
본 문서의 수치·분포(추천 커버율 등 프로토 KPI)는 예시값이며 개발DB 실측이 아니다. 스키마 존재 여부(테이블/컬럼/nullable)만 app-db 실측 기준이다.