Initialize RTGS prototype repository
3센터 A-A-A RTGS 실시간총액결제 프로토타입 최초 버전관리 시작. - backend: Kotlin/Gradle 멀티모듈(sequencer, common, 채널/센터 모듈) - frontend: Vite + TS - infra: docker-compose, prometheus/grafana, ELK 네이티브 스크립트 - loadtest(k6), sim(장애/순서/정합성 시나리오), docs(PoC 보고서/복원력방안)
This commit is contained in:
BIN
docs/(참고1) RTGS_ActiveActive_복원력방안.pdf
Normal file
BIN
docs/(참고1) RTGS_ActiveActive_복원력방안.pdf
Normal file
Binary file not shown.
173
docs/(참고2) RTGS_ActiveActive_복원력방안에 대한 의견.txt
Normal file
173
docs/(참고2) RTGS_ActiveActive_복원력방안에 대한 의견.txt
Normal file
@@ -0,0 +1,173 @@
|
||||
=========================================================================
|
||||
리뷰결과
|
||||
=========================================================================
|
||||
1. 리뷰어1(최과장) — 항목별 해석 + 저의 판단
|
||||
리뷰어1은 서두에서 자기 방법론을 밝혔습니다. "이 보고서는 앞 절에서 단순하게 던지고 뒤 절에서 조건을 붙여 정정하는 구조라, 특정 절만 보면 틀려 보인다. 뒤에서 해소되면 OK, 안 되면 표현을 고쳐라." — 이건 공정한 태도입니다. 우리 보고서 구조를 정확히 읽었습니다.
|
||||
|
||||
① (1.3) "거액결제 완결 단위를 다자간 동시처리로 단정했다"
|
||||
해석: 우리는 표에서 "거액결제 = 다자간 동시처리(여러 건 묶어 처리)"라고 썼습니다. 리뷰어는 "거액결제는 실제로는 건별 실시간 총액결제(RTGS)가 기본이고, 다자간 차액결제는 일부일 뿐인데, 마치 거액 전체가 묶음처리인 것처럼 단정했다"고 지적합니다. 그리고 "거액 vs 소액의 진짜 차이는 '묶음이냐 건별이냐'가 아니라 '복구 시한이 있느냐 없느냐'"라고 봅니다.
|
||||
|
||||
저의 판단: 부분 수용 (타당). 리뷰어 지적이 사실관계로 맞습니다. 한은금융망(거액 RTGS)의 심장은 건별 실시간 총액결제이고, 다자간 동시처리(다자간 차액결제 동시처리, DvP·차액 등)는 그 일부입니다. 우리 보고서 1.3의 표와 76번 줄은 거액 전체를 "다자간 동시처리라 못 쪼갠다"로 단정하는데, 이는 과도한 단순화입니다.
|
||||
|
||||
다만 우리 논지 자체는 살아있습니다. "거액은 분산이 어렵다"의 진짜 근거는 다자간 동시처리(그로스가 아닌 배치성 정산)와 다자간 잔액 상호의존성이고, 소액 P2P는 건별 독립이라 분산이 쉽다는 대비는 유효합니다. 즉 결론은 유지, 전제 서술만 정정하면 됩니다.
|
||||
리뷰어가 제시한 "진짜 차이 = 복구 시한 유무"는 매우 날카롭습니다. 이건 오히려 우리 보고서의 핵심 주장(소액은 RTO≈0이라 무중단이 최강 난제)과 정확히 일치합니다. 이 프레임을 받아들이면 1.3이 더 강해집니다.
|
||||
② (2.1) "센터 간 지연이 감내 가능하다는 결론이 이론적 거리 기반 수치뿐 — 실측 필요"
|
||||
해석: 우리는 "광교–강남 25km, 남대문 10km라 동기복제 가능(왕복 수 ms)"이라고 썼는데, 리뷰어는 "그건 직선거리로 계산한 이론값이고, 실제 통신사 회선은 돌아가고 중간 장비를 거쳐 더 느리다. 실측하라"고 합니다.
|
||||
|
||||
저의 판단: 완전 수용 (매우 타당). 이건 리뷰어2의 28페이지 지적, 35페이지 지적과 완전히 같은 말입니다(세 리뷰가 한 점으로 수렴 = 신호가 강함). 우리 보고서도 이미 6.1-3, 6.4-2에서 "실측 필요"로 잡아뒀지만, 본문 2.1의 서술 톤이 "수 ms라 성립한다"고 단정적입니다(320번 줄). "이론 거리 기준 근사이며 회선·보안장비 지연 포함 실측이 성립 전제"라고 본문에서 못박아 톤을 낮춰야 합니다.
|
||||
|
||||
③ (2.2) "순차 코어는 수평확장해도 처리량이 안 늘고 상한이 있다 — 확장 가능 영역(주변)과 불가 영역(코어)이 구분 안 됨"
|
||||
해석: "노드를 늘리면 처리량이 비례해 는다(scale-out)"고 했는데, 리뷰어는 "그건 주변부(입구·검증·조회) 얘기다. 코어(순번기, 한 줄로 세우는 부분)는 아무리 노드를 늘려도 '한 줄'이라 안 늘어난다. 이 둘을 섞어 쓰면 독자가 '이 시스템은 무한 확장된다'고 오해한다"고 합니다.
|
||||
|
||||
저의 판단: 이미 반영됨 — 단, 강화 여지. 우리 보고서는 이 구분을 이미 명확히 하고 있습니다: 240번 줄 "수평 확장이 그대로 통하는 곳은 병렬 구간… 직렬 코어(순번기)는 노드를 늘려도 '한 줄'이라는 본질이 바뀌지 않으므로", 2.3-부속 전제① "단일 순번기 병목". 리뷰어가 이 대목을 못 보고 지적했거나, 핵심 권고 4번(24번 줄)처럼 요약부에서 "확장 가능"만 강조된 것을 문제 삼은 것으로 보입니다.
|
||||
|
||||
→ 부분 수용: 본문은 이미 구분돼 있으나, 요약(0장)과 2.2 도입부에서 "확장"을 말할 때 "단, 코어는 예외"를 한 박자 먼저 붙여 오해 소지를 줄이는 게 좋습니다. 서술 위치 조정 문제이지 논리 결함은 아닙니다.
|
||||
|
||||
④ (2.3) "저널이 확정 순서로 기록된 뒤 그 순서대로 소비하면 순서 역전이 불가능한데, 순서오류를 별도 위험으로 서술한 건 전제와 모순"
|
||||
해석: "번호표를 순서대로 뽑아 순서대로 처리하는데 어떻게 순서가 뒤집히나? 순서오류가 진짜 난다면 그건 너희 저널 설계 자체가 잘못된 것이다."
|
||||
|
||||
저의 판단: 기각 (근거 있음) — 단, 서술 명확화는 수용. 이 지적은 리뷰어가 분산 시스템의 현실을 놓친 것입니다. 핵심 구분은 "순번 부여(numbering)"와 "순번 적용(applying)"은 다른 단계라는 점입니다.
|
||||
|
||||
순번기가 번호를 정확히 1,2,3으로 매겨도, 세 센터가 그 저널을 네트워크로 병렬 수신하는 과정에서 패킷 재정렬·버퍼·비동기 I/O로 도착·적용 순서가 드물게 어긋날 수 있습니다. 이건 실제 분산 시스템의 정상적 현상이고, 우리 보고서 288번 줄이 이미 정확히 이렇게 설명하고 있습니다. 국내 PoC에서도 실제 관찰됐다고 명시돼 있습니다.
|
||||
리뷰어가 "순서오류가 실재하면 저널 소비 구조의 설계 문제"라고 한 것은 절반만 맞습니다 — 맞아요, 그래서 우리는 그걸 "탐지·교정 장치"로 설계에 포함시킨 겁니다. 완벽한 순차 소비를 가정하지 않는 것이 오히려 정직한 엔지니어링입니다.
|
||||
리뷰어의 오해는 "동기화가 확정 순서로 완료되었다는 가정하에서"라는 그의 단서에 드러납니다. 바로 그 가정(완벽 동기 완료)을 결제 시스템은 무비판적으로 믿으면 안 된다는 것이 우리 2.3-부속의 논지입니다.
|
||||
→ 따라서 논리적으로 기각하되, 리뷰어가 오해했다는 것은 서술이 그만큼 오해를 유발했다는 뜻이므로, 2.3-부속 도입부에 "순번 부여 ≠ 순번 적용" 구분을 더 앞세워 방어를 명시적으로 하겠습니다. (기각이지만 표현 보강.)
|
||||
|
||||
⑤ (2.5) "결제엔 IBM MQ가 적합하다고 해놓고, 뒤에서 MQ와 GSLB를 혼용"
|
||||
해석: "MQ가 좋다더니 왜 GSLB랑 섞어 쓰냐, 헷갈린다."
|
||||
|
||||
저의 판단: 기각 (명백히 근거 있음). 이건 리뷰어가 우리 2.5절의 핵심 논지를 정면으로 놓친 것입니다. 우리 보고서 369~388번 줄은 정확히 이 오해를 풀려고 **"GSLB와 MQ는 대체 관계가 아니라 다른 계층"**임을 표까지 그려 설명하고, **경로별 분담(결제 전문=MQ 페일오버 / 접속·조회 API=GSLB)**을 명시적으로 정의합니다. 혼용이 아니라 의도적 역할 분리입니다.
|
||||
|
||||
→ 기각. 다만 리뷰어1·리뷰어2가 모두 이 부분을 헷갈렸다면(리뷰어2는 헷갈리지 않았지만), 이 절이 길고 복잡해 오해 소지가 있다는 신호입니다. 2.5 도입에 "결론 한 줄"(결제=MQ, API=GSLB, 둘은 다른 층)을 먼저 박아 넣으면 오해가 줄어듭니다. 표현 보강만.
|
||||
|
||||
⑥ (4) "FedNow 잔액기록을 '분산 인메모리 그리드'로 썼는데, 실제 FedNow는 애플리케이션 레벨 Raft 저널복제 + 코어별 인메모리 처리 — TIPS와 다른 메커니즘"
|
||||
해석: "해외사례 표에서 FedNow 방식을 틀리게 적었다. FedNow는 사실 우리(저널복제) 방식에 더 가깝다."
|
||||
|
||||
저의 판단: 수용 (사실 정정, 그리고 우리에게 유리). 리뷰어 지적이 사실로 맞다면 — FedNow가 Raft 기반 저널 복제라면 — 이는 우리 4장 표 637번 줄 "분산 인메모리 그리드(구현 사례 기준)"를 정정해야 합니다. 그리고 오히려 우리 논지를 강화합니다: 우리의 "저널 순차처리" 방식이 TIPS뿐 아니라 FedNow와도 일치한다는 뜻이니까요.
|
||||
|
||||
단, 검증 필요: FedNow 내부 아키텍처는 상당부분 비공개입니다(우리 보고서도 649번 줄에서 "미공개"라 인정). 리뷰어가 "확인됨(confirmed)"이라 했지만, 출처가 불명확합니다. "Raft 기반 저널 복제로 알려짐/추정" 수준으로 표현하고, 단정은 피하겠습니다. 무엇보다 리스크가 낮은 방향의 수정(우리에게 유리 + 부정확한 단정 제거)이라 수용합니다.
|
||||
⑦ (6.1) "샤딩을 교차거래 비율 측정에 따라 최적화 옵션으로 채택한다는 서술 정정 필요 — FedNow 샤딩은 원장이 아니라 전문·메시지 복제/결과저장용 NoSQL에 적용"
|
||||
해석: "샤딩(쪼개기)을 원장(잔액장부)에 적용하는 옵션처럼 써놨는데, FedNow의 샤딩은 원장이 아니라 **주변부(전문 저장·NoSQL)**에만 쓴다. 원장을 쪼개는 건 위험하니 그렇게 읽히면 안 된다."
|
||||
|
||||
저의 판단: 수용 (타당, 안전한 방향). 이 지적은 우리 보고서의 다른 부분과 오히려 정합적입니다. 우리는 이미 217번 줄과 2.1 샤딩 각주에서 "샤딩(계좌 소유 분담)은 본안이 아니고, 교차거래 비율 낮을 때의 부하분산 최적화 옵션"으로 격하해뒀습니다. 그런데 리뷰어 말대로 "원장 계좌 샤딩"으로 읽힐 여지가 있고, 그건 우리의 "원장은 단일 순번으로 강한 일관성" 대원칙과 충돌합니다.
|
||||
|
||||
→ 수용: 샤딩 옵션을 언급할 때 **"원장을 쪼개는 것이 아니라 주변부(전문 저장·조회 사본)에 한한다"**는 단서를 명확히 붙입니다. 6.4-1의 "최후엔 샤딩 옵션 재검토"도 같은 맥락에서 오해되지 않게 다듬습니다.
|
||||
|
||||
2. 리뷰어1이 우리 보고서 방향에 주는 영향 — 종합
|
||||
핵심: 리뷰어1은 우리 보고서의 골격(근거리 3센터 A-A-A + 저널 순차처리)을 흔들지 않습니다. 7개 지적 중 방향을 바꾸는 것은 하나도 없고, 모두 "표현 정정·명확화" 수준입니다. 이는 역설적으로 우리 설계가 논리적으로 견고하다는 방증입니다.
|
||||
|
||||
지적 판단 보고서 영향 작업 강도
|
||||
① 거액=다자간 단정 부분수용 1.3 전제 서술 정정("건별 RTGS 기본, 다자간은 일부") + 리뷰어의 "복구시한" 프레임 흡수 中
|
||||
② 지연 실측 완전수용 2.1 톤 하향("이론 근사, 회선·장비 포함 실측이 전제") 中
|
||||
③ 코어/주변 확장 구분 부분수용 0장·2.2 도입에 "코어는 확장 예외" 선제 명시 小
|
||||
④ 순서오류 모순 기각+보강 2.3-부속에 "번호부여≠번호적용" 방어 강화 小
|
||||
⑤ MQ/GSLB 혼용 기각+보강 2.5 도입에 결론 한 줄 선제 小
|
||||
⑥ FedNow 메커니즘 수용 4장 표 정정("Raft 저널복제로 알려짐") 小
|
||||
⑦ 샤딩 범위 수용 샤딩=주변부 한정 단서 명확화 小
|
||||
방향 추천: 리뷰어1 지적은 "우리 논지를 유지하되 방어를 더 촘촘히" 하는 재료입니다. 특히 ①의 "복구시한 유무" 프레임과 ⑥의 "FedNow도 저널복제" 사실은 받아들이면 오히려 우리 주장이 강해집니다. 기각한 ④⑤도 "리뷰어조차 헷갈렸다"는 신호로 받아 서술을 더 친절하게 만드는 계기로 씁니다.
|
||||
|
||||
부장님, 여기까지가 **리뷰어1(1·2단계)**입니다. 이어서 **리뷰어2(3단계)**로 넘어가겠습니다.
|
||||
|
||||
3. 리뷰어2(양팀장님) — 항목별 해석 + 저의 판단
|
||||
리뷰어2는 페이지별 하이라이트+메모 형식입니다. 10개 메모 중 대부분이 "좋다 + 이걸 더하라"입니다. 성격별로 묶어 보겠습니다.
|
||||
|
||||
A. BMT로 실증하라 (메모 3건: p1×2, p11) — 가장 강한 반복 신호
|
||||
해석: "저널 순차처리로 이중지급·순서 막는다는 것, 3센터 AAA 가용성, 15,000 TPS '접수처리' 수치 — 이거 다 말로만 하지 말고 BMT(실증시험)로 확인해야 진짜다. 특히 15,000 TPS는 '접수'까지지 '최종 이체확인'까지가 아니다."
|
||||
|
||||
저의 판단: 완전 수용 (핵심 관통). 이건 리뷰어2의 가장 일관된 메시지이고 전적으로 옳습니다. 우리 보고서는 6.4 BMT 8개 항목으로 이미 대응하고 있고, "15,000 TPS는 접수 한정·end-to-end 아님"도 242번 줄·6.4-8에서 명시했습니다. 리뷰어2가 요구하는 것을 우리가 이미 상당부분 갖췄다는 게 확인됩니다.
|
||||
|
||||
→ 다만 리뷰어2가 명시적으로 요청한 것 중 신규 추가할 것: "3센터 AAA와 2센터 AA도 가능한지 BMT에서 함께 확인"(p1 첫 메모). 이건 우리에게 없던 좋은 지적입니다 — 5.3-②에 "2센터 잠정 개시" 옵션은 있지만, BMT 항목에 "2센터 AA 대안의 복원력 실측"을 명시하면 의사결정 폭이 넓어집니다. 수용.
|
||||
|
||||
B. 최대 목표치(peak) 산정 필요 (메모 p10)
|
||||
해석: "2,000 TPS 기준선은 좋다. 근데 그건 하루 2만건 거액 수준이다. 소액은 거액의 100배 이상 건수인데, 기준선만 있고 **최대목표치(스파이크 정점)**가 없다. 그건 잡아야 한다."
|
||||
|
||||
저의 판단: 완전 수용 (날카롭고 정당). 이건 리뷰어2 지적 중 가장 실질적입니다. 우리 보고서는 "고정 숫자 안 박고 scalable" 논리로 238번 줄 최대치 산정을 회피하는 경향이 있는데, 리뷰어 말이 맞습니다 — 확장성만 강조하고 정점 목표를 안 잡으면 용량 설계·비용 산정이 불가능합니다. "scalable하니 됐다"는 우리 논리의 약한 고리를 정확히 찔렀습니다.
|
||||
|
||||
→ 수용: 2.2에 **"기준선(2,000) + 최대목표치(스파이크 정점 배수) 산정"**을 추가하고, 6.1-1에 "최대목표치 산정" 항목을 넣습니다. 다만 정확한 수치는 이용규모 예측이 필요하므로 "산정 방법론과 확인필요"로 잡습니다. (숫자를 지금 지어내지 않음 — 리뷰어도 "산정 필요"라 했지 "얼마"라 하지 않음.)
|
||||
|
||||
C. 정책·비즈니스 선결 (메모 p1 원장동기화, p5 심플, p2 참가기관)
|
||||
해석:
|
||||
|
||||
p1: "센터간 원장 동기화 해결책이 핵심. 3센터가 순서만 보장하면 센터간 동기화 관리가 불필요함을 보장하라."
|
||||
p5: "RTGS 서비스를 최대한 심플하게. 단방향만, 취소·반려는 새 거래로."
|
||||
p2: "정책 필요성 서술 좋다. 참가기관 관점(통신망 신설, 거액/소액 구분 이용, 장애 대응 변화)도 추가하라."
|
||||
저의 판단: 대체로 수용.
|
||||
|
||||
p1은 이미 우리 핵심 논지 — "동기화가 아니라 하나의 순서를 셋이 재생"(17번 줄)이 정확히 "순서 보장 → 동기화 관리 불필요"입니다. 리뷰어2가 우리 방향에 동의·강조한 것. 그 "보장"을 더 명시적으로 못박으라는 요구로 받아 2.3에 한 줄 강화. 수용.
|
||||
p5 "심플하게(단방향·취소는 신규거래)": 매우 좋은 실무 원칙이나 이건 비즈니스 프로세스 설계 영역(금융결제국 소관)입니다. 우리 보고서 482번 줄이 이미 "비즈니스 요구가 먼저 확정되어야"라고 원칙을 세워뒀습니다. → 부분 수용: 5.3 또는 1장에 "서비스 단순성 원칙(단방향·취소는 신규거래 등)을 비즈니스 설계 시 지향"을 시사점으로 한 줄 추가하되, 우리가 확정하지 않고 금융결제국 협의 사안으로 넘깁니다. (리뷰어1의 "비즈니스 요구가 기술을 끌어야" 프레임과도 정합.)
|
||||
p2 참가기관 관점: 정당합니다. 우리 2.5 참가기관 접속·420번 줄에 참가기관 대응이 있지만, "통신망 신설·거액/소액 구분 이용·장애 대응 변화"를 참가기관 부담 관점에서 1장 또는 2.5에 명시적으로 정리하면 보고서가 더 균형잡힙니다. 수용.
|
||||
D. 거리·비용 현실 (메모 p28, p35) — 리뷰어1 ②와 수렴
|
||||
해석:
|
||||
|
||||
p28: "TIPS 3센터는 각각 15km 이내인데, 광교–강남–본부는 그 2~3배다. 이걸 감안하라. 10Gbps 회선 비용 평가도."
|
||||
p35: "3센터 물리거리 latency + 중간 보안장비 latency까지 감안한 BMT 필요."
|
||||
저의 판단: 완전 수용 (리뷰어1 ②와 3중 수렴 = 최강 신호). 세 지적(리뷰어1 ②, 리뷰어2 p28·p35)이 모두 **"거리·회선·보안장비 지연을 이론값이 아니라 실측으로"**를 가리킵니다. 이건 반드시 반영해야 합니다.
|
||||
|
||||
**특히 "TIPS 15km vs 우리 2~3배"**는 아프지만 정확한 지적입니다. 우리 보고서는 211번 줄 등에서 "TIPS 구조를 충실히 재현"이라 하는데, 거리 조건이 TIPS보다 불리하다는 점을 정직하게 명시해야 합니다. "TIPS를 좇는다"면서 물리 전제(거리)가 다르다는 걸 숨기면 방어 불가능해집니다.
|
||||
10Gbps 회선 비용·보안장비 지연: 6.1-15(비용)·6.4-2(합의지연)에 이미 관련 항목이 있으나, "보안장비 통과 지연"과 "회선 등급별 비용"을 명시적으로 추가합니다.
|
||||
→ 수용: 2.1에 "TIPS 대비 거리 열위(TIPS ≤15km vs 우리 10~30km)를 정직하게 명시 + 보안장비 지연 포함 실측이 성립 전제", 6.1/6.4에 회선비용·보안장비 지연 항목 보강.
|
||||
|
||||
E. 조직·개발 모델 (메모 p34)
|
||||
해석: "신기술이라 완전 외주개발이면 당행 직원 내재화 불가능. 최소 공동개발이 필수다."
|
||||
|
||||
저의 판단: 완전 수용 (강한 실무 통찰). 우리 보고서는 752번 줄에서 "실행모델(자체/공동/외주) 비교 선택"이라고 중립적으로 써뒀는데, 리뷰어2는 "중립적으로 두지 말고, 완전외주는 내재화 불가이므로 최소 공동개발이 필수"라고 방향을 특정하라고 요구합니다. 이건 타당합니다 — 코어(순번기·원장 엔진)를 완전 외주하면 24/365 운영·장애대응 역량이 조직에 안 남습니다.
|
||||
|
||||
→ 수용: 5.3-⑤와 6.2-3에서 "완전 외주 지양, 최소 공동개발 이상을 코어 내재화의 하한선으로" 명시. (리뷰어의 방향 제시를 우리 판단으로 재검증한 결과 동의.)
|
||||
|
||||
F. 시각화 (메모 p36)
|
||||
해석: "부록 A 시뮬레이션 결과를 그래프로 보여줄 수 있나?"
|
||||
|
||||
저의 판단: 조건부 수용 (좋으나 우선순위·검증 유의). 부록 A의 S1~S5 결과(잔액 일치, 오거절 1,300건 등)를 그래프화하면 설득력이 올라갑니다. 다만:
|
||||
|
||||
시뮬레이션이 실제 실행된 것인지 sim 폴더 확인이 필요합니다(그래프는 실데이터라야 의미). 지어낸 그래프는 금물.
|
||||
리뷰어2 종합 (4단계 일부) — 방향 영향
|
||||
메모 판단 보고서 영향 강도
|
||||
A. BMT 실증(3건) 수용 대부분 반영됨 + "2센터 AA 대안 BMT" 신규 中
|
||||
B. 최대목표치 완전수용 2.2·6.1에 peak 목표 산정 추가 (약한고리 보강) 中
|
||||
C-p1 순서보장→동기화불요 수용(동의) 2.3 한 줄 강화 小
|
||||
C-p5 서비스 심플 부분수용 시사점 한 줄(금융결제국 협의) 小
|
||||
C-p2 참가기관 관점 수용 1장/2.5에 참가기관 부담 정리 中
|
||||
D. 거리·회선·보안장비 완전수용 2.1 거리열위 정직 명시 + 6장 비용/지연 中
|
||||
E. 최소 공동개발 완전수용 5.3-⑤·6.2 방향 특정 小
|
||||
F. 그래프 조건부 sim 데이터 있으면 그래프 1개 小
|
||||
리뷰어2도 골격을 흔들지 않습니다. 오히려 **우리 방향에 동의하며 "더 정직하게, 더 실측으로, 더 정책까지"**를 요구합니다. 가장 값진 지적은 **B(최대목표치)**와 D(거리 열위 정직화) — 우리 보고서의 두 약한 고리입니다.
|
||||
|
||||
=========================================================================
|
||||
리뷰결과 의견1
|
||||
=========================================================================
|
||||
제 의도와는 다른 해석이 있어 내용을 일부 추가합니다. (제가 내용을 너무 단순화하여 잘못 쓴 것 같아요)
|
||||
|
||||
(2.3)
|
||||
|
||||
IP 계층에서는 패킷이 뒤섞여 도착할 수 있으나, TCP는 단일 커넥션 내에서 이를 재정렬해 애플리케이션에 순서대로 전달
|
||||
따라서 순서 역전의 실질적 원인은 네트워크 계층 자체가 아니라,
|
||||
(1) 처리량 확보를 위한 멀티 파티션/멀티 스레드 병렬화
|
||||
(2) 3개 센터가 각자 독립된 스트림으로 수신/적용 하는 구조로
|
||||
(1)은 파티션, 재정렬 설계로, (2)는 센터간 진행상태 동기화로 대응
|
||||
|
||||
가정(완벽 동기 완료)을 결제 시스템은 무비판적으로 믿으면 안 된다
|
||||
=>믿는 것이 아니라, 순번부여+3센터 동기화 로직 자체가 애플리케이션 레벨에서 보장되어야 한다는 의미
|
||||
|
||||
|
||||
(2.5 - 아마도 주기능(결제)는 MQ로, 관리/부가 서비스는 GSLB로 두 가지 방법 병행한다는 제 보고서 내용이 들어간 듯 보입니다)
|
||||
|
||||
MQ 페일오버 클러스터와 GSLB를 나눈 스코프가 적절하지 않음
|
||||
결제전문 경로의 "접속+인증"은 하나의 연속된 절차로서 MQ경로에 포함되어야 함 (설계에 따라 연결 후 별도 인증절차를 거칠 수도 있음)
|
||||
GSLB는 관리용 웹페이지 접속 등 부가 기능에 사용
|
||||
|
||||
18p 본안 A, 대안 B 비교 표에서,
|
||||
본안A에 MQ와 GSLB가 모두 포함되어 있는데 서로 다른 두 매커니즘을 하나의 열에 합쳐서 서술함
|
||||
- "부하·전환" 행의 "중앙에서 동적 조정·자동 전환(설정 변경 불요)"은 GSLB와 MQ 페일오버의 속성을 혼합 서술
|
||||
- "중앙에서 동적 조정"은 GSLB 특성 -> 중앙이 DNS 응답 변경으로 능동적·사전적 트래픽 유도
|
||||
- "설정 변경 불요"는 MQ 페일오버 특성 -> 참가기관 클라이언트가 로컬 연결목록 기반, 장애 후 자체 재연결
|
||||
- 전환 주체(중앙 vs 참가기관)·시점(사전 vs 사후)이 상이한 두 방식을 단일 셀에 병기
|
||||
|
||||
이상입니다.
|
||||
|
||||
=========================================================================
|
||||
리뷰결과 의견2
|
||||
=========================================================================
|
||||
처리 순서가 제일 중요합니다.
|
||||
순서는 맨앞단에 GSLB에서 3센터 공통으로 지정해 줘야는데
|
||||
순서만 지정하는 거라 이 부분에서 병목현상은 없을텐데
|
||||
이 GSLB를 3센터 중에 한 곳에서 수행할지 또다른 곳에 놓을지를 가용성 보장 측면에서 결정해야 할 것 같습니다.
|
||||
BIN
docs/2센터흐름도.png
Normal file
BIN
docs/2센터흐름도.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 227 KiB |
80
docs/RTGS 3센터 이관·기동 체크리스트.md
Normal file
80
docs/RTGS 3센터 이관·기동 체크리스트.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# RTGS 3센터 삼중화 — 이관·기동 체크리스트
|
||||
|
||||
> 대상: 고사양 PC(40코어/128GB) 이관 후 **테스트 시작**용. 코드(B1/B2/B3)·설정공통화·스크립트는
|
||||
> 8코어 PC에서 구현·빌드·단위테스트 완료(2026-07-13). 이 문서대로 기동하면 3센터 실측 가능.
|
||||
|
||||
## 0. 구현 요약 (이번에 반영된 것)
|
||||
| 항목 | 내용 | 파일 |
|
||||
|---|---|---|
|
||||
| B1 무손실 순번기 | SEQUENCE `global_seq_seq` + `journal_outbox`(durable-before-publish) + 재발행 스케줄러 | `sequencer/SequencerService.kt`, `louisvuitton/.../db/migration/V4__seq_outbox.sql` |
|
||||
| B1 리더선출 | 순번기 그룹 `sequencer` 단일 리더(Kafka 리밸런스). **전역 단일 인스턴스 = DC1** | 동상 |
|
||||
| B2 정족수 | `rtgs.result`=applied-ack, `QuorumAggregator`가 센터 집계 → 과반(2/3) ACCC | `prada/QuorumAggregator.kt`, `prada/PradaService.kt` |
|
||||
| B3 gap 타임아웃 | `GapBuffer` + 스케줄러: 정체 시 phantom skip(단일 파티션 근거) + `rtgs.journal.gap.timeout` 지표 | `hermes/GapBuffer.kt`, `hermes/HermesService.kt` |
|
||||
| 설정공통화 | 원장/엣지 groupId 센터접미사(`hermes-DC1`…) 파라미터화, 순번기만 단일 그룹 | dior/hermes/prada/chanel Service |
|
||||
| 이관 스크립트 | `run-dc2/dc3.cmd`, `infra/init-3centers-db.cmd`, `run-dc1.cmd`(3센터 모드 지원) | 루트/infra |
|
||||
|
||||
- 단위테스트: `QuorumAggregatorTest`(6), `GapBufferTest`(6) — `gradlew build` 통과.
|
||||
|
||||
## 1. 이관 검증 (고사양 PC 도착 직후)
|
||||
1. `C:\ai-dev` 트리를 동일 경로로 복사(포터블 무설치 철학 — 경로만 같으면 됨).
|
||||
2. 빌드 확인: `cd C:\ai-dev\workspace\rtgs\backend && gradlew.bat build`
|
||||
- JDK21 고정(`gradle.properties`의 `org.gradle.java.home`)이라 별도 설정 불필요.
|
||||
- 기대: `BUILD SUCCESSFUL`, 단위테스트 12건 통과.
|
||||
3. 단일센터 스모크(선택): `run-dc1.cmd`(기본 count=1, db=rtgs) → Chanel :8091로 1건 이체 → ACCC 확인.
|
||||
|
||||
## 2. 3센터 기동 절차 (순서 중요)
|
||||
```
|
||||
1) 공유 인프라 + DC1(3센터 모드) 기동:
|
||||
set CENTER_COUNT=3 & set POSTGRES_DB=rtgs_dc1 & run-dc1.cmd
|
||||
→ postgres:5433, kafka:9092, 단일 순번기 :8090, DC1 원장/엣지(809x) 기동.
|
||||
|
||||
2) 3개 DB 생성(멱등):
|
||||
infra\init-3centers-db.cmd
|
||||
→ rtgs_dc1/dc2/dc3 (스키마는 각 센터 LouisVuitton의 Flyway가 생성).
|
||||
※ DC1은 이미 기동 중이라 rtgs_dc1은 DC1 LouisVuitton이 마이그레이션함.
|
||||
|
||||
3) DC2 기동: run-dc2.cmd (원장/엣지만, 819x, db=rtgs_dc2)
|
||||
4) DC3 기동: run-dc3.cmd (원장/엣지만, 829x, db=rtgs_dc3)
|
||||
```
|
||||
- **순번기는 전역 단일(DC1 :8090)**. DC2/DC3는 저널(rtgs.journal, 단일 파티션)을 **독립 그룹**으로
|
||||
전량 재생 → 각자 rtgs_dc2/dc3 원장에 동일 상태 도달(결정론적 재생).
|
||||
- 완결: 각 센터 Prada가 `rtgs.result`를 전 센터분 집계 → 서로 다른 센터 **2/3** 도달 시 ACCC.
|
||||
|
||||
## 3. 검증 항목 (§설계서 6장 대응)
|
||||
| 시나리오 | 방법 | 기대 |
|
||||
|---|---|---|
|
||||
| 평상시 3센터 | Chanel(:8091 등)로 이체 N건 | 3 DB의 transfer/account **완전 일치**(총액·상태·순번), 즉시 과반 ACCC |
|
||||
| 1센터 강제종료 | DC3 창 닫기 → 이체 계속 | 남은 2센터 ack=2 → 과반 유지 → **완결 지속**, 이중지급 0 |
|
||||
| 재해센터 복구 | DC3 run-dc3 재기동 | 밀린 저널 따라잡기(earliest 재생) → 잔액 일치 수렴 |
|
||||
| 2센터 종료 | DC2·DC3 종료 → 이체 | ack=1 → 과반 미달 → **ACSP(완결 보류)**, 오처리 0 |
|
||||
| 2센터 복구 | DC2·DC3 재기동 | 따라잡기 후 보류분 **ACCC 재개** |
|
||||
| 순서 gap | (인위적 phantom) | `rtgs.journal.gap.timeout` 증가 + skip 후 진행 재개 |
|
||||
|
||||
**대사(정합성) 확인 쿼리** (psql, 각 DB 반복):
|
||||
```
|
||||
psql -h localhost -p 5433 -U rtgs -d rtgs_dc1 -c "SELECT status,count(*),sum(amount) FROM transfer GROUP BY status ORDER BY status;"
|
||||
psql ... -d rtgs_dc2 -c "동일"
|
||||
psql ... -d rtgs_dc3 -c "동일"
|
||||
-- 3개 결과가 동일해야 함. account 총합도 세 DB 동일해야 함.
|
||||
```
|
||||
|
||||
## 4. 포트·토폴로지 참조
|
||||
| 서비스 | DC1 | DC2 | DC3 | 그룹 |
|
||||
|---|---|---|---|---|
|
||||
| Sequencer | 8090 | (없음) | (없음) | `sequencer`(단일 리더) |
|
||||
| Chanel | 8091 | 8191 | 8291 | `chanel-DCx` |
|
||||
| Dior | 8092 | 8192 | 8292 | `dior-DCx` |
|
||||
| Hermes | 8093 | 8193 | 8293 | `hermes-DCx` |
|
||||
| Prada | 8094 | 8194 | 8294 | `prada-DCx` |
|
||||
| Gucci | 8095 | 8195 | 8295 | (무상태) |
|
||||
| LouisVuitton | 8099 | 8199 | 8299 | (Flyway/관리) |
|
||||
- 공유: PostgreSQL 5433(rtgs_dc1/dc2/dc3), Kafka 9092(단일 파티션 저널), Prometheus 9090, Grafana 3000, ELK.
|
||||
|
||||
## 5. 주의·후속 (테스트 중 확인)
|
||||
- **순번기 HA(선택 검증)**: 리더선출 시연하려면 순번기 인스턴스를 추가 기동하되 **반드시 동일
|
||||
SEQUENCE DB**(POSTGRES_DB=rtgs_dc1)를 바라보게 할 것. 서로 다른 DB면 순번 충돌.
|
||||
- **Gucci 콜백 시드**: `institution_endpoint`가 `localhost:8095`(DC1 Gucci)로 시드됨. 센터별
|
||||
콜백 왕복까지 볼 땐 DC2/DC3 endpoint URL 조정 필요(핵심 정족수 검증엔 무관).
|
||||
- **phantom skip 임계**: `rtgs.gap-timeout-ms`(기본 5000) — 실측 부하에 맞게 조정 가능(env/프로퍼티).
|
||||
- **재발행 grace**: `rtgs.outbox-republish-grace-ms`(기본 3000) — 인라인 발행과의 경합 회피 창.
|
||||
- 21 JVM 동시기동은 고사양 PC 전제. 8코어에선 경량 스모크만 권장.
|
||||
187
docs/RTGS 삼중화(B1-B3) 설계.md
Normal file
187
docs/RTGS 삼중화(B1-B3) 설계.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# RTGS 3센터 삼중화 설계 (B1·B2·B3) — 구현 착수 확정본
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 목적 | 2센터 PoC → **3센터 Active-Active-Active** 전환. 코드 구현은 현 PC, 전체 3센터 실측은 고사양 PC 이관 후 |
|
||||
| 범위 | B1 순번기 이중화·무손실, B2 정족수(2/3) 완결, B3 순서결번 처리 + 멀티센터 런타임·설정 공통화 |
|
||||
| 전제 | 코드는 이미 `CENTER_ID`/`CENTER_COUNT` 파라미터화. **§9 결정사항 확정 완료**(단일 인스턴스 3DB·인메모리 ack·순번기 3인스턴스) |
|
||||
| 실행 환경 | 실측 전제 = 고사양 PC(40코어/128GB) 3터미널. **단, 현 개발 PC는 8코어** → 이 PC는 코드/빌드/단위테스트/경량 스모크(1~2센터)까지, 전체 3센터 21 JVM 실측은 이관 후 |
|
||||
| 상태 | **설계 확정 + 코드 구현·빌드·단위테스트 완료(2026-07-13, 8코어 PC) · 3센터 실측은 고사양 PC** |
|
||||
| 구현 산출물 | B1/B2/B3 코드 + `V4__seq_outbox.sql` + 단위테스트(QuorumAggregator·GapBuffer) + `run-dc2/dc3.cmd`·`init-3centers-db.cmd`. 기동·검증은 **`docs/RTGS 3센터 이관·기동 체크리스트.md`** 참조 |
|
||||
|
||||
---
|
||||
|
||||
## 0. 핵심 원리 (재확인)
|
||||
- **결정론적 재생**: 3센터가 **같은 저널을 같은 순서로** 재생하면 각자 동일 상태 도달 → **원장 데이터 동기화 불필요**(양팀장님 이론 맞음).
|
||||
- 센터 간 조율이 필요한 건 딱 둘: **(a) 하나의 전역 순서 생성**(순번기) + **(b) 완결 선언 시 과반 확인**(정족수).
|
||||
- 재해 규율: **1센터 down = 서비스 지속**(복구 후 밀린 저널 **진도처리=따라잡기**) / **2센터 down = 서비스 중단(안전정지)** / 복구 후 **3센터 정상 재개**.
|
||||
|
||||
---
|
||||
|
||||
## 1. 현재 상태 (전환 대상)
|
||||
| 구분 | 현재(1센터) | 삼중화 전환 |
|
||||
|---|---|---|
|
||||
| Sequencer | 단일 인스턴스, seq=인메모리→DB max복원 | **durable-before-publish + 리더선출(3인스턴스) + 펜싱** |
|
||||
| 완결(Prada) | `confirmations=1` **하드코딩** → 즉시 ACCC | **센터별 applied ack 집계 → 과반(2/3) 후 ACCC** |
|
||||
| 순서(Hermes) | gap 버퍼링(무한대기 가능) | **gap 타임아웃 시 durable(Kafka/journal_log)에서 결번 pull** |
|
||||
| 원장 DB | 단일 `rtgs` | **센터별 `rtgs_dc1/dc2/dc3`** |
|
||||
| 서비스 | 1세트(7) | **3세트(센터별) + 단일 순번기 그룹** |
|
||||
| 설정 | 서비스별 yml 중복 | **공통 yml + env 파라미터화**(A안) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 멀티센터 런타임 구성 (단일 호스트, 3터미널)
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph SHARED["공유 인프라 (단일)"]
|
||||
K["Kafka :9092<br/>rtgs.inbound / rtgs.journal(단일파티션) / rtgs.result(=applied-ack) / rtgs.notify"]
|
||||
PG[("PostgreSQL :5433<br/>rtgs_dc1 · rtgs_dc2 · rtgs_dc3")]
|
||||
OBS["Prometheus:9090 · Grafana:3000 · ELK"]
|
||||
end
|
||||
subgraph SEQ["전역 순번기 (그룹=sequencer, 1 active=리더)"]
|
||||
S1["seq@DC1"]:::a --- S2["seq@DC2"]:::s --- S3["seq@DC3"]:::s
|
||||
end
|
||||
DC1["DC1 스택 809x<br/>Chanel/Dior/Hermes/Prada/Gucci/LV"]
|
||||
DC2["DC2 스택 819x<br/>동일"]
|
||||
DC3["DC3 스택 829x<br/>동일"]
|
||||
K --- SEQ
|
||||
K --- DC1 & DC2 & DC3
|
||||
DC1 --> PG
|
||||
DC2 --> PG
|
||||
DC3 --> PG
|
||||
classDef a fill:#cfe8cf; classDef s fill:#eee
|
||||
```
|
||||
|
||||
**포트 오프셋** (센터별 +100): DC1 8090~8099·5174 / DC2 8190~8199·5175 / DC3 8290~8299·5176.
|
||||
**공유**: Kafka 9092 · PostgreSQL 5433(3 DB) · Prometheus 9090 · Grafana 3000 · ELK 9200/5000/5601.
|
||||
**컨슈머 그룹**: 원장서비스는 **센터별 접미사**(예: `hermes-DC1/DC2/DC3`) → 각 센터가 저널 전량 독립 재생.
|
||||
순번기는 **단일 그룹 `sequencer`**(3인스턴스, 1파티션 → 1개만 활성 = 리더).
|
||||
|
||||
> **구현지점(현 코드 상태)**: `@KafkaListener(groupId="hermes")` 등 groupId가 **하드코딩**되어 있음
|
||||
> (dior/hermes/prada/chanel/sequencer 각 서비스). 센터별 독립 재생을 위해 원장서비스 groupId를
|
||||
> `CENTER_ID` 접미사로 파라미터화 필요 → SpEL `groupId = "hermes-#{@centerId}"` 또는
|
||||
> `groupId = "\${rtgs.group.hermes}"`(프로퍼티 주입). **순번기만 예외**(접미사 없이 단일 그룹 `sequencer` 유지).
|
||||
|
||||
---
|
||||
|
||||
## 3. B1 — 순번기 이중화 + 무손실 (durable-before-publish)
|
||||
|
||||
> **현 코드 상태(전환 대상)**: `SequencerService.kt`는 인메모리 `AtomicLong` + 부팅 시
|
||||
> `@PostConstruct`로 `MAX(journal_log.global_seq, transfer.global_seq)` 복원. **순번기 자신은
|
||||
> `journal_log`/outbox를 쓰지 않고** 바로 `rtgs.journal` 발행(발행-후 크래시 시 무손실 미보장).
|
||||
> 따라서 아래 SEQUENCE·outbox·재발행은 **전량 신설**이다(현 코드에 없음).
|
||||
|
||||
**리더선출(간결·견고)**: 순번기 3인스턴스를 **같은 컨슈머 그룹 `sequencer`** 로 `rtgs.inbound`(단일 파티션) 구독 →
|
||||
Kafka가 파티션을 **1개 인스턴스에만 배정 = 자동 단일 리더**. 리더 死 시 Kafka 리밸런스로 **자동 승계**(별도 선출 로직 불필요).
|
||||
|
||||
**durable-before-publish (무손실·무결번)**:
|
||||
1. 리더가 접수 수신 → **Postgres SEQUENCE `global_seq_seq`.nextval**(원자적·durable 순번)
|
||||
2. 저널 엔트리를 **outbox 테이블에 저장(commit)** → 그 후 `rtgs.journal` 발행
|
||||
3. 크래시로 발행 전 죽어도, 재기동/승계 리더가 **outbox의 미발행분을 재발행**(at-least-once)
|
||||
4. 소비측은 seq 기준 **멱등**(이미 있음) → 중복 무해
|
||||
|
||||
**펜싱(split-brain)**: nextval이 원자적이라 **중복 순번 불가**. 순간적 이중리더가 있어도 서로 다른 seq만 발급 →
|
||||
저널 오염 없음. (엄격 fencing이 필요하면 outbox에 leader epoch 컬럼 추가—후순위)
|
||||
|
||||
**작업**: 새 서비스/모듈 대신 SequencerService 개편 + `global_seq_seq`·`journal_outbox` + 재발행 스케줄러.
|
||||
- **Flyway 위치**: 마이그레이션은 `backend/louisvuitton/src/main/resources/db/migration/`에 있고
|
||||
현재 `V1__baseline`·`V2__admin_login`·`V3__service_heartbeat`까지 존재 → SEQUENCE+outbox는 **`V4__seq_outbox.sql`** 로 추가.
|
||||
- **재발행**: `journal_outbox`에 `published` 플래그 컬럼 → 발행 성공 시 true. 스케줄러가 미발행분 주기 재발행(at-least-once).
|
||||
|
||||
---
|
||||
|
||||
## 4. B2 — 정족수(2/3) 완결
|
||||
|
||||
**흐름**: 각 센터 Hermes가 seq 적용(선저널 commit) 후 **applied ack** 발행 → `rtgs.applied`(bmi, seq, center).
|
||||
각 센터 Prada가 **bmi별 서로 다른 center 수 집계** → **≥ quorum(=CENTER_COUNT/2+1)** 도달 시 **ACCC 확정**.
|
||||
**결과통보는 origin 센터만** → 과반 도달 후 pacs.002 송부.
|
||||
|
||||
**구현 결정 — `rtgs.result`를 applied-ack으로 재활용(별도 토픽 불필요)**:
|
||||
- 당초 `rtgs.applied` 신설을 검토했으나, `rtgs.result`가 이미 **커밋 후에만**(publish-after-commit)
|
||||
발행되고 `processedCenter`를 담으므로 "그 센터가 durable 적용했다"는 ack와 **정확히 동일**하다.
|
||||
→ 토픽/Hermes 변경·마이그레이션 없이 더 단순·안전. `Topics`에 APPLIED 추가하지 않음.
|
||||
- **구현 완료(2026-07-13)**:
|
||||
- `prada/QuorumAggregator.kt`(신규, 순수로직+단위테스트 6): bmi별 서로 다른 `processedCenter` 집계,
|
||||
과반 **최초 도달** 판정, 완결 후 멱등 가드.
|
||||
- `PradaService.finalize()`: `confirmations=1` 제거 → `aggregator.record()` 기반. 과반 미달=ACSP,
|
||||
도달=ACCC(반려면 RJCT). `@KafkaListener(groupId="prada-${rtgs.center-id}")`로 전 센터분 소비.
|
||||
- 중복 통보 방지: **origin 센터 Prada만** notify 발행(`notice.originCenter == centerId`).
|
||||
`NotificationService`의 origin 필터는 그대로 유지(이중 안전).
|
||||
|
||||
**ack 집계 저장(§9.4 확정)**: **인메모리 맵**(bmi→집계한 center 집합). `finality_ack` 테이블은 **불채택**(간단성 우선).
|
||||
|
||||
**재기동 경계조건(인메모리 손실 대응)**:
|
||||
- 각 센터 Prada는 자기 컨슈머그룹으로 `rtgs.applied`를 소비 → 재기동 시 커밋된 오프셋에서 재개.
|
||||
- 인메모리 집계 맵이 재기동으로 비어도: **이미 ACCC 확정분은 `transfer.status`에 영속**되어 안전(재판정해도 멱등).
|
||||
아직 과반 미달(ACSP)인 건만 이후 도착하는 ack로 재집계 → 최종 수렴. **이중완결/누락 없음**.
|
||||
|
||||
**가용성 매핑**:
|
||||
- 1센터 down → 남은 2센터가 ack 2개 → 과반 충족 → **완결 지속**.
|
||||
- 2센터 down → ack 1개 → 과반 미달 → **완결 보류(안전정지)**. 복구 후 자동 재개.
|
||||
|
||||
---
|
||||
|
||||
## 5. B3 — 순서 결번(gap) 처리
|
||||
|
||||
- B1으로 **영구 결번 소멸**(발급=durable). 지연으로 인한 일시 gap은 Hermes 버퍼가 재정렬(S4 검증됨).
|
||||
- 방어책: `expected` 순번이 **T초 이상 미도착** 시 → **경보 + Kafka에서 해당 seq 오프셋 재읽기(pull)**
|
||||
(durable 저널에서 직접 가져옴). **스킵 금지**(실제 엔트리는 반드시 적용).
|
||||
- 지표: `rtgs.journal.gap`(이미 추가) + 타임아웃 카운터 → Grafana 경보.
|
||||
|
||||
---
|
||||
|
||||
## 6. 재해·복구 시나리오 (검증 항목)
|
||||
| 시나리오 | 기대 | 검증(7/13) |
|
||||
|---|---|---|
|
||||
| 평상시 3센터 | 동일 순서·동일 잔액, 완결 즉시 과반 | 3 DB 대사 일치, 총액 보존 |
|
||||
| 1센터 강제종료 | 서비스 지속(과반 2/3), 이중지급 0 | 나머지 2센터 완결 지속 |
|
||||
| 재해센터 복구 | 밀린 저널 **진도처리(따라잡기)** 후 3센터 수렴 | 오프셋 재생→잔액 일치 |
|
||||
| 2센터 종료 | 완결 보류(안전정지), 오처리 0 | 과반 미달로 ACCC 정지 |
|
||||
| 2센터 복구 | 따라잡기 후 **3센터 정상 재개** | 완결 재개·정합성 |
|
||||
| 순서 역전/gap | 재정렬·pull로 정답 수렴 | S4 확장(3센터) |
|
||||
|
||||
---
|
||||
|
||||
## 7. 설정 공통화 (A안, 삼중화 전제작업)
|
||||
- `common`에 `application-common.yml`(datasource·kafka·management 공통) → 각 서비스 `spring.config.import`.
|
||||
- 센터·포트·DB·그룹접미사는 **env 파라미터**: `CENTER_ID`, `CENTER_COUNT=3`, `POSTGRES_DB=rtgs_dc1`, `PORT_OFFSET`, `GROUP_SUFFIX`.
|
||||
- 실행 스크립트 3종: `run-dc1.cmd`(현행) + **`run-dc2.cmd`·`run-dc3.cmd`**(env만 다름) + 공유 인프라/ELK/Prometheus는 1회 기동.
|
||||
|
||||
---
|
||||
|
||||
## 8. 착수 체크리스트 (순서)
|
||||
|
||||
**A. 현 개발 PC(8코어)에서 가능 — 코드·빌드·경량 검증**
|
||||
2. **설정 공통화(A안)** + env 파라미터화(`CENTER_ID`/`CENTER_COUNT`/`PORT_OFFSET`/`GROUP_SUFFIX`) + `run-dc2/dc3.cmd`.
|
||||
3. **DB 분리**: 단일 인스턴스(5433)에 `rtgs_dc1/dc2/dc3` 생성(Flyway가 각 DB 스키마 생성).
|
||||
4. **B1**: `V4` SEQUENCE+outbox+재발행 스케줄러, 순번기 3인스턴스(그룹 리더선출) → 단일 순번 검증.
|
||||
5. **B2**: `rtgs.applied` 토픽 + Hermes ack 발행 + Prada 과반 집계(인메모리) → 완결 규율.
|
||||
6. **B3**: gap 타임아웃·pull.
|
||||
→ 각 단계마다 **`./gradlew build` + 단위테스트 + 경량 스모크(관자 1~2센터 기동)**로 확인.
|
||||
|
||||
**B. 고사양 PC 이관 후 — 전체 3센터 실측**
|
||||
1. **환경 이관 검증**: C:\ai-dev 복사본으로 빌드·기동 확인(포터블 무설치 철학이라 경로만 동일하면 OK).
|
||||
7. **재해/복구 시나리오**(§6) + **성능 실측**(터미널3, 21 JVM, k6/테스트화면, 순번·지연 병목).
|
||||
8. sim S3(정족수/펜싱)·S5(스파이크) 추가.
|
||||
|
||||
---
|
||||
|
||||
## 9. 결정사항 — **확정 완료** (2026-07-13, 양팀장님)
|
||||
| # | 항목 | 확정 | 비고 |
|
||||
|---|---|---|---|
|
||||
| 1 | PostgreSQL | **단일 인스턴스 · 3 DB**(5433, `rtgs_dc1/dc2/dc3`) | 자원 절약, 8코어 PC 적합. Flyway가 각 DB 스키마 생성 |
|
||||
| 2 | 프론트 콘솔 | **DC1 콘솔 하나로 3센터 조회**(제안) | 간단. 센터별 3콘솔(5174~5176)은 필요 시 후속 |
|
||||
| 3 | 순번기 배치 | **3인스턴스**(센터당 1, 그룹 `sequencer` 리더선출) | Kafka 리밸런스 자동 승계 |
|
||||
| 4 | applied ack 저장 | **인메모리 맵** | `finality_ack` 테이블 불채택. 재기동 경계조건은 §4 참조 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 리스크·주의
|
||||
- 21개 서비스 JVM 동시 구동은 **고사양 PC 전용**(여유). **현 8코어 PC에선 경량 스모크(1~2센터, 일부 서비스)**로
|
||||
코드 정상동작만 확인하고, 전체 동시기동 실측은 이관 후. 개별 명령 기동(bash for-loop 동시기동 실패 이슈).
|
||||
- Kafka 저널은 **반드시 단일 파티션**(전역 순서). 3센터가 같은 토픽 독립 그룹으로 소비.
|
||||
- 정합성 대사: 3 DB의 `transfer`/`account`가 동일해야(총액·상태·순번). 자동 대사 스크립트 준비.
|
||||
- 참조: 전문 보강(BAH/pacs.002 검증)은 `docs/pacs.7z` 기준(B7과 함께).
|
||||
|
||||
*본 설계는 구현 착수 기준 확정본(리뷰·보완 2026-07-13). §9 결정 반영 완료 — 이후 구현 중 세부만 조정.*
|
||||
402
docs/RTGS 아키텍처 설계서.md
Normal file
402
docs/RTGS 아키텍처 설계서.md
Normal file
@@ -0,0 +1,402 @@
|
||||
# RTGS 프로토타입 아키텍처 설계서
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 문서명 | 소액 RTGS(실시간총액결제) 프로토타입 아키텍처 설계서 |
|
||||
| 버전 | v1.0 |
|
||||
| 작성일 | 2026-07-10 |
|
||||
| 작성 | 루비(AI) · 검토 양희정 팀장(한국은행 RTGS시스템팀) |
|
||||
| 분류 | SW 산출물 — 아키텍처 설계 |
|
||||
| 대상 시스템 | `C:\ai-dev\workspace\rtgs` (로컬 포터블 프로토타입, 1센터 DC1) |
|
||||
| 관련 문서 | 개발일지 · 워크플로우 · 로컬 테스트 시나리오 · 복원력방안(과제⑧ v2.0) · PoC 1~3차 결과보고 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 개요
|
||||
|
||||
### 1.1 목적
|
||||
클라우드 PoC(3회차, "접수"까지 검증)의 아키텍처를 로컬에서 **업무기능 완결(신청→접수→청산·정산→완결 ACCC)** 로
|
||||
재현하고, 복원력방안(Active-Active-Active)의 미검증 급소를 검증하기 위한 프로토타입의 **소프트웨어/배포 아키텍처**를 정의한다.
|
||||
|
||||
### 1.2 범위
|
||||
- 1센터(DC1) 로컬 구성의 논리·물리(OS-WAS-DB) 아키텍처, SW 스택, 데이터/인터페이스/보안 설계.
|
||||
- 다센터(DC2/DC3) A-A-A 확장 설계는 §11에 방향만 제시(구현은 고사양 PC 이관 후).
|
||||
|
||||
### 1.3 용어
|
||||
| 약어 | 의미 |
|
||||
|---|---|
|
||||
| RTGS | Real-Time Gross Settlement(실시간총액결제) |
|
||||
| A-A-A | Active-Active-Active(3센터 동시 가동) |
|
||||
| BMI | Business Message Identifier(거래식별자 22자리) |
|
||||
| 저널 | 전역순번이 부여된 처리 명단(Kafka 단일 파티션) |
|
||||
| 정족수 | Quorum(N/2+1). 3센터=2 |
|
||||
| 선저널 | Write-ahead journal(잔액연산 전 순번 확정) |
|
||||
| WAS | Web Application Server(본 시스템은 Spring Boot 내장 Tomcat) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 아키텍처 원칙 (복원력방안 반영)
|
||||
|
||||
1. **단일 전역 순번기**로 입구에서 전역순번 부여 → 저널 → N센터 **결정론적 재생**(동일순서·동일업무).
|
||||
2. **원장 = RDB(PostgreSQL) 강한 일관성** — 잔액 확정의 권위 저장소.
|
||||
3. **정족수(2/3) + 펜싱**으로 split-brain 차단(1센터=자기 과반, 구조 선반영).
|
||||
4. **선(先)저널(write-ahead)** 후 잔액연산 → 노드사(死) 무손실 승계.
|
||||
5. **완결 규율** — 과반 확정 후 최종(ACCC).
|
||||
6. **경량 명령문 + 원전문 해시(SHA-256)** — 센터 간엔 수백B 구조체만 복제, 원전문은 별도 저장.
|
||||
7. **순서=Sequencer(상태O) / 분산·관문=Gucci·GSLB(상태X)** 역할 분리.
|
||||
8. **포터블 네이티브** — 모든 런타임을 `C:\ai-dev\apps` 하위에 무설치 배치(회사 PC 가상화 차단으로 Docker 불가).
|
||||
|
||||
---
|
||||
|
||||
## 3. 논리 아키텍처
|
||||
|
||||
> **그림 3-0. 업무 흐름도(기준)** — 3센터 A-A-A 동일 순서·동일 업무 처리
|
||||
>
|
||||
> 
|
||||
|
||||
### 3.1 계층 구조
|
||||
|
||||
**그림 3-1. 컴포넌트·데이터 흐름 (7개 서비스)**
|
||||
```mermaid
|
||||
flowchart TD
|
||||
EXT["참가기관 / k6"] --> G["Gucci 관문 :8095<br/>인증·유량·재전송·콜백"]
|
||||
CON["운영·관리 콘솔<br/>React :5174"] --> C
|
||||
CON --> LV["LouisVuitton :8099<br/>시스템관리"]
|
||||
G --> C["Chanel :8091<br/>접수·검증·통보"]
|
||||
C -->|rtgs.inbound| K
|
||||
K["Kafka :9092<br/>저널/결과/통보"] --> S["Sequencer :8090<br/>전역순번"]
|
||||
S -->|rtgs.journal| K
|
||||
K --> D["Dior :8092<br/>접수동기화"]
|
||||
K --> H["Hermes :8093<br/>정산·원장엔진"]
|
||||
H -->|rtgs.result| K
|
||||
K --> P["Prada :8094<br/>완결"]
|
||||
P -->|rtgs.notify| K
|
||||
D --> PG[("PostgreSQL :5433<br/>원장")]
|
||||
H --> PG
|
||||
P --> PG
|
||||
C --> PG
|
||||
G -->|콜백 송부| EXT
|
||||
```
|
||||
|
||||
**그림 3-2. 계층 구조**
|
||||
```
|
||||
┌── 표현 계층 ─────────────────────────────────────────────┐
|
||||
│ 운영/관리 콘솔 (React + Vite, :5174) │
|
||||
└───────────────┬──────────────────────────────────────────┘
|
||||
┌── 경계/관문 계층 ────────────▼───────────────────────────┐
|
||||
│ Gucci(:8095) — 로그인·API인증·유량제어·재전송차단·결과콜백 │
|
||||
└───────────────┬──────────────────────────────────────────┘
|
||||
┌── 접속/연계 계층 ────────────▼───────────────────────────┐
|
||||
│ Chanel(:8091) — pacs.008 접수·XSD검증·경량화·결과통보 │
|
||||
└───────────────┬──────────────────────────────────────────┘
|
||||
┌── 코어(순번/정산) 계층 ──────▼───────────────────────────┐
|
||||
│ Sequencer(순번) → [저널] → Dior(접수동기화) │
|
||||
│ Hermes(정산·원장엔진) │
|
||||
│ Prada(완결·결과동기화) │
|
||||
└───────────────┬──────────────────────────────────────────┘
|
||||
┌── 데이터/원장 계층 ──────────▼───────────────────────────┐
|
||||
│ PostgreSQL(원장·강한 일관성) · Kafka(입구/저널/결과/통보) │
|
||||
└───────────────┬──────────────────────────────────────────┘
|
||||
┌── 관리/관측 계층 ────────────▼───────────────────────────┐
|
||||
│ LouisVuitton(:8099) 시스템관리 · ELK 로그(9200/5000/5601)│
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 서비스(컴포넌트) 구성 — 7개 (명품 코드네임)
|
||||
| # | 서비스 | 코드네임 의미 | 유형 | 논리포트 | 책임 |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | **Sequencer** | 전역 순번기 | Headless(JVM) | 8090 | 전역순번·결정론적값 확정 → 저널 발행. **순번 영속화**(재기동 시 MAX(global_seq) 복원) |
|
||||
| 2 | **Chanel** | Contact Hub…Entry Liaison | **Web(Tomcat)** | 8091 | pacs.008 접수·XSD검증·경량화·원전문 저장·입구 발행 / **결과통보**(pacs.002 생성·아웃박스) |
|
||||
| 3 | **Dior** | Data Integration…Relay | Headless(JVM) | 8092 | 저널 소비 → 원장 PDNG 기록(접수 동기화) |
|
||||
| 4 | **Hermes** | …Rapid Memory Execution…Settlement | Headless(JVM) | 8093 | 저널 순차소비(cc=1)·순서교정·선저널·당좌 차/대변(강한 일관성)·ACSP |
|
||||
| 5 | **Prada** | Persistent Repository…Aggregation | Headless(JVM) | 8094 | 결과 소비·완결 규율(과반)·ACCC 확정·조회사본·**결과통보 발행** |
|
||||
| 6 | **Gucci** | Global User Communication Control Interface | **Web(Tomcat)** | 8095 | 외부 경계 관문: 로그인·JWT·API인증·유량제어·재전송차단·리버스프록시·결과 콜백송부 |
|
||||
| 7 | **LouisVuitton** | Leading Operations Unified Info Systems… | **Web(Tomcat)** | 8099 | 시스템관리: DB초기화·코드·사용자권한·대사 대시보드 |
|
||||
|
||||
> **유형 구분**: Web 서비스(Chanel·Gucci·LouisVuitton)만 내장 Tomcat으로 HTTP 리슨. 코어 처리 서비스
|
||||
> (Sequencer·Dior·Hermes·Prada)는 Kafka 컨슈머 JVM. **관측성(B4)** 도입으로 내장 Tomcat을
|
||||
> 활성화해 예약 포트(8090/8092/8093/8094)에 `/actuator`(health·prometheus)만 노출한다(업무 HTTP 아님).
|
||||
|
||||
---
|
||||
|
||||
## 4. 물리(배포) 아키텍처 — OS · WAS · DB 스택 ★
|
||||
|
||||
### 4.1 배포 토폴로지 (로컬 1센터, 단일 호스트)
|
||||
```
|
||||
┌──────────────── 물리 호스트 1대 (개발 PC, Windows 11) ────────────────┐
|
||||
│ 포터블 루트: C:\ai-dev\apps (런타임) · C:\ai-dev\home (데이터/상태) │
|
||||
│ │
|
||||
│ [WAS 계층 — JVM 프로세스] │
|
||||
│ 내장 Tomcat: Chanel:8091 · Gucci:8095 · LouisVuitton:8099 │
|
||||
│ Headless JVM: Sequencer · Dior · Hermes · Prada (전부 JDK 21) │
|
||||
│ │ JDBC(5433) │ Kafka(9092) │
|
||||
│ ▼ ▼ │
|
||||
│ [DB 계층] PostgreSQL 16.4 :5433 [MQ] Apache Kafka 3.8.1(KRaft):9092 │
|
||||
│ data: home\pgsql-data data: home\kafka\kraft-logs │
|
||||
│ │
|
||||
│ [프론트] Node.js 26 + Vite dev server :5174 (개발) │
|
||||
│ [관측] Elasticsearch:9200 · Logstash:5000(TCP) · Kibana:5601 │
|
||||
│ data: home\es-data │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
* 프로토타입은 **단일 호스트에 전 구성요소 공존**. 운영/BMT는 계층별 물리 분리 + 3센터 배치(§11).
|
||||
|
||||
**그림 4-1. 배포 스택 (단일 호스트)**
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph HOST["개발 PC · Windows 11 · C:/ai-dev"]
|
||||
subgraph WAS["WAS · JDK21 · Spring Boot 내장 Tomcat"]
|
||||
W1["Chanel :8091"]
|
||||
W2["Gucci :8095"]
|
||||
W3["LouisVuitton :8099"]
|
||||
end
|
||||
subgraph CORE["Headless JVM · Kafka 컨슈머"]
|
||||
c1["Sequencer"]
|
||||
c2["Dior"]
|
||||
c3["Hermes"]
|
||||
c4["Prada"]
|
||||
end
|
||||
DB[("PostgreSQL 16.4 :5433")]
|
||||
MQ["Kafka 3.8.1 KRaft :9092"]
|
||||
OBS[("ELK 8.15.2<br/>9200/5000/5601")]
|
||||
FE["Vite :5174 · Node 26"]
|
||||
end
|
||||
FE --> W1
|
||||
WAS --> DB
|
||||
WAS --> MQ
|
||||
CORE --> DB
|
||||
CORE --> MQ
|
||||
WAS -->|로그| OBS
|
||||
CORE -->|로그| OBS
|
||||
```
|
||||
|
||||
### 4.2 OS-WAS-DB 스택 매트릭스
|
||||
| 구분 | 구성요소 | 제품/버전 | 포트 | 데이터 경로 | 비고 |
|
||||
|---|---|---|---|---|---|
|
||||
| **OS** | 운영체제 | Windows 11 Enterprise 10.0.26100 (x64) | — | — | CPU 가상화 차단 → Docker 미사용, 네이티브 배치 |
|
||||
| **런타임** | JVM | Eclipse Temurin **JDK 21.0.11 LTS** | — | `apps\jdk-21` | 빌드·실행 공통(전역 JAVA_HOME은 jdk-26 별도) |
|
||||
| **WAS** | 앱서버 | Spring Boot **3.3.4** 내장 **Apache Tomcat** | 8091/8095/8099 | — | Web 3종. 코어 4종은 headless JVM |
|
||||
| **언어/빌드** | 언어 | **Kotlin 2.0.20** / Java 21 toolchain | — | — | — |
|
||||
| | 빌드 | **Gradle 8.10.2** (Kotlin DSL, 멀티모듈) | — | `home\gradle` | JDK26 미지원 → JDK21 고정 |
|
||||
| | DB 마이그레이션 | **Flyway**(Spring Boot 관리 버전) | — | `db/migration/V*.sql` | 스키마 형상관리(LouisVuitton 실행) |
|
||||
| **DB(원장)** | RDBMS | **PostgreSQL 16.4** | 5433 | `home\pgsql-data` | 강한 일관성 원장. acs(5432) 회피 |
|
||||
| **MQ/브로커** | 이벤트 | **Apache Kafka 3.8.1** (KRaft, 무 Zookeeper) | 9092 | `home\kafka\kraft-logs` | 입구·저널·결과·통보. 저널은 단일 파티션 |
|
||||
| **로그** | 검색엔진 | **Elasticsearch 8.15.2** | 9200 | `home\es-data` | 인덱스 `rtgs-logs-*` |
|
||||
| | 수집 | **Logstash 8.15.2** | 5000(TCP) | — | json_lines → ES. encoder 7.4 |
|
||||
| | 시각화 | **Kibana 8.15.2** | 5601 | — | Discover "RTGS Logs" |
|
||||
| **메트릭** | 수집 | **Prometheus 3.13.0** | 9090 | `home\prometheus-data` | 7서비스 `/actuator/prometheus` 스크랩(5s), `infra\start-prometheus.cmd` |
|
||||
| | 시각화 | **Grafana 13.1.0** (OSS) | 3000 | `home\grafana-data` | Prometheus 데이터소스+RTGS Overview 대시보드 자동, `infra\start-grafana.cmd` |
|
||||
| **프론트** | 런타임 | **Node.js 26.3.0** | — | — | 개발 서버 |
|
||||
| | 프레임워크 | **React 18.3.1 + Vite 5.4.2 + TS 5.5.4** | 5174 | — | 운영/관리 콘솔 |
|
||||
| **부하** | 테스트 | **k6 0.56.0** | — | — | 성능/스파이크 |
|
||||
| **(미사용)** | 문서DB | MongoDB 7.0.14 | (27017) | — | EDR 파일락 크래시 → PostgreSQL로 대체 |
|
||||
|
||||
### 4.3 프로세스·환경
|
||||
- 기동: `run-dc1.cmd`(인프라 + 7서비스) · `infra\start-elk-native.cmd`(ELK) · `run-frontend.cmd`(콘솔). 종료: `stop-dc1.cmd`.
|
||||
- 환경변수: `C:\ai-dev\scripts\env.cmd`(JDK21_HOME/GRADLE_HOME/PATH). 센터 파라미터 `CENTER_ID=DC1`, `CENTER_COUNT=1`.
|
||||
- 각 서비스는 `java -jar <service>-0.1.0.jar`(Spring Boot fat jar)로 독립 실행.
|
||||
|
||||
---
|
||||
|
||||
## 5. 데이터 아키텍처
|
||||
|
||||
### 5.1 저장소 역할 분리
|
||||
| 저장소 | 역할 | 근거 |
|
||||
|---|---|---|
|
||||
| PostgreSQL | **권위 원장**(잔액·거래상태·선저널·조회사본·원전문·통보·사용자·콜백) | 강한 일관성(이중지급 방지) |
|
||||
| Kafka | 입구 완충 + **전역순서 저널 전파** + 결과/통보 | 순서 공유·비동기·완충 |
|
||||
| (MongoDB) | 원전문/조회사본 (클라우드/BMT) | 로컬은 EDR 이슈로 PostgreSQL 대체 |
|
||||
|
||||
### 5.2 PostgreSQL 테이블 (DB: `rtgs`)
|
||||
| 테이블 | 용도 | 소유(주 기록) |
|
||||
|---|---|---|
|
||||
| `account` | 참가기관 당좌계좌 잔액(19개 기관 시드) | Hermes |
|
||||
| `transfer` | 거래 원장(BMI PK, 상태·순번·금액) | Dior/Hermes/Prada |
|
||||
| `journal_log` | 선저널(global_seq PK) | Hermes |
|
||||
| `raw_message` | 원전문 pacs.008 XML + SHA-256 | Chanel |
|
||||
| `settlement_view` | 조회 전용 사본(완결 확정) | Prada |
|
||||
| `notification` | 결과통보 아웃박스(pacs.002, delivered/attempts) | Chanel(적재)/Gucci(송부확정) |
|
||||
| `app_user` | 사용자·권한(ADMIN/ORG_S/ORG_R) + secret | LouisVuitton/Gucci |
|
||||
| `institution_endpoint` | 기관 콜백 URL 레지스트리 | Gucci |
|
||||
|
||||
* 컬럼 한글명은 `COMMENT ON COLUMN`(데이터 사전)으로 관리 → 화면 라벨의 단일 출처.
|
||||
* **스키마 형상관리 = Flyway**: `backend/louisvuitton/src/main/resources/db/migration/V*.sql`가 유일 출처.
|
||||
LouisVuitton 기동 시 자동 적용(`flyway_schema_history` 기록). 기존 DB는 baseline 처리, 신규 DB는 V1부터 생성.
|
||||
변경은 새 `V+1__*.sql` 추가(수기 ALTER·기적용 마이그레이션 수정 금지).
|
||||
|
||||
### 5.3 Kafka 토픽
|
||||
| 토픽 | 발행 → 소비 | 파티션 | 용도 |
|
||||
|---|---|---|---|
|
||||
| `rtgs.inbound` | Chanel → Sequencer | 1 | 접수 원시 요청 |
|
||||
| `rtgs.journal` | Sequencer → Dior·Hermes | **1(전역순서)** | 전역순번 저널 |
|
||||
| `rtgs.result` | Hermes → Prada | 1 | 결제 결과(ACSP/RJCT) |
|
||||
| `rtgs.notify` | Prada → Chanel | 1 | 완결 결과통보 트리거 |
|
||||
|
||||
### 5.4 상태 전이 (TxSts)
|
||||
`RCVD`(접수) → `ACTC`(순번) → `PDNG`(원장기록) → `ACSP`(정산반영) → `ACCC`(입금처리완료) / 실패 `RJCT`.
|
||||
|
||||
**그림 5-1. 상태 전이도**
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> RCVD: 접수(Chanel)
|
||||
RCVD --> ACTC: 순번(Sequencer)
|
||||
ACTC --> PDNG: 원장기록(Dior)
|
||||
PDNG --> ACSP: 정산(Hermes)
|
||||
ACSP --> ACCC: 완결·과반(Prada)
|
||||
ACCC --> [*]
|
||||
RCVD --> RJCT: 검증실패(XSD/업무규칙)
|
||||
ACSP --> RJCT: 잔액부족/미등록기관
|
||||
RJCT --> [*]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 인터페이스 설계
|
||||
|
||||
### 6.1 전문(ISO 20022, 공식 XSD)
|
||||
| 전문 | 표준/버전 | 방향 | 검증 |
|
||||
|---|---|---|---|
|
||||
| 결제의뢰 | **pacs.008.001.08** (FIToFICstmrCdtTrf) | 참가기관 → RTGS | 공식 XSD(XXE 차단, DOM 파싱) |
|
||||
| 결과통보 | **pacs.002.001.10** (FIToFIPmtStsRpt) | RTGS → 참가기관 | 정식 네임스페이스 생성 |
|
||||
| 식별자 | BMI 22자리 = 영업일(8)+기관(4)+일련(10) | — | 멱등키 |
|
||||
|
||||
### 6.2 주요 API
|
||||
| 계층 | 메서드·경로 | 인증 | 설명 |
|
||||
|---|---|---|---|
|
||||
| 관문(Gucci) | `POST /gucci/auth/login` | — | 로그인 → JWT 발급 |
|
||||
| | `POST /gucci/pay/customer` | Bearer+nonce | 인증 접수(→Chanel 프록시) |
|
||||
| | `GET /gucci/inquiry/{bmi}` · `/accounts` | Bearer | 인증 조회 |
|
||||
| | `GET /gucci/health/centers` · `GET/PUT /gucci/callbacks` | —/ADMIN | 헬스·콜백 레지스트리 |
|
||||
| 코어(Chanel) | `POST /pay/customer`, `GET /inquiry/{bmi}`, `/accounts` | (내부) | 접수·조회 |
|
||||
| | `GET /notifications[/{bmi}]`, `/rawmessage/{bmi}`, `/meta/labels` | (내부) | 통보·원전문·데이터사전 |
|
||||
| 관리(LouisVuitton) | `POST /admin/reset`, `/admin/institutions|users|status-codes|summary|ledger/*` | (관리) | 시스템관리 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 처리 흐름 (요약)
|
||||
```
|
||||
신청 → [Chanel] RCVD·XSD·경량화 → rtgs.inbound
|
||||
→ [Sequencer] 전역순번·ACTC → rtgs.journal
|
||||
├ [Dior] PDNG(접수동기화)
|
||||
└ [Hermes] 순서교정·선저널·잔액 차/대변(강한 일관성)·ACSP → rtgs.result
|
||||
→ [Prada] 과반확정·ACCC·조회사본 → (커밋 후) rtgs.notify
|
||||
→ [Chanel] pacs.002 생성·아웃박스 → [Gucci] 콜백 송부(재시도/ACK)
|
||||
```
|
||||
**그림 7-1. 거래 처리 시퀀스**
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant ORG as 참가기관(ORG_S)
|
||||
participant G as Gucci
|
||||
participant C as Chanel
|
||||
participant S as Sequencer
|
||||
participant H as Hermes
|
||||
participant P as Prada
|
||||
ORG->>G: 로그인 → JWT
|
||||
ORG->>G: pacs.008 (Bearer,nonce)
|
||||
G->>C: 인증·검사 후 프록시
|
||||
C-->>ORG: pacs.002 (RCVD)
|
||||
C->>S: rtgs.inbound
|
||||
S->>H: rtgs.journal (ACTC, Dior도 소비→PDNG)
|
||||
H->>H: 선저널·잔액 차/대변 (ACSP)
|
||||
H->>P: rtgs.result
|
||||
P->>P: 과반 확정 (ACCC)
|
||||
P->>C: rtgs.notify
|
||||
C->>G: 결과통보(pacs.002)
|
||||
G->>ORG: 콜백 송부 (ACK)
|
||||
```
|
||||
|
||||
* 상세는 「RTGS 프로토타입 워크플로우.md」 참조.
|
||||
|
||||
---
|
||||
|
||||
## 8. 비기능 요구 설계
|
||||
|
||||
### 8.1 성능
|
||||
- 목표 10,000~20,000 TPS(개요). 저널 **단일 파티션**이 순서 보장의 대가로 순번기 처리량 상한이 됨 → 성능 급소로 계측(sim).
|
||||
- Gucci **유량제어**(기관별 50/10s 기본)로 코어·순번기 보호(백프레셔).
|
||||
|
||||
### 8.2 가용성 (정족수)
|
||||
- 3센터 정족수=2: **1센터 다운=서비스 지속 / 2센터 다운=안전 정지**(장애). 1센터 로컬은 자기=과반.
|
||||
- Web 서비스는 무상태(세션리스 JWT) → 수평 확장·GSLB 분산 적합.
|
||||
|
||||
### 8.3 복원성 (무손실)
|
||||
- **선저널 + Kafka 오프셋 재생**: 원장엔진(Hermes) 재기동 시 유실 없이 승계.
|
||||
- **순번 영속화**: 순번기 재기동 시 MAX(global_seq)에서 이어서 발번.
|
||||
|
||||
### 8.4 데이터 정합성
|
||||
- 전역순번 직렬화 + 강한 일관성 트랜잭션 → **이중지급 0 / 총액 보존** 불변식. 원장=저널=조회사본 대사.
|
||||
- 멱등성: BMI 기준 `ON CONFLICT` upsert(중복 접수 무해).
|
||||
|
||||
### 8.5 보안
|
||||
| 영역 | 설계 |
|
||||
|---|---|
|
||||
| 인증 | Gucci 로그인 → **JWT(HS256)**. 자격증명=app_user(권한 마스터), 비밀키 **BCrypt+솔트 해시** 저장(기동 시 평문 자동 승격) |
|
||||
| 인가 | 역할(ADMIN/ORG_S/ORG_R) + **기관 바인딩**(토큰 org == 전문 송신기관) |
|
||||
| 관리자 콘솔 | LouisVuitton `/admin/**` 은 **ADMIN JWT 필수**(인터셉터 검증). Gucci 발급 토큰을 동일 서명키로 검증. 초기암호 변경(must_change_password) 지원. 테스트계정 a/1 |
|
||||
| API 보호 | 유량제어 · **재전송 차단**(nonce+timestamp) · 감사로그(ELK) |
|
||||
| 전문 무결성 | 원전문 **SHA-256** 해시, XSD 검증, **XXE 차단**(DTD/외부엔티티 비허용) |
|
||||
| 전달 보장 | 결과 콜백 at-least-once(재시도+ACK) + 수신측 dedup 전제 |
|
||||
| (후속) | mTLS·비밀키 해시/회전·펜싱 토큰 |
|
||||
|
||||
### 8.6 관측성
|
||||
- **로그**: 전 서비스 Logback(JSON, `service`/`center` 필드) → Logstash(:5000) → ES(`rtgs-logs-*`) → Kibana.
|
||||
- **메트릭(B4)**: 전 서비스 Micrometer → `/actuator/prometheus` 노출. 업무 메트릭 `rtgs.settlements`(정산·status),
|
||||
`rtgs.settle`(정산 지연 Timer), `rtgs.finality`(완결·status), `rtgs.journal.gap`(순서 gap), 접수 TPS/지연은
|
||||
`http_server_requests`(Chanel /pay), Kafka consumer lag는 Micrometer 자동. **Prometheus 3.13.0(:9090)** 설치·7타깃 스크랩(5s) + **Grafana 13.1.0(:3000)** "RTGS Overview" 대시보드(접수 TPS·정산율·완결·Kafka lag·JVM) 완료.
|
||||
- **헬스(B5)**: 전 서비스(헤드리스 포함)가 `service_heartbeat`에 주기 하트비트 → LouisVuitton `/admin/health` 및
|
||||
관리자 대시보드에서 UP/STALE 표시(DB 기반 liveness).
|
||||
- 감사 이벤트(AUTH/AUTHZ/RATE/REPLAY/ORGBIND/PAY/DELIVER)와 처리 로그 중앙 수집.
|
||||
|
||||
---
|
||||
|
||||
## 9. 검증 설계 (복원력 급소, sim/)
|
||||
| ID | 축 | 검증 | 상태 |
|
||||
|---|---|---|---|
|
||||
| S1 | 기능성 | 정합성·총액보존·계층대사 | **PASS** |
|
||||
| S2 | 복원성 | Hermes 강제종료·재기동 무손실 승계 | **PASS** |
|
||||
| S4 | 기능성 | 저널 순번 역전 주입 → 재정렬 | **PASS** |
|
||||
| S3/S5 | 가용성/성능 | 정족수·펜싱 / 스파이크 완충 | 다센터(§11) |
|
||||
|
||||
---
|
||||
|
||||
## 10. 형상·배포 운영
|
||||
- 소스: `backend`(Gradle 멀티모듈 7+1 common) · `frontend` · `infra` · `iso20022` · `sim` · `loadtest` · `docs`.
|
||||
- 산출물: 서비스별 Spring Boot fat jar(`*-0.1.0.jar`). 프론트는 Vite 빌드.
|
||||
- 스키마: **Flyway** `db/migration/V1__baseline.sql`(+ 이후 V2…) — LouisVuitton 기동 시 자동 적용. (구 `postgres-init.sql`은 legacy)
|
||||
- 반영: 백엔드=재빌드+해당 서비스 재기동(무관 서비스 무중단), 프론트=Vite HMR.
|
||||
|
||||
---
|
||||
|
||||
## 11. 확장 아키텍처 (다센터 A-A-A, 향후 · 고사양 PC 이후)
|
||||
```
|
||||
┌── DC1 (7서비스 + PostgreSQL rtgs_dc1)
|
||||
[단일 Sequencer(리더선출)]─저널(Kafka 공유)─┼── DC2 (동일 + rtgs_dc2)
|
||||
└── DC3 (동일 + rtgs_dc3)
|
||||
· 컨슈머 그룹 센터별 분리 · 센터별 DB/포트 오프셋 · 정족수 2/3 자동전환 · 펜싱
|
||||
· Gucci 센터별 배치 + 앞단 GSLB(무상태 라우팅) · 3센터 데이터 동일성 대사
|
||||
```
|
||||
- 순번기: active-standby 리더선출(4번째 사이트 불필요) — GSLB로 순서 결정 금지(무상태라 순서 권위 없음).
|
||||
- 헬스체크(Gucci G3): 관측·멤버십 입력용, 실제 페일오버는 정족수+펜싱(단순 ping 페일오버는 split-brain).
|
||||
|
||||
---
|
||||
|
||||
## 12. 제약·전제·미결
|
||||
- **제약**: 단일 호스트(가상화 차단으로 Docker 불가) · 로컬 1센터 · MongoDB 미사용.
|
||||
- **전제**: 사전확인(잔액·계좌)은 참가기관(클라이언트) 책임, RTGS 코어는 기관 당좌계좌 간 이체만.
|
||||
- **미결/후속**: 다센터 S3/S5, 프론트 Gucci 경유 로그인 UI, mTLS·비밀키 해시, Logstash 힙 256m, PC 이관.
|
||||
|
||||
---
|
||||
|
||||
## 부록 A. 포트 일람
|
||||
Sequencer(논리)8090 · Chanel 8091 · Dior(논리)8092 · Hermes(논리)8093 · Prada(논리)8094 ·
|
||||
Gucci 8095 · LouisVuitton 8099 · Frontend 5174 · PostgreSQL 5433 · Kafka 9092 ·
|
||||
ES 9200 · Logstash 5000 · Kibana 5601. (acs와 전면 분리: acs 8080/5173/5432)
|
||||
|
||||
## 부록 B. 디렉터리
|
||||
```
|
||||
C:\ai-dev\apps\ 포터블 런타임(jdk-21, gradle, kafka, postgresql, elk, k6, nodejs)
|
||||
C:\ai-dev\home\ 데이터·상태(pgsql-data, kafka, es-data, gradle 캐시)
|
||||
C:\ai-dev\workspace\rtgs\ backend · frontend · infra · iso20022 · sim · loadtest · docs
|
||||
```
|
||||
|
||||
*본 설계서는 개발 진행에 따라 갱신한다.*
|
||||
BIN
docs/RTGS 프로로타입 시스템 PoC 테스트 3차 결과보고.pdf
Normal file
BIN
docs/RTGS 프로로타입 시스템 PoC 테스트 3차 결과보고.pdf
Normal file
Binary file not shown.
234
docs/RTGS 프로토타입 개발일지.md
Normal file
234
docs/RTGS 프로토타입 개발일지.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# RTGS 프로토타입 개발일지
|
||||
|
||||
> 대상: 한국은행 소액RTGS(실시간총액결제) 프로토타입 — 로컬(포터블 Windows) 자체 개발
|
||||
> 목적: 클라우드 PoC(1~3차)에서 검증한 아키텍처의 **업무기능을 로컬에서 완결 개발·테스트**
|
||||
> 작성: 루비 · 담당: 양팀장님(한국은행 RTGS시스템팀)
|
||||
> 위치: `C:\ai-dev\workspace\rtgs`
|
||||
|
||||
---
|
||||
|
||||
## 0. 한눈에 보기
|
||||
|
||||
| 구분 | 내용 |
|
||||
|---|---|
|
||||
| 기술 스택 | Kotlin 2.0.20 · Spring Boot 3.3.4 · JDK 21(Temurin) · Gradle 8.10.2 |
|
||||
| 아키텍처 | 전역 순번기(Sequencer) + 저널 순차처리 + 강한 일관성 원장(PostgreSQL) · N센터 확장형 |
|
||||
| 서비스(6) | Sequencer · Chanel(전문송수신) · Dior(접수동기화) · Hermes(결제/원장엔진) · Prada(결과동기화) · LouisVuitton(관리자) |
|
||||
| 인프라 | **네이티브 포터블**(Docker 아님): Kafka(KRaft) · PostgreSQL · Elasticsearch/Logstash/Kibana |
|
||||
| 전문 | ISO20022 pacs.008(요청)/pacs.002(응답), BMI 22자리 식별자 |
|
||||
| 상태머신 | RCVD→ACTC→PDNG→ACSP→ACCC (실패 RJCT) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 일자별 상세 내역
|
||||
|
||||
### 📅 2026-07-08 (1일차) — 분석 · 설계 · 개발환경 · 골격 개발
|
||||
|
||||
**분석**
|
||||
- 워크스페이스 현황 파악(acs 기존 프로젝트, rtgs는 docs만 있는 빈 폴더)
|
||||
- 설계 자료 정독: PoC 1·2·3차 결과보고 PDF, "RTGS ActiveActive 복원력방안"(과제⑧ v2.0), 리뷰어/저자 의견, 프로젝트 개요
|
||||
- acs와 **포트/compose 비충돌** 원칙 확인
|
||||
|
||||
**설계 결정**
|
||||
- 언어 Kotlin, 로컬 인프라 실물 경량 스택, 센터는 1센터 시작·N센터 확장 구조
|
||||
- 복원력방안 반영: **전역 순번기 / 원장=RDB(PostgreSQL) 강한 일관성 / 정족수 / 저널 결정론적 재생 / 선저널 / 완결 규율**
|
||||
- 원장은 RDB, 인메모리(Hazelcast)는 코어 연산, 문서형(MongoDB)은 원전문/조회사본으로 역할 분리(설계)
|
||||
|
||||
**설치 (포터블, `C:\ai-dev\apps`)**
|
||||
- **JDK 21 (Temurin 21.0.11)** — Gradle 8.10.2가 JDK26 미지원이라 별도 설치. `env.cmd`에 `JDK21_HOME` 추가(전역 JAVA_HOME은 jdk-26 유지)
|
||||
- **Gradle 8.10.2** — `GRADLE_HOME` 추가, 캐시는 `home\gradle`
|
||||
|
||||
**개발 (backend 멀티모듈)**
|
||||
- Gradle Kotlin DSL 멀티모듈 구성(common/sequencer/chanel/dior/hermes/prada) + Gradle Wrapper 생성
|
||||
- **common 모듈**: `TxSts`(상태머신), `CoreMessage`(경량명령문+SHA-256), `JournalEntry`(전역순번+결정론적값), `BmiGenerator`(22자리), `QuorumContext`(정족수), ISO20022 DTO(Pacs008/Pacs002)+코덱, `XmlValidator`(XSD), 토픽/맵/테이블 상수 → **빌드·단위테스트 통과**
|
||||
- **5개 서비스** 구현(Sequencer 순번부여·저널발행, Chanel 접수·검증·경량화·응답, Dior 접수동기화, Hermes 순차소비·선저널·이체·결과발행, Prada 완결) → **빌드 성공**
|
||||
- infra: `docker-compose.yml`(초안, Kafka/PostgreSQL/MongoDB) + `postgres-init.sql`(참가기관 19개 시드)
|
||||
- loadtest: `pacs008.k6.js`(정상/스파이크), 샘플 전문
|
||||
- 실행 스크립트(run-dc1/stop-dc1), React(Vite) 프론트엔드 스캐폴드
|
||||
- ※ 일 사용한도 초과로 `kotlin("plugin.jpa")` 적용 직후 중단
|
||||
|
||||
### 📅 2026-07-09 (2일차) — 통합 · 인프라 전환 · 검증 · 고도화
|
||||
|
||||
**환경 이슈 해결**
|
||||
- IDE(VS Code Java확장)가 JDK26으로 import 실패 → `gradle.properties`에 `org.gradle.java.home=jdk-21` 고정, `.vscode/settings.json` 지정, wrapper 8.10.2 복원
|
||||
- `kotlin("plugin.jpa")`를 common에 적용(JPA 엔티티 open/no-arg) → **전체 빌드 성공**
|
||||
- 사내 EDR 파일락으로 Gradle 캐시 이동 실패 → **재시도**로 통과(Maven/git과 동일 계열)
|
||||
|
||||
**Docker → 네이티브 전환 (중대 전환점)**
|
||||
- Docker Desktop 실행 시 **"Virtualization support not detected"** — 회사 PC의 CPU 가상화 차단(BIOS/정책). Docker(WSL2) 사용 불가 판정
|
||||
- **Docker 대신 네이티브 포터블 인프라로 전환**(이 PC의 포터블 철학에 오히려 부합):
|
||||
- **Kafka 3.8.1**(KRaft 단일노드, :9092) 설치·기동. Windows bat의 `wmic`(제거됨) 호출 실패 → `KAFKA_HEAP_OPTS` 사전 설정으로 우회
|
||||
- **PostgreSQL 16.4**(바이너리, :5433) initdb·기동, DB `rtgs` + 스키마/시드
|
||||
- **MongoDB 7.0.14** 설치했으나 **WiredTiger 체크포인트가 EDR 파일락에 걸려 반복 크래시(exit 14)** → 원전문/조회사본 저장을 **PostgreSQL로 이전**(raw_message/settlement_view). MongoDB는 미사용
|
||||
|
||||
**E2E 검증 성공**
|
||||
- 비웹 서비스 ObjectMapper 빈 누락(spring-web 부재) 수정 → 5서비스 기동
|
||||
- pacs.008 1건(1001→1002 150원): 접수 RCVD → **최종 ACCC**, 송신 −150/수신 +150, transfer·journal_log·settlement_view·raw_message 전 계층 대사 일치, **19계좌 총액 190억 보존(이중지급 0)**
|
||||
|
||||
**ELK 로그관리 구축**
|
||||
- Elasticsearch 8.15.2 설치·기동 → **EDR 환경 생존 검증 통과**(Lucene은 WiredTiger보다 견고)
|
||||
- Kibana 8.15.2, Logstash 8.15.2 설치. 서비스 공통 `logback-spring.xml`(logstash-logback-encoder, TCP :5000) → Logstash → ES `rtgs-logs-*` → Kibana. 5개 서비스 로그 수집 확인
|
||||
|
||||
**k6 부하테스트 + 버그 2건 발견·수정**
|
||||
- k6 0.56.0 설치, 30 rps×15초 부하
|
||||
- **버그① ACSP 잔류 레이스**: Hermes가 결과를 트랜잭션 커밋 *전* 발행 → Prada가 커밋 전 읽음 → **커밋 후 발행(publish-after-commit)** 으로 수정
|
||||
- **버그② 중복키 ERROR**: Dior/Hermes가 원장 같은 행 동시 INSERT → **원자적 upsert(ON CONFLICT)** 로 수정, 기존 ERROR 로그 정리
|
||||
|
||||
**프론트엔드 개선**
|
||||
- **자금이체 신청 폼**(송신/수신 드롭다운·금액→RCVD→ACCC 자동조회·잔액갱신)
|
||||
- 거래조회 **한글 항목명**(DB 컬럼 코멘트=데이터 사전 기반) · 상태 한글값 · 시각 `YYYYMMDDHH24MISS` · **원문 pacs.008** 표시
|
||||
|
||||
**관리자(LouisVuitton) 서비스 신규** (3차 PoC 미구현분)
|
||||
- `louisvuitton` 서비스(:8099) + 콘솔 "🛠 관리자" 탭
|
||||
- 기능: **DB 초기화**, **코드(참가기관) 관리**, **사용자 권한 관리**(app_user, ADMIN/ORG_S/ORG_R — 로그인 강제는 후속), **대사 대시보드**(서비스별 처리 현황)
|
||||
|
||||
**개인화**
|
||||
- 어시스턴트 이름 "루비"(Louis Vuitton 줄임), 사용자 호칭 "양팀장님" — 메모리 저장
|
||||
|
||||
### 📅 2026-07-10 (3일차) — 세션 재개 · 문서화 · 고도화(순번영속화·정식ISO·검증하니스)
|
||||
|
||||
- 세션 종료 후에도 **네이티브 프로세스 전부 생존** 확인(인프라·5+1 서비스), 데이터(거래·총액 190억) 보존. **프론트엔드(Vite)만 재기동**
|
||||
- 개발 문서 3종 작성: **개발일지 / 워크플로우 / 테스트 시나리오 갱신**
|
||||
|
||||
**고도화 ① Sequencer 전역순번 영속화 (2-1, 정합성 개선)**
|
||||
- 문제: 순번기 카운터가 인메모리라 재기동 시 1로 리셋 → journal_log(global_seq PK) 충돌/스테일
|
||||
- 해결: 기동 시 `@PostConstruct`에서 원장의 `MAX(global_seq)`(journal_log·transfer) 조회 → 그 다음부터 발번(high-water mark 복원). sequencer에 JDBC(:5433) 추가
|
||||
- 검증: 재기동 후 실제 이체가 **globalSeq=943**(복원 max 942의 다음)으로 처리 — 리셋 없음 확인
|
||||
|
||||
**고도화 ② 정식 ISO20022 XSD 적용 (2-2, 실제 전문 검증)**
|
||||
- iso20022.org 공식 XSD 도입: **pacs.008.001.08**(접수)·**pacs.002.001.10**(결과통보) → `common/resources/iso20022/xsd/`
|
||||
- 전문 모델을 실제 구조로 전환: `Document/FIToFICstmrCdtTrf`(GrpHdr: MsgId/CreDtTm/NbOfTxs/SttlmInf=CLRG, CdtTrfTxInf: PmtId/EndToEndId·IntrBkSttlmAmt@Ccy·ChrgBr=SLEV·Dbtr/DbtrAcct/DbtrAgt(ClrSysMmbId/MmbId)·CdtrAgt·Cdtr/CdtrAcct)
|
||||
- 파서를 **DOM 방식**(지역명 기준·네임스페이스 견고·XXE 차단)으로 재작성, pacs.002 응답은 정식 네임스페이스 템플릿 생성
|
||||
- 샘플 5종·프론트 신청폼 XML·공통 테스트 전면 정합. 검증: 공식 XSD로 정상 4건 통과, `badformat`(ChrgBr=ZZZZ) → `cvc-enumeration-valid` 반려
|
||||
- 적용: sequencer·chanel 재빌드·재기동(나머지 4서비스는 CoreMessage/JournalEntry 불변이라 무중단)
|
||||
|
||||
**고도화 ③ 검증 하니스 sim/ (S1/S2/S4)**
|
||||
- `sim/`(Git Bash): `s1_consistency`(정합성)·`s2_failover`(무손실 승계)·`s4_order`(순서 재정렬)·`reconcile.sql`·`lib.sh`·`README.md`
|
||||
- **결과 전건 PASS**: S1(20건 총액보존·원장=사본), S2(30건 투입 중 Hermes 강제종료·재기동, 유실 0), S4(seq 965→966→964 역전주입 → 964,965,966 순서기록·전건 ACCC)
|
||||
**고도화 ④ 결과통보(이체결과 송부) Chanel 이관**
|
||||
- Gucci 후보 기능 검토 결과, "이체신청기관앞 이체결과 송부"는 Chanel 헌장(접수+통보)에 맞아 **Chanel로 이관**
|
||||
- 신규 토픽 `rtgs.notify`: **Prada**가 완결(ACCC/RJCT) 후 발행(publish-after-commit) → **Chanel**이 소비
|
||||
- Chanel: 접수센터(originCenter)만 최종 pacs.002 생성 → **신청기관(송신) 결과통보**(항상)·**수취기관 입금통보**(ACCC만) → `notification` 아웃박스 기록. 조회 API `/notifications`, `/notifications/{bmi}`
|
||||
- 검증: ACCC(1003→1008)=APPLICANT+BENEFICIARY 2건, RJCT(9990 미등록)=APPLICANT 1건(사유 포함) 확인
|
||||
- Gucci 잔여 범위(관문/인증/헬스체크 등)는 요건 정의 후 착수
|
||||
|
||||
**고도화 ⑤ Gucci(외부 경계 관문) 신규 — 로컬 단계 G1·G2**
|
||||
- 6개 후보 기능 검토 후 **관문 계층으로 한정**(순서결정=Sequencer, 통보=Chanel로 이미 분담). 신규 `backend/gucci`(:8095)
|
||||
- **G1 인증 관문**: 로그인(app_user.secret 대조) → **JWT(HS256 자체구현)** 발급 · 인증된 리버스프록시(→Chanel) · 가드[JWT검증·역할·**기관바인딩**(토큰org=전문송신)·**유량제어**(50/10s)·**재전송차단**(nonce+timestamp)] · 감사로그(ELK) · 헬스체크
|
||||
- **G2 결과 콜백송부(전달보장)**: Chanel 아웃박스(delivered=false) → Gucci `DeliveryService`(3s 폴링)가 `institution_endpoint` 콜백으로 pacs.002 POST → 2xx ACK시 확정, 실패시 재시도(max5). 데모 sink + 레지스트리 관리(admin)
|
||||
- 검증: curl E2E(로그인/정상pay/무토큰401/기관불일치403/재전송400/관리자accounts/변조401) + 가드 단위테스트 통과. G2: 등록기관 즉시송부·미등록 재시도→등록→송부성공 확인
|
||||
- app_user.secret·notification.attempts·institution_endpoint 스키마 추가, run-dc1.cmd에 Gucci 기동 추가 → **7개 서비스 아키텍처 완성**
|
||||
- (남은 것) 프론트 Gucci 경유 로그인 UI(선택), G3(센터간 헬스/펜싱/GSLB)=다센터 단계
|
||||
|
||||
**보안 강화 A2 — 비밀키 BCrypt 해시**
|
||||
- `app_user.secret` 평문 → **BCrypt+솔트 해시** 저장. Gucci `AuthService`가 `PasswordEncoder.matches`로 대조
|
||||
- `SecretMigrator`(ApplicationRunner): 기동 시 평문(비 BCrypt) 시크릿을 자동 해시 승격(멱등). spring-security-crypto 도입
|
||||
- 검증: DB secret `$2a$10$…`(60자), 원 시크릿 로그인 200·오답 401
|
||||
|
||||
**A4 — Flyway 스키마 형상관리 도입**
|
||||
- 수기 SQL/ALTER → **Flyway** 단일 출처. 현재 스키마를 `louisvuitton/.../db/migration/V1__baseline.sql`로 이관, LouisVuitton이 실행
|
||||
- `baseline-on-migrate` → **기존 운영 DB는 baseline(무변경)**, 신규 빈 DB는 V1부터 생성. 인프라 스크립트는 빈 DB만 생성(스키마는 Flyway). 구 `postgres-init.sql`은 legacy 표시
|
||||
- 검증: `flyway_schema_history`에 baseline(1)+V2 기록, 운영 DB 무변경 확인
|
||||
|
||||
**A1 — 관리자 로그인(Gucci JWT 재사용, 옵션1)**
|
||||
- LouisVuitton `/admin/**`에 **JWT 인터셉터**(role ADMIN 필수). Gucci 발급 토큰을 동일 서명키로 **검증만** 수행
|
||||
- Gucci: 로그인 응답에 `mustChangePassword` 추가 + **비밀번호 변경 API**(`/gucci/auth/change-password`)
|
||||
- **V2 마이그레이션**: `must_change_password` 컬럼 + **테스트관리자 a/1**(ADMIN)
|
||||
- 프론트: 관리자 탭 **로그인 화면**(a/1) + Bearer 전송 + 초기암호변경 화면 + 로그아웃, vite proxy `/gucci`
|
||||
- 검증(프론트 프록시 경유): a/1→ADMIN 로그인, `/admin/summary` 토큰有 200·無 401, ORG_S 401, 비번변경 왕복(1→12→1)
|
||||
|
||||
**Gucci 요건 검토(설계 방향 합의)**
|
||||
- 양팀장님 제시 6개 후보 기능을 **경계 관문 계층으로 재배치**: ①헬스체크=관측만(페일오버는 정족수+펜싱) ②센터간MQ=Kafka가 이미 담당(Gucci는 외부관문) ③기관로그인=Gucci인증+LouisVuitton권한마스터 ④API인증=JWT+서명/nonce/timestamp ⑤"API순서지정"=라우팅/유량제어(전역순번은 Sequencer 단독) ⑥이체결과송부=**Chanel 이관**
|
||||
- 원칙 합의: **순서=Sequencer(리더선출)/분산=GSLB·Gucci(무상태)**. 로컬 단계 G1(인증관문)/G2(콜백송부)/G3(센터간)
|
||||
|
||||
**작업 분담 (양팀장님 지시)**
|
||||
- **ACS 프로젝트의 개선·서버배포는 Codex에게 일임**, 루비는 **RTGS 전담**. 동일 workspace 병렬작업 → 포트 이미 분리(ACS 8080/5173/5432 ↔ RTGS 8090~8095/5174/5433), 공유 `env.cmd`·Gradle 캐시는 RTGS 관련만 수정, cold 빌드 순차
|
||||
|
||||
**문서 산출물 관리 체계 수립**
|
||||
- **아키텍처 설계서** 신규 작성(`RTGS 아키텍처 설계서.md`) — OS-WAS-DB 스택 매트릭스 포함(Win11/JDK21.0.11/SpringBoot3.3.4 내장Tomcat/PostgreSQL16.4/Kafka3.8.1/ELK8.15.2/React18+Vite5+Node26/k6). **Mermaid 다이어그램**(컴포넌트·배포·상태·시퀀스) + 팀장님 참고 PNG(2센터흐름도·서비스png) 임베드
|
||||
- **문서 운영방침**(양팀장님): 개발 중 `.md` → 완성 시 **PPT/PDF**(HWP 불필요). pandoc 확인(pptx 직접·pdf는 Edge인쇄), `docs/tools/convert.cmd` 작성. 다이어그램=Mermaid+참고PNG, UML.xlsx는 필요 시 추출
|
||||
- **SW 산출물 관리대장**(`SW 산출물 관리대장.md`) 신규 — 과거 PoC의 코드-문서 드리프트 재발 방지. **단일 출처 원칙**(포트=run-dc1/yml·토픽=Constants·스키마=Flyway·데이터사전=DB COMMENT·API=Controller·전문=XSD), 변경유형→갱신문서 체크리스트, 정합성 점검 항목
|
||||
|
||||
**개선점 진단(루비 관점) → 우선순위 논의**
|
||||
- A(즉시): A1 관리자무인증·A2 평문시크릿·A3 관문우회·A4 수기스키마 / B(운영전): B1 순번기SPOF·B2 정족수하드코딩·B3 head-of-line·B4 관측성·B5 헤드리스헬스·B6 테스트자동화·B7 TLS/암호화 / C(다센터·성능)
|
||||
- **A1·A2·A4 완료**(위). A3=합의(부하테스트는 Chanel 직접 유지, 운영 전 차단). B군은 아래 결정대로 진행
|
||||
|
||||
**B6 — 테스트 실행 화면 (완료)**
|
||||
- 관리자 콘솔에 **이체 대량생성** 섹션: BMI 시작번호·건수 입력 → 송/수신기관 랜덤·금액 1,000~1,000,000 랜덤, Chanel 직접 호출(관문 우회), 20건 동시배치, 진행/접수/반려 집계. k6 대체 간이도구
|
||||
- 검증: 프론트 tsc 통과. (프론트 HMR 반영)
|
||||
|
||||
**B1/B2/B3 — 3센터 삼중화 방향 확정(설계 논의 중, 구현 예정)**
|
||||
- 양팀장님 결정: 이전 PoC는 2센터, **이제부터 3센터 기준 삼중화 구조로 전환**
|
||||
- B1 순번기: **durable-before-publish**(저널을 durable 저장 후 발행, 재기동 시 미발행분 재발행=outbox) + 리더선출·펜싱(에폭)
|
||||
- B2 정족수: 결정론적 재생이므로 데이터 동기화는 불필요, 단 **완결(응답) 시 과반(2/3) 확정 대기**로 1센터 손실 안전. `confirmations` 하드코딩→센터별 ack 집계로
|
||||
- B3 head-of-line: 영구 gap의 원인은 B1 유실 → B1 해결 시 소멸. 방어책=gap 타임아웃 시 **journal_log(durable)에서 결번 직접 pull**(skip 아님)
|
||||
**B4 관측성(메트릭) + B5 서비스 헬스 (완료)**
|
||||
- **B4**: 전 서비스 Micrometer + `/actuator/prometheus` 노출. 헤드리스 4종(Sequencer/Dior/Hermes/Prada)에 starter-web 추가→예약포트(8090/8092/8093/8094)에 actuator만 노출. 업무 메트릭: `rtgs.settlements`(status)·`rtgs.settle`(Timer)·`rtgs.journal.gap`(Hermes), `rtgs.finality`(status, Prada), 접수 TPS/지연=`http_server_requests`(Chanel), Kafka lag 자동. (Prometheus 서버/Grafana는 후속)
|
||||
- **B5**: `common.ops.HeartbeatAutoConfiguration`(스프링 자동설정, `@AutoConfigureAfter` DataSource) → 전 서비스가 `service_heartbeat`(Flyway V3)에 주기 기록. LouisVuitton `/admin/health` + 관리자 대시보드 **🩺 서비스 상태**(UP/STALE, 헤드리스 포함). micrometer-registry-prometheus는 common으로 전파
|
||||
- 검증: 7서비스 `/actuator/prometheus` 200, 하트비트 7건, 이체1건 후 settlements/finality/http 메트릭 증가, /admin/health 7 UP
|
||||
- (교훈) 자동설정 `@ConditionalOnSingleCandidate(DataSource)`는 `@AutoConfigureAfter(DataSourceAutoConfiguration)` 없으면 순서상 미적용. 헤드리스→web 전환으로 기동 ~13s(정상)
|
||||
|
||||
**B4 Prometheus 서버 설치 (완료)**
|
||||
- 포터블 **Prometheus 3.13.0**(`apps\prometheus-3.13.0`, 데이터 `home\prometheus-data`, :9090) 설치. `infra\prometheus.yml`(7서비스 스크랩·5s)·`infra\start-prometheus.cmd`
|
||||
- 검증: **7/7 타깃 up**, PromQL `rtgs_finality_total{status="ACCC"}` 조회 성공. README apps 표 등록
|
||||
- (교훈) `start "title" cmd /k ""exe" args"` 중첩따옴표는 배치에서 실패 → `start "title" "exe" args` 형태로
|
||||
|
||||
**B4 Grafana 시각화 (완료)**
|
||||
- 포터블 **Grafana 13.1.0**(`apps\grafana-13.1.0`, 데이터 `home\grafana-data`, :3000, 익명 Admin). `infra\start-grafana.cmd` + 프로비저닝(`infra\grafana\provisioning`: Prometheus 데이터소스 rtgs-prom + "RTGS Overview" 대시보드)
|
||||
- 대시보드 패널: 접수 TPS/지연(p95)·정산 처리율/지연·Kafka consumer lag·JVM heap·완결 누계. 검증: 데이터소스·대시보드 프로비저닝 확인
|
||||
- **관측성(B4/B5) 완료** — 로그(ELK)+메트릭(Prometheus/Grafana)+헬스(하트비트)
|
||||
|
||||
- (다음) **B1/B2/B3 3센터 삼중화**(durable-before-publish·정족수·gap pull) → **B7 암복호화+TLS**. C(다센터 실측)·성능은 **7/13(월) 고사양 PC 이관 후**(터미널 3개로 DC1~DC3 병렬 실측). D: pacs.7z를 전문 보강(BAH/pacs.002 검증) 착수 시 참조
|
||||
|
||||
---
|
||||
|
||||
## 2. 주요 의사결정·전환점 요약
|
||||
|
||||
| 결정/전환 | 이유 |
|
||||
|---|---|
|
||||
| JDK21 별도 설치 | Gradle 8.10.2가 JDK26 미지원 |
|
||||
| **Docker → 네이티브 포터블** | 회사 PC CPU 가상화 차단(스펙 아닌 BIOS/정책). 포터블 철학과 부합 |
|
||||
| **MongoDB → PostgreSQL** | MongoDB WiredTiger가 EDR 파일락에 크래시. 문서저장을 RDB로 대체(클라우드는 문서형 유지) |
|
||||
| 원장 = RDB 강한 일관성 | 복원력방안 원칙(이중지급 방지엔 단일순번+강한일관성) |
|
||||
| publish-after-commit | 결과발행을 커밋 후로 → Prada 완결 레이스 제거 |
|
||||
| 원자적 upsert(ON CONFLICT) | Dior/Hermes 동시삽입 중복키 제거 |
|
||||
| 정식 ISO20022 공식 XSD | 실제 전문 검증(pacs.008.001.08/002.001.10) |
|
||||
| 결과통보 = Chanel | Chanel 헌장(접수+통보)에 부합 |
|
||||
| Gucci = 외부 경계 관문만 | 순서=Sequencer, 분산=Gucci(무상태) 역할 분리 |
|
||||
| **Flyway 스키마 형상관리** | 수기 SQL 드리프트 방지(코드-문서 정합성) |
|
||||
| 관리자 로그인(JWT/BCrypt) | 관리자 무인증·평문시크릿 급소 제거 |
|
||||
| **ACS=Codex / RTGS=루비** | 병렬 개발 분담(양팀장님 지시) |
|
||||
| 문서: md→PPT/PDF, 다이어그램 Mermaid+PNG | 개발중 관리 용이, 완성시 정식 산출물 |
|
||||
| **3센터 삼중화 전환** | 이전 PoC는 2센터, 본 개발은 3센터 A-A-A 기준 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 현재 산출물
|
||||
|
||||
- **backend/**(Gradle 멀티모듈, **7서비스**): common + sequencer/chanel/dior/hermes/prada/**gucci**/louisvuitton
|
||||
- **frontend/**(React+Vite): 운영 콘솔(이체신청·조회·계좌) + 관리자 탭(로그인·대사·초기화·코드·사용자·**테스트 실행**)
|
||||
- **infra/**: 네이티브 기동 스크립트(start-infra-native[빈DB만 생성]·start-elk-native), init/postgres-init.sql(**legacy**), (참고용) docker-compose
|
||||
- **DB 스키마**: **Flyway** `louisvuitton/.../db/migration/`(V1__baseline, V2__admin_login)
|
||||
- **loadtest/**: k6, **sim/**: S1/S2/S4 하니스, **iso20022/**: 공식 XSD·샘플 5종
|
||||
- **docs/**: 개요·보고서·개발일지·워크플로우·테스트시나리오·**아키텍처 설계서**·**SW 산출물 관리대장**·참고(2센터흐름도·서비스png·UML.xlsx)·tools/convert.cmd
|
||||
- 포터블 앱: `C:\ai-dev\apps`(jdk-21, gradle, kafka, postgresql, elk, k6, nodejs 등)
|
||||
|
||||
## 4. 남은 과제 (백로그)
|
||||
|
||||
**완료(2026-07-10)**: ✅순번 영속화 ✅정식 ISO20022 XSD ✅sim S1/S2/S4 ✅결과통보 Chanel이관 ✅Gucci G1/G2(인증관문·콜백) ✅A2 BCrypt ✅A4 Flyway ✅A1 관리자로그인 ✅B6 테스트화면 ✅아키텍처설계서·관리대장 ✅**B4 메트릭(Micrometer/Prometheus 노출)** ✅**B5 서비스 헬스(하트비트 대시보드)** ✅**B4 Prometheus 서버(:9090)**
|
||||
|
||||
**진행/예정**:
|
||||
- **B1/B2/B3 3센터 삼중화** — *설계 확정 + **코드 구현·빌드·단위테스트 완료(2026-07-13, 8코어 PC)***:
|
||||
- **B1**: `V4__seq_outbox.sql`(SEQUENCE `global_seq_seq` + `journal_outbox`), `SequencerService` durable-before-publish + 재발행 스케줄러(@EnableScheduling). 순번기 전역 단일 리더(그룹 `sequencer`).
|
||||
- **B2**: `prada/QuorumAggregator`(신규, 단위테스트 6) — `rtgs.result`=applied-ack 재활용(별도 토픽 불필요), `processedCenter` 집계 과반(2/3) → ACCC. origin Prada만 통보.
|
||||
- **B3**: `hermes/GapBuffer`(신규, 단위테스트 6) + 타임아웃 스케줄러 → 단일파티션 근거 phantom skip, `rtgs.journal.gap.timeout` 지표.
|
||||
- **설정공통화**: 원장/엣지 groupId 센터접미사 파라미터화(`hermes-${center-id}`…), 순번기만 단일그룹.
|
||||
- **이관 산출물**: `run-dc2/dc3.cmd`, `infra/init-3centers-db.cmd`, `run-dc1.cmd`(3센터 모드), `docs/RTGS 3센터 이관·기동 체크리스트.md`.
|
||||
- **남은 것**: 전체 3센터 21 JVM 기동·재해/복구 시나리오·성능 실측 = **고사양 PC 이관 후**(팀장님 테스트).
|
||||
- **B4 관측성**: Micrometer + Prometheus(TPS/지연/consumer lag)
|
||||
- **B5 헤드리스 헬스**: LouisVuitton 대시보드에 서비스 하트비트(DB)
|
||||
- **B7 전문 암복호화 + TLS**: 기관 수신 ISO전문 복호화→처리, 통보 암호화 송부(성능테스트에 암복호화 포함)
|
||||
- **다센터(DC2/DC3) 실행·S3/S5**: 고사양 PC 이관 후
|
||||
- 프론트 Gucci 경유 로그인 UI(선택), Logstash 힙 256m, PC 백업/이관 체크리스트(다음주)
|
||||
|
||||
*본 일지는 개발 진행에 따라 계속 갱신.*
|
||||
341
docs/RTGS 프로토타입 로컬 테스트 시나리오.md
Normal file
341
docs/RTGS 프로토타입 로컬 테스트 시나리오.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# RTGS 프로토타입 로컬 테스트 시나리오
|
||||
|
||||
> 대상: 로컬(이 PC) RTGS 프로토타입 (1센터 DC1)
|
||||
> 작성: 루비 · 사용: 양팀장님
|
||||
> 구성: Docker 없이 **네이티브 포터블**(Kafka·PostgreSQL·ELK) + **6개 서비스** + React 콘솔(운영/관리자 탭)
|
||||
> 갱신(2026-07-10): 자금이체 **신청 폼**, 조회 **한글화·원문**, **관리자(LouisVuitton) 탭** 반영
|
||||
|
||||
---
|
||||
|
||||
## 0. 접속 정보 (브라우저로 여는 것)
|
||||
|
||||
| 화면/엔드포인트 | 주소 | 용도 |
|
||||
|---|---|---|
|
||||
| **RTGS 콘솔(운영)** | http://localhost:5174 | **자금이체 신청 폼** · 거래조회(한글·원문) · 계좌 잔액 |
|
||||
| **RTGS 콘솔(관리자)** | http://localhost:5174 → "🛠 관리자" 탭 | DB초기화 · 코드 · 사용자권한 · 대사 대시보드 |
|
||||
| **Kibana(로그)** | http://localhost:5601 | ☰ → Discover → **RTGS Logs** |
|
||||
| Chanel API(접수·코어) | http://localhost:8091/pay/customer | pacs.008 전문 POST(내부 코어) |
|
||||
| 조회 API | http://localhost:8091/inquiry/{BMI} , /accounts | 상태·잔액 |
|
||||
| 관리자 API | http://localhost:8099/admin/* | reset·institutions·users·summary |
|
||||
| **Gucci 관문 API** | http://localhost:8095/gucci/* | 로그인·인증 접수/조회·콜백(외부 경계) |
|
||||
|
||||
포트 요약: Chanel 8091 · Sequencer 8090 · Dior 8092 · Hermes 8093 · Prada 8094 · **Gucci 8095** · **LouisVuitton 8099** ·
|
||||
Kafka 9092 · PostgreSQL **5433** · Elasticsearch 9200 · Logstash 5000 · Kibana 5601 · Frontend 5174
|
||||
|
||||
> 💡 **가장 쉬운 테스트 경로는 브라우저(:5174)** 입니다. 아래 시나리오는 화면(GUI)과 명령(curl) 두 방법을
|
||||
> 함께 적었으니 편한 쪽을 쓰세요. curl은 자동화/부하테스트에, 화면은 눈으로 확인할 때 좋습니다.
|
||||
|
||||
> 참고: 명령은 **CMD 또는 PowerShell**을 열고 아래 폴더로 이동해 실행하세요.
|
||||
> `cd C:\ai-dev\workspace\rtgs`
|
||||
|
||||
---
|
||||
|
||||
## 1. 사전 확인 (헬스체크)
|
||||
|
||||
시작 전, 인프라·서비스가 떠 있는지 확인합니다.
|
||||
|
||||
```cmd
|
||||
rem 계좌 19개가 나오면 백엔드 정상
|
||||
curl http://localhost:8091/accounts
|
||||
|
||||
rem Elasticsearch 상태(green/yellow면 정상)
|
||||
curl http://localhost:9200/_cluster/health?pretty
|
||||
```
|
||||
|
||||
- 브라우저에서 http://localhost:5174 열어 **계좌 목록**이 보이면 프론트도 정상.
|
||||
- 안 뜨면 → **6. 기동/종료** 참조.
|
||||
|
||||
---
|
||||
|
||||
## 2. 테스트용 샘플 전문 (iso20022\samples\)
|
||||
|
||||
| 파일 | 내용 | 기대 |
|
||||
|---|---|---|
|
||||
| `pacs.008.xml` | 1001→1002, 150원 | 정상 완결(ACCC) |
|
||||
| `pacs.008-normal.xml` | 1005→1010, 5,000원 | 정상 완결(ACCC) |
|
||||
| `pacs.008-selftransfer.xml` | 1007→1007 (자기이체) | **접수 반려(RJCT)** |
|
||||
| `pacs.008-unknownbank.xml` | 9990(미등록)→1002 | **결제단계 반려(RJCT)** |
|
||||
| `pacs.008-badformat.xml` | ChrgBr=ZZZZ (스키마 위반) | **접수 반려(RJCT, XSD)** |
|
||||
|
||||
> 전문은 **정식 ISO20022 pacs.008.001.08**(접수)/**pacs.002.001.10**(응답) 공식 XSD로 검증합니다
|
||||
> (2026-07-10 전환). 응답 전문은 `urn:iso:std:iso:20022:tech:xsd:pacs.002.001.10` 네임스페이스로 옵니다.
|
||||
|
||||
> ⚠️ **BMI(거래식별자)는 유일해야 합니다.** 같은 파일을 두 번 보내면 두 번째는 "이미 처리됨"으로
|
||||
> 멱등 스킵됩니다(정상 동작). **다시 테스트하려면** 파일 안 `<BizMsgIdr>`의 뒷자리 숫자를 바꾸세요.
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 A. 정상 자금이체 (기능·완결) ⭐핵심
|
||||
|
||||
**목적**: 신청→접수→청산·정산→완결(ACCC) 전 과정과 잔액 증감 확인.
|
||||
|
||||
### A-1) 화면(폼)으로 — 권장 ✅
|
||||
1. 브라우저 http://localhost:5174 (운영 탭) → **자금이체 신청** 폼
|
||||
2. **송신기관·수신기관** 드롭다운 선택(서로 다르게), **금액** 입력 → **신청** 클릭
|
||||
3. 화면이 자동으로 접수(RCVD) → 조회를 폴링해 **ACCC**(입금처리완료)로 바뀌는 것을 표시
|
||||
4. 아래 계좌 테이블에서 **송신기관 잔액 −금액 / 수신기관 잔액 +금액** 확인
|
||||
- BMI는 폼이 자동 생성(매번 유일)하므로 중복 걱정 없음
|
||||
|
||||
### A-2) 명령(curl)으로
|
||||
```cmd
|
||||
cd C:\ai-dev\workspace\rtgs
|
||||
curl -X POST http://localhost:8091/pay/customer -H "Content-Type: application/xml" --data-binary @iso20022\samples\pacs.008-normal.xml
|
||||
```
|
||||
**기대 (접수 응답, pacs.002)**: `<TxSts>RCVD</TxSts>` (접수됨)
|
||||
|
||||
**확인 (2~3초 후)**
|
||||
```cmd
|
||||
curl http://localhost:8091/inquiry/2026070911110000000001
|
||||
```
|
||||
- `status` = **ACCC** (입금처리완료)
|
||||
- 그 후 `curl http://localhost:8091/accounts` → **1005 잔액 −5,000**, **1010 잔액 +5,000**
|
||||
- 브라우저(5174)의 "거래 상태 조회"에 BMI `2026070911110000000001` 입력해도 동일 확인.
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 B. 조회 (계좌·거래 · 한글화 · 원문) ⭐개선
|
||||
|
||||
**목적**: 조회 화면의 한글 항목명·상태·시각 포맷·원문 표시 확인.
|
||||
|
||||
### 화면(권장): 거래 상태 조회
|
||||
- 브라우저(5174) "거래 상태 조회"에 BMI 입력 → 결과 확인:
|
||||
- **항목명 한글 표시**(DB 컬럼 코멘트=데이터 사전 기반), **상태 한글값**(예: ACCC → *입금처리완료*)
|
||||
- 시각은 **YYYYMMDDHH24MISS** 포맷(예: 20260710153012)
|
||||
- **원문 pacs.008(ISO20022 XML)** 도 함께 표시 → 접수된 실제 전문 확인
|
||||
- 계좌 테이블 "새로고침" → 19개 기관 당좌계좌 잔액
|
||||
|
||||
### 명령(curl)
|
||||
```cmd
|
||||
curl http://localhost:8091/accounts rem 19개 기관 당좌계좌 잔액
|
||||
curl http://localhost:8091/inquiry/{BMI} rem 특정 거래 상태(JSON)
|
||||
curl http://localhost:8091/rawmessage/{BMI} rem 접수된 원문 pacs.008
|
||||
curl http://localhost:8091/meta/labels rem 항목 한글 데이터사전
|
||||
curl http://localhost:8091/notifications/{BMI} rem 결과통보 내역(신청/수취기관 앞 pacs.002)
|
||||
curl http://localhost:8091/notifications rem 최근 결과통보 100건
|
||||
```
|
||||
|
||||
> **결과통보(이체결과 송부)**: 거래가 완결(ACCC)/반려(RJCT)되면 접수센터의 Chanel이 최종 pacs.002를
|
||||
> 생성해 **신청기관(송신) 앞 결과통보**(항상)와 **수취기관 앞 입금통보**(ACCC만)를 `notification`에 기록합니다.
|
||||
> (당초 Gucci 후보 기능을 Chanel 헌장에 맞춰 이관 — 2026-07-10)
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 C. 반려(RJCT) 3종
|
||||
|
||||
**목적**: 잘못된 요청이 안전하게 거절되는지(이중지급·오처리 방지) 확인.
|
||||
|
||||
**C-1) 자기 이체 (송신=수신)** → 접수 즉시 반려
|
||||
```cmd
|
||||
curl -X POST http://localhost:8091/pay/customer -H "Content-Type: application/xml" --data-binary @iso20022\samples\pacs.008-selftransfer.xml
|
||||
```
|
||||
기대: 응답 `<TxSts>RJCT</TxSts>` (업무규칙 위반, 원장에 기록 안 됨)
|
||||
|
||||
**C-2) 미등록 기관 (9990)** → 접수는 되나 결제단계에서 반려
|
||||
```cmd
|
||||
curl -X POST http://localhost:8091/pay/customer -H "Content-Type: application/xml" --data-binary @iso20022\samples\pacs.008-unknownbank.xml
|
||||
```
|
||||
기대: 응답 `RCVD` → 잠시 후 `curl http://localhost:8091/inquiry/2026070999900000000003` → **status RJCT** (사유: unknown account)
|
||||
|
||||
**C-3) 스키마 위반 (ChrgBr=ZZZZ)** → 공식 XSD 검증 실패로 접수 반려
|
||||
```cmd
|
||||
curl -X POST http://localhost:8091/pay/customer -H "Content-Type: application/xml" --data-binary @iso20022\samples\pacs.008-badformat.xml
|
||||
```
|
||||
기대: 응답 `<TxSts>RJCT</TxSts>` (ClrSysRef에 `cvc-enumeration-valid: 'ZZZZ' ...` XSD 오류 사유)
|
||||
|
||||
> 세 경우 모두 **잔액은 전혀 변하지 않아야** 합니다(시나리오 D로 총액 확인).
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 D. 정합성 — 총액 보존 & 계층 대사 ⭐핵심
|
||||
|
||||
**목적**: 어떤 이체를 해도 **19개 계좌 잔액 합계 = 190억(19,000,000,000)** 이 유지되는지(돈이 생기거나
|
||||
사라지지 않음 = 이중지급 0)와, 원장 각 계층이 일치하는지 확인.
|
||||
|
||||
```cmd
|
||||
"C:\ai-dev\apps\postgresql-16.4\bin\psql.exe" -h localhost -p 5433 -U rtgs -d rtgs -c "SELECT sum(balance) AS 총액, count(*) AS 계좌수 FROM account;"
|
||||
"C:\ai-dev\apps\postgresql-16.4\bin\psql.exe" -h localhost -p 5433 -U rtgs -d rtgs -c "SELECT status, count(*) FROM transfer GROUP BY status ORDER BY status;"
|
||||
```
|
||||
기대: 총액 = **19000000000**, 상태 분포는 대부분 **ACCC**(+반려테스트한 RJCT 일부).
|
||||
|
||||
**계층 대사**(원장 transfer vs 조회사본 settlement_view 일치):
|
||||
```cmd
|
||||
"C:\ai-dev\apps\postgresql-16.4\bin\psql.exe" -h localhost -p 5433 -U rtgs -d rtgs -c "SELECT (SELECT count(*) FROM transfer WHERE status='ACCC') AS 원장ACCC, (SELECT count(*) FROM settlement_view WHERE final_status='ACCC') AS 사본ACCC;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 E. 멱등성 (중복 전문)
|
||||
|
||||
**목적**: 같은 거래(BMI)가 두 번 들어와도 한 번만 반영(이중지급 방지).
|
||||
|
||||
```cmd
|
||||
rem 같은 파일을 연속 2회 전송
|
||||
curl -X POST http://localhost:8091/pay/customer -H "Content-Type: application/xml" --data-binary @iso20022\samples\pacs.008.xml
|
||||
curl -X POST http://localhost:8091/pay/customer -H "Content-Type: application/xml" --data-binary @iso20022\samples\pacs.008.xml
|
||||
```
|
||||
기대: 1001→1002 잔액 변동이 **150원 한 번만** 발생(2회 보내도 총액·잔액 동일). `/accounts`로 확인.
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 F. 부하 테스트 (k6) ⭐성능
|
||||
|
||||
**목적**: 동시 다발 접수 시 처리량·응답시간·정합성 확인.
|
||||
|
||||
**정상 부하 (초당 30건 × 15초 ≈ 450건)**
|
||||
```cmd
|
||||
cd C:\ai-dev\workspace\rtgs
|
||||
"C:\ai-dev\apps\k6-0.56.0\k6.exe" run -e RATE=30 -e DURATION=15s loadtest\pacs008.k6.js
|
||||
```
|
||||
**스파이크(정점) 테스트**
|
||||
```cmd
|
||||
"C:\ai-dev\apps\k6-0.56.0\k6.exe" run -e MODE=spike -e PEAK=500 loadtest\pacs008.k6.js
|
||||
```
|
||||
- 조절 옵션: `-e RATE=`(초당건수) `-e DURATION=`(시간) / spike는 `-e PEAK=`(정점 초당건수).
|
||||
- 확인: k6 요약(`http_reqs`, `http_req_duration`), 그 후 **시나리오 D**로 전건 ACCC·총액 보존 확인.
|
||||
- 주의: 이 PC 메모리(약 16GB)에서 ELK까지 다 켠 상태면 너무 높은 PEAK는 버거울 수 있음(300~1000 권장).
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 G. 로그 확인 (Kibana / ELK) ⭐로그관리
|
||||
|
||||
**목적**: 모든 서비스 로그가 중앙 수집·검색되는지 확인.
|
||||
|
||||
1. 브라우저 http://localhost:5601 → ☰ **Discover** → 데이터뷰 **RTGS Logs**
|
||||
2. 검색창(KQL)에 필터 입력 예:
|
||||
- `service : "hermes"` — 결제처리 로그만
|
||||
- `service : "prada" and message : "ACCC"` — 완결 로그
|
||||
- `level : "ERROR"` — 오류만
|
||||
3. 오른쪽 위 시간범위를 "Last 1 hour"로.
|
||||
- 부하테스트(F) 돌린 직후 보면 로그가 실시간으로 쌓이는 것을 확인 가능.
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 H. 관리자 기능 (LouisVuitton) ⭐시스템관리
|
||||
|
||||
**목적**: 테스트 초기화·코드·사용자권한·대사 화면 동작 확인. 브라우저(5174) → **"🛠 관리자" 탭**.
|
||||
|
||||
> 🔐 **관리자 로그인 필요(A1)**: 관리자 탭 진입 시 로그인 화면 → 테스트계정 **`a` / `1`**(ADMIN)으로 로그인.
|
||||
> (본격 운영은 초기암호 로그인 후 변경 강제 — `must_change_password`.) 비ADMIN·무토큰은 `/admin/*` 401 차단.
|
||||
> 부하테스트(k6)는 관문/인증과 무관하게 **Chanel(:8091) 직접** 호출로 수행.
|
||||
|
||||
**H-1) DB 초기화 (테스트 리셋)**
|
||||
- 관리자 탭 → **DB 초기화** 버튼(확인창) → 거래/저널/조회사본/원전문 비우고 **계좌 잔액 10억으로 리셋**
|
||||
- 초기화 후 시나리오 D로 총액 190억·거래 0건 확인. (curl: `curl -X POST http://localhost:8099/admin/reset`)
|
||||
- 💡 반복 테스트 전에 초기화하면 상태 분포가 깔끔해집니다.
|
||||
|
||||
**H-2) 코드 관리 (참가기관 마스터)**
|
||||
- 참가기관(당좌계좌) 목록 조회·추가·수정. 상태코드(RCVD~ACCC/RJCT) 데이터사전 확인.
|
||||
- (curl: `curl http://localhost:8099/admin/institutions` , `/admin/status-codes`)
|
||||
|
||||
**H-3) 사용자·권한 관리 (관리 마스터)**
|
||||
- app_user 목록·추가·수정 — role **ADMIN/ORG_S/ORG_R**. *로그인 강제는 후속 단계*(지금은 마스터 관리만).
|
||||
- (curl: `curl http://localhost:8099/admin/users`)
|
||||
|
||||
**H-4) 대사 대시보드**
|
||||
- 상태별 건수·총액 보존 체크 + 서비스별 처리 현황(transfer/journal_log/settlement_view 건수 비교).
|
||||
- 시나리오 A·F 후 열어 **원장=저널=사본 건수 일치**를 눈으로 확인.
|
||||
- (curl: `curl http://localhost:8099/admin/summary`)
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 I. 복원력 검증 하니스 (sim/) ⭐자동검증
|
||||
|
||||
**목적**: 복원력방안 부록A 급소(S1 정합·S2 무손실승계·S4 순서교정)를 자동 검증. **Git Bash**에서 실행.
|
||||
|
||||
```bash
|
||||
bash sim/s1_consistency.sh 30 # 정합성: 총액보존·원장=사본·이중지급 0
|
||||
bash sim/s2_failover.sh 30 # 복원성: 부하중 Hermes 강제종료·재기동 → 유실 0
|
||||
bash sim/s4_order.sh # 기능성: 저널 순번 역전주입 → 재정렬 완결
|
||||
# 수동 대사 리포트:
|
||||
"C:\ai-dev\apps\postgresql-16.4\bin\psql.exe" -h localhost -p 5433 -U rtgs -d rtgs -f sim\reconcile.sql
|
||||
```
|
||||
- 각 스크립트가 `PASS/FAIL`을 출력합니다. 자세한 원리는 [sim/README.md](../sim/README.md).
|
||||
- ⚠️ **S2·S4는 서비스(Hermes/Sequencer)를 죽였다 살립니다.** 수동 테스트와 겹치지 않을 때 실행하세요.
|
||||
|
||||
---
|
||||
|
||||
## 시나리오 J. Gucci 관문 (인증·유량·재전송·결과송부) ⭐게이트웨이
|
||||
|
||||
**목적**: 참가기관이 **Gucci(:8095)** 를 통해 인증·접수하고, 결과가 콜백으로 송부되는지 확인. **Git Bash** 권장.
|
||||
|
||||
**dev 계정(시크릿)**: `kookmin_s`/`kookmin-secret`(ORG_S,1001) · `shinhan_r`/`shinhan-secret`(ORG_R,1002) · `admin`/`admin-secret`(ADMIN)
|
||||
|
||||
```bash
|
||||
G=http://localhost:8095
|
||||
# ① 로그인 → 토큰
|
||||
TOKEN=$(curl -s -X POST $G/gucci/auth/login -H "Content-Type: application/json" \
|
||||
-d '{"username":"kookmin_s","secret":"kookmin-secret"}' | python -c "import sys,json;print(json.load(sys.stdin)['token'])")
|
||||
|
||||
# ② 인증 이체신청(토큰+nonce+timestamp). 전문 송신기관(1001)은 토큰 기관과 일치해야 함
|
||||
TS=$(date +%s)
|
||||
curl -s -X POST $G/gucci/pay/customer -H "Authorization: Bearer $TOKEN" \
|
||||
-H "X-Nonce: n-$TS" -H "X-Timestamp: $TS" -H "Content-Type: application/xml" \
|
||||
--data-binary @iso20022/samples/pacs.008-normal.xml # (송신 1005면 403 — 토큰 org와 불일치)
|
||||
|
||||
# ③ 인증 조회
|
||||
curl -s $G/gucci/inquiry/{BMI} -H "Authorization: Bearer $TOKEN"
|
||||
# ④ 헬스체크(무인증)
|
||||
curl -s $G/gucci/health/centers
|
||||
```
|
||||
**기대(거부 케이스)**: 무토큰→401 · 변조토큰→401 · 토큰org≠전문송신→403 · nonce 재사용→400 · ORG_S가 /gucci/accounts→403(ADMIN 전용) · 유량 초과→429
|
||||
|
||||
**결과 콜백송부(G2)**: 완결되면 Chanel이 `notification`에 적재(delivered=false) → Gucci가 3초 폴링으로 기관 콜백에 pacs.002 송부 → ACK시 delivered=true.
|
||||
```bash
|
||||
# 콜백 레지스트리(관리자) / 송부 상태 확인
|
||||
AT=$(curl -s -X POST $G/gucci/auth/login -H "Content-Type: application/json" -d '{"username":"admin","secret":"admin-secret"}' | python -c "import sys,json;print(json.load(sys.stdin)['token'])")
|
||||
curl -s $G/gucci/callbacks -H "Authorization: Bearer $AT"
|
||||
"C:\ai-dev\apps\postgresql-16.4\bin\psql.exe" -h localhost -p 5433 -U rtgs -d rtgs -c "SELECT bmi,to_org,delivered,attempts FROM notification ORDER BY created_at DESC LIMIT 10;"
|
||||
```
|
||||
> 로컬 데모는 기관 수신서버를 Gucci 자체 sink(`/gucci/sink/{org}`)로 모사합니다. 미등록 기관은 재시도(attempts↑)하다가
|
||||
> 관리자가 `PUT /gucci/callbacks/{org}`로 URL 등록하면 다음 폴링에 송부 성공합니다.
|
||||
|
||||
---
|
||||
|
||||
## 6. 기동 / 종료
|
||||
|
||||
**전체 기동** (인프라 + 7서비스: Sequencer/Chanel/Dior/Hermes/Prada/LouisVuitton/Gucci)
|
||||
```cmd
|
||||
C:\ai-dev\workspace\rtgs\run-dc1.cmd
|
||||
```
|
||||
**ELK(로그) 기동** (선택, 메모리 여유 있을 때)
|
||||
```cmd
|
||||
C:\ai-dev\workspace\rtgs\infra\start-elk-native.cmd
|
||||
```
|
||||
**프론트엔드 기동**
|
||||
```cmd
|
||||
C:\ai-dev\workspace\rtgs\run-frontend.cmd
|
||||
```
|
||||
**종료**: 각 서비스/인프라 창을 닫거나 → `C:\ai-dev\workspace\rtgs\stop-dc1.cmd` (인프라 종료, 데이터 보존)
|
||||
|
||||
> 지금은 이미 떠 있는 상태라 바로 시나리오 A부터 하시면 됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 7. 부록
|
||||
|
||||
### 유용한 psql 조회
|
||||
```cmd
|
||||
set PSQL="C:\ai-dev\apps\postgresql-16.4\bin\psql.exe" -h localhost -p 5433 -U rtgs -d rtgs
|
||||
%PSQL% -c "SELECT bmi,global_seq,status,sender_code,receiver_code,amount FROM transfer ORDER BY global_seq DESC LIMIT 10;"
|
||||
%PSQL% -c "SELECT code,name,balance FROM account ORDER BY code;"
|
||||
```
|
||||
|
||||
### 데이터 초기화(계좌 잔액 리셋 등)가 필요하면
|
||||
- **가장 쉬운 방법**: 콘솔(5174) "🛠 관리자" 탭 → **DB 초기화** 버튼 (시나리오 H-1).
|
||||
- 명령으로: `curl -X POST http://localhost:8099/admin/reset`
|
||||
- (거래/저널/사본/원전문 비우고 계좌 잔액을 10억으로 되돌림)
|
||||
|
||||
### BMI(거래식별자) 규칙 — 직접 전문 만들 때
|
||||
- 22자리 = **영업일(8, YYYYMMDD) + 기관코드(4) + 일련번호(10)**
|
||||
- 예: `2026070911110000000009` (2026-07-09 · 기관 1111 · 일련 0000000009)
|
||||
- 매 요청마다 뒷자리를 다르게 하면 유일성 확보.
|
||||
|
||||
### 트러블슈팅
|
||||
- `/accounts`가 안 나옴 → 백엔드 미기동. `run-dc1.cmd` 실행.
|
||||
- 접수는 되는데 계속 IN_FLIGHT → Sequencer/Hermes 창 확인(기동 여부).
|
||||
- Kibana가 안 열림 → 기동에 ~1분 소요, 또는 `start-elk-native.cmd`로 ES/Logstash/Kibana 기동.
|
||||
- 포트 충돌 → acs 등 다른 프로젝트와 포트 분리돼 있으나, 8091/5433/9092 등이 이미 쓰이면 해당 프로세스 확인.
|
||||
|
||||
---
|
||||
|
||||
*문의/이상 발견 시 루비에게 알려주시면 바로 확인하겠습니다.*
|
||||
BIN
docs/RTGS 프로토타입 시스템 PoC 테스트 1차 결과보고.pdf
Normal file
BIN
docs/RTGS 프로토타입 시스템 PoC 테스트 1차 결과보고.pdf
Normal file
Binary file not shown.
BIN
docs/RTGS 프로토타입 시스템 PoC 테스트 2차 결과보고.pdf
Normal file
BIN
docs/RTGS 프로토타입 시스템 PoC 테스트 2차 결과보고.pdf
Normal file
Binary file not shown.
BIN
docs/RTGS 프로토타입 시스템 PoC 테스트 4차 계획.pdf
Normal file
BIN
docs/RTGS 프로토타입 시스템 PoC 테스트 4차 계획.pdf
Normal file
Binary file not shown.
186
docs/RTGS 프로토타입 워크플로우.md
Normal file
186
docs/RTGS 프로토타입 워크플로우.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# 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 이관 후 수월.*
|
||||
75
docs/SW 산출물 관리대장.md
Normal file
75
docs/SW 산출물 관리대장.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# RTGS SW 산출물 관리대장
|
||||
|
||||
> 목적: **코드와 문서의 정합성 유지**(과거 PoC 시 문서 미갱신으로 코드와 설계서가 어긋난 문제 재발 방지).
|
||||
> 원칙: 개발·변경이 있을 때 **관련 산출물을 같은 작업에서 즉시 갱신**하고, 개발일지에 이력을 남긴다.
|
||||
> 관리: 루비(AI) · 검토: 양팀장님 · 최초 2026-07-10
|
||||
|
||||
---
|
||||
|
||||
## 1. 정합성 원칙 (Single Source of Truth)
|
||||
|
||||
| 정보 | 진실의 출처(코드/설정) | 문서는 이를 "반영"만 |
|
||||
|---|---|---|
|
||||
| 포트·서비스 구성 | `run-dc1.cmd`, 각 `application.yml` | 아키텍처 설계서 §3·§4, 부록 A |
|
||||
| Kafka 토픽 | `common/Constants.kt (Topics)` | 아키텍처 §5.3, 워크플로우 §4 |
|
||||
| DB 스키마 | **Flyway** `backend/louisvuitton/.../db/migration/V*.sql` (수기 ALTER 금지, 신규는 V+1 추가) + JPA 엔티티 | 아키텍처 §5.2 |
|
||||
| 인증/권한(관리자) | Gucci JWT 발급 + LouisVuitton 검증(role ADMIN), 비밀키 BCrypt | 아키텍처 §8.5 |
|
||||
| 항목 한글명(데이터 사전) | **DB `COMMENT ON COLUMN`** (→ `/meta/labels`) | 화면 라벨·문서 표 |
|
||||
| 전문(ISO20022) | `common/resources/iso20022/xsd/*` + 샘플 | 아키텍처 §6.1, 테스트 시나리오 |
|
||||
| API 목록 | 각 `*Controller.kt` | 아키텍처 §6.2, 테스트 시나리오 |
|
||||
| 상태머신 | `common/TxSts.kt` + 서비스 로직 | 아키텍처 §5.4, 워크플로우 §3 |
|
||||
|
||||
> 문서가 코드와 다르면 **코드가 맞다고 간주**하고 문서를 고친다(반대 아님). 의도적 설계 변경이면 코드·문서를 함께 바꾼다.
|
||||
|
||||
---
|
||||
|
||||
## 2. 필수 산출물 목록
|
||||
|
||||
| 산출물 | 목적 | 갱신 트리거 | 최신 갱신 |
|
||||
|---|---|---|---|
|
||||
| **RTGS 아키텍처 설계서.md** | 구조·스택(OS-WAS-DB)·데이터·인터페이스·보안 | 서비스/포트/스택/스키마/API/전문/보안 변경 | 2026-07-10 |
|
||||
| **RTGS 프로토타입 워크플로우.md** | 처리 흐름·토픽·서비스 책임 | 처리흐름/토픽/서비스 역할 변경 | 2026-07-10 |
|
||||
| **RTGS 프로토타입 로컬 테스트 시나리오.md** | 기능·엔드포인트·샘플 시험 절차 | 기능/엔드포인트/샘플/포트 변경 | 2026-07-10 |
|
||||
| **RTGS 프로토타입 개발일지.md** | 일자별 개발·변경 이력(감사 추적) | **모든 작업·변경 시(必)** | 2026-07-10 |
|
||||
| **sim/README.md** | 복원력 검증(S1/S2/S4) 절차·결과 | 검증 수행·시나리오 변경 | 2026-07-10 |
|
||||
| SW 산출물 관리대장.md (본 문서) | 산출물·정합성 관리 규칙 | 산출물/규칙 변경 | 2026-07-10 |
|
||||
| (메모리) rtgs-project 등 | 세션 간 지식 유지 | 아키텍처·결정 변경 | 2026-07-10 |
|
||||
|
||||
**참고 자료(입력물, 갱신 대상 아님)**: `2센터흐름도.png`, `chanel/dior/hermes/prada.png`, `UML.xlsx`, `pacs.7z`,
|
||||
복원력방안 PDF, PoC 1~3차 결과보고, `rtgs 프로젝트 개요.txt`(양팀장님 관리).
|
||||
|
||||
---
|
||||
|
||||
## 3. 변경 유형 → 갱신 대상 매핑 (체크리스트)
|
||||
|
||||
작업 시 해당 행의 문서를 **모두** 갱신한다.
|
||||
|
||||
| 변경 유형 | 갱신할 산출물 |
|
||||
|---|---|
|
||||
| 서비스 추가/포트 변경 | 아키텍처(§3·§4·부록A) · `run-dc1.cmd` · 테스트 시나리오(포트) · 개발일지 · 메모리 |
|
||||
| 처리 흐름/토픽 변경 | 워크플로우 · 아키텍처(§5.3·§7) · 개발일지 |
|
||||
| DB 스키마 변경 | **Flyway 새 마이그레이션 `V+1__*.sql` 추가**(+ `COMMENT`) · 엔티티 · 아키텍처(§5.2) · (초기화 대상이면 admin reset) · 개발일지 |
|
||||
| API 추가/변경 | 컨트롤러 · 아키텍처(§6.2) · 테스트 시나리오 · 개발일지 |
|
||||
| 전문(ISO) 변경 | XSD/샘플 · 아키텍처(§6.1) · 테스트 시나리오 · 개발일지 |
|
||||
| 보안/인증 변경 | 아키텍처(§8.5) · 워크플로우(Gucci) · 개발일지 |
|
||||
| 검증(sim/k6) 수행 | sim/README(결과) · 개발일지 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 정합성 점검 (주기·릴리스 전)
|
||||
|
||||
아래가 **문서와 실제가 일치**하는지 확인한다(불일치 시 문서 수정):
|
||||
- 포트: `run-dc1.cmd`/`application.yml` ↔ 아키텍처 부록 A
|
||||
- 토픽: `Constants.kt` ↔ 아키텍처 §5.3
|
||||
- 테이블: Flyway 마이그레이션(V*) ↔ 아키텍처 §5.2 (실DB `flyway_schema_history`로 적용본 확인)
|
||||
- API: `*Controller.kt` ↔ 아키텍처 §6.2 / 테스트 시나리오
|
||||
- 전문 버전: XSD 파일명 ↔ 아키텍처 §6.1
|
||||
- 서비스 수: 빌드 모듈 수 ↔ 문서의 "N개 서비스"
|
||||
|
||||
> (자동화 여지) 위 항목은 grep 기반 점검 스크립트로 만들 수 있음 — 필요 시 `docs/tools/`에 추가.
|
||||
|
||||
---
|
||||
|
||||
## 5. 최종 산출물 변환
|
||||
- 개발 중: `.md` 유지. 완성 시: `docs/tools/convert.cmd`로 **PPT/PDF** 생성(→ `docs/_dist/`).
|
||||
- 다이어그램: 참고 PNG 임베드 + 신규는 Mermaid. PPT/PDF 변환 전 Mermaid는 PNG로 선렌더.
|
||||
BIN
docs/UML.xlsx
Normal file
BIN
docs/UML.xlsx
Normal file
Binary file not shown.
BIN
docs/chanel.png
Normal file
BIN
docs/chanel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 218 KiB |
BIN
docs/dior.png
Normal file
BIN
docs/dior.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 214 KiB |
BIN
docs/hermes.png
Normal file
BIN
docs/hermes.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 KiB |
BIN
docs/pacs.7z
Normal file
BIN
docs/pacs.7z
Normal file
Binary file not shown.
BIN
docs/prada.png
Normal file
BIN
docs/prada.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 219 KiB |
70
docs/rtgs 프로젝트 개요.txt
Normal file
70
docs/rtgs 프로젝트 개요.txt
Normal file
@@ -0,0 +1,70 @@
|
||||
------------------------------------------------------------------------------------
|
||||
▣ rtgs 프로젝트 개요
|
||||
클라우드 환경에서 기본적인 기능에 대해서는 3회차에 걸쳐 테스트를 했는데,
|
||||
이번 프로젝트에서는 클로드 코드를 통해 업무기능을 완벽히 개발해서 테스트 해보려고해
|
||||
개발환경은 로컬PC에서 수행
|
||||
------------------------------------------------------------------------------------
|
||||
□ 참고 : C:\ai-dev\workspace\rtgs\docs 폴더내 3회차에 걸친 테스트 결과 보고서, 방안 문서와 의견 등
|
||||
|
||||
□ 시스템 — 실시간총액결제(RTGS: Real-Time Gross Settlement)
|
||||
□ 시스템 목표 — 원거리에 위치한 3개 센터에서 A-A-A로 365*24시간 연중무휴 결제서비스를 안정적적으로 제공
|
||||
□ 프록젝트 목적 — RTGS 프로토타입 시스템 구축 및 테스트
|
||||
□ 성능 목표 — 10000~20000TPS(이전의 3회차 테스트에서는 접수기능까지만 확인)
|
||||
□ 기능 범위 — 자금이체 신청/접수/청산/조회/정산
|
||||
□ 데이터 성격 — 트랜잭션(전문/거래), 마스터(기관별 당좌계좌)
|
||||
□ 데이터 형식 — 원본 데이터 XML을 접수 받아서 RTGS시스템 내부에서는 저널(text(?) 축약)로 송수신후 XML로 응답(ISO20022 전문) → 전문 정보는 C:\ai-dev\workspace\rtgs\docs\pacs.7z 파일 참조
|
||||
|
||||
|
||||
□ 사용자 — ADMIN(관리자)/ORG_S(송신기관)/ORG_R(수신기관)
|
||||
□ 기술 스택 — Spring Boot + React + PostgreSQL + 인메모리DB, 이벤트 브로커, 메시지큐 등
|
||||
□ 시스템 연동 — ERP를 통한 사용자 정보 입수 및 관련 시스템(기존의 회계시스템, 감사시스템, BI시스템 등) 간의 인터페이스(본 프로토타입에서는 제외)
|
||||
센터 — 3개 센터 Active-Active-Active 동일한 순서로 같은 처리를 수행
|
||||
□ 테스트 — 3센터 구분은 터미널로 구분하여 동시 부하 테스트 실시
|
||||
□ 점검대상 —
|
||||
(1) 원격지에 떨어진 3개 센터에서 동일한 순서로 동일한 업무를 수행하는 기능성
|
||||
(2) 3센터 중에 한개의 센터가 다운되어도 서비스 보장(2개 센터가 다운되면 장애)하는 가용성
|
||||
(3) 장애가 발생해도 신속하게 데이터 소실없이 자동 복구하는 복원성
|
||||
|
||||
□ 프로세스 —
|
||||
(0) RTGS시스템 호출 전에 송신기관에서 송신인 계좌 잔액을 확인하고, 수신기관의 수신인 계좌 확인완료
|
||||
(1) 송신기관에서 수신기관을 지정하여 3센터 중에 한 센터의 RTGS시스템에 자금이체 전문을 신청
|
||||
(2) 처리 순서가 정해지고, 나머지 2센터에 순서가 공유(순서를 정하는 역할 GSLB로 구현한다면, 이 GSLB는 3개 센터 중에 한 곳에 위치해야 할지, 또다른 곳에 위치해야 할지, 또따른 곳이라면 이건 센터 간 이중화 안해도 될지 고민임)
|
||||
(3) 각 3센터 모두 동일한 이체업무(RTGS시스템내 송신기관의 당좌계좌에서 이체한 금액을 제하고, 수신기관의 당좌계좌에 더하여 잔액처리)
|
||||
(4) 실제 접수받은 센터에서는 수신기관 앞 이체결과 전문을 통보
|
||||
|
||||
□ 관리 — ELK 구성을 통한 로그 수집및 조회 기능 구현
|
||||
|
||||
▣ PC 교체 주의사항 — PC 교체시 C:\ai-dev만 복사 (새 PC에서도 경로를 똑같이 C:\ai-dev로 설정). 복사 전 서비스·인프라를 멈추고 복사(실행 중 DB파일이 잠겨 깨질 수 있음). home\pgsql-data / kafka / es-data / mongo-data는 빼고 복사 → 새 PC에서 재초기화(스크립트가 알아서 다시 생성. 코드·설정만 확실히 오면 됨)
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------
|
||||
▣ 서비스
|
||||
→ 전체 프로세스는 C:\ai-dev\workspace\rtgs\docs\UML.xlsx 파일 참조
|
||||
|
||||
|
||||
□CHANEL(Contact Hub for Advanced Networked Entry Liaison)
|
||||
참가기관(송신)으로부터 거래요청 건을 접수받아 참가기관(송신, 수신)에게 처리결과를 통보하는 서비스
|
||||
프로세스 개념도 참조 : C:\ai-dev\workspace\rtgs\docs\chanel.png
|
||||
|
||||
□HERMES(High-Efficiency Rapid Memory Execution for Settlement)
|
||||
각 센터에서 동일한 거래를 순서대로 처리하여 각 센터 내에 위치한 원장 데이터베이스에 저장하는 결제처리 서비스
|
||||
프로세스 개념도 참조 : C:\ai-dev\workspace\rtgs\docs\hermes.png
|
||||
|
||||
□DIOR(Data Integration and Organization Relay)
|
||||
참가기관(송신)에게 접수한 거래 건을 다중 센터에서 동일하게 처리하기 위한 접수 동기화 서비스
|
||||
프로세스 개념도 참조 : C:\ai-dev\workspace\rtgs\docs\dior.png
|
||||
|
||||
□PRADA(Persistent Repository Administrator for Data Aggregation)
|
||||
각센터에서 처리한 거래처리 결과에 대한 결과 동기화 서비스
|
||||
프로세스 개념도 참조 : C:\ai-dev\workspace\rtgs\docs\prada.png
|
||||
|
||||
□LOUIS VUITTON : Leading Operations Unified Information Systems for Validation, Updates, Integration, Tracking, Technology, Operations, and Networks
|
||||
|
||||
□GUCCI (Global User Communication Control Interface)
|
||||
3개 센터 간 헬스 체크
|
||||
센터간 통신을 위한 연계서버(Gateway 역할, Message Queue 역할)
|
||||
금융기관들(송신기관, 수신기관) 로그인 인증
|
||||
금융기관들(송신기관, 수신기관)이 호출하는 API 인증
|
||||
API 서비스 순서 지정
|
||||
이체신청기관앞 이체결과 송부
|
||||
|
||||
64
docs/tools/convert.cmd
Normal file
64
docs/tools/convert.cmd
Normal file
@@ -0,0 +1,64 @@
|
||||
@echo off
|
||||
rem ===================================================================
|
||||
rem RTGS 문서 변환기 — 개발 중엔 .md 로 관리하고, 완성 시 정식 파일 생성.
|
||||
rem 사용법:
|
||||
rem convert.cmd pptx ["문서.md"] -> PowerPoint (pandoc, 직접)
|
||||
rem convert.cmd docx ["문서.md"] -> Word (중간 산출물/편집용)
|
||||
rem convert.cmd pdf ["문서.md"] -> PDF (pandoc html -> Edge 헤드리스 인쇄)
|
||||
rem convert.cmd all -> docs 폴더 모든 *.md 를 pptx+pdf 로
|
||||
rem 결과물: docs\_dist\
|
||||
rem 주의: Mermaid( ```mermaid ) 다이어그램은 PPT/PDF에서 자동 렌더 안 됨.
|
||||
rem 그림으로 넣으려면 완성 시 render-mermaid 로 PNG 선(先)렌더 후 변환.
|
||||
rem (참조용 *.png 는 그대로 이미지로 들어감)
|
||||
rem ===================================================================
|
||||
setlocal enabledelayedexpansion
|
||||
set "DOCS=%~dp0.."
|
||||
set "OUTDIR=%DOCS%\_dist"
|
||||
if not exist "%OUTDIR%" mkdir "%OUTDIR%"
|
||||
where pandoc >nul 2>&1 || (echo [오류] pandoc 없음 & exit /b 1)
|
||||
|
||||
set "FMT=%~1"
|
||||
if "%FMT%"=="" set "FMT=pptx"
|
||||
|
||||
if /I "%FMT%"=="all" (
|
||||
pushd "%DOCS%"
|
||||
for %%F in (*.md) do ( call :pptx "%%~fF" & call :pdf "%%~fF" )
|
||||
popd
|
||||
goto :done
|
||||
)
|
||||
|
||||
if not "%~2"=="" ( call :%FMT% "%~2" ) else (
|
||||
pushd "%DOCS%"
|
||||
for %%F in (*.md) do call :%FMT% "%%~fF"
|
||||
popd
|
||||
)
|
||||
goto :done
|
||||
|
||||
:pptx
|
||||
echo [PPTX] %~nx1
|
||||
pandoc "%~1" -o "%OUTDIR%\%~n1.pptx" -f gfm
|
||||
exit /b 0
|
||||
|
||||
:docx
|
||||
echo [DOCX] %~nx1
|
||||
pandoc "%~1" -o "%OUTDIR%\%~n1.docx" --toc --toc-depth=3 -f gfm
|
||||
exit /b 0
|
||||
|
||||
:pdf
|
||||
echo [PDF ] %~nx1
|
||||
pandoc "%~1" -o "%OUTDIR%\%~n1.html" -s --embed-resources -f gfm --metadata title="%~n1"
|
||||
set "EDGE=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
|
||||
if not exist "%EDGE%" set "EDGE=C:\Program Files\Microsoft\Edge\Application\msedge.exe"
|
||||
if exist "%EDGE%" (
|
||||
"%EDGE%" --headless --disable-gpu --no-pdf-header-footer --print-to-pdf="%OUTDIR%\%~n1.pdf" "%OUTDIR%\%~n1.html" >nul 2>&1
|
||||
echo -> %~n1.pdf
|
||||
) else (
|
||||
echo [알림] Edge 미발견 -> %~n1.html 생성됨. Word/PPT에서 PDF로 내보내세요.
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
:done
|
||||
echo.
|
||||
echo [완료] 결과물 폴더: %OUTDIR%
|
||||
endlocal
|
||||
exit /b 0
|
||||
Reference in New Issue
Block a user