백엔드 기초 재정비/ERD 기반 API 설계

구독 시스템 설계에서 status가 핵심인 이유

namerong 2026. 2. 23. 11:07

구독 상태(status)가 왜 필요한가

구독 시스템에서 status 컬럼은 선택이 아니라 필수에 가깝다.
구독은 단순한 연결 관계가 아니라 “현재 어떤 상태에 있는 계약인가”를 표현해야 하기 때문이다.


1. 구독 테이블에 status가 없다면

  • 현재 구독 중인지 즉시 판단할 수 없다.
  • end_date만으로 판단해야 하므로 로직이 복잡해진다.
  • 결제 실패, 일시정지, 해지 예약 같은 상태를 표현하기 어렵다.
  • 중복 구독, 이중 결제, 환불 충돌 같은 문제가 발생할 수 있다.

즉, 상태 개념이 없으면 모든 판단을 날짜 계산과 조건문으로 처리해야 하고,
결과적으로 로직이 복잡해지고 버그가 많아진다.


2. 구독 테이블에 status가 필요한 이유

1) 현재 구독 상태를 즉시 판단하기 위해

예시:

  • ACTIVE → 이용 가능
  • PAUSED → 이용 불가
  • CANCELED → 재결제 필요
  • EXPIRED → 자동 종료

비즈니스 로직은 대부분 “상태 기준”으로 동작한다.

 

2) 과거 이력 보존을 위해

사용자가 해지했다고 row를 삭제하면 다음이 불가능해진다.

  • 매출 분석
  • 재구독 패턴 분석
  • 고객 이탈 분석
  • 특정 기간 구독 유지율 계산

구독은 계약 이력 데이터이므로 삭제하지 않고 상태로 관리해야 한다.

 

3) 동시성 및 결제 문제 방지

구독은 결제 시스템과 밀접하게 연결된다.

예시 상태 흐름:

  • 결제 요청 → PENDING
  • 결제 성공 → ACTIVE
  • 결제 실패 → FAILED 또는 PENDING 유지

status가 없다면:

  • 중복 결제 처리
  • 이중 구독 생성
  • 환불 충돌
  • 비정상 상태 발생

등의 문제가 생길 수 있다.

status는 “현재 구독의 비즈니스 상태를 명확하게 표현하기 위한 핵심 컬럼”이다.


3. 구독 시작 시나리오

1. 사용자 흐름

  • 사용자가 구독 버튼 클릭
  • POST /api/subscriptions 요청

2. 서버 로직

1. 기존 ACTIVE 구독이 있는지 확인

2. 결제 요청 진행

3. 결제 성공 시 상태 변경

예시:

status = ACTIVE
started_at = now
ended_at = now + 1month

4. DB 저장

 

3. 상태 변화 흐름

PENDING → ACTIVE → EXPIRED
  • PENDING: 결제 대기
  • ACTIVE: 이용 가능
  • EXPIRED: 기간 만료

4. 구독 해지 시나리오

중요한 포인트는 “해지 정책”이다.

  • 즉시 종료형인지
  • 다음 결제 주기까지 유지형인지

서비스 정책에 따라 다르다.

1. 즉시 해지형

PATCH /api/subscriptions/{id}/cancel

처리:

status = CANCELED
ended_at = now
  • 즉시 이용 중단

2. 기간 만료 후 종료형

status = CANCELED
ended_at = 기존 유지
  • 현재 결제 주기까지 ACTIVE처럼 동작
  • 다음 자동 갱신만 차단
  • 기간이 끝나면 EXPIRED로 전이

3. 상태 전이 다이어그램

           (결제 성공)
PENDING  ------------> ACTIVE
                             |
                             | (해지 요청)
                             v
                         CANCELED
                             |
                             | (기간 만료)
                             v
                          EXPIRED

이 구조가 바로 상태 기반 설계(State Transition)다.


5. 잘못된 설계 vs 개선 설계

1. 잘못된 설계

  • status 없음
  • end_date만으로 구독 판단
  • 해지 시 row 삭제

결과:

  • 로직 복잡
  • 예외 처리 어려움
  • 분석 불가
  • 버그 발생 가능성 증가

2. 개선 설계

  • status 명확하게 정의
  • 날짜 + 상태 병행 사용
  • 삭제 대신 상태 변경
  • 상태 전이 흐름을 명시적으로 설계

이 방식이 안정적인 도메인 모델이다.


6. REST 관점에서의 cancel

왜 cancel을 DELETE가 아니라 PATCH로 할까?

DELETE는 리소스를 삭제하는 행위다.
그러나 구독 취소는 리소스를 삭제하는 것이 아니라 상태를 변경하는 것이다.

따라서 다음이 더 적절하다.

PATCH /api/subscriptions/{id}/cancel

이는 “구독 리소스의 상태를 변경한다”는 의미에 가깝다.


최종 정리

구독 시스템에서 status 컬럼은 단순한 값이 아니다.
구독의 현재 비즈니스 상태를 명확하게 표현하기 위한 핵심 설계 요소다.

날짜만으로 구독 여부를 판단하면 로직이 복잡해지고 예외 처리가 어려워진다.
ACTIVE, PENDING, CANCELED, EXPIRED 같은 상태를 명확히 정의하면
비즈니스 로직을 단순하고 안정적으로 구성할 수 있다.

구독 시작은 결제 성공 이후
PENDING → ACTIVE로 전이되는 과정이다.

구독 해지는 데이터를 삭제하는 것이 아니라
ACTIVE → CANCELED → EXPIRED처럼
상태를 변경하며 관리해야 한다.

구독 설계의 핵심은 날짜 계산이 아니라
상태 기반(State Transition) 설계다.

리소스를 삭제하는 것이 아니라
상태를 관리하는 도메인 모델링이 정답이다.