acquire-core-x/docs/MIGRATION_COMPLEXITY.md
hyeongwoo ab7983318d Wave I: 대량·다양 데이터 (다년치 135만 매입 → 총 1,130만 행)
기존 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>
2026-07-23 12:24:42 +09:00

6.6 KiB

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.h139개 프로그램이 #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)+1nextval 혼용으로 형제 브랜치가 동일 PK INSERT → 인덱스 블록.
  4. 스택 파괴strncpy(out, val, 127) 의 NUL 패딩이 호출자 스택 112바이트를 훼손 → 세그폴트. (DB 락이 0인데 60초 블록되는 증상)

모두 실행 검증에서만 드러났고, 소스에서 고쳐 재검증했다. "자동 문법 변환으로 옮기면 이 결함들이 신규 시스템에 그대로 이식된다" 가 핵심 메시지다.


재현 방법

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). "이 숫자 진짜냐"는 질문에 코드로 답할 수 있다.