Files
rtgs/docs/RTGS 프로토타입 로컬 테스트 시나리오.md
rtgs 58ca23b5d9 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 보고서/복원력방안)
2026-07-15 15:37:32 +09:00

342 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 등이 이미 쓰이면 해당 프로세스 확인.
---
*문의/이상 발견 시 루비에게 알려주시면 바로 확인하겠습니다.*