IR 자료: 마이그레이션 난이도 브리핑 (3축 전량 실측)
"Forge가 얼마나 복잡한 레거시를 다룰 수 있는가"를 증명하는 Before 자산.
지표는 전부 소스에서 자동 집계(추정·과장 없음).
- tools/gen_metrics.py: 소스 실측 → app/ui/metrics.js + index.html 인라인
· 축A 규모: 파일 4,626 / 33만 LOC / EXEC SQL 23,742 / tpcall 989 /
tpbegin 1,234 / UBF 46,904 / 테이블 75 / 카피북 2,207
· 축B 자동변환 실패지점: ATMI·XA2PC(6단)·ECPG·UBF·카피북팬아웃·매크로
각각 실측 카운트 + Java/Spring 이전이 어려운 이유
· 축B 거래추적: ACQUIRE 6프로그램·6XA브랜치, PAY_APPROVE 5·5, CL_DAILY 6·6
(프로그램→테이블쓰기→XA브랜치 트리)
· 축C 레거시 인벤토리: acq_common.h 139프로그램 공유, 매크로 2,247,
sqlca 수동분기 18,719, EXEC밀도 9.8/파일
- 포털 신규 화면 "마이그레이션 난이도"(AR0002, #mig): 위 3축을 한 화면에
- docs/MIGRATION_COMPLEXITY.md: 피치덱용 브리프 (실동작 증거 + 발견된 실결함 4건)
- README: IR 자료 안내 추가
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5b572989e3
commit
46ff41b633
5 changed files with 352 additions and 1 deletions
102
docs/MIGRATION_COMPLEXITY.md
Normal file
102
docs/MIGRATION_COMPLEXITY.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# 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.
|
||||
- **업무 포털**: 운영 대시보드·매입·정산·가맹점·원장·아키텍처·난이도 화면. ~17.5만 행의 운영 데이터.
|
||||
|
||||
## 발견된 실결함 (레거시가 실제로 위험하다는 증거)
|
||||
|
||||
예제를 만들며 실행 중에 밟은, **정적 분석으로는 잡히지 않는** 결함들 — 이게 곧 마이그레이션 리스크의 실물이다.
|
||||
|
||||
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`).
|
||||
"이 숫자 진짜냐"는 질문에 코드로 답할 수 있다.
|
||||
Loading…
Add table
Add a link
Reference in a new issue