야미의 개발

여러 명이 나누는 월세, 어떻게 정산할까? – UserPayment와 Payment로 구현하는 자동 정산 시스템 본문

스프링/웹 개발

여러 명이 나누는 월세, 어떻게 정산할까? – UserPayment와 Payment로 구현하는 자동 정산 시스템

채야미 2025. 5. 6. 22:16

이번 글에서는 얼마전에 만들었던

블록체인 기반 룸쉐어 서비스 ‘ChainG' 의 자동이체 + 상태 관리 시스템 설계 과정을 소개해보려 합니다.

 

💡 서비스 개요

ChainG는 여러 명이 함께 거주하는 룸쉐어 환경에서, 월세와 공과금 등을 자동이체로 분할 납부하고,
그 상태를 정확히 추적 및 관리할 수 있는 서비스를 제공하였습니다.

 

간단하게 납부 흐름을 보면 다음과 같이 그릴 수 있습니다.

 

1. 모으기

 

 

2. 집주인에게 송금하기

 

 

 

단순하게 월세를 '한 명이 내고 나중에 정산'하는 구조가 아니라,
각자 자신의 몫만큼 자동으로 이체되며, 전체적으로 실패나 재시도에 따라
다양한 상태 흐름을 가질 수 있도록 설계했습니다.

 

 

⚙️ 주요 컴포넌트

  • PaymentEntity: 전체 월세 납부를 나타내며, 달마다 생성
  • UserPaymentEntity: 각 유저가 부담해야 하는 개별 납부 항목
  • PaymentStatus: 그룹 전체 상태를 나타내며, 총 금액이 모아졌는지, 어디에서 실패했는지 등...
  • UserPaymentStatus: 각 유저의 납부 상태를 표현

 (참고로 PaymentStatus와UserPaymentStatus는 공동 Enum으로 재사용)

 

 

🔁 자동이체 상태 간단하게 보기

 

  • 그룹 상태 (PaymentStatus):
    STARTED(생성 직후)
    → COLLECTED(모두 모아졌을 때) / PARTIALLY_PAID(연체 유저가 있을 때)
    → PAID(납부 성공) / FAILED(완전히 실패)

  • 개별 상태 (UserPaymentStatus):
    PENDING(생성 직후)
    → COLLECTED(납부 완료)
    → FAILED(납부 실패)

 

 

🧩 상태 흐름 정리

 

1. 월세 납부 생성 (PaymentEntity)

  • 매월 특정 시점(예: 1일 기준), 그룹별로 월세 납부 건이 생성됩니다.
  • 이때 그룹 상태는 STARTED로 설정됩니다.

2. 하위 개별 납부 건 생성 (UserPaymentEntity)

  • 각 유저는 계약된 비율에 따라 개별 UserPayment가 생성됩니다.
  • 이들은 기본적으로 PENDING 상태에서 시작합니다.


3. 월세 전날 모으기 작업 (collectToJointAccount)

  • 지정된 날짜에 각 유저의 계좌에서 공동 계좌로 자동이체를 시도합니다.
  • 결과에 따라 UserPaymentStatus는 다음과 같이 바뀝니다:
    • 성공: COLLECTED
    • 실패: FAILED → 이후 스케줄러가 RETRY_PENDING으로 재등록
  • 모든 유저가 성공하면 → PaymentStatus = COLLECTED
  • 일부만 성공하면 → PARTIALLY_PAID, 일부 실패 → RETRY_PENDING


4. 월세 당일 송금 (payToOwner)

  • 모든 유저의 납부가 COLLECTED일 경우, 집주인 계좌로 이체
  • 이체 성공 시 전체 상태 → PAID


5. 재시도 로직

  • FAILED인 유저는 최대 n회까지 재시도를 시도하며, 성공 시 COLLECTED로 전환
  • 계속 실패하면 최종적으로 FAILED 상태 유지

 

 

 

🔎 상태 머신

머메이드 상태 머신 다이어그램으로 실제 납부 상태를 다음과같은 흐름으로 도식화 할 수 있습니다.

저는 하면서 어려웠는데... 실제로 개발하다 보면 그렇게 어렵지 않다고 하네요

 

티스토리에서는 머메이드 변환 해주는 기능이 없어서 아래에 첨부!

stateDiagram-v2
    title : 📦 Payment (그룹 상태) 상태머신

    [*] --> STARTED

    STARTED --> COLLECTED : 전원 모으기 성공
    STARTED --> PARTIALLY_COLLECTED : 일부 유저 송금 실패
    STARTED --> FAILED : 전체 실패

    PARTIALLY_COLLECTED --> COLLECTED : 재시도 후 전원 성공
    PARTIALLY_COLLECTED --> FAILED : 재시도 실패

    COLLECTED --> RETRY_PENDING : 집주인 송금 실패
    RETRY_PENDING --> FAILED : 최대 재시도 실패

    COLLECTED --> PAID : 집주인 송금 성공

 

 

 

 

🍡 마무리하며

이처럼 ChainG에서는 단순한 자동이체를 넘어서, 다양한 실패와 재시도, 그리고 상태 전이까지 명확하게 관리하며 안정적인 월세 정산 서비스를 구축해낼 수 있었습니다.

 

개발 당시에는 추상적인 정책들을 실제 서비스로 정의하고 설계하는 것이 어려웠지만, 이제는 곧잘 할 수 있을거같다는 생각이 듭니다. 개발전에 상태 머신같은 다이어그램을 미리 설계를 해보는 것도 중요할 듯 합니다. :)

Comments