3센터 A-A-A RTGS 실시간총액결제 프로토타입 최초 버전관리 시작. - backend: Kotlin/Gradle 멀티모듈(sequencer, common, 채널/센터 모듈) - frontend: Vite + TS - infra: docker-compose, prometheus/grafana, ELK 네이티브 스크립트 - loadtest(k6), sim(장애/순서/정합성 시나리오), docs(PoC 보고서/복원력방안)
11 KiB
11 KiB
RTGS 프로토타입 워크플로우
로컬(1센터 DC1) RTGS 프로토타입의 전문 처리 흐름 · 서비스별 처리 · 데이터 흐름 정리 작성: 루비 · 담당: 양팀장님
1. 시스템 구성도
[참가기관 / 부하생성기(k6) / 조회·신청 콘솔(:5174)]
│ pacs.008 (HTTP, XML)
▼
┌──────────────────────────── 1센터 (CENTER_ID=DC1) ────────────────────────────┐
│ │
│ ① Chanel(전문송수신) :8091 │
│ XSD검증 → 경량명령문(75B)+원전문해시 → 원전문 저장 → 입구 발행 → pacs.002 응답 │
│ │ rtgs.inbound └→ raw_message(PostgreSQL) │
│ ▼ │
│ ③ Sequencer(순번기) :8090 전역순번 부여 + 결정론적값 확정 → 저널 발행 │
│ │ rtgs.journal (단일 파티션 = 전역 순서) │
│ ├───────────────┬───────────────────────────┐ │
│ ▼ ▼ ▼ │
│ Dior(접수동기화) Hermes(결제/원장엔진, cc=1) (저널 구독) │
│ PDNG 기록 순차소비→선저널→잔액이체→ACSP→결과발행 │
│ │ │ rtgs.result │
│ │ ▼ │
│ │ Prada(결과동기화) 과반확정→ACCC 최종확정 │
│ ▼ ▼ ▼ │
│ ┌──────────────── PostgreSQL(:5433, 강한 일관성 원장) ────────────────┐ │
│ │ account(잔액) · transfer(거래상태) · journal_log(선저널) · │ │
│ │ settlement_view(조회사본) · raw_message(원전문) · app_user │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ ⑥ LouisVuitton(관리자) :8099 초기화·코드·사용자·대사 (← 콘솔 관리자 탭)
│ ⑦ Gucci(외부 경계 관문) :8095 로그인·API인증·유량제어·재전송차단·결과콜백송부(→참가기관) │
└────────────────────────────────────────────────────────────────────────────────┘
로그: 각 서비스(Logback) ──TCP:5000──▶ Logstash ──▶ Elasticsearch(:9200) ──▶ Kibana(:5601)
메시지: Kafka(:9092, KRaft) 토픽: rtgs.inbound / rtgs.journal / rtgs.result / rtgs.notify
2. 자금이체 처리 워크플로우 (핵심)
흐름 요약
[신청] ORG_S가 pacs.008 전송
└▶ [접수] Chanel : XSD검증·경량화·원전문저장 → 상태 RCVD → rtgs.inbound
└▶ [순번] Sequencer : 전역순번+결정론적값 → 상태 ACTC → rtgs.journal(저널)
├▶ [접수동기화] Dior : 저널 소비 → PostgreSQL 원장에 PDNG 기록
└▶ [청산·정산] Hermes : 저널 순차소비(cc=1)
→ gap/역전 탐지·재정렬 → 선저널(write-ahead)
→ 송신기관 당좌 −금액 / 수신기관 당좌 +금액 (강한 일관성 트랜잭션)
→ 상태 ACSP → (커밋 후) rtgs.result 발행
└▶ [결과동기화] Prada : 결과 소비 → 과반(2/3) 확정
→ 상태 ACCC(입금처리완료) 최종확정 + 조회사본 기록
→ (커밋 후) rtgs.notify 발행
└▶ [결과통보] Chanel(접수센터) : rtgs.notify 소비 → 최종 pacs.002 생성
→ 신청기관(송신) 앞 결과통보 + 수취기관 앞 입금통보(ACCC) → notification 아웃박스
단계별 상세 (각 서비스가 다루는 토픽/DB)
| 순서 | 서비스 | 입력 | 처리 | 출력 | 상태 |
|---|---|---|---|---|---|
| ① | Chanel | HTTP pacs.008 | XSD검증→경량명령문+원전문해시, 원전문 저장 | raw_message 저장, rtgs.inbound 발행, pacs.002 응답 |
RCVD |
| ② | Sequencer | rtgs.inbound |
전역순번 부여 + 결정론적값(수신시각) 확정 | rtgs.journal 발행(저널) |
ACTC |
| ③ | Dior | rtgs.journal |
BMI 중복체크(upsert) | transfer PDNG 기록 |
PDNG |
| ④ | Hermes | rtgs.journal(cc=1 순차) |
순서 탐지·교정 → 선저널 → 당좌계좌 차·대변(강한 일관성) | account·journal_log·transfer(ACSP), rtgs.result 발행 |
ACSP |
| ⑤ | Prada | rtgs.result |
완결 규율(과반 확정) | transfer(ACCC)·settlement_view, (커밋 후) rtgs.notify 발행 |
ACCC |
| ⑥ | Chanel(결과통보) | rtgs.notify |
최종 pacs.002 생성, 접수센터만 송부 | notification(신청기관+수취기관) |
— |
핵심 원칙: 어느 센터로 접수돼도 단일 순번기가 하나의 전역순번으로 줄 세우고, 저널(단일 파티션)을 N센터가 같은 순서로 결정론적 재생 → 세 센터 데이터가 항상 동일. 잔액 변경이 하나의 순번 줄로 직렬화되므로 "동시에 같은 계좌"가 없어 이중지급 원천 차단.
2.5 서비스별 처리 개념도 (참조)
| 서비스 | 개념도 |
|---|---|
| Chanel(전문송수신) | ![]() |
| Dior(접수동기화) | ![]() |
| Hermes(결제/원장엔진) | ![]() |
| Prada(결과동기화) | ![]() |
3. 상태 전이도 (TxSts)
(접수) (승인) (대기) (예약) (완결)
RCVD ──▶ ACTC ──▶ [PDNG] ──▶ ACSP ──────────▶ ACCC
│Chanel │Sequencer │Dior │Hermes(이체) │Prada(과반확정)
│
└─(검증실패/잔액부족/미등록기관)─▶ RJCT (반려)
- RCVD 접수 / ACTC 승인(순번부여) / PDNG 대기(원장 기록) / ACSP 예약(결제 반영, 결과대기) / ACCC 입금처리완료(최종) / RJCT 반려
4. 데이터 흐름
Kafka 토픽 (전 센터 공유 단일 클러스터 = 순서 공유)
rtgs.inbound: Chanel → Sequencer (접수 원시)rtgs.journal: Sequencer → 전 센터(Dior/Hermes) (전역순번 저널, 단일 파티션)rtgs.result: Hermes → Prada (결제결과)rtgs.notify: Prada → Chanel (완결 결과통보 트리거; 접수센터가 신청/수취기관 앞 pacs.002 송부)
PostgreSQL 테이블 (센터별 원장, 강한 일관성)
account참가기관 당좌계좌 잔액 |transfer거래 원장·상태 |journal_log선저널settlement_view조회 사본 |raw_message원전문(pacs.008) |notification결과통보 아웃박스 |app_user사용자·권한
로그(ELK): 서비스 Logback(JSON, service/center 필드) → Logstash(:5000) → ES rtgs-logs-* → Kibana
5. 관리자(LouisVuitton) 워크플로우
콘솔(:5174) "🛠 관리자" 탭 ──▶ louisvuitton(:8099) /admin/*
· DB 초기화 : transfer/journal_log/settlement_view/raw_message TRUNCATE + 계좌 잔액 리셋
· 코드 관리 : 참가기관(account) 마스터 CRUD + 상태코드 데이터사전
· 사용자 권한 : app_user CRUD (ADMIN/ORG_S/ORG_R) — 로그인 강제는 후속
· 대사 대시보드 : 상태별 건수·총액 보존 체크 + 서비스별 처리 현황(transfer/journal_log/settlement_view)
6. 개발 · 실행 워크플로우
빌드 (JDK21로 — Gradle 8.10.2는 JDK26 미지원; gradle.properties에 고정)
cd C:\ai-dev\workspace\rtgs\backend
gradlew build (EDR 파일락 시 재시도)
기동/종료
run-dc1.cmd : 네이티브 인프라(Postgres/Kafka) + 6개 서비스
infra\start-elk-native.cmd : ES/Logstash/Kibana (로그)
run-frontend.cmd : 콘솔(:5174)
stop-dc1.cmd : 인프라 종료(데이터 보존)
접속: 콘솔 http://localhost:5174 · Kibana http://localhost:5601
변경→반영: 백엔드 = 재빌드(bootJar)+해당 서비스 재기동 / 프론트 = Vite HMR(자동)
6.5 Gucci(외부 경계 관문, :8095) 워크플로우
참가기관은 코어(Chanel:8091)에 직접 붙지 않고 Gucci 관문을 통한다(외부↔RTGS 북-남 경계). 전역 결제순서는 Sequencer 단독이며 Gucci는 관문/인증/유량제어/전달보장만 담당(순서 결정 아님).
참가기관 ──① 로그인(POST /gucci/auth/login, secret) ──▶ Gucci ── app_user.secret 대조 ──▶ JWT 발급
──② 이체신청(POST /gucci/pay/customer, Bearer+X-Nonce+X-Timestamp) ──▶ Gucci 가드:
[JWT검증] → [역할 ORG_S/ADMIN] → [유량제어 50/10s] → [재전송 nonce/timestamp]
→ [기관바인딩: 토큰 org == 전문 DbtrAgt MmbId] → (통과) Chanel /pay/customer 프록시
◀── pacs.002(RCVD) ──
[완결 후 결과 콜백송부 — G2]
Prada 완결 → Chanel notification 아웃박스(delivered=false)
→ Gucci DeliveryService(3s 폴링) → institution_endpoint 콜백 URL로 pacs.002 POST
→ 2xx ACK: delivered=true / 실패: attempts++ 재시도(max 5)
- 감사로그(인증·허용/거부·송부)는 ELK(service=gucci)로 수집.
- 헬스체크
GET /gucci/health/centers(관측). 다센터 정족수/펜싱·GSLB 라우팅은 G3(고사양 PC 이후).
7. 확장 워크플로우 (다센터 A-A-A, 향후)
┌── DC1 (Chanel/Dior/Hermes/Prada + PostgreSQL rtgs_dc1)
[단일 Sequencer] ──저널(Kafka 공유)──┼── DC2 (동일 서비스 + rtgs_dc2)
└── DC3 (동일 서비스 + rtgs_dc3)
· 컨슈머 그룹 센터별 분리(hermes-DC1/DC2/DC3) · 센터별 DB · 포트 오프셋
· 정족수 2/3 자동전환 · 3센터 데이터 동일성 대사로 검증(개요 "3센터 동일순서 동일업무")
필요 작업·자원은 개발일지 백로그 참조. 고사양 PC 이관 후 수월.




