기존 3개월치(~20만행)를 5.5년치 대량 히스토리로 확대. 성장추세·계절성· 가맹점 생애주기 반영. ID 오프셋 대역(1천만+)으로 기존/라이브 채번과 비충돌, 전부 idempotent 가드. - 98-bulk-seed.sql: 가맹점 40→200(MCC·요율·개점일·CLOSED/HOLD 다양) + 매입 135만건(2021-01~2026-04, 성장추세 연 0.5→1.4배·12월 계절피크·주중주말· 다양 채널/발급사/카드종류/금액구간). 27초. - 99-bulk-derive.sql: 승인 135만·원장 2.6M(RECON+SETTLE)·정산 1.29M· 가맹점집계 358k. 시퀀스 정렬. 31초. - 99b-bulk-econ.sql: 정산경제 파생 — 수수료3층분해 1.29M·프레젠트먼트 1.29M· ISO8583 클리어링 1.29M·순액정산(발급사×일) 16k·펀딩 355k. - 99c-bulk-index.sql: 운영 인덱스 14개(biz_date/merchant/status 등) + ANALYZE. 검증 (실측) - 총 1,130만 행, DB 1.8GB. 연도별 성장 2021년 14.5만→2025년 35.3만건. - 3층 항등식 1,293,145/1,293,145, 펀딩 항등식 355,284/355,284 (100% 성립). - 대시보드/정산경제 화면 정상: 활성가맹점 191, 총매입 3,997억, 입금률 95.7%. - 집계쿼리 0.5초(인덱스). 최근 3개월 라이브 데이터 무손상. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
104 lines
6.6 KiB
Markdown
104 lines
6.6 KiB
Markdown
# acquire-core-x — 마이그레이션 난이도 브리핑 (IR)
|
|
|
|
> **한 줄 요약.** 이 저장소는 "Klaro Forge가 어느 정도로 복잡하게 얽힌 레거시까지
|
|
> 다룰 수 있는가"를 증명하기 위한 **실동작 예제(Before)** 다. 실제 카드 매입시스템
|
|
> (Tuxedo / Pro\*C / XA / Oracle 계열)의 아키텍처를 그대로 재현하되, 오픈소스 스택
|
|
> (Enduro/X · ECPG · PostgreSQL)으로 **실제로 빌드·부팅·거래가 되게** 만들었다.
|
|
> 아래 수치는 전부 소스코드에서 직접 집계한 값이며 과장이 없다.
|
|
> 포털에서 실시간으로 같은 수치를 볼 수 있다: `#mig`(마이그레이션 난이도), `#arch`(호출그래프).
|
|
|
|
---
|
|
|
|
## 왜 이게 "어려운 마이그레이션"인가 — 3축
|
|
|
|
### 축 A. 양적 압도 (규모)
|
|
|
|
| 지표 | 실측 |
|
|
|---|---|
|
|
| 소스 파일 (.pgc/.c/.h) | **4,626 개** |
|
|
| 총 코드 라인 | **330,642 LOC** |
|
|
| 온라인 서비스 | **1,463 본** |
|
|
| 배치 잡 | **506 본** |
|
|
| 임베디드 SQL (EXEC SQL) 블록 | **23,742** |
|
|
| Tuxedo tpcall 호출부 | **989** |
|
|
| 전역 트랜잭션 tpbegin | **1,234** |
|
|
| UBF 버퍼 필드접근 | **46,904** |
|
|
| DB 테이블 | **75** |
|
|
| 카피북 헤더(.h) | **2,207** |
|
|
|
|
BAIS(비씨카드 글로벌 매입시스템) RFI 기준 온라인 1,466 · 배치 503과 **1:1 규모**로 맞췄다.
|
|
|
|
### 축 B. 질적 난이도 (자동 변환이 실패하는 지점)
|
|
|
|
단순 문법 치환기·규칙 기반 변환기가 통째로 깨지는 지점들이다. 이게 곧 Forge의 존재 이유다.
|
|
|
|
| 레거시 요소 | 실측 | Java·Spring 이전이 어려운 이유 |
|
|
|---|---|---|
|
|
| Tuxedo ATMI (tpcall/tpservice/tpreturn) | 989 호출 | Java·Spring에 1:1 대응 API가 없다. 서비스 호출 모델 자체를 REST/메시지로 **재설계**해야 한다. |
|
|
| XA 2PC 전역 트랜잭션 (최대 **6단 중첩**) | 1,234 tpbegin | 형제 XA 브랜치 격리·2단계 커밋 의미론이 Spring `@Transactional` 경계와 다르다. 잘못 옮기면 **교착·이중전기**가 그대로 재현된다. |
|
|
| ECPG 임베디드 SQL (호스트변수·커서·sqlca) | 23,742 블록 | 블록마다 호스트변수 바인딩·`sqlcode` 분기·커서 수명을 JPA/MyBatis 의미로 옮겨야 한다. 자동 변환기가 가장 많이 깨지는 지점. |
|
|
| UBF(FML32) 타입리스 버퍼 | 46,904 접근 | 필드ID 기반 동적 버퍼라 타입·필수여부가 컴파일타임에 없다. Java DTO로 옮기려면 4.6만 접근점의 계약을 **사람이 복원**해야 한다. |
|
|
| 카피북 공유(#include) 팬아웃 | 6,892 참조 | 한 카피북을 수십 프로그램이 공유해 구조체 변경이 전방위로 파급된다. 모듈 단위 절단이 어렵다. |
|
|
| 전처리 매크로(#define) | 2,247 정의 | 매크로가 코드 생성·분기를 숨겨 정적 분석을 방해한다. 전개 후에야 실제 로직이 드러난다. |
|
|
|
|
**거래 하나의 파급** — "매입 1건이 몇 개 프로그램·테이블·트랜잭션 경계를 건드리나":
|
|
|
|
| 대표 거래 | 연쇄 프로그램 | XA 브랜치(2PC) | 기록 테이블 | 체인 |
|
|
|---|---|---|---|---|
|
|
| `ACQUIRE` (매입 접수) | 6 | 6 | 3 | ACQUIRE→RECONCILE→SETTLE→ST_MDR→LG_SETTLEPOST→LG_BALCHK |
|
|
| `PAY_APPROVE` (지급 승인) | 5 | 5 | 3 | PAY_APPROVE→PAY_FILEGEN→PAY_ACCTCHK→LG_POST→LG_BALCHK |
|
|
| `CL_DAILY` (일마감) | 6 | 6 | 5 | CL_DAILY→VL_SALES→RC_EXCEPTION→CM_LOG / →LG_CLOSE_LINK→CL_LOCK |
|
|
|
|
하나의 `tpbegin` 아래 여러 XA 브랜치가 서로 다른 테이블을 잠근 채 2단계 커밋으로 원자성을
|
|
보장한다. 이 경계를 잘못 옮기면 형제 브랜치 교착이 재현된다 — **실제로 이 저장소를 만들며
|
|
그 교착을 밟고 고쳤다**(아래 "발견된 실결함" 참조).
|
|
|
|
### 축 C. 리얼리티 (실전 레거시임)
|
|
|
|
"깔끔하게 만든 예제"가 아니라 실전 레거시 특유의 냄새가 실측된다.
|
|
|
|
| 레거시 특성 | 실측 |
|
|
|---|---|
|
|
| 가장 많이 공유되는 카피북 | `acq_common.h` — **139개** 프로그램이 #include (구조체 1줄 바꾸면 139곳 파급) |
|
|
| 전처리 매크로(#define) | 2,247 개 |
|
|
| `sqlca.sqlcode` 수동 에러분기 | **18,719 곳** (예외 대신 리턴코드 분기 — 전부 개별 이관 필요) |
|
|
| 임베디드 SQL 밀도 | 파일당 평균 **9.8 블록** |
|
|
| 운영 `userlog` 호출 | 17,122 곳 |
|
|
| `goto` 잔존 | 64 곳 |
|
|
|
|
---
|
|
|
|
## 실동작 증거 (예제가 "그림"이 아님)
|
|
|
|
- **빌드**: `docker compose up` → Enduro/X 소스빌드 + ECPG 프리컴파일 + 11 모듈서버 + 506 배치, 오류 0.
|
|
- **부팅**: 45 프로세스, 1,466 서비스 AVAIL.
|
|
- **실거래**: 6단 XA 체인 `ACQUIRE→…→LG_BALCHK` 실행 → `COMMIT OK`, 미결 2PC 0.
|
|
- **전수 스모크**: 체인 보유 477 서비스 100% 응답, 교착/타임아웃 0, 최대 530ms.
|
|
- **업무 포털**: 운영 대시보드·매입·정산·가맹점·원장·아키텍처·난이도 화면.
|
|
- **운영 데이터**: 매입 **135만건**(2021~2026, 5.5년치) 포함 **~1,130만 행**(DB ~1.8GB).
|
|
성장추세·계절성·가맹점 생애주기 반영, 파생(원장 2.6M·정산 1.3M·수수료분해 1.3M) 전부 항등 검증.
|
|
|
|
## 발견된 실결함 (레거시가 실제로 위험하다는 증거)
|
|
|
|
예제를 만들며 실행 중에 밟은, **정적 분석으로는 잡히지 않는** 결함들 — 이게 곧 마이그레이션 리스크의 실물이다.
|
|
|
|
1. **크로스브랜치 핫로우 교착** — 한 글로벌 트랜잭션의 형제 XA 브랜치가 같은 행을 UPDATE → 60초 블록 후 트랜잭션 타임아웃. (`mg_channel`, `lg_merch_bal` 등)
|
|
2. **원장 이중전기** — 같은 정산건이 다이아몬드 호출로 원장에 두 번 기표되던 회계 오류.
|
|
3. **PK 채번 충돌** — `max(log_id)+1` 과 `nextval` 혼용으로 형제 브랜치가 동일 PK INSERT → 인덱스 블록.
|
|
4. **스택 파괴** — `strncpy(out, val, 127)` 의 NUL 패딩이 호출자 스택 112바이트를 훼손 → 세그폴트. (DB 락이 0인데 60초 블록되는 증상)
|
|
|
|
모두 실행 검증에서만 드러났고, 소스에서 고쳐 재검증했다. **"자동 문법 변환으로 옮기면 이 결함들이 신규 시스템에 그대로 이식된다"** 가 핵심 메시지다.
|
|
|
|
---
|
|
|
|
## 재현 방법
|
|
|
|
```bash
|
|
docker compose -f docker/docker-compose.yml up -d --build # 빌드+부팅
|
|
# 포털: http://localhost:8090 (#arch 호출그래프 · #mig 마이그레이션 난이도)
|
|
python3 tools/gen_callgraph.py # 호출그래프 재추출 (소스 기준)
|
|
python3 tools/gen_metrics.py # 난이도 지표 재집계 (소스 기준)
|
|
```
|
|
|
|
지표는 전부 소스에서 자동 집계된다(`tools/gen_metrics.py`, `tools/gen_callgraph.py`).
|
|
"이 숫자 진짜냐"는 질문에 코드로 답할 수 있다.
|