테마
Story: 상품 복구 API
메타
| 항목 | 값 |
|---|---|
| Story ID | E-09-S-03 |
| Epic | E-09 미사용 상품 정리 |
| 상태 | ready-for-dev |
| 우선순위 | P1 |
| 규모 | S |
| 담당 개발자 | BE |
사용자 스토리
As a 셀러,
I want 실수로 삭제한 상품을 복구하고 싶다,
So that 다시 원가를 관리할 수 있다.
수락 기준 (Acceptance Criteria)
AC-01: 복구 가능 조건
| 항목 | 내용 |
|---|---|
| Given | 삭제 후 7일 이내인 상품에 대해 |
| When | 복구 API를 호출하면 |
| Then | 상품이 복구되어 목록에 다시 표시된다 |
AC-02: 복구 불가 조건
| 항목 | 내용 |
|---|---|
| Given | 삭제 후 7일이 지난 상품에 대해 |
| When | 복구 API를 호출하면 |
| Then | "복구 기간이 만료되었습니다" 에러 반환 |
AC-03: 삭제된 상품 목록 조회
| 항목 | 내용 |
|---|---|
| Given | 셀러가 삭제한 상품이 있을 때 |
| When | 삭제 목록 API를 호출하면 |
| Then | 7일 이내 삭제된 상품 목록이 반환된다 |
태스크 분해
Task 1: 삭제 상품 목록 API
- [ ] 1.1: GET /products/deleted
- 7일 이내 삭제된 상품만
- 삭제일, 남은 복구 기간 포함
- [ ] 1.2: 페이지네이션 지원
Task 2: 개별 복구 API
- [ ] 2.1: POST /products/{productId}/restore
- deleted_at = NULL
- deleted_by = NULL
- [ ] 2.2: 복구 가능 기간 검증 (7일)
Task 3: 일괄 복구 API
- [ ] 3.1: POST /products/restore/batch
- Request:
- [ ] 3.2: 부분 복구 처리 (일부만 복구 가능한 경우)
API 스펙
삭제 상품 목록
GET /api/products/deleted
Response 200:
{
"products": [
{
"id": "prod_001",
"name": "테스트 상품",
"sku": "SKU-001",
"deletedAt": "2026-01-20T10:00:00Z",
"recoveryDeadline": "2026-01-27T10:00:00Z",
"daysUntilPermanentDelete": 5
}
],
"total": 15,
"page": 1,
"limit": 20
}개별 복구
POST /api/products/{productId}/restore
Response 200:
{
"success": true,
"message": "상품이 복구되었습니다.",
"product": {
"id": "prod_001",
"name": "테스트 상품"
}
}
Response 400 (복구 기간 만료):
{
"success": false,
"error": "RECOVERY_PERIOD_EXPIRED",
"message": "복구 기간이 만료되었습니다. 다음 데이터 수집 시 자동으로 재등록됩니다."
}일괄 복구
POST /api/products/restore/batch
Request:
{
"productIds": ["prod_001", "prod_002", "prod_003"]
}
Response 200:
{
"success": true,
"restoredCount": 2,
"failedCount": 1,
"failures": [
{
"productId": "prod_003",
"reason": "RECOVERY_PERIOD_EXPIRED"
}
]
}개발 노트
복구 로직
typescript
async restoreProduct(productId: string, sellerId: string) {
const product = await this.productRepo.findOne({
id: productId,
sellerId,
deletedAt: Not(IsNull())
});
if (!product) {
throw new NotFoundException('삭제된 상품을 찾을 수 없습니다');
}
// 7일 경과 체크
const daysSinceDelete = daysDiff(product.deletedAt, new Date());
if (daysSinceDelete >= 7) {
throw new BadRequestException('복구 기간이 만료되었습니다');
}
// 복구
await this.productRepo.update(productId, {
deletedAt: null,
deletedBy: null
});
return { success: true };
}이벤트 로깅
| 이벤트 | 파라미터 |
|---|---|
product_restore | seller_id, product_count |
product_restore_failed | seller_id, product_id, reason |
생성일: 2026-01-20
