장바구니 품절·미노출 대응 (앱)
사장님 앱 장바구니에서 담긴 상품이 품절/장기품절/미노출로 바뀌었을 때, 배지로 알리고 같은 그룹·카테고리의 대체 상품을 추천한다. 강제 교체는 하지 않는다.
📌 한눈 결론
sold_out · long_term_sold_out · is_original_displayedseller_product_sold_out_log 재사용01개요
왜 필요한가 — 장바구니 담을 때는 판매 중이던 상품이, 결제 시점엔 품절·미노출로 바뀌어 있는 경우가 잦다.
사장님(회원)은 여러 날에 걸쳐 장바구니를 유지·재방문한다. 그 사이 공급사가 상품을 품절 처리하거나(sold_out), 장기 품절로 전환하거나(long_term_sold_out), 노출을 내리면(is_original_displayed=false) 담긴 상품을 그대로 주문할 수 없다. 지금은 이를 결제 단계에서야 알게 되어 이탈·재검색 비용이 크다.
MD-26은 장바구니 진입 시점에 담긴 hero_product_code를 seller_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_id가NULL이거나 그룹 내 대체재가 없으면 카테고리 기준으로 폴백. - 추천 없음: 그룹·카테고리 모두에서 대체재가 없으면 배지만 노출하고 추천 카드는 생략(어드민에서 별도 추적).
- 담기 동작: 대체 상품 "담기"는 장바구니에 추가만 한다. 원본 품절 상품은 자동 삭제하지 않는다.
결제 전 처리
- 주문하기 시 장바구니에 품절/장기품절이 남아있으면 바텀시트로 경고하고, 해당 건은 자동 제외 후 나머지만 주문한다.
- 원본은 삭제하지 않고 장바구니에 남긴다(재입고 시 다시 담을 수 있도록).
03데이터 모델
실측(app-db 개발DB) 기준. 기존 = 이미 존재, 신규 = 이번에 생성.
기존 member_cart — 장바구니 (857행)
| 컬럼 | 타입 | 비고 |
|---|---|---|
member_id PK | varchar(20) | FK → member.id |
hero_product_code PK | varchar(100) | FK → seller_product.hero_product_code ← 판정 조인 키 |
product_qty | integer | 수량 |
product_option_json | jsonb | 옵션 |
selected_delivery_date | date | 희망 납기 |
기존 seller_product — 판정 소스
| 컬럼 | 타입 | 판정 역할 |
|---|---|---|
hero_product_code | varchar(100) | 조인 키 |
sold_out | boolean NOT NULL | 품절 판정 |
long_term_sold_out | boolean NOT NULL | 장기품절 판정 |
is_original_displayed | boolean NULLABLE | 미노출 판정 — NULL 처리 미정 |
group_id | integer NULLABLE | 추천 그룹 — NULL이면 폴백 |
net_unit_price | bigint NULLABLE | 추천 정렬(오름차순) |
기존 seller_product_sold_out_log — 상태 변경 이력
이미 hero_product_code · group_id · seller_id · change_type · sold_out · created_at를 적재 중. 실시간 반영 시 트리거/캐시 무효화 소스로 재사용 가능.
신규 member_cart_alert — 노출/추천/전환 로그
member_cart_alert는 아직 스키마에 없다. 중복 배너 억제(dedup)와 어드민 성과 튜닝의 근거가 되므로 컬럼·유니크 키를 확정해야 한다(아래 미정의 U-3).| 컬럼(제안) | 타입 | 용도 |
|---|---|---|
id | bigint PK | — |
member_id | varchar(20) | 회원 |
hero_product_code | varchar(100) | 대상 상품 |
alert_type | varchar(20) | SOLD_OUT / LONG_TERM / HIDDEN |
group_id | integer null | 추천 기준 |
rec_count | integer | 추천 노출 건수(0=추천없음) |
rec_basis | varchar(12) | GROUP / CATEGORY / NONE |
converted | boolean | 대체 담기 전환 |
session_key | varchar | dedup 단위 — 정의 미정 |
created_at | timestamp | — |
04화면 · 데이터 흐름
장바구니 진입 → 판정 → 표시 → (선택) 대체 담기 → 결제 차단.
일괄 조회
판정
(group→category)
적재·dedup
경고 시트
05프로토타입
화면 요소별 개발 항목은 우측 설명 패널(번호 핀)에 있다. 넓은 화면에서 패널이 보인다.
탭 ①앱 장바구니(판정+추천) · ②결제 전 차단 시트 · ③어드민 대응 로그. 대체 "담기" 버튼과 "주문하기"는 실제로 동작한다.
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_product–category 연결 컬럼을 실물로 확정해야 한다(이번 실측에서 미확인).
▶ 프로토 가정: 원본 상품의 최하위 카테고리 동일 기준. 연결 컬럼 확인 필요.
DEP MD-15 그룹3 흡수/분리 (선행 미정의)
추천의 1순위 축인 group_id 그룹핑 정책이 MD-15(그룹3 흡수/분리)에서 확정된다. 그룹 정의가 바뀌면 이 티켓의 추천 후보군이 통째로 달라진다.
▶ 의존: MD-15 확정 전까지 현행 group_id 기준으로 구현하되, 그룹 정의 변경에 추천 로직이 종속됨을 명시.
07다음 단계
- 미정의 확정 회의 — U-1~U-7 및 MD-15 의존 결정. 특히 U-2(NULL 판정)·U-3(dedup 키)는 스키마에 직결.
- member_cart_alert DDL 확정·생성 — 컬럼·유니크 제약(dedup 단위) 반영.
- 판정 API — 담긴 hero_product_code 배열 → 상태+대체추천 응답. 반영 타이밍(U-1) 방식 반영.
- seller_product ↔ category 연결 실물 확인 — 폴백(U-7) 기준 컬럼 확정.
- 앱 장바구니 UI — 배지·추천 카드·비활성 처리·결제 전 시트.
- 어드민 대응 로그/KPI — 추천 커버율·전환·"추천없음" 추적(그룹핑 품질 개선 피드백).