-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Description
Description
예약 데이터 독립성 확보 / 고객 이력 영구 보존 / CASCADE 삭제 시 스냅샷 백업
배경
현재 문제 상황
1단계: CASCADE 삭제 도입
- Menu/Business 삭제 시 FK 제약조건 위반 문제 해결
@OnDelete(action = OnDeleteAction.CASCADE)적용- Menu 삭제 → BookingSlot 삭제 → Reservation 삭제 (연쇄 삭제)
- Business 삭제 → 모든 연관 데이터 삭제
2단계: 고객 이력 손실 문제 발생
시나리오 1: 업체 폐업
사업자가 업체 삭제 → Business 삭제
↓
Reservation CASCADE 삭제
↓
고객: "내가 예약했던 ○○헤어샵 정보가 사라졌어요!" ❌
시나리오 2: 메뉴 단종
사업자가 "파마 메뉴" 삭제 → Menu 삭제
↓
해당 메뉴의 Reservation CASCADE 삭제
↓
고객: "지난 달 받았던 파마 서비스 기록이 없어요!" ❌
3단계: 고객 관점의 요구사항
- 업체가 폐업하거나 메뉴가 단종되어도 자신의 예약 이력은 계속 볼 수 있어야 함
- 과거 서비스명, 가격, 날짜, 시간 등 당시 예약 정보를 영구 보존
- 법적 증빙 (예: 카드 결제 내역 vs 예약 내역 대조)
- 통계 및 분석 (예: "작년 이맘때 어떤 서비스를 받았지?")
기술적 문제
FK 기반 데이터 참조의 한계:
@Entity
public class Reservation {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "menu_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE) // ← Menu 삭제 시 함께 삭제
private Menu menu;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "business_id", nullable = false)
@OnDelete(action = OnDeleteAction.CASCADE) // ← Business 삭제 시 함께 삭제
private Business business;
}문제점:
- 데이터 의존성: Menu/Business가 삭제되면 Reservation도 함께 삭제
- 이력 손실: 고객이 과거 예약 정보를 볼 수 없음
- 통계 불가: 삭제된 업체/메뉴의 예약 통계 분석 불가능
- 법적 리스크: 결제 내역은 있는데 예약 기록이 없는 상황
UI 요구사항
고객이 예약 내역을 볼 때:
예약 이력 화면:
┌────────────────────────────────┐
│ 2025년 1월 15일 │
│ [업체명 클릭 → 업체 페이지 이동] │ ← 업체가 존재하면 링크 활성
│ 서비스: 디자인 컷 │
│ 가격: 30,000원 │
│ 시간: 14:00 - 15:00 │
└────────────────────────────────┘
업체 폐업 후:
┌────────────────────────────────┐
│ 2025년 1월 15일 │
│ [○○헤어샵 (폐업)] │ ← 업체가 삭제되어도 이름 표시
│ 서비스: 디자인 컷 │ ← 삭제된 메뉴 정보도 표시
│ 가격: 30,000원 │
│ 시간: 14:00 - 15:00 │
└────────────────────────────────┘
핵심 요구사항:
- 업체/메뉴가 삭제되어도 예약 이력은 독립적으로 존재
- 업체명, 서비스명, 가격 등 당시 정보를 스냅샷으로 저장
- 업체가 존재하면 클릭 시 페이지 이동 (인터랙션 유지)
- 업체가 삭제되면 "폐업" 표시 + 링크 비활성화
목표
- 고객 이력 영구 보존: Menu/Business 삭제와 무관하게 Reservation 데이터 유지
- 스냅샷 백업 시스템 구현: Reservation 삭제 시 자동으로 백업 테이블에 저장
- 데이터 독립성 확보: FK 참조가 아닌 스냅샷 값으로 이력 표시
- UI 인터랙션 유지: 업체가 존재하면 링크 활성화, 삭제되면 읽기 전용
- 법적 요구사항 충족: 결제/세금 증빙용 예약 기록 보존
설계 방향
방안 1: Soft Delete (기각)
구현:
@Entity
public class Menu {
@Column(name = "deleted_at")
private LocalDateTime deletedAt;
}기각 사유:
- 모든 쿼리에
WHERE deleted_at IS NULL추가 필요 - 테이블 크기 무한 증가 (삭제 데이터 영구 누적)
isActive컬럼과 역할 중복 (이미 다른 용도로 사용 중)
방안 2: ReservationSnapshot 테이블 (채택)
구현 (AI로 생성한 가안 엔티티. 언제든지 바뀔 수 있습니다. ):
@Entity
@Table(name = "reservation_snapshot")
public class ReservationSnapshot {
@Id
private UUID snapshotId;
// 원본 Reservation 정보
private UUID originalReservationId;
private LocalDateTime deletedAt;
// 스냅샷 데이터 (JSON 또는 개별 컬럼)
private UUID customerId;
private String businessName; // Business.businessName 스냅샷
private UUID businessId; // 참조용 (업체 존재 시 링크)
private String serviceName; // Menu.serviceName 스냅샷
private Integer price; // Menu.price 스냅샷
private LocalDate reservationDate;
private LocalTime startTime;
private LocalTime endTime;
private ReservationStatus status;
// 추가 정보
@Column(columnDefinition = "TEXT")
private String snapshotData; // 전체 JSON (확장성)
}Todo
-
1. ReservationSnapshot Entity 설계
- Entity 클래스 작성
- Repository 작성
-
2. Reservation 삭제 시 백업 로직 구현
@PreRemove또는 Service Layer 어디에서 처리할지 결정해야 합니다.- 스냅샷 생성 시점, 범위 조건 설계하기
-
3. 스냅샷 데이터 구조 설계
-
4. CASCADE 삭제 시 백업 트리거 구현
-
5. 고객 예약 이력 조회 API 개선
-
6. 테스트 시나리오 작성
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels