acquire-core-x/docs/ARCHITECTURE.md
hyeongwoo e834692abb 문서: README 전면 개편 + 상세 ARCHITECTURE.md 신규 (산출물)
- README.md: 전체 현황 반영 개편 — 실측 지표(4,661파일·33만LOC·1,478svc·506배치·
  101테이블·1,173만행·패리티≈89%), 스택·Oracle계보, 빠른시작(빌드/거래/ISO왕복/
  클리어링파일), 포털 7화면(#해시), 저장소 구조, 문서 인덱스, 핵심 설계제약.
- docs/ARCHITECTURE.md: 상세 아키텍처 — 런타임 토폴로지(모듈서버 패턴·tmsrv/XA),
  11 도메인 분해표, 6단 XA 체인·브랜치 격리 규율, 데이터모델(정합성 불변식),
  외부연동 ISO8583 전문처리 다이어그램, 빌드시스템, Oracle 계보 3단, 관측·검증.
- 구 architecture.md → ARCHITECTURE.md 로 대체(원문 architecture.legacy.md 보존).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 15:06:00 +09:00

177 lines
12 KiB
Markdown
Raw Permalink 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
카드 매입·정산 시스템의 런타임 구조·도메인 분해·트랜잭션 흐름·데이터 모델·외부 연동·
빌드 시스템을 기술한다. 상용 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종(`<mod>_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``lib<mod>dbio.a`, `svc/*.pgc``lib<mod>svc.a`,
`<mod>_svr.pgc``buildserver``<mod>_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/<mod>/{<mod>_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%.