Skip to content

Reservation 삭제 시 고객 예약 이력 보존 (스냅샷 백업) #76

@sehan528

Description

@sehan528

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;
}

문제점:

  1. 데이터 의존성: Menu/Business가 삭제되면 Reservation도 함께 삭제
  2. 이력 손실: 고객이 과거 예약 정보를 볼 수 없음
  3. 통계 불가: 삭제된 업체/메뉴의 예약 통계 분석 불가능
  4. 법적 리스크: 결제 내역은 있는데 예약 기록이 없는 상황

UI 요구사항

고객이 예약 내역을 볼 때:

예약 이력 화면:
┌────────────────────────────────┐
│ 2025년 1월 15일                │
│ [업체명 클릭 → 업체 페이지 이동] │  ← 업체가 존재하면 링크 활성
│ 서비스: 디자인 컷               │
│ 가격: 30,000원                 │
│ 시간: 14:00 - 15:00            │
└────────────────────────────────┘

업체 폐업 후:
┌────────────────────────────────┐
│ 2025년 1월 15일                │
│ [○○헤어샵 (폐업)]              │  ← 업체가 삭제되어도 이름 표시
│ 서비스: 디자인 컷               │  ← 삭제된 메뉴 정보도 표시
│ 가격: 30,000원                 │
│ 시간: 14:00 - 15:00            │
└────────────────────────────────┘

핵심 요구사항:

  1. 업체/메뉴가 삭제되어도 예약 이력은 독립적으로 존재
  2. 업체명, 서비스명, 가격 등 당시 정보를 스냅샷으로 저장
  3. 업체가 존재하면 클릭 시 페이지 이동 (인터랙션 유지)
  4. 업체가 삭제되면 "폐업" 표시 + 링크 비활성화

목표

  • 고객 이력 영구 보존: 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. 테스트 시나리오 작성


Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions