결제 시점에 단일 테이블로 정산금을 확정하던 레거시(payout)를
결제 시점과 정산 시점을 분리한 2단계 파이프라인(settlement_prep → SQS → settlement)으로 재설계했습니다.
혼합결제 환불을 결제수단별로 정확히 역산하는 오케스트레이션도 함께 구축했습니다.
나아가 정산을 받아 계산·평탄화하는 정산 서버(전략 엔진 · CQRS 조회 · 복구 파이프라인)까지 맡아 정산 데이터 생성부터 최종 정산·조회 모델까지 전체 파이프라인을 설계·구현했습니다.
기간 2024.02 – 운영 중 (~2년+)역할 정산 도메인 단독 설계·구현·운영스택 NestJS · PostgreSQL · AWS SQS(FIFO) · TypeORM · Multi-DB
진화 타임라인
2024.02 · Phase 0
payout 도입
결제 확정 시점에 단일 테이블로 total = 상품금액 + 배송비 − 수수료 정산 예정액 확정
2024.04 – 2025 · 한계 노출
멤버십·구독·다건·환불 증가 → 단일 금액 모델이 버그 온상화
수수료 계산 주석 처리, 정기결제 회차 누락, payouts[0] 단일 요소 가정 등 구조적 결함
2025.10 · Phase 1
settlement_prep 재설계
결제수단별 금액 분해 + 전략 패턴 + 순수 도메인 객체 도입 / 약 1,900줄 규모의 레거시 정산 엔진 재작성
2025.10 – 11 · Phase 2
AWS SQS(FIFO) 비동기 파이프라인
정산 계산을 결제 임계경로에서 분리, groupId·dedup 분리 설계로 순서·멱등 보장
2026.04 – 06 · Phase 3
order_payment + 환불 오케스트레이션
결제수단별 원장 정규화 + PostgreSQL advisory lock + 커밋 후 SQS 발행
아키텍처 — 2단계 비동기 정산 파이프라인
결제 완료
OrderPayment
멤버십 사용
UseLog
동기 · 즉시응답
→
SettlementPrep
결제수단별 금액 분해 (cash/point/pg)
DB commit 후
→
SQS FIFO
groupId=순서 dedupId=멱등
비동기 consume
→
정산 서버
익일 PG수수료 반영 → Settlement 생성
settlement.processed.* 이벤트
→
조회 전용 DB
ReadDBUpdater 평탄화 CQRS · 비정규화
핵심: 메인 백엔드는 최종 settlement을 직접 만들지 않습니다. 결제 시점에 확정 가능한 정보만 prep에 기록하고 익일 확정되는 PG 수수료는 별도 정산 서버가 SQS로 받아 채웁니다 — 결제(동기)와 정산(비동기)의 시점 분리.
정산 서버는 Settlement를 만든 뒤 settlement.processed.* 이벤트로 조회용 Flat 테이블을 비동기 평탄화해 쓰기와 조회를 물리적으로 분리합니다(CQRS).
핵심 프로젝트
Phase 1
구조적 한계에 도달한 payout 진단 → settlement_prep 재설계
● 문제
단일 금액 모델 vs 혼합결제 현실 — 결제수단별 금액 분해 개념이 없어, 혼합결제·부분환불이 들어오자 수수료 계산이 비활성화되고 확장 요구는 jsonb 필드에 누적 → 도메인 응집도 약화
결제 시점 ≠ 정산 시점 — 정기결제 N회차에서 미리 생성된 payout이 회차 계산을 깨뜨림 → 시간 컬럼·휴리스틱 기반 보완 로직이 점진적으로 누적
payouts[0] 단일 요소 가정 — 0개·복수일 때 정산 누락/예외 → 방어코드 반복 누적
메인 백엔드가 보낸 settlement_prep을 받아 최종 정산을 계산하고, 조회·리포팅을 읽기 전용 모델로 분리한 정산 전용 서비스. 코드 지표는 리포지토리 실측값 — 프로덕션 약 25k LOC(208 TS 파일), 테스트 비중 약 37%, 정산 전략 9종, 엔티티·모듈 43/16 규모.
Engine
9개 정산 모델을 Strategy + Template Method로 통합
● 설계
단건·이용권·월정액·사용량 구독·문항별 정산(영어, 국어)·PG수수료 동기화 등 모델마다 데이터 출처와 분배 수식이 다르지만, execute() → getTargets() → processTargets() → buildResult() 골격은 동일
공통 골격을 BaseSettlementStrategy의 템플릿 메서드로 고정 → 신규 정산 모델은 공통 실행 흐름을 재사용하고 모델별 계산 규칙만 구현하도록 분리해, 기존 전략 수정 없이 새 모델을 추가
계산은 3계층 파이프라인 — 기본 분배 → 정산 타입별(이용권 COUNT/AMOUNT, 문항별 정산 3단계 단가·상한 80원, 일부 저자 유형은 정액 10원) → 저자/출판사 계약 비율 → 최소금액 보정(최소 10원)
● 효과
유형 증가에 따라 커지는 조건 분기를 전략 패턴으로 구조화. 전략별로 분리·테스트 가능한 구조
Strategy/Template Method계산 규칙 분리정산 데이터 모델링
Correctness
금액 정밀성과 회계 정합성 — 정수 연산·불변성·감사 추적
● 설계
부가세 round(price / 11), 분배가 round(price / N) 정수 연산으로 계산하고 잔여 손실을 통제
핵심 원칙은 확정된 정산 금액을 in-place로 수정하지 않는다는 것입니다 — 변경이 필요하면 기존 건을 취소(soft-delete)하고 새 건으로 재정산합니다
● 근거
회계·세무·저작권 정산은 "언제·무엇이·왜 바뀌었는가"가 데이터에 영원히 남아야 분쟁과 마감 정정에 대응할 수 있고 in-place update는 그 과거를 지웁니다
정수 연산 분배불변성감사 추적(audit trail)
CQRS
멀티 DB 분리와 이벤트 기반 비정규화
● 설계
트랜잭션용 정규화 DB(master/slave 복제)와 조회·리포팅용 비정규화 DB를 물리적으로 분리
정산 전략이 처리를 끝내면 settlement.processed.* 이벤트를 발행하고, ReadDBUpdater가 복잡한 JOIN 결과를 Flat 테이블로 평탄화
조회용 평탄화 실패가 정산 본처리에 영향을 주지 않도록 읽기 모델 갱신 오류는 별도로 기록하고 진행 — 장애 격리
● 효과
대시보드·엑셀의 무거운 조회가 정산 쓰기 트랜잭션을 방해하지 않음. 천만 건대 조회를 다중 조인 없이 단일 테이블 스캔으로 처리 (메인 포트폴리오 ② 조회 개선과 이어지는 구조)
CQRS읽기/쓰기 분리이벤트 기반 비정규화장애 격리
Recovery
오류 분류·추적·복구 파이프라인 — 검증 3단계 · 에러코드 분류 · 복구 액션 표준화
● 설계
정산 오류를 "나면 사람이 본다"가 아니라 분류 → 추적 → 표준화된 복구 파이프라인으로 전환
ErrorLog(감지) + ResolutionLog(해결 이력) 2계층 로깅으로 누가·어떻게·무엇을 고쳤는지 영구 기록
복구 파이프라인에러코드 체계2계층 로깅운영 안정화
Idempotency
멱등성과 대용량 — 재시도가 데이터를 망치지 않게
● 설계
생성 중복 차단 — prepId/payoutId 기준 기존 정산 조회 후 있으면 그대로 반환
UPSERT 에러로그 — orUpdate + indexPredicate('resolved_at IS NULL')로 같은 오류 N회 감지돼도 행 1개, 해결된 건은 재오픈 안 됨
soft-delete 재생성 — 재생성 시 원본 createdAt 유지, 삭제 건은 중복검사에서 자동 제외
스트리밍 엑셀 — pg-query-stream → 백프레셔 → ExcelJS WorkbookWriter → S3, 시트당 100만 행 자동 분할로 수백만 행을 상수 메모리로 처리
멱등성UPSERT 부분 인덱스soft-delete스트리밍
핵심 결과
엔지니어링 결과는 비즈니스 지표로도 이어졌습니다. 파트너 계약은 2026.06 기준 약 500건 규모로 확대됐고 대표 거래 구조 산식 기준 단순영업이익률은 -4%에서 +16%로 개선됐습니다(실제 회계 실적이 아닌 산식 시뮬레이션 기준). 정산 마감·보정 업무는 개발팀 의존에서 담당자 셀프서비스로 전환됐습니다.
결제 응답 경로에서 정산 계산 제거 — 무거운 비율·분배·세금 계산을 SQS 뒤로 옮겨, 주문 트랜잭션은 prep만 저장하고 즉시 응답
정산 장애의 주문 전파 차단 — 정산 실패가 결제·주문 실패로 번지지 않도록 격리
혼합결제 환불의 정확한 역산 — 포인트·캐시·PG 결제수단별 원장으로 부분/전액·다회 환불을 정확히 되돌리고, 누적 환불의 원결제 초과를 방지
중복 정산·이중 취소 차단 — FIFO 멱등성과 advisory lock 직렬화로 재시도·동시 요청 상황에서도 중복 정산과 이중 취소를 애플리케이션 레벨에서 방지
레거시 정산 엔진의 구조적 재작성 — 단일 금액 모델의 한계를 진단하고 정산 엔진을 전략별로 분리·테스트 가능한 구조로 재설계