From 7139fdd2fda19f011018328c6324991e4f50d335 Mon Sep 17 00:00:00 2001 From: hyeongwoo-choi Date: Sun, 19 Jul 2026 23:09:39 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20=EC=83=81=EC=84=B8=20README=20(?= =?UTF-8?q?=EC=95=84=ED=82=A4=ED=85=8D=EC=B2=98=C2=B7XA=ED=9D=90=EB=A6=84?= =?UTF-8?q?=C2=B7=EB=B9=8C=EB=93=9C/=EC=8B=A4=ED=96=89=C2=B7=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=C2=B7=EB=AA=A8=EB=93=88=C2=B7=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20=EB=A7=A4=ED=95=91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 237 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 201 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 9bee295..23800b7 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,221 @@ -# acquire-core-x — 카드 매입·정산 시스템 (실동작 Enduro/X) +# acquire-core-x -실제 **Enduro/X**(오픈소스 Tuxedo/ATMI TP 모니터) + **ECPG/PostgreSQL** 위에서 -**실제로 기동·트랜잭션 처리되는** 카드 매입·정산 레거시 시스템. 포지(Klaro Forge) -자율 마이그레이션 도구의 대형 전환 대상 픽스처 — 이 C/ATMI 시스템을 Java Spring Boot로 -전환한다. +**실제로 동작하는** 카드 매입·정산(acquiring & settlement) 레거시 시스템 — +오픈소스 TP 모니터 **Enduro/X**(open Tuxedo/ATMI) + **ECPG/PostgreSQL** + **XA 2PC** 위에서 +`tpcall` 서비스와 배치가 진짜로 기동·트랜잭션 처리된다. -> 앞서의 `acquire-core-full`(자체 TxCore 셔임, 컴파일만 되는 픽스처)과 달리, 이 프로젝트는 -> **진짜 TP 미들웨어에서 tpcall·XA 트랜잭션이 실제로 동작**한다. (사용자 요구 = B방식) +> **목적** — Klaro Forge 자율 마이그레이션 도구의 **대형 전환 대상 픽스처**. +> 이 C/ATMI 시스템(2,000+본)을 Java Spring Boot로 전환하는 시나리오를 검증한다. +> 컴파일만 되는 스텁이 아니라 **상용급 TP 미들웨어에서 실제로 도는** 시스템이라는 점이 핵심이다. -## 스택 +--- -| 계층 | 실체 | -|---|---| -| TP 모니터 | Enduro/X 7.0.12 (오픈 Tuxedo/ATMI: `tpservice`/`tpcall`/`tpadvertise`/`tpbegin`) — 소스빌드 | -| 전문 버퍼 | UBF (≈FML32): `Bget`/`Bchg`, 필드테이블 `mkfldhdr` | -| DB 접근 | ECPG (`EXEC SQL`) → PostgreSQL 15 | -| 분산 트랜잭션 | XA 2PC — `libndrxxaecpg.so` ECPG XA 스위치, `tmsrv`가 조율 | -| 오케스트레이션 | `ndrxd` + `ndrxconfig.xml` + `app.ini`(CCONFIG) | +## 1. 스택 -## 실행 (docker-compose) +| 계층 | 실체 | 비고 | +|---|---|---| +| 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) | 재현 가능한 단일 스택 | -```bash -docker compose -f docker/docker-compose.yml up -d --build # 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 테이블) ``` -### 필수 런타임 요건 (없으면 ndrxd 부팅 실패 — 실측으로 규명됨) -POSIX 큐/세마포어 한도 때문에 app 컨테이너에 반드시 필요: +### 2.2 "모듈 서버" 패턴 (실제 Tuxedo 방식) + +서비스 하나당 프로세스 하나가 아니라, **모듈 서버 1개가 그 모듈의 다수 서비스를 advertise**한다. +서비스 로직은 파일 하나에 하나씩(`svc/.pgc` + 카피북 `.h`), 서버(`_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-.sql (75 테이블) +├─ app/ +│ ├─ conf/ # app.ini(CCONFIG), ndrxconfig.xml(생성), setapp.sh(XA env) +│ ├─ ubftab/acq.fd # UBF 필드테이블 (전 모듈 공용) +│ ├─ build.sh # 모듈 자동발견 빌드 + ndrxconfig 생성 +│ ├─ entrypoint.sh # 빌드 → ndrxd 기동 +│ └─ src// # 11개 모듈 (ac au rc st py lg cl mm vl mg cm) +│ ├─ _svr.pgc # 얇은 모듈 서버(디스패처) +│ ├─ svc/*.pgc + *.h # 1서비스=1파일 + 카피북 (모듈당 30) +│ ├─ dbio/*.pgc + *.h # DB 접근 계층 (모듈당 ~40) +│ ├─ batch/*.pgc # XA 배치 (모듈당 ~22) +│ └─ run/*.sh # 운영 기동 스크립트 (모듈당 ~17) +└─ 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`를 부팅한다. + +### 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: 536870912 # 큐 1개 = msg_max×msgsize_max ≈ 5.6MB, 기본 RLIMIT_MSGQUEUE(800KB) 초과 + msgqueue: 2147483648 # 큐 1개 = MSGMAX×MSGSIZEMAX, 다수 큐 → RLIMIT_MSGQUEUE 상향 nofile: 65536 ``` -PostgreSQL는 XA(2단계 커밋) 위해 `-c max_prepared_transactions=100`. +`app.ini`: `NDRX_MSGMAX=50`, `NDRX_MSGSIZEMAX=16000` (큐당 메모리 축소). +PostgreSQL: `-c max_prepared_transactions=100` (XA 2단계 커밋용). -## 구성 +--- -- `docker/` — `endurox.Dockerfile`(Enduro/X 소스빌드, `-DENABLE_POSTGRES=ON`), `docker-compose.yml` -- `db/` — `schema.sql`(merchant/purchase/approval/settlement/ledger …) -- `app/` — `conf/`(ndrxconfig.xml, app.ini, 환경), `ubftab/`(UBF 필드테이블), - 서비스 `.pgc`/`.c`, `entrypoint.sh` -- `docs/` — `architecture.md`, `service-catalog.md` -- `ref/` — Enduro/X upstream PostgreSQL 통합테스트(참고용, .gitignore) -- `slice/` — 최초 검증용 non-DB ATMI 슬라이스(부팅+tpcall 증명 완료) +## 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.6MB→0.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` 부팅 (10 프로세스 runok) -- [x] 커스텀 `tpservice` advertise + **실제 `tpcall` 왕복** (ACQUIRE: fee=2500 net=997500) -- [x] ECPG XA 서비스 체인(매입→대사→정산) + 커밋 row psql 검증 + 롤백 검증 ✅ -- [x] 2000본+ 실동작 시스템 (2,052 파일 / .pgc 1,015본, 전 모듈 dbio40·batch22) -- [ ] 포지 마이그레이션 → Spring Boot (Phase 3) +- [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) +- [ ] Klaro Forge 마이그레이션 → Spring Boot (Phase 3, 예정)