diff --git a/README.md b/README.md index ed5860b..ca4e598 100644 --- a/README.md +++ b/README.md @@ -1,253 +1,144 @@ # acquire-core-x -**실제로 동작하는** 카드 매입·정산(acquiring & settlement) 레거시 시스템 — -오픈소스 TP 모니터 **Enduro/X**(open Tuxedo/ATMI) + **ECPG/PostgreSQL** + **XA 2PC** 위에서 -`tpcall` 서비스와 배치가 진짜로 기동·트랜잭션 처리된다. +**실제로 동작하는** 카드 매입·정산(card acquiring & settlement) 레거시 시스템 — +상용 카드사 스택(Tuxedo / Pro\*C / Oracle / XA)을 **오픈소스 등가물**(Enduro/X · +ECPG/PostgreSQL · XA 2PC) 위에 재현해 **진짜로 빌드·부팅·거래가 커밋**된다. -> **목적** — Klaro Forge 자율 마이그레이션 도구의 **대형 전환 대상 픽스처**. -> 이 C/ATMI 시스템(2,000+본)을 Java Spring Boot로 전환하는 시나리오를 검증한다. -> 컴파일만 되는 스텁이 아니라 **상용급 TP 미들웨어에서 실제로 도는** 시스템이라는 점이 핵심이다. - -> **IR·데모용 자료** — [`docs/MIGRATION_COMPLEXITY.md`](docs/MIGRATION_COMPLEXITY.md): -> "Forge가 얼마나 복잡한 레거시를 다룰 수 있는가"를 3축(양적 규모·자동변환 실패지점· -> 실전 레거시성)으로 **전량 실측**해 정리. 포털에서도 실시간 확인: -> `#arch`(호출그래프 552노드·41 모듈간 경로), `#mig`(마이그레이션 난이도 브리핑). -> 지표는 전부 소스에서 자동 집계된다(`tools/gen_callgraph.py`, `tools/gen_metrics.py`). -> -> **Oracle 계보(As-Is)** — [`docs/ORACLE_PROVENANCE.md`](docs/ORACLE_PROVENANCE.md): -> 원본은 Oracle 19c + Pro\*C 였고, 현행 코드는 그 1차 이관본(ECPG/PostgreSQL)이다. -> `legacy-oracle/` 에 As-Is Pro\*C/PL-SQL 역사 원본을 보존(컴파일 제외)하고, Oracle→ -> PostgreSQL 방언 이관 매핑을 문서화한다. -> -> **포털 화면 구성** — `#sys`(시스템 현황: 상태·아키텍처 토폴로지·채널·큐·정산사이클), -> `#arch`(아키텍처 토폴로지), `#econ`(정산 경제: 수수료 3층·펀딩·순액정산·MCC·ISO8583), -> `#mig`(마이그레이션 난이도, Forge 관점). +> **목적** — Klaro **Forge**(자율 마이그레이션 도구)의 **대형 전환 대상 픽스처**. +> "Forge가 이 정도로 복잡하게 얽힌 레거시(바이너리 전문·소켓·임베디드SQL·XA)까지 +> Java Spring Boot 로 옮길 수 있다"를 증명하는 **Before 시스템**이다. 컴파일만 되는 +> 스텁이 아니라 **상용급 TP 미들웨어에서 실제로 도는** 시스템이라는 점이 핵심이다. --- -## 1. 스택 +## 1. 한눈에 (실측) -| 계층 | 실체 | 비고 | +| 지표 | 값 | +|---|---| +| 소스 파일 (.pgc/.c/.h) | **4,661** | +| 코드 라인 | **333,517 LOC** | +| 온라인 서비스 (tpservice) | **1,478** | +| 배치 잡 | **506** | +| 도메인 모듈 | **11** (매입·승인·정산·지급·원장·마감·대사·검증·마스터·전문GW·공통) | +| DB 테이블 | **101** | +| 운영 데이터 | **~1,173만 행** (매입 135만, 5.5년치, DB ~1.8GB) | +| ISO 8583 전문 코덱 | 실제 비트맵·DE 인코딩 (`common/acq_iso8583.c`) | +| 실제 매입시스템 대비 패리티 | **≈ 89%** ([상세](docs/PARITY_ANALYSIS.md)) | + +BAIS(비씨카드 글로벌 매입시스템) RFI 기준 온라인 1,466 · 배치 503 과 **1:1 규모**. + +--- + +## 2. 스택 + +| 계층 | 실체 | As-Is(상용) 대응 | |---|---|---| -| 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) | 재현 가능한 단일 스택 | +| TP 모니터 | **Enduro/X 7.0.12** (open Tuxedo/ATMI) | Oracle Tuxedo / Tmax ProFrame | +| 전문 버퍼 | **UBF** (≈ FML32) | Tuxedo FML32 | +| DB 접근 | **ECPG** (`EXEC SQL`) | **Pro\*C** | +| DB | **PostgreSQL 15** | Oracle 19c | +| 분산 트랜잭션 | **XA 2PC** (`libndrxxaecpg`, `tmsrv` 조율) | Oracle XA | +| 외부 연동 | **ISO 8583** over TCP (`acq_extsw`) | 밴사/VAN/카드망 회선 | +| 컨테이너 | `docker compose` (postgres + endurox app) | — | -> **주의(라이선스):** Enduro/X 런타임은 **AGPLv3**(Mavimax). 내부 테스트 자산으로 사용. -> 상용 Tuxedo(Oracle)·Tmax ProFrame의 오픈소스 대응이며, 진짜 Tuxedo/Tmax는 사용하지 않는다. +> **Oracle 계보**: 원본은 Oracle 19c + Pro\*C 였다. `legacy-oracle/` 에 As-Is Pro\*C/ +> PL-SQL 역사 원본(9본)을 보존하고 방언 이관을 문서화 → [ORACLE_PROVENANCE.md](docs/ORACLE_PROVENANCE.md). +> **라이선스**: Enduro/X 런타임은 AGPLv3(Mavimax). 내부 테스트 자산으로만 사용. --- -## 2. 아키텍처 +## 3. 빠른 시작 -### 2.1 런타임 토폴로지 +```bash +# 전체 스택 빌드+부팅 (Postgres + Enduro/X 도메인 + 게이트웨이 + 외부망 시뮬레이터) +docker compose -f docker/docker-compose.yml up -d --build -``` - ┌──────────────────────── 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 테이블) +# 업무 포털 (브라우저) +open http://localhost:8090 + +# 실제 매입 거래 태우기 (6단 XA 체인 → 2PC 커밋) +docker compose -f docker/docker-compose.yml exec app /app/run-driver.sh +# → >>> COMMIT OK: purchase_id=... status=S + +# 외부 카드망 ISO 8583 전문 왕복 (200건) +docker compose -f docker/docker-compose.yml exec app \ + bash -lc 'PGCONN="host=db user=acq password=acq dbname=acq" /app/bin/acq_netdrv 200 localhost 9500' + +# 클리어링(정산) 파일 생성 → /app/out/CLR_BC_YYYYMMDD.dat +docker compose -f docker/docker-compose.yml exec app \ + bash -lc 'PGCONN="host=db user=acq password=acq dbname=acq" /app/bin/acq_clrfile 2026-07-19 /app/out' ``` -### 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. 디렉터리 구조 +## 4. 업무 포털 (`http://localhost:8090`) + +Xplatform 스타일 MDI 포털. 모든 화면이 **실측 데이터** 기반이며 `#해시`로 직접 진입. + +**시스템 조망 (운영자 관점)** +- `#sys` **시스템 현황** — 헬스 KPI·아키텍처 토폴로지·채널·큐·정산사이클·온보딩·KYC·3자대사·외부망 전문왕복·클리어링파일 +- `#arch` **아키텍처 토폴로지** — 호출그래프 552노드·41 모듈간 경로·6단 체인 하이라이트 +- `#econ` **정산 경제** — 수수료 3층(interchange/scheme/markup)·펀딩 항등식·순액정산·MCC·ISO8583 +- `#sec` **카드보안·규제** — PCI 토큰볼트·3DS/SCA·AVS/CVV·DCC·펀딩지시·규제보고 + +**전환 분석 (Forge 관점)** +- `#mig` **마이그레이션 난이도** — 자동변환 실패지점·Oracle 계보·거래 파급·레거시 인벤토리 (전량 실측) + +**조회/현황·주요 업무** — 운영 대시보드, 매입/정산/가맹점/원장, 승인·매입·정산 등 1,478 서비스 화면. + +--- + +## 5. 저장소 구조 ``` 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 기동 (+ 업무화면 게이트웨이 :8090) -│ ├─ ui/ # 업무화면 포털 (index.html + screens.js — 330 화면 매니페스트) -│ └─ 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) -├─ tools/ -│ └─ gen_screens.py # svc 소스 → 화면 매니페스트(screens.js) 재생성기 -└─ docs/ - ├─ architecture.md # 상세 아키텍처 - └─ service-catalog.md # 서비스 카탈로그 +├── app/ +│ ├── src/ +│ │ ├── / # 11 도메인 모듈 (ac au st py lg cl rc vl mm mg cm) +│ │ │ ├── _svr.pgc # 모듈 서버 (≈133 서비스 advertise) +│ │ │ ├── svc/*.pgc # 온라인 서비스 (1 서비스 = 1 파일) +│ │ │ ├── dbio/*.pgc # DB 접근 함수 (ECPG) +│ │ │ └── batch/*.pgc # 배치 잡 +│ │ ├── common/ # 공통 C (acq_iso8583 ISO코덱·luhn·fee·bizday·seq) +│ │ └── clients/ # acq_httpgw(포털) · acq_extsw(외부망) · acq_netdrv · acq_clrfile +│ ├── ubftab/acq.fd # UBF 필드 테이블 +│ ├── conf/ # ndrxconfig.xml, setapp.sh (XA env) +│ ├── ui/index.html # 업무 포털 (SPA, 인라인) +│ ├── build.sh # 재사용 빌드 + ndrxconfig 생성 (모듈 자동발견) +│ └── entrypoint.sh # 빌드→부팅→게이트웨이·시뮬레이터 기동 +├── db/schema.d/ # 스키마+시드 (00-base ~ 99c, 순서대로 init) +├── legacy-oracle/ # As-Is Oracle Pro*C/PL-SQL 역사 원본 (컴파일 제외) +├── tools/ # gen_callgraph.py · gen_metrics.py (소스 자동 집계) +├── docker/ # docker-compose.yml, endurox.Dockerfile +└── docs/ # 아래 문서 인덱스 ``` --- -## 4. 빌드 & 실행 +## 6. 문서 인덱스 -```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=&_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) — 파일마다 실제 다른 로직. +| [ARCHITECTURE.md](docs/ARCHITECTURE.md) | 런타임 토폴로지·도메인 분해·트랜잭션 흐름·데이터모델·외부연동·빌드 | +| [PARITY_ANALYSIS.md](docs/PARITY_ANALYSIS.md) | 실제 매입시스템 대비 도메인별 패리티(≈89%) | +| [GAP_ANALYSIS.md](docs/GAP_ANALYSIS.md) | 딥리서치 기반 실제 매입사 구조 대조 | +| [MIGRATION_COMPLEXITY.md](docs/MIGRATION_COMPLEXITY.md) | 마이그레이션 난이도 3축(규모·실패지점·레거시성) 실측 | +| [ORACLE_PROVENANCE.md](docs/ORACLE_PROVENANCE.md) | Oracle 계보 + 방언 이관 매핑 | +| [OPS_RUNBOOK.md](docs/OPS_RUNBOOK.md) | 비기능(HA/DR·PCI·보존·규제) 설계 | +| [service-catalog.md](docs/service-catalog.md) | 서비스 카탈로그 | --- -## 8. 규명·해결한 실런타임 이슈 (재현 노트) +## 7. 핵심 설계 제약 (마이그레이션 난제) -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 규율 + 서비스별 분리 서버. +- **XA 브랜치 격리** — 한 글로벌 트랜잭션의 형제 XA 브랜치는 서로의 미커밋 행을 못 본다. + 판정값은 UBF 전문으로 실어 나른다(owner-writes 규율). 위반 시 교착. +- **6단 XA 체인** — `ACQUIRE→RECONCILE→SETTLE→ST_MDR→LG_SETTLEPOST→LG_BALCHK` 가 + 하나의 2PC 로 원자 커밋. 모듈내 tpcall 을 위해 서버 카피 4개. +- **ISO 8583 와이어** — 비트맵·DE 인코딩·소켓 프레이밍·타임아웃→취소 전문처리. +- **임베디드 SQL 23,000+ 블록·UBF 47,000+ 접근** — 자동 문법변환이 깨지는 지점. ---- - -## 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 화면, HTTP→tpcall 게이트웨이, 화면에서 XA 체인 실행 검증 -- [ ] Klaro Forge 마이그레이션 → Spring Boot (Phase 3, 예정) +이 제약들이 곧 Forge 마이그레이션의 난이도이며, 지표는 `tools/gen_metrics.py` 가 +소스에서 자동 집계한다("이 숫자 진짜냐"에 코드로 답한다). diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..6a76816 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,177 @@ +# 아키텍처 — acquire-core-x + +카드 매입·정산 시스템의 런타임 구조·도메인 분해·트랜잭션 흐름·데이터 모델·외부 연동· +빌드 시스템을 기술한다. 상용 Tuxedo/Pro\*C/Oracle 스택을 오픈 등가물(Enduro/X·ECPG· +PostgreSQL)로 재현했으며, **모듈 서버 패턴**으로 1,478 온라인 서비스를 11개 모듈 서버가 +광고(advertise)한다. + +--- + +## 1. 런타임 토폴로지 + +``` +┌─────────────────── db 컨테이너 ──────────────────┐ ┌──────────────────────── app 컨테이너 (Enduro/X) ────────────────────────┐ +│ PostgreSQL 15 │ │ ndrxd (TP 모니터 데몬) │ +│ - max_prepared_transactions=100 (XA prepare) │◀──┤ ├─ tmsrv XA 트랜잭션 매니저 (RM1=ECPG/PG, prepare/commit/rollback) │ +│ - 101 테이블 / ~1,173만 행 │XA │ ├─ ac_svr 매입 (ACQUIRE, ACQ_CANCEL, ACQ_PRESENT … ≈133 svc) │ +│ │ │ ├─ au_svr 승인/한도 (AUTH, TOKENIZE, AUTH_3DS, LIMIT_DEC …) │ +└───────────────────────────────────────────────────┘ │ ├─ st_svr 정산/수수료 (SETTLE, ST_MDR, ST_FEEDECOMP, ST_FUNDING …) │ + │ ├─ py_svr 지급 (PAY_APPROVE, PAY_FILEGEN …) │ + 브라우저 ─HTTP→ acq_httpgw :8090 ─tpcall→ ndrxd ──────▶│ ├─ lg_svr 원장 (LG_POST, LG_BALCHK, LG_SETTLEPOST …) │ + (업무 포털 게이트웨이) │ ├─ cl_svr 마감 (CL_DAILY, CL_LOCK …) │ + │ ├─ rc_svr 대사 (RECONCILE, RC_3WAY …) │ + 외부 카드망 ─ISO8583/TCP:9500─ acq_extsw ◀──socket────│ ├─ vl_svr 정합성검증 (VL_SALES, VL_RULE …) │ + (acq_netdrv 왕복 드라이버) │ ├─ mm_svr 마스터 (MM_ONBOARD, MM_KYC_CHECK, MM_MERCH_REG …) │ + │ ├─ mg_svr 전문게이트웨이 (MG_ISO8583, MG_CLEARING_INQ …) │ + acqdrv ─tpbegin/tpcall/tpcommit→ ndrxd ──────────────▶│ └─ cm_svr 공통 (CM_SEQ, CM_LOG, CM_FX …) │ + (매입 체인 드라이버) │ 각 서버가 svc/*.pgc 를 buildserver 로 링크·advertise │ + │ EXEC SQL (ECPG) → libndrxxaecpg XA 스위치 → tmsrv → PostgreSQL │ + └────────────────────────────────────────────────────────────────────────┘ +``` + +- **모듈 서버 패턴**: 각 도메인이 서버 바이너리 1종(`_svr`)이고, 그 모듈의 + `svc/*.pgc` 서비스 전부를 `tpsvrinit` 에서 `tpadvertise` 한다. 서버 카피 min/max=4 + (모듈내 tpcall 체인 자기교착 회피). +- **XA 조율**: `tmsrv`(RM1) 가 PostgreSQL 을 XA 리소스로 관리. 서비스는 + `EXEC SQL CONNECT` 를 쓰지 않는다 — XA 스위치가 `tpopen()` 에서 커넥션을 연다. +- **부팅**: `entrypoint.sh` → `build.sh`(빌드+ndrxconfig 생성) → `xadmin start` → + `acq_httpgw`(:8090) + `acq_extsw`(:9500) 기동. + +--- + +## 2. 도메인 분해 (11 모듈) + +| 모듈 | 도메인 | 대표 서비스 | 주요 테이블 | +|---|---|---|---| +| **ac** | 매입 (acquiring) | ACQUIRE·ACQ_CANCEL·ACQ_PRESENT·ACQ_DISPUTE_*·AC_DCC | purchase·approval·ac_cancel·ac_dispute·ac_dcc·cl_presentment | +| **au** | 승인/한도 | AUTH·TOKENIZE·AUTH_3DS·AUTH_VERIFY·LIMIT_DEC·FRAUD_DETECT | au_authorization·card_limit·card_vault·au_3ds·au_verify·au_fraud_log | +| **st** | 정산/수수료 | SETTLE·ST_MDR·ST_FEEDECOMP·ST_NETSETTLE·ST_FUNDING·ST_FUNDINSTR | settlement·st_fee_component·st_net_settlement·st_merchant_funding·st_funding_instr | +| **py** | 지급 | PAY_APPROVE·PAY_FILEGEN·PAY_ACCTCHK | py_payout·py_file·py_account·py_ledger | +| **lg** | 원장 | LG_POST·LG_BALCHK·LG_SETTLEPOST·LG_REVERSE | ledger·lg_ledger_line·lg_voucher·lg_account·lg_trial_bal | +| **cl** | 마감 | CL_DAILY·CL_LOCK·CL_APPR* | cl_close_log·cl_period_lock·cl_snapshot·cl_clearing_file | +| **rc** | 대사 | RECONCILE·RC_3WAY·RC_EXCEPTION | rc_match_log·rc_diff·rc_exception·rc_3way | +| **vl** | 정합성검증 | VL_SALES·VL_RULE·VL_SUSPECT | vl_check_log·vl_rule·vl_suspect | +| **mm** | 마스터 | MM_ONBOARD·MM_KYC_CHECK·MM_MERCH_REG·MM_FEERATE_SET | merchant·mm_application·mm_kyc·mm_merch_mcc·mm_terminal·mm_mcc | +| **mg** | 전문게이트웨이 | MG_ISO8583·MG_CLEARING_INQ·MG_SEND·MG_ROUTE | mg_channel·mg_msg_log·mg_clearing·mg_netlog·mg_queue | +| **cm** | 공통 | CM_SEQ·CM_LOG·CM_FX·CM_PARAM | cm_seq·cm_log·cm_fx_rate·cm_param·cm_holiday | + +모듈간 호출: 552 노드·41 모듈간 경로(`cl→vl→cm`, `ac→st→lg`, `au→mg` 등). +전체 그래프는 포털 `#arch` 또는 `tools/gen_callgraph.py` 산출. + +--- + +## 3. 트랜잭션 흐름 — 6단 XA 체인 + +매입 접수 한 건이 **하나의 글로벌 트랜잭션** 안에서 6개 프로그램·6개 XA 브랜치를 관통해 +2PC 로 원자 커밋한다: + +``` +acqdrv: tpbegin + └─ ACQUIRE (ac) 매입 접수·한도점검·purchase/approval 적재 + └─ RECONCILE (rc) 대사 (미커밋 매입은 전문값으로 판정) + └─ SETTLE (st) 정산 확정·settlement 적재 + └─ ST_MDR (st) MDR 수수료 원장 기표 + └─ LG_SETTLEPOST (lg) 정산 원장 전기 + └─ LG_BALCHK (lg) 차대변 균형 검증 +acqdrv: tpcommit → tmsrv: prepare(모든 브랜치) → commit → COMMIT OK +``` + +### XA 브랜치 격리 규율 (핵심 제약) +`tpcall` 로 호출한 서비스는 **다른 XA 브랜치**다 → 호출측이 방금 INSERT/UPDATE 한 +**미커밋 행을 볼 수 없다**. 따라서: +1. 판정에 필요한 값은 **UBF 전문(`T_*` 필드)으로 실어 보낸다.** +2. 피호출자는 `sqlcode=100`(미존재)을 정상 흐름으로 처리(FAIL 금지). +3. **owner-writes**: 각 테이블 쓰기 소유자를 하나로 둔다. 형제 브랜치가 같은 행을 + UPDATE 하면 같은 트랜잭션이라 락이 안 풀려 타임아웃까지 교착 → 이 규율로 방지. + +> 이 격리 의미론이 Spring `@Transactional` 경계와 달라, 잘못 옮기면 교착·이중전기가 +> 그대로 재현된다 — Forge 마이그레이션의 핵심 난제. + +--- + +## 4. 데이터 모델 (101 테이블, 도메인별) + +- **거래 원장**: purchase(135만)·approval·ledger(258만)·settlement(129만)·merchant_summary +- **수수료·정산 경제**: st_fee_component(3층 분해)·st_scheme_fee·st_interchange_rate(MCC×카드×채널)· + st_net_settlement(BIN 순액)·st_merchant_funding(펀딩 항등식)·st_reserve·st_cb_fee +- **클리어링**: cl_presentment·mg_clearing(ISO8583 메시지클래스)·cl_clearing_file +- **카드보안**: card_vault(토큰·PAN미저장)·au_3ds·au_verify +- **분쟁**: ac_dispute(라이프사이클)·ac_retrieval +- **마스터·생애주기**: merchant·mm_application(온보딩)·mm_kyc·mm_terminal·mm_mcc +- **외부연동**: mg_netlog(전문왕복)·mg_channel·mg_msg_log +- **회계**: lg_account(계정과목 suspense/clearing)·lg_ledger_line(복식부기)·lg_trial_bal +- **공통**: cm_seq·cm_log(감사)·cm_fx_rate·cm_param + +**정합성 불변식(실측 검증)**: 복식부기 차대변 균형(DR=CR), 수수료 3층 항등 +(interchange+scheme+markup=mdr_total, 129만/129만), 펀딩 항등 +(총매출−환불−채그백−수수료−리저브=입금, 35.5만/35.5만). + +--- + +## 5. 외부 연동 · 전문처리 (ISO 8583) + +실제 카드망(밴사/VAN) 인터페이스를 **실제 프로토콜**로 구현 — 마이그레이션 최난이도 계층. + +``` +매입사 외부 카드망 (시뮬레이터) +───── ────────────────────── +acq_netdrv acq_extsw (:9500) + │ 실매입건 → iso_build(0200) │ + │ MTI + 비트맵(64bit) + DE │ + │ (PAN·금액·STAN·MID·TID·통화 …) │ + │ ── 길이접두 프레이밍 + hex ──socket──▶ │ iso_parse → 승인판정 + │ │ iso_build(0210) DE38 승인번호·DE39 응답코드 + │ ◀────────── 0210 응답 ────────socket── │ + │ iso_parse → resp_code·approval·RTT │ + └─ mg_netlog 적재 │ +``` + +- **코덱**: `common/acq_iso8583.c` — 1차 비트맵 + DE 인코딩/파싱(고정·LLVAR), + `iso_build`/`iso_parse` 왕복 무손실. +- **왕복 실측**: 200건 승인 198/거절 2/오류 0, 0200→0210, 평균 RTT 0.3ms. +- **클리어링 파일**: `acq_clrfile` 가 cl_presentment → 고정폭 정산파일 + (HD 헤더 + DT 레코드 + TR 트레일러·체크섬)을 `/app/out/CLR_BC_YYYYMMDD.dat` 로 생성, + cl_clearing_file 등록. 실제 SFTP 배치 파일교환 대역. + +--- + +## 6. 빌드 시스템 + +`app/build.sh` 는 **모듈을 자동 발견**해 빌드하고 `ndrxconfig.xml` 을 생성한다: + +1. `mkfldhdr acq.fd` — UBF 필드 헤더 생성. +2. `common/*.c` → `libacqcommon.a` (ISO코덱·luhn·fee·bizday·seq). +3. 모듈별: `dbio/*.pgc` → `libdbio.a`, `svc/*.pgc` → `libsvc.a`, + `_svr.pgc` → `buildserver` 로 `_svr` 서버 바이너리, `batch/*.pgc` → 배치. +4. `clients/*.c` → `buildclient` (libacqcommon.a 링크 = ISO 코덱 사용). +5. `ndrxconfig.xml` 자동 생성 (tmsrv + 모듈서버 11종, min/max=4). + +ECPG 전처리(`.pgc → .c`)는 gcc 와 동일한 `-I` 경로로 수행(copybook 해석). +새 모듈은 `app/src//{_svr.pgc,svc,dbio,batch}` 를 두고 재빌드하면 자동 편입. + +--- + +## 7. Oracle 계보 (As-Is → 현행 → To-Be) + +``` + As-Is (상용) 현행 (이 저장소) To-Be (Forge 목표) + Oracle 19c + Pro*C → PostgreSQL + ECPG → PostgreSQL + Spring Boot + Tuxedo/ATMI Enduro/X (open ATMI) @Service / REST·메시지 + PL/SQL 패키지 서비스 인라인 @Transactional + legacy-oracle/*.pc app/src/**/*.pgc (target) +``` + +`legacy-oracle/` 의 Oracle Pro\*C/PL-SQL 역사 원본(9본, 컴파일 제외)이 현행 ECPG +이식본과 1:1 대조된다. DB 엔진 이관(Oracle→PG)은 완료 상태로 두고, Forge 는 남은 +**애플리케이션 계층 이관**(ATMI·임베디드SQL·UBF·XA·ISO8583)에 집중한다. +상세: [ORACLE_PROVENANCE.md](ORACLE_PROVENANCE.md). + +--- + +## 8. 관측 · 검증 + +- **포털**(`acq_httpgw` :8090): 7개 운영·분석 화면, 전부 실측 데이터 조회. +- **자동 지표**: `tools/gen_metrics.py`·`gen_callgraph.py` 가 소스에서 규모·호출그래프· + Oracle 방언을 집계 → 포털/문서 반영("이 숫자 진짜냐"에 코드로 답). +- **런타임 검증**: 6단 체인 COMMIT OK·미결 2PC 0, 체인 전수 스모크 477/477, + ISO 8583 왕복 200건, 정합성 불변식 100%. diff --git a/docs/architecture.legacy.md b/docs/architecture.legacy.md new file mode 100644 index 0000000..2a9f503 --- /dev/null +++ b/docs/architecture.legacy.md @@ -0,0 +1,73 @@ +# 아키텍처 — acquire-core-x + +## 1. 런타임 토폴로지 + +``` + ┌──────────────────────────── app 컨테이너 (Enduro/X) ────────────────────────────┐ + acqdrv ──▶ │ ndrxd (TP 모니터) │ + (client) │ ├─ tmsrv (RM1 = PostgreSQL, XA 조율: prepare/commit/rollback) │ + tpbegin │ ├─ cconfsrv / tpevsrv / cpmsrv (시스템) │ + tpcall ───▶│ ├─ acq_online : ACQUIRE, CANCEL, CORRECT … (매입 온라인 tpservice) │ + tpcommit │ ├─ rc_online : RECONCILE, REMATCH … (대사) │ + │ ├─ st_online : SETTLE, FEECALC … (정산·수수료) │ + │ └─ *_batch : 커서 배치 (마감/집계) │ + │ │ EXEC SQL (ECPG, XA 스위치 libndrxxaecpg) │ + └────────┼──────────────────────────────────────────────────────────────────────┘ + ▼ + PostgreSQL (db 컨테이너, max_prepared_transactions=100) + merchant / purchase / approval / settlement / ledger … +``` + +## 2. 트랜잭션 모델 (핵심 — 진짜 XA 2PC) + +클라이언트가 `tpbegin()`으로 **글로벌 트랜잭션** 시작 → `tpcall("ACQUIRE")` → +ACQUIRE 서비스가 `EXEC SQL INSERT purchase` (자동으로 XA `xa_start`로 글로벌 tx에 편입) → +같은 tx 안에서 `tpcall("RECONCILE")` → `tpcall("SETTLE")` → 클라이언트 `tpcommit()` → +`tmsrv`가 **xa_prepare → xa_commit** (2단계 커밋)으로 모든 서비스의 DB 작업을 원자적으로 확정. +`tpabort()` 시 전부 롤백 → DB에 아무것도 남지 않음. + +이것이 레거시 Tuxedo/ProFrame 시스템의 표준 패턴이며, 마이그레이션 도구가 실제로 마주할 idiom이다. + +## 3. 모듈 (11) — acquire-core-full 도메인 계승 + +| 코드 | 모듈 | 대표 서비스/배치 | +|---|---|---| +| ac | 매입(acquiring) | ACQUIRE, DDC, EDI청구, 취소/정정, 부분/할부/해외, 미매입추출 | +| au | 승인/한도 | AUTH중계, 한도차감/복원, 대행승인, 이상거래탐지 | +| rc | 대사(reconcile) | RECONCILE(승인-매입-입금 3-way), tolerance, 재대사, 반송 | +| st | 정산/수수료 | SETTLE, MDR/VAN수수료, netting, 원천징수, T+n | +| py | 지급(payment) | 지급파일생성, 결과반영, 재처리, 이체연계 | +| lg | 원장(ledger) | 복식전표, 잔액검증, 전기, 역분개, 마감연계 | +| cl | 마감(closing) | 일/월/분기 마감, 스냅샷, 재마감 | +| mm | 마스터 | 가맹점/수수료율/BIN/한도 마스터 | +| vl | 정합성검증 | 매출/카드/한도/금액 정합성, 이상탐지 | +| mg | 전문게이트웨이 | 전문 수/송신, ISO8583 파싱, STAN채번, 라우팅 | +| cm | 공통 | 공통코드, 영업일, 금액유틸, 채번, Luhn/CRC | + +## 4. 파일 아키타입 (2000본 구성 근거) + +| 아키타입 | 형식 | 개수(목표) | 성격 | +|---|---|---|---| +| online service | `.pgc` (`tpservice` + EXEC SQL) | ~350 | tpcall 진입점, XA | +| dbio | `.pgc` (EXEC SQL 함수) | ~300 | 서비스가 호출하는 DB 접근 계층 | +| batch | `.pgc`/`.c` (`main`, 커서) | ~180 | 마감/집계, XA | +| common util | `.c` | ~120 | leaf 유틸 | +| copybook/header | `.h`, UBF 필드테이블 `.fd` | ~600 | 구조체·FML 필드 | +| config | ndrxconfig.xml, app.ini, UBFTAB, XA RM | ~50 | TP/서버/RM 설정 | +| shell/run | `.sh` | ~180 | 기동/배치 스크립트 | +| schema/seed | `.sql` | ~50 | 스키마·시드 | +| **합계** | | **~2,030** | | + +## 5. 마이그레이션 타깃 매핑 (포지 → 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 서비스 등록 | 컴포넌트 스캔/라우팅 | +| `schema.sql` | Flyway | diff --git a/docs/architecture.md b/docs/architecture.md index 2a9f503..6cd6ab4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,73 +1,4 @@ -# 아키텍처 — acquire-core-x +# (구) 아키텍처 문서 -## 1. 런타임 토폴로지 - -``` - ┌──────────────────────────── app 컨테이너 (Enduro/X) ────────────────────────────┐ - acqdrv ──▶ │ ndrxd (TP 모니터) │ - (client) │ ├─ tmsrv (RM1 = PostgreSQL, XA 조율: prepare/commit/rollback) │ - tpbegin │ ├─ cconfsrv / tpevsrv / cpmsrv (시스템) │ - tpcall ───▶│ ├─ acq_online : ACQUIRE, CANCEL, CORRECT … (매입 온라인 tpservice) │ - tpcommit │ ├─ rc_online : RECONCILE, REMATCH … (대사) │ - │ ├─ st_online : SETTLE, FEECALC … (정산·수수료) │ - │ └─ *_batch : 커서 배치 (마감/집계) │ - │ │ EXEC SQL (ECPG, XA 스위치 libndrxxaecpg) │ - └────────┼──────────────────────────────────────────────────────────────────────┘ - ▼ - PostgreSQL (db 컨테이너, max_prepared_transactions=100) - merchant / purchase / approval / settlement / ledger … -``` - -## 2. 트랜잭션 모델 (핵심 — 진짜 XA 2PC) - -클라이언트가 `tpbegin()`으로 **글로벌 트랜잭션** 시작 → `tpcall("ACQUIRE")` → -ACQUIRE 서비스가 `EXEC SQL INSERT purchase` (자동으로 XA `xa_start`로 글로벌 tx에 편입) → -같은 tx 안에서 `tpcall("RECONCILE")` → `tpcall("SETTLE")` → 클라이언트 `tpcommit()` → -`tmsrv`가 **xa_prepare → xa_commit** (2단계 커밋)으로 모든 서비스의 DB 작업을 원자적으로 확정. -`tpabort()` 시 전부 롤백 → DB에 아무것도 남지 않음. - -이것이 레거시 Tuxedo/ProFrame 시스템의 표준 패턴이며, 마이그레이션 도구가 실제로 마주할 idiom이다. - -## 3. 모듈 (11) — acquire-core-full 도메인 계승 - -| 코드 | 모듈 | 대표 서비스/배치 | -|---|---|---| -| ac | 매입(acquiring) | ACQUIRE, DDC, EDI청구, 취소/정정, 부분/할부/해외, 미매입추출 | -| au | 승인/한도 | AUTH중계, 한도차감/복원, 대행승인, 이상거래탐지 | -| rc | 대사(reconcile) | RECONCILE(승인-매입-입금 3-way), tolerance, 재대사, 반송 | -| st | 정산/수수료 | SETTLE, MDR/VAN수수료, netting, 원천징수, T+n | -| py | 지급(payment) | 지급파일생성, 결과반영, 재처리, 이체연계 | -| lg | 원장(ledger) | 복식전표, 잔액검증, 전기, 역분개, 마감연계 | -| cl | 마감(closing) | 일/월/분기 마감, 스냅샷, 재마감 | -| mm | 마스터 | 가맹점/수수료율/BIN/한도 마스터 | -| vl | 정합성검증 | 매출/카드/한도/금액 정합성, 이상탐지 | -| mg | 전문게이트웨이 | 전문 수/송신, ISO8583 파싱, STAN채번, 라우팅 | -| cm | 공통 | 공통코드, 영업일, 금액유틸, 채번, Luhn/CRC | - -## 4. 파일 아키타입 (2000본 구성 근거) - -| 아키타입 | 형식 | 개수(목표) | 성격 | -|---|---|---|---| -| online service | `.pgc` (`tpservice` + EXEC SQL) | ~350 | tpcall 진입점, XA | -| dbio | `.pgc` (EXEC SQL 함수) | ~300 | 서비스가 호출하는 DB 접근 계층 | -| batch | `.pgc`/`.c` (`main`, 커서) | ~180 | 마감/집계, XA | -| common util | `.c` | ~120 | leaf 유틸 | -| copybook/header | `.h`, UBF 필드테이블 `.fd` | ~600 | 구조체·FML 필드 | -| config | ndrxconfig.xml, app.ini, UBFTAB, XA RM | ~50 | TP/서버/RM 설정 | -| shell/run | `.sh` | ~180 | 기동/배치 스크립트 | -| schema/seed | `.sql` | ~50 | 스키마·시드 | -| **합계** | | **~2,030** | | - -## 5. 마이그레이션 타깃 매핑 (포지 → 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 서비스 등록 | 컴포넌트 스캔/라우팅 | -| `schema.sql` | Flyway | +> 이 문서는 초기 토폴로지 기준이라 낡았습니다. 최신 상세 문서는 **[ARCHITECTURE.md](ARCHITECTURE.md)** 를 참조하세요. +> (원문은 참고용으로 architecture.legacy.md 에 보존)