테마
E-03: 원가 히스토리
담당: 희선님 (수민님) 상태: 설계 중 우선순위: P0
WHY (왜 필요한가?)
현재 문제
장사왕의 핵심 해자는 원가 → 순이익 계산인데, 원가 히스토리가 없어서 해자가 초장부터 무너지고 있음.
[DELETE-INSERT ETL 구조 문제]
1. 셀러가 원가 변경: 14,520원 → 29,040원 (1월 12일)
2. ETL 실행 시 SettlementMargin 전체 재생성
3. 과거 주문(12월)도 현재 원가(29,040원)로 계산
4. → 12월 순이익 전부 틀림 (2배 차이)데이터 분석 결과 (2026-01-27)
실제 케이스 (vendorId: A01208524, productId: 94185147814):
| 테이블 | 데이터 |
|---|---|
| PurchasePrice | id=78936: 14,520원 (09:06:07) → id=78938: 29,040원 (09:06:33) |
| Product | purchasePrice=29,040원, purchaseId=78938 (최신만 참조) |
| SettlementMargin | 12월 주문 전부 29,040원으로 계산됨 |
핵심 발견:
- PurchasePrice 테이블에 히스토리 row가 쌓이긴 함
- 그러나 Product.purchaseId는 최신 row만 참조
- SettlementMargin ETL은 현재 Product.purchasePrice로 덮어씀
- → 히스토리가 있어도 아무도 안 씀
영향받는 셀러
| 카테고리 | 원가 변동 | 현재 상태 |
|---|---|---|
| 공산품 | 안정적 | ✅ 사용 가능 |
| 농수산물 | 시세 변동 | ❌ 순이익 부정확 |
| 식자재 | 계절/수급 | ❌ 순이익 부정확 |
| 수입품 | 환율 변동 | 🟡 부분적 문제 |
전략적 의미
- 해자 강화: 원가 히스토리까지 관리 → 경쟁사 따라오기 더 어려움
- TAM 확장: 농수산물/식자재 셀러 세그먼트 진입 가능
- AI 진단 전제조건: E-04 AI 진단 엔진이 정확하려면 순이익이 정확해야 함
- 신뢰도: "장사왕 숫자가 맞다" → Aha Moment → 유료 전환
WHAT (무엇을 만드는가?)
핵심 요구사항
Backend (P0)
| 기능 | 설명 | 우선순위 |
|---|---|---|
| 원가 변경 시점 기록 | 원가 변경 시 시점별 히스토리 보관 | P0 |
| 주문 시점 원가 적용 | SettlementMargin 계산 시 주문일 기준 원가 사용 | P0 |
| ETL 로직 수정 | DELETE-INSERT 시 원가 히스토리 참조 | P0 |
| 원가 히스토리 CRUD API | 셀러가 히스토리 조회/추가/수정할 수 있는 API | P0 |
Frontend (P0)
| 기능 | 설명 | 우선순위 |
|---|---|---|
| 원가 히스토리 관리 모달 | 상품별 원가 이력 조회/추가/수정 UI | P0 |
| 매입원가 테이블 연동 | 히스토리 있는 상품 아이콘 표시, 모달 진입 | P0 |
| 간편 원가 수정 | 테이블에서 직접 수정 시 오늘부터 적용 | P0 |
적용 범위
- (A) 신규 원가 변경부터 적용 ✅ 채택
(B) 기존 데이터 마이그레이션(복잡도 높음, 제외)
HOW (어떻게 구현하는가?)
해결 방안 옵션
| 방안 | 설명 | 장점 | 단점 |
|---|---|---|---|
| A | SettlementMargin에 주문 시점 원가 스냅샷 저장 | ETL 로직만 수정 | 테이블 크기 증가 |
| B | PurchasePrice 히스토리 + 주문일 기준 조회 | 정규화된 구조 | 조회 복잡도 증가 |
| C | ETL 시 원가 변경 없으면 기존값 유지 | 최소 변경 | 근본 해결 아님 |
기술 설계
관련 테이블:
Product
├── purchasePrice (int) - 현재 원가
├── purchaseId (int) - PurchasePrice FK
└── marginUpdatedAt (timestamp) - 수정일
PurchasePrice
├── id (PK, auto increment)
├── bizId, marketCode, vendorId, productId (인덱스)
├── purchasePrice (int)
├── createdAt, updatedAt (timestamp)
└── [히스토리 row 쌓이는 중, 그러나 미활용]
SettlementMargin
├── orderDate (파티션 키)
├── purchasePrice (decimal) - 현재: ETL 시점 값
├── profitAmount (decimal) - 순이익
└── [DELETE-INSERT ETL로 매번 재생성]고려사항:
- PurchasePrice 테이블 활용 가능 (이미 히스토리 쌓이는 중)
- SettlementMargin ETL 로직 수정 필요
- Product 테이블 full scan 절대 금지 (무거운 테이블)
UI 설계: 원가 히스토리 관리 모달
진입점: 매입원가 테이블에서 히스토리 아이콘(📜) 클릭
┌─────────────────────────────────────────────────────────────────────┐
│ 원가 관리 [X] │
│ 오뚜_그로스2 (8479697209) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 현재 적용 원가: 25,800원 (2026-01-12 ~) │
│ │
│ ─── 원가 이력 ─── │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 적용 기간 │ 원가 │ │ │
│ ├────────────────────────┼───────────┼────────────────────────┤ │
│ │ 2026-01-12 ~ 현재 │ 25,800원 │ [수정] │ │
│ │ 2025-12-15 ~ 01-11 │ 14,520원 │ [수정] │ │
│ │ 2025-11-01 ~ 12-14 │ 12,000원 │ [수정] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ─── 새 원가 추가 ─── │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 적용 시작일 [2026-01-27 📅] │ │
│ │ 원가 [28,000 ] 원 │ │
│ │ [추가하기] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ℹ️ 해당 기간의 주문은 이 원가로 순이익이 계산됩니다 │
│ │
└─────────────────────────────────────────────────────────────────────┘동작 방식
| 위치 | 동작 | 적용 기준일 |
|---|---|---|
| 테이블에서 직접 수정 | 원가만 변경 | 오늘 (간편) |
| 모달에서 추가 | 원가 + 적용 시작일 | 지정한 날짜 |
| 모달에서 수정 | 기존 이력 기간/원가 수정 | 지정한 기간 |
EDGE (예외/엣지 케이스)
Backend
| 케이스 | 처리 방안 |
|---|---|
| 원가 미입력 상품 | 기존 로직 유지 (0원 또는 null) |
| 같은 날 여러 번 원가 변경 | 마지막 값 사용 또는 시간 단위 구분 |
| 과거 주문 소급 적용 | ❌ 하지 않음 (신규 변경부터 적용) |
| PurchasePrice 중복 row | 기존 데이터 정합성 확인 필요 |
| 기간 중복 원가 추가 | 기존 기간 자동 조정 (종료일 업데이트) |
Frontend
| 케이스 | 처리 방안 |
|---|---|
| 시작일이 기존 이력과 겹침 | 경고 후 기존 이력 종료일 자동 조정 |
| 미래 날짜 시작일 입력 | 허용 (예약 원가 개념) |
| 현재 적용 중인 원가 삭제 | ❌ 불가 (비활성화만 허용) |
| 모달/테이블 동시 수정 | 낙관적 잠금 또는 새로고침 안내 |
| 원가 0원 또는 음수 입력 | 양수 검증 + 에러 메시지 |
성공 기준 (KR3)
| 지표 | 목표 |
|---|---|
| 원가 변동 셀러 순이익 정확도 | 주문 시점 원가 적용 |
| 변동성 카테고리 셀러 원가 입력률 | 측정 (베이스라인 확보) |
| 순이익 페이지 재방문율 | 측정 (개선 기대) |
Story 목록
| Story ID | 제목 | 영역 | 규모 | 우선순위 | 상태 |
|---|---|---|---|---|---|
| E-03-S-01 | 원가 히스토리 조회 로직 | BE | M | P0 | draft |
| E-03-S-02 | SettlementMargin ETL 수정 | BE | L | P0 | draft |
| E-03-S-03 | 원가 히스토리 CRUD API | BE | M | P0 | draft |
| E-03-S-04 | 원가 히스토리 관리 모달 UI | FE | L | P0 | draft |
| E-03-S-05 | 매입원가 테이블 연동 | FE | M | P0 | draft |
| E-03-S-06 | 정합성 검증 및 모니터링 | BE | S | P1 | draft |
| E-03-S-07 | 원가 CSV 다운로드 | BE+FE | M | P0 | draft |
| E-03-S-08 | 원가 CSV 업로드 | BE+FE | L | P0 | draft |
의존성 그래프
[BE 핵심]
E-03-S-01 (조회 로직) ──→ E-03-S-02 (ETL 수정)
└──→ E-03-S-03 (CRUD API) ──┬──→ E-03-S-04 (모달 UI)
└──→ E-03-S-05 (테이블 연동)
[검증]
E-03-S-02 완료 후 ──→ E-03-S-06 (정합성 검증)
[CSV 기능] (독립적, 병렬 진행 가능)
E-03-S-07 (CSV 다운로드) ──→ E-03-S-08 (CSV 업로드)
└──→ E-05 Feature 매트릭스 연계참고 자료
- DB 분석 세션:
s53-prep(2026-01-27) - 관련 테이블: Product, PurchasePrice, SettlementMargin
- 전략 문서:
.context/global/strategy.md
생성일: 2026-01-27최종 수정: 2026-01-27
