Post

운영팀 자율화를 위한 워크플로우 재설계 - 상태 머신 패턴

운영팀 자율화를 위한 워크플로우 재설계 - 상태 머신 패턴

배경

대출 연체 후에는 화해(합의), 워크아웃(자율 채무조정), 회생(법원 개입) 세 가지 후처리 프로세스가 있다. 이 세 프로세스의 공통점은 등록 → 상환 → 종결이라는 생명주기를 갖는다는 것이다.

문제는 이 워크플로우가 여러 도구와 팀에 분산되어 있었다는 점이다.

AS-IS: 3개 도구, 2개 팀

flowchart TD
    subgraph as_is["AS-IS 프로세스"]
        A["채권관리팀: 운영 도구에서 등록"] --> B["채권관리팀: Django Admin에서 상태 확인"]
        B --> C["채권관리팀 → 개발팀에 상환 처리 요청"]
        C --> D["개발팀: 스크립트 수동 실행"]
        D --> E["채권관리팀 → 개발팀에 종결 요청"]
        E --> F["개발팀: 금액 검증 후 종결 스크립트 실행"]
    end
  • 채권관리팀이 운영 도구과 Django Admin을 오가며 관리
  • 상환과 종결은 반드시 개발팀에 요청해야 함
  • 개발팀이 수동으로 스크립트를 실행

TO-BE: 1개 도구, 1개 팀

flowchart TD
    subgraph to_be["TO-BE 프로세스"]
        A["채권관리팀: 운영 도구에서 등록"] --> B["채권관리팀: 운영 도구에서 상환 추가"]
        B --> C["시스템: 자동 익일 지급 처리"]
        C --> D["채권관리팀: 최종 상환 추가"]
        D --> E["시스템: 금액 검증 → 자동 종결"]
    end

핵심: 3개 프로세스의 공통 패턴 추출

화해, 워크아웃, 회생은 법적 절차와 관련 기관이 다르지만, 백엔드 관점에서의 생명주기는 동일하다.

stateDiagram-v2
    [*] --> 등록
    등록 --> 진행중: 상환 추가
    진행중 --> 진행중: 추가 상환
    진행중 --> 종결: 최종 상환 + 금액 검증
    
    note right of 등록: 상태는 되돌릴 수 없음
    note right of 종결: 오직 상환 누적으로만 도달
규칙설명
상태 불가역이전 단계로 되돌릴 수 없음 (건너뛰기는 가능)
순차 상환상환 회차는 순차 증가만 허용
금액 검증상환 금액 + 이전 총 상환금 ≤ 총 변제액
자동 종결총 상환금 = 변제예정금액일 때만 종결 (수동 종결 불가)
자동 지급상환 등록 시 익일 자동 지급 처리

이 규칙들을 코드에 넣으면 운영팀이 실수할 수 없는 구조가 된다.


백엔드 아키텍처: 3개 도메인 × 공통 패턴

flowchart TD
    subgraph api["API Layer<br>internal_api/post_management/"]
        A["화해 API"]
        B["워크아웃 API"]
        C["회생 API"]
    end
    subgraph domain["Domain Layer<br>apps/post_managements/"]
        D["화해<br>View + Operations"]
        E["워크아웃<br>View + Operations"]
        F["회생<br>View + Operations"]
    end
    subgraph repay["Repayment Layer<br>apps/repayments/operations/repay/"]
        G["화해 상환 처리기"]
        H["워크아웃 상환 처리기"]
        I["회생 상환 처리기"]
    end
    A --> D --> G
    B --> E --> H
    C --> F --> I

각 레이어의 역할:

레이어역할예시
APIURL 라우팅, 인증POST /post_management/workout/repayment/
ViewDTO 변환, 입력 검증요청 데이터 → 내부 객체
Operations핵심 비즈니스 로직상태 전이 규칙, 금액 검증
Repayment상환 처리금액 기록, 자동 지급, 자동 종결

비즈니스 규칙의 코드화

상태 불가역

1
2
3
4
5
# 상태를 되돌리려는 시도 차단
def validate_status_transition(current, new):
    STATUS_ORDER = ['registered', 'in_progress', 'closed']
    if STATUS_ORDER.index(new) <= STATUS_ORDER.index(current):
        raise ValidationError("상태를 이전 단계로 되돌릴 수 없습니다")

상환 금액 검증

flowchart TD
    A["상환 요청<br>(금액: 50만원)"] --> B{"이전 총 상환금 + 50만원<br>≤ 총 변제액?"}
    B -->|Yes| C["상환 등록"]
    C --> D{"이전 총 상환금 + 50만원<br>= 총 변제액?"}
    D -->|Yes| E["자동 종결"]
    D -->|No| F["다음 상환 대기"]
    B -->|No| G["거부: 변제액 초과"]
    style G fill:#ff6b6b,stroke:#333,color:#fff

자동 종결 조건

1
2
3
4
5
6
7
8
9
10
11
def process_repayment(plan, amount):
    # 금액 검증
    if plan.total_repaid + amount > plan.projected_amount:
        raise ValidationError("총 변제액을 초과합니다")
    
    # 상환 기록
    plan.add_repayment(amount)
    
    # 자동 종결 판단
    if plan.total_repaid == plan.projected_amount:
        plan.close()  # 수동 종결 API 없음 — 오직 이 경로로만

“종결” 상태를 수동으로 변경하는 API는 아예 존재하지 않는다. 오직 상환 누적이 변제예정금액과 일치할 때만 시스템이 자동으로 종결한다. 이렇게 하면 금액 불일치 상태의 종결이 불가능하다.


실제 운영에서 나온 피드백

운영 배포 후 채권관리팀에 교육을 진행하고 피드백을 받았다.

추가 요구사항: 당일 지급

기존 설계는 “익일 지급”이었는데, 운영에서 “오늘 등록한 상환을 오늘 지급해야 하는” 케이스가 있었다. 당일 지급 기능을 추가했다.

운영 버그 사례

운영 배포 후 채권관리팀이 직접 시스템을 사용하다가 에러를 발견:

“규보님이 7월에 상환등록/지급준비 작업 가능하다고 하셔서 직접 수행하려 했으나 잘못된 요청이라는 오류 메세지가 나옵니다.”

본인이 만든 시스템을 운영팀이 실제로 사용하면서 발견한 엣지 케이스. 당일 핫픽스로 해결했다.


느낀 점

“수동 요청 → 스크립트 실행”은 기술 부채의 냄새다

운영팀이 개발팀에 요청하고, 개발팀이 스크립트를 실행하는 구조는 양쪽 모두에게 부담이다. 비즈니스 규칙을 코드에 넣고 UI를 제공하면, 운영팀은 자율적으로 일하고 개발팀은 본업에 집중할 수 있다.

상태 머신의 핵심은 “못 하게 하는 것”이다

“무엇을 할 수 있는가”보다 “무엇을 할 수 없는가”를 정의하는 것이 중요하다. 상태를 되돌릴 수 없게, 금액을 초과할 수 없게, 수동 종결을 할 수 없게 만들면 운영 실수가 방지된다.

3개 도메인의 공통 패턴을 먼저 찾아라

화해/워크아웃/회생을 각각 따로 설계했으면 3배의 코드와 3배의 버그가 생겼을 것이다. 비즈니스 규칙이 동일하다면 백엔드 구조도 동일해야 한다. 차이가 있는 부분만 분리하면 된다.

This post is licensed under CC BY 4.0 by the author.