# 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 등이 이미 쓰이면 해당 프로세스 확인.

---

*문의/이상 발견 시 루비에게 알려주시면 바로 확인하겠습니다.*
