acquire-core-x/README.md
hyeongwoo-choi 844a1d50fc 프론트 전면 구성: 330개 전 서비스 화면 포털 (매니페스트 자동생성)
- tools/gen_screens.py: svc 소스에서 입력(getl/gets_)·출력(setl/sets) 필드와
  한글 설명을 추출해 app/ui/screens.js(330 화면 매니페스트) 생성
- index.html 확장: 모듈별 메뉴트리(11그룹×30화면) + 화면검색 + 주요업무 즐겨찾기 9종,
  화면별 입력폼 자동구성(한글라벨·기본값), 조회성 화면 XA 자동 off, 결과그리드·거래저널
- README: 화면 계층(4.0) 문서화
- 검증: screens.js 서빙, ACQ_DUPCHK(조회)·RC_STATS(통계)·MM_MERCH_REG(XA 등록) 실호출 정상

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 00:32:53 +00:00

238 lines
12 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.

# acquire-core-x
**실제로 동작하는** 카드 매입·정산(acquiring & settlement) 레거시 시스템 —
오픈소스 TP 모니터 **Enduro/X**(open Tuxedo/ATMI) + **ECPG/PostgreSQL** + **XA 2PC** 위에서
`tpcall` 서비스와 배치가 진짜로 기동·트랜잭션 처리된다.
> **목적** — Klaro Forge 자율 마이그레이션 도구의 **대형 전환 대상 픽스처**.
> 이 C/ATMI 시스템(2,000+본)을 Java Spring Boot로 전환하는 시나리오를 검증한다.
> 컴파일만 되는 스텁이 아니라 **상용급 TP 미들웨어에서 실제로 도는** 시스템이라는 점이 핵심이다.
---
## 1. 스택
| 계층 | 실체 | 비고 |
|---|---|---|
| TP 모니터 | **Enduro/X 7.0.12** (open Tuxedo/ATMI) | `tpservice`/`tpcall`/`tpadvertise`/`tpbegin`/`tpcommit`, 소스빌드 |
| 전문 버퍼 | **UBF** (≈ Tuxedo FML32) | `Bget`/`Bchg`, 필드테이블 `mkfldhdr` |
| DB 접근 | **ECPG** (`EXEC SQL`) → **PostgreSQL 15** | Pro\*C 대응 오픈소스 |
| 분산 트랜잭션 | **XA 2PC**`libndrxxaecpg.so` ECPG XA 스위치 | `tmsrv`가 prepare/commit/rollback 조율 |
| 오케스트레이션 | `ndrxd` + `ndrxconfig.xml` + `app.ini`(CCONFIG) | 서버 그룹·서비스·RM 설정 |
| 컨테이너 | `docker compose` (postgres + endurox app) | 재현 가능한 단일 스택 |
> **주의(라이선스):** Enduro/X 런타임은 **AGPLv3**(Mavimax). 내부 테스트 자산으로 사용.
> 상용 Tuxedo(Oracle)·Tmax ProFrame의 오픈소스 대응이며, 진짜 Tuxedo/Tmax는 사용하지 않는다.
---
## 2. 아키텍처
### 2.1 런타임 토폴로지
```
┌──────────────────────── app 컨테이너 (Enduro/X) ─────────────────────────┐
acqdrv ─────▶ │ ndrxd (TP 모니터) │
(client) │ ├─ tmsrv (RM1 = PostgreSQL, XA 조율: prepare/commit/rollback) │
tpinit │ ├─ cconfsrv / tpevsrv / cpmsrv (시스템 서버) │
tpbegin │ ├─ ac_svr … 매입 30 서비스 (ACQUIRE, ACQ_DDC, ACQ_CANCEL …) │
tpcall ─────▶│ ├─ au_svr … 승인/한도 30 │ 11개 "모듈 서버" │
tpcommit │ ├─ rc_svr … 대사 30 │ 각 서버가 자기 모듈의 │
│ ├─ st_svr … 정산/수수료 30 │ ~30 서비스를 tpadvertise │
│ └─ … lg cl mm vl mg cm py │ (총 333 서비스) │
│ │ EXEC SQL (ECPG, XA 스위치 libndrxxaecpg) │
└────────┼───────────────────────────────────────────────────────────────┘
PostgreSQL (db 컨테이너, max_prepared_transactions=100)
merchant · purchase · approval · settlement · ledger · … (75 테이블)
```
### 2.2 "모듈 서버" 패턴 (실제 Tuxedo 방식)
서비스 하나당 프로세스 하나가 아니라, **모듈 서버 1개가 그 모듈의 다수 서비스를 advertise**한다.
서비스 로직은 파일 하나에 하나씩(`svc/<SVCNAME>.pgc` + 카피북 `<SVCNAME>.h`), 서버(`<mod>_svr.pgc`)는
얇은 디스패처(`tpsvrinit`에서 `tpadvertise` 테이블만). DB 접근은 `dbio/*.pgc`(정적 라이브러리로 링크),
배치는 독립 실행파일(`batch/*.pgc`).
### 2.3 XA 2PC 트랜잭션 흐름 (핵심)
```
client: tpbegin() # 글로벌 트랜잭션 시작
└▶ tpcall("ACQUIRE") # ac_svr: EXEC SQL INSERT purchase (XA 브랜치 A)
└▶ tpcall("RECONCILE") # rc_svr: EXEC SQL INSERT ledger (XA 브랜치 B)
└▶ tpcall("SETTLE") # st_svr: EXEC SQL INSERT settlement(XA 브랜치 C)
client: tpcommit()
└▶ tmsrv: xa_prepare(A,B,C) → xa_commit(A,B,C) # 2단계 커밋, 원자적 확정
```
`tpabort()` 시 tmsrv가 세 브랜치를 모두 `xa_rollback` → DB에 아무것도 남지 않는다.
ECPG XA 스위치가 `tpopen()`에서 연결을 열므로 **`EXEC SQL CONNECT`가 없다**. XA 브랜치 간에는
서로의 미커밋 행이 안 보이므로 각 서비스는 **자기 테이블만 쓰는(owner-writes)** 규율을 지킨다.
---
## 3. 디렉터리 구조
```
acquire-core-x/
├─ docker/
│ ├─ endurox.Dockerfile # Enduro/X 소스빌드 (-DENABLE_POSTGRES=ON → libndrxxaecpg)
│ └─ docker-compose.yml # postgres + endurox app (sysctl/ulimit 포함)
├─ db/
│ └─ schema.d/ # 00-base.sql + 모듈별 NN-<mod>.sql (75 테이블)
├─ app/
│ ├─ conf/ # app.ini(CCONFIG), ndrxconfig.xml(생성), setapp.sh(XA env)
│ ├─ ubftab/acq.fd # UBF 필드테이블 (전 모듈 공용)
│ ├─ build.sh # 모듈 자동발견 빌드 + ndrxconfig 생성
│ ├─ entrypoint.sh # 빌드 → ndrxd 기동 (+ 업무화면 게이트웨이 :8090)
│ ├─ ui/ # 업무화면 포털 (index.html + screens.js — 330 화면 매니페스트)
│ └─ src/<mod>/ # 11개 모듈 (ac au rc st py lg cl mm vl mg cm)
│ ├─ <mod>_svr.pgc # 얇은 모듈 서버(디스패처)
│ ├─ svc/*.pgc + *.h # 1서비스=1파일 + 카피북 (모듈당 30)
│ ├─ dbio/*.pgc + *.h # DB 접근 계층 (모듈당 ~40)
│ ├─ batch/*.pgc # XA 배치 (모듈당 ~22)
│ └─ run/*.sh # 운영 기동 스크립트 (모듈당 ~17)
├─ tools/
│ └─ gen_screens.py # svc 소스 → 화면 매니페스트(screens.js) 재생성기
└─ docs/
├─ architecture.md # 상세 아키텍처
└─ service-catalog.md # 서비스 카탈로그
```
---
## 4. 빌드 & 실행
```bash
docker compose -f docker/docker-compose.yml up -d --build # postgres + endurox app 기동
```
빌드(전 모듈 ecpg→buildserver, ~240 배치 컴파일)는 수 분 걸린다. 완료되면 `ndrxd`
11개 모듈 서버 + `tmsrv`를 부팅하고, **업무화면 포털**이 http://localhost:8090/ 에 뜬다.
### 4.0 업무화면 (프론트 — RFI의 Xplatform 내부화면 포지션)
```
브라우저 업무화면(app/ui, Xplatform풍) → acq_httpgw(C, Webtier 포지션) → tpcall → 모듈서버 → PostgreSQL
```
- `app/src/clients/acq_httpgw.c` — C 경량 HTTP 게이트웨이(:8090, libatmiclt만 링크).
`GET /api/call?svc=<SVC>&_tx=1&T_필드=값...` → UBF 구성(CBchg) → (옵션) 글로벌 XA
`tpbegin/tpcommit``tpcall` → 응답 UBF 전체를 JSON 직렬화.
- `app/ui/index.html` + `screens.js` — 한국어 업무포털. **330개 전 서비스가 화면으로 노출**
(모듈별 메뉴트리·검색·주요업무 즐겨찾기 9종). 각 화면의 입력폼은 해당 tpservice 소스에서
추출한 실제 입력 필드(getl/gets_)로 자동 구성되며(`tools/gen_screens.py`), 실행 시 실제
tpcall 이 돌고 결과 그리드·거래 저널에 표시된다.
### 4.1 필수 런타임 요건 (없으면 ndrxd 부팅 실패 — 실측으로 규명)
POSIX 메시지큐/세마포어 한도 때문에 **app 컨테이너에 반드시** 필요 (compose에 이미 반영):
```yaml
sysctls:
fs.mqueue.msg_max: "512"
fs.mqueue.msgsize_max: "65536"
fs.mqueue.queues_max: "8192" # 333 서비스 = 333+ 큐, 기본 256 초과
ulimits:
msgqueue: 2147483648 # 큐 1개 = MSGMAX×MSGSIZEMAX, 다수 큐 → RLIMIT_MSGQUEUE 상향
nofile: 65536
```
`app.ini`: `NDRX_MSGMAX=50`, `NDRX_MSGSIZEMAX=16000` (큐당 메모리 축소).
PostgreSQL: `-c max_prepared_transactions=100` (XA 2단계 커밋용).
---
## 5. 검증 (실행 중인 시스템 확인)
```bash
C="docker compose -f docker/docker-compose.yml"
# 서버 프로세스 — 12개 전부 runok 이어야 함
$C exec app bash -lc '. /app/conf/setapp.sh; xadmin ppm'
# advertise된 서비스 — 333 AVAIL
$C exec app bash -lc '. /app/conf/setapp.sh; xadmin psc'
# 매입 거래 실행 (tpbegin → ACQUIRE→RECONCILE→SETTLE → tpcommit)
$C exec app bash -lc '. /app/conf/setapp.sh; /app/bin/acqdrv M0001 1000000'
# >>> COMMIT OK: purchase_id=… fee=2500 net=997500 status=S
# DB 반영 + XA 정합성
$C exec db psql -U acq -d acq -c "select * from purchase; select * from settlement;"
$C exec db psql -U acq -d acq -c "select count(*) from pg_prepared_xacts;" # 0 = 2PC 정상
```
**검증 완료 상태:** 12 서버 runok · 333 서비스 AVAIL · 매입체인 XA 원자 커밋(status=S) ·
`pg_prepared_xacts=0`(롤백 시 무잔존) · 전 `.pgc` 정규화-고유(클론 0).
---
## 6. 모듈 & 도메인
| 코드 | 모듈 | 대표 서비스 |
|---|---|---|
| **ac** | 매입(acquiring) | ACQUIRE, ACQ_DDC/EDI/EDC, CANCEL/CORRECT, PARTIAL/INSTALL/FOREIGN, DUPCHK, WHT, TAXINV |
| **au** | 승인/한도 | AUTH, LIMIT_CHK/DEC/RST, STANDIN, FRAUD_DETECT, PREAUTH, DCC_QUOTE |
| **rc** | 대사(reconcile) | RECONCILE, RC_3WAY(승인·매입·입금), RC_TOLERANCE, RC_AMTDIFF, RC_AUTOFIX |
| **st** | 정산/수수료 | SETTLE, ST_MDR, ST_VANFEE, ST_NETTING, ST_VAT, ST_WHT, ST_PAYDATE(T+n) |
| **py** | 지급(payment) | PAY_FILEGEN(고정길이 전문), PAY_RESULT, PAY_SPLIT, PAY_XFERLINK |
| **lg** | 원장(ledger) | LG_POST, LG_DOUBLE(복식부기 차/대변), LG_BALCHK, LG_REVERSE, LG_TRIALBAL |
| **cl** | 마감(closing) | CL_DAILY/MONTHLY/QUARTER, CL_SNAPSHOT, CL_RECLOSE, CL_YEAREND |
| **mm** | 마스터 | MM_MERCH_REG/UPD, MM_FEERATE, MM_BIN, MM_LIMIT, MM_GRADE |
| **vl** | 정합성검증 | VL_SALES, VL_CARD, VL_AMOUNT, VL_ANOMALY, VL_REFINTEG |
| **mg** | 전문게이트웨이 | MG_ISO8583, MG_BITMAP, MG_STAN(채번), MG_ROUTE, MG_MAC |
| **cm** | 공통 | CM_CODE, CM_BIZDAY, CM_LUHN, CM_CRC, CM_FX, CM_SEQ |
**대표 tpcall 체인:** `ACQUIRE → RECONCILE → SETTLE`(글로벌 XA, 매입→대사→정산→원장 반영).
---
## 7. 파일 통계
| 종류 | 수 |
|---|---|
| 전체 (git 추적) | **2,052** |
| 프로그램 `.pgc` (svc 330 + dbio ~440 + batch ~240) | **1,015** |
| 카피북/헤더 `.h` | 817 |
| 기동/배치 스크립트 `.sh` | 191 |
| 스키마 `.sql` | 11 |
| — 모듈 서버 11 · 서비스 330 · 배치 바이너리 242 · DB 테이블 75 | |
`.pgc`는 정규화(식별자→X, 숫자→N, 공백제거) 후 **md5 고유**(클론 0) — 파일마다 실제 다른 로직.
---
## 8. 규명·해결한 실런타임 이슈 (재현 노트)
1. **gpgme 빌드의존** — Enduro/X `tpbridge``gpgme.h` 요구 → `libgpgme-dev` 추가.
2. **atmitest 빌드깨짐**`DEFINE_DISABLETEST`가 제외 안 함 → CMakeLists에서 `add_subdirectory(atmitest)` 제거.
3. **mqueue 한도**`fs.mqueue.queues_max`(기본 256 < 333 서비스), `RLIMIT_MSGQUEUE`(큐당 5.6MB0.8MB로 축소 + ulimit 상향).
4. **XA 브랜치 격리** 형제 브랜치는 서로의 미커밋 불가시 owner-writes 규율 + 서비스별 분리 서버.
---
## 9. 마이그레이션 타깃 매핑 (→ Spring Boot)
| 레거시 (Enduro/X) | Spring Boot |
|---|---|
| `tpservice` / `tpadvertise` | `@Service` + 메서드 |
| `tpcall(SVC)` | 주입 호출(동기) |
| UBF `Bget`/`Bchg` (전문버퍼) | DTO / record |
| `tpbegin`/`tpcommit` (XA) | `@Transactional` (JTA 또는 단일 DB) |
| `EXEC SQL` / ECPG dbio | MyBatis / JPA repository |
| 커서 배치 | Spring Batch |
| 고정길이 전문 | 코덱(fixed-length) |
| `ndrxconfig` 서비스 등록 | 컴포넌트 스캔 / 라우팅 |
| `db/schema.d/*.sql` | Flyway |
---
## 10. 검증 체크리스트
- [x] Enduro/X 소스빌드 (PostgreSQL XA 포함) 이미지 `acquire-x/endurox:7.0.12`
- [x] `ndrxd` 부팅 (11 모듈서버 + tmsrv 전부 runok)
- [x] 커스텀 `tpservice` advertise + 실제 `tpcall` 왕복
- [x] ECPG XA 서비스 체인(매입대사정산) + 커밋 row psql 검증 + 롤백(`prepared_xacts=0`)
- [x] 2,000본+ 실동작 시스템 (2,052 파일 / `.pgc` 1,015본, 모듈 dbio·batch 세분화)
- [x] `.pgc` 정규화-고유 (클론 0)
- [x] 업무화면 포털(:8090) 330 화면, HTTPtpcall 게이트웨이, 화면에서 XA 체인 실행 검증
- [ ] Klaro Forge 마이그레이션 Spring Boot (Phase 3, 예정)