A-JADM EXTENSION · VERSION 0.1 · APRIL 2026

대규모 시스템 및
레거시 DB 대응 전략

ERP급 도메인이 많은 시스템 개발 전략과 레거시 DB 리버스 엔지니어링 방법론.
A-JADM v1.3의 실전 확장 문서입니다.

2
Part
4
레거시 유형
5
세션 유형
1
신규 계약 파일

개요

본 문서는 A-JADM v1.3의 확장 문서로, 실전에서 가장 빈번한 두 가지 시나리오를 다룹니다.
두 주제는 별개이지만, 현실의 ERP 현대화 프로젝트는 거의 항상 두 문제를 동시에 직면하므로 하나의 확장 문서로 묶었습니다.

분할
BC 단위로 프로젝트 분리
정렬
Wave 위상으로 순서 강제
진단
A/B/C/D 유형 분류 선행
점진
Strangler로 레거시 대체
공통 설계 원칙 — Claude Code의 비결정성과 컨텍스트 한계를 극복하기 위해:
① 계약 파일로 경계를 고정한다 · ② BC 단위로 프로젝트를 쪼갠다 · ③ Wave 위상 정렬로 순서를 강제한다 · ④ 세션을 물리적으로 분리한다
PART I
ERP급 대규모 시스템 개발 전략
도메인 10개 이상, BC 5~10개 규모의 시스템을 Claude Code로 효과적으로 개발하는 방법

핵심 원칙 · 1 프로젝트 = 1~소수 BC

ERP를 하나의 모노리식 프로젝트로 열어서 Claude Code로 개발하려 하면 실패합니다.
컨텍스트 윈도우, 추론 비결정성, 세션 간 일관성 문제가 규모에 비례해 누적되기 때문입니다.

물리적 프로젝트 분할 예시

erp-system/
├── erp-shared-kernel/     # 공통 VO만
├── erp-master-data/       # 기준정보 BC
├── erp-sales/             # 영업·수주 BC
├── erp-purchase/          # 구매 BC
├── erp-inventory/         # 재고 BC
├── erp-accounting/        # 회계 BC
└── erp-hr/                # 인사 BC

각 BC가 독립된 Claude Code 세션을 갖습니다.
루트 디렉토리·CLAUDE.md·계약 파일·커맨드 세트를 별도로 유지합니다.

분할이 주는 이점

1
컨텍스트 윈도우 여유
BC 하나의 도메인/코드만 세션에 올림
2
병렬 개발 가능
BC별로 담당자·Claude Code 세션 분리
3
실패 영향 국소화
한 BC 재생성이 다른 BC에 영향 없음
4
릴리스 독립성
BC별로 배포 주기 독립

Wave 위상 정렬

ERP에서 BC 간 의존성은 명확합니다. 의존성 방향대로 Wave(물결)로 묶어 순차 개발합니다.
같은 Wave 내의 BC는 병렬 개발이 가능합니다.

ERP 7 BC Wave 배치 예시

WAVE 0
shared
공통 VO
WAVE 1
master-data
기준정보
WAVE 2 — 병렬
inventory
재고
purchase
구매
WAVE 3 — 병렬
sales
영업·수주
accounting
회계
WAVE 4
hr
인사
reporting
리포팅

Wave Gate — 전환 조건

컴파일 통과
mvn compile
🧪
도메인 테스트
mvn test -pl domain
🔌
포트 연결
Spring 컨텍스트 로딩
🚫
순환 없음
DFS 사이클 검사
Gate 엄격 적용: 이전 Wave가 Gate 조건을 통과해야 다음 Wave로 넘어갑니다.
그렇지 않으면 상위 BC를 구현하다가 하위 BC 인터페이스를 거꾸로 수정하게 되어 전체가 흔들립니다.

BC 간 통신 원칙

ERP에서 가장 위험한 실수는 BC 간 직접 참조입니다.
다음 두 패턴만 허용하고, 어느 것을 쓸지는 arch-decisions.md에 명시합니다.

동기 ACL

즉시 응답이 필요할 때

수주 시점에 재고 확인 같은 경우.
포트 인터페이스는 호출 BC의 application 레이어, 구현체는 구현 BC의 adapter 레이어에 둡니다.
pom.xml에서 양방향 직접 참조 금지.

// sales/application/port/output/
public interface InventoryPort {
    boolean checkStock(ProductId id, int qty);
}

// inventory/adapter/outbound/
@Component
public class InventoryPortAdapter
        implements InventoryPort { ... }
비동기 Event

결과적 일관성 허용

수주 완료 후 회계 전표 자동 생성 같은 경우.
Domain Event를 Kafka/RabbitMQ/Spring Events로 발행.
수신 BC는 ACL Translator로 자신의 도메인 모델로 변환.

// sales → OrderConfirmed → Kafka
// accounting이 order.confirmed 구독

@KafkaListener(topics = "order.confirmed")
public void onOrderConfirmed(
        OrderConfirmedMessage msg) { ... }

arch-decisions.md 선언 예시

communications:
  - from: sales
    to: inventory
    mode: sync-acl
    port: InventoryPort

  - from: sales
    to: accounting
    mode: async-event
    event: OrderConfirmed
    topic: order.confirmed
이 결정을 계약 파일에 명시하면 /implement-bc추론 없이 포트 인터페이스와 어댑터 위치를 그대로 생성합니다.

Claude Code 세션 운영 전략

Claude Code로 ERP를 개발할 때 가장 중요한 결정은 어디서 세션을 끊을 것인가입니다.
세션 누적이 품질을 결정합니다.

❌ 나쁜 패턴

한 세션에서 전체 구현

"영업 모듈 만들고 재고 모듈 만들고 회계 모듈도 만들어줘"

→ 컨텍스트가 누적되며 후반 BC의 품질이 급격히 떨어짐
→ Claude가 앞서 생성한 코드를 혼동하기 시작
→ 일관성 없는 패키지 구조 · 중복 인터페이스 발생

✅ 좋은 패턴

역할별 세션 분리

설계 세션 (1회) → 계약 파일만 생성 후 폐기
BC별 구현 세션 (N회) → BC 하나만 담당
테스트 세션 (구현과 분리) → 순환논리 방지

세션 간 상태 전달은 오직 계약 파일로만 수행

세션 유형별 역할 분리

세션 유형실행 횟수입력산출물
설계 세션 1회 도메인 설명 domain-context.md, arch-decisions.md, bc-plan.md
BC별 구현 세션 N회 계약 파일 3종 + 해당 BC의 usecases/ BC의 domain / application / adapter 코드
테스트 세션 N회 (구현과 분리) 계약 파일 + 구현 코드 테스트 코드
테스트 세션 분리의 중요성: 동일 세션에서 구현·테스트를 모두 생성하면 순환논리가 되어 검증이 무의미해집니다. 반드시 새 세션에서 구현 코드만 읽어 테스트를 작성해야 합니다.

실전 체크리스트

ERP 프로젝트 시작 전 반드시 확인할 사항입니다.

확인 항목통과 조건
BC 개수 각 BC가 Aggregate 3~7개 내외. 10개 넘으면 분할 검토
공유 커널 크기 Money, Address, UserId 등 정말 불변인 VO만. 10개 이하 권장
순환 의존 bc-plan.md의 depends-on을 DFS로 검사해 사이클 0
Wave 크기 한 Wave에 BC 3개 이하 (병렬 개발 피로도 고려)
세션 분리 규칙 설계 / BC별 구현 / 테스트 세션이 물리적으로 분리됨
계약 파일 존재 domain-context.md, arch-decisions.md가 없으면 /implement-bc 실행 금지
PART II
레거시 DB 리버스 엔지니어링 전략
수십 년 누적된 레거시 DB와 타협하면서 현대화하는 방법. 레거시 DB를 또 하나의 계약 파일로 취급합니다.
전략의 핵심: 순수 Green Field DDD는 레거시 앞에서 거의 항상 무너집니다. 테이블 하나가 5개 도메인에서 쓰이고, FK 대신 문자열 관례로 참조하고, 이미 20개 배치가 특정 컬럼명에 하드코딩되어 있기 때문입니다.

추론을 최소화하고, 실제 스키마에서 추출한 사실을 legacy-db-context.md로 고정한 뒤, 이 위에서 DDD 모델링을 수행합니다.

진단 프레임 · A/B/C/D 유형 분류

리버스 엔지니어링을 시작하기 전에 레거시 DB의 유형을 먼저 판정해야 합니다.
유형에 따라 전략이 완전히 달라지기 때문입니다.

진단 축
X축 — 스키마 품질: FK · 정규화 · 네이밍 일관성
Y축 — 비즈니스 로직 위치: 앱 집중 vs DB 집중 (SP / Trigger)
B형 · 스키마 혼란
로직 앱 · 스키마 낮음
FK 없음 · 네이밍 불일치 · NULL 허용 과도
전략: 정제된 뷰 레이어 선행 → View로 Aggregate 구성
공수 · 리스크: 중 · 중
A형 · 이상적
로직 앱 · 스키마 양호
FK 존재 · 네이밍 일관 · 로직은 Service 레이어
전략: 표준 DDD 적용 · 테이블을 Aggregate에 매핑
공수 · 리스크: 낮음 · 낮음
D형 · 최악 조합
로직 DB · 스키마 낮음
DB 로직 + 스키마 혼란 동시 발생
전략: 부분 마이그레이션만 · 신규 기능은 별도 BC + CDC
공수 · 리스크: 매우 높음 · 높음
C형 · DB 로직 집중
로직 DB · 스키마 양호
Stored Procedure · Trigger 다수 · 스키마는 양호
전략: SP를 Outbound Port로 래핑 · Strangler 패턴
공수 · 리스크: 높음 · 중

진단 체크리스트

진단 항목확인 방법판정 기준
FK 존재율 information_schema.referential_constraints 70% 미만이면 B/D형
Stored Procedure 수 information_schema.routines 테이블 수의 30% 넘으면 C/D형
Trigger 개수 information_schema.triggers 테이블당 평균 1개 초과면 C/D형
네이밍 일관성 테이블 prefix, 컬럼 suffix 패턴 분석 3개 이상의 네이밍 규칙 혼재 시 B/D형
NULL 허용 비율 컬럼 nullable 비율 80% 넘으면 제약 부재 → B/D형
1:N 역참조 애플리케이션 코드 grep 문자열 기반 참조가 많으면 B/D형
이 진단은 Claude Code 세션을 열기 전에 DBA 또는 pg_dump --schema-only / mysqldump -d 결과로 먼저 수행합니다.
진단 결과는 legacy-db-context.mdclassification 필드에 기록됩니다.

리버스 엔지니어링 파이프라인

A-JADM의 표준 파이프라인 앞단에 레거시 처리 단계를 추가합니다.
/reverse-schema/ddd-modeling보다 먼저 실행된다는 것이 핵심입니다.
기존 테이블의 물리적 경계를 무시하고 DDD부터 시작하면 나중에 매핑이 불가능해집니다.

[ Phase 0 ] 레거시 진단 — 세션 밖에서 사람이 수행
   → classification: A/B/C/D 판정

[ Phase 1 ] 스키마 추출
/reverse-schema --source postgres --conn "jdbc:..."
   → legacy-db-context.md (계약 파일)

[ Phase 2 ] UC 추출 (기존 화면/배치 기반)
/usecase --from-legacy
   → usecases/UCxxx.md

[ Phase 3 ] DDD 모델링 (레거시 참조)
/ddd-modeling --with-legacy
   → domain-context.md (legacy-db-context.md 참조)

[ Phase 4 ] 매핑 결정
/arch-design --legacy-strategy strangler
   → arch-decisions.md (매핑 전략 포함)

[ Phase 5 ] 구현 (기존 A-JADM과 동일)
/uc-to-skeleton → /implement-bc → /gen-test
변경 사항: Phase 0·1은 신규. Phase 3·4에서 --with-legacy, --legacy-strategy 플래그로 레거시 참조.
Phase 5 이후는 기존 A-JADM 파이프라인과 동일합니다.

legacy-db-context.md 계약 파일 구조

이것이 전략의 핵심입니다.
레거시 DB의 사실(fact)을 Claude Code가 추론 없이 참조할 수 있도록 YAML로 고정합니다.

## Meta
schema-version: 1.0
content-version: 1.0.0
extracted-at: 2026-04-16
source-db: postgres 12.4
classification: B  # A/B/C/D

## Summary
total-tables: 347
total-views: 42
total-procedures: 18
total-triggers: 9
fk-coverage: 43%  # 낮음 → B형 근거

## Tables
tables:
  - name: TB_ORDER_MST
    purpose: "수주 마스터"
    primary-key: [ORD_NO]
    candidate-aggregate-root: Order  # ddd-modeling에 힌트
    row-count: 4200000
    columns:
      - name: ORD_NO
        type: VARCHAR(20)
        nullable: false
        semantic: "YYYYMMDD + 일련번호 5자리"
      - name: CUST_CD
        type: VARCHAR(10)
        nullable: false
        implicit-fk: TB_CUSTOMER.CUST_CD  # 물리 FK 없음
        cross-bc-reference: master-data   # BC 간 참조로 변환 대상

## Implicit References
# 앱 코드에서만 존재하는 참조 관계 (grep으로 발견)
implicit-references:
  - from: TB_ORDER_DTL.ITEM_CD
    to: TB_ITEM_MST.ITEM_CD
    evidence: "src/order/OrderService.java:142"

## Stored Procedures (C/D형일 때 필수)
stored-procedures:
  - name: SP_CALCULATE_ORDER_AMOUNT
    purpose: "수주 금액 계산 (할인/세금 포함)"
    migration-strategy: keep-as-adapter  # keep-as-adapter / rewrite / delete
    risk: high  # 회계 영향

## Mapping Decisions
# 테이블 → Aggregate 매핑 초안 (사람이 확정)
mapping:
  - aggregate: Order
    root-table: TB_ORDER_MST
    child-tables: [TB_ORDER_DTL, TB_ORDER_DISC]
    bc: sales
    notes: "TB_ORDER_DLVR은 별도 Shipment Aggregate로 분리"

  - aggregate: Customer
    root-table: TB_CUSTOMER
    bc: master-data
    notes: "TB_CUST_CREDIT은 sales BC의 CreditLimit VO로 인입"
이 파일이 있으면 /ddd-modeling은 "수주 도메인을 설계해줘"라는 막연한 요청에 추론으로 대응하지 않고, TB_ORDER_MST의 실제 구조를 읽어 Aggregate를 제안합니다.

유형별 구체 전략

진단 결과 확정된 유형에 따라 완전히 다른 접근을 적용합니다.

A형

표준 DDD 적용

FK가 잘 잡혀 있고 로직이 앱 계층에 있는 드문 경우입니다.
테이블을 그대로 Aggregate로 매핑해도 대부분 맞습니다. 다만 기존 DB를 그대로 쓸 것인지, 새 스키마로 갈 것인지 결정합니다.

기존 DB 재사용
persistence: jpa + @Table(name="TB_ORDER_MST")로 매핑. /gen-schema 실행하지 않고 legacy DDL을 그대로 schema.sql로 사용
새 스키마로 이관
/gen-schema로 새 ERD 생성. ETL 스크립트 별도 작성. 데이터 이관 기간 확보
B형

정제된 뷰 레이어 선행

FK 없고 네이밍 혼란.
테이블을 직접 Aggregate로 매핑하면 도메인 모델이 오염됩니다. 해법은 뷰(VIEW)를 중간 계층으로 삽입하는 것입니다.

-- 도메인 관점의 정제된 뷰
CREATE VIEW V_ORDER AS
SELECT
  ORD_NO        AS order_id,
  CUST_CD       AS customer_id,
  ORD_DT        AS order_date,
  ORD_AMT       AS total_amount,
  CASE ORD_STS
    WHEN '1' THEN 'PENDING'
    WHEN '2' THEN 'CONFIRMED'
    WHEN '9' THEN 'CANCELLED'
  END AS status
FROM TB_ORDER_MST
WHERE DEL_YN = 'N';

persistence: jpa+jdbc로 가면 Command(JPA)는 실제 테이블에, Query(JdbcTemplate)는 뷰에 매핑하는 식으로 분리할 수 있습니다.
legacy-db-context.md에는 이 뷰 매핑을 명시적으로 기록합니다.

C형

Stored Procedure를 계약으로 고정

SP가 20개 넘게 있고 회계/재고 계산 로직이 들어 있는 경우.
이것을 Java로 옮기겠다고 덤비면 반드시 망합니다. 전략은 SP를 Outbound Port로 래핑하는 것입니다.

1
SP를 Outbound Port로 래핑
인터페이스를 application 레이어에 선언, adapter에서 SP 호출
2
도메인 모델은 DTO만 정의
계산 로직은 건드리지 않음, SP 결과만 받아서 사용
3
Strangler 점진 이동
새 계산 로직은 Java로, SP는 Deprecated 처리 후 수년에 걸쳐 대체
// application/port/output/
public interface LegacyOrderCalculator {
    OrderAmount calculate(
        OrderCalcRequest req);
}

// adapter/outbound/
@Repository
public class LegacyOrderCalculatorAdapter
        implements LegacyOrderCalculator {
    @Override
    public OrderAmount calculate(
            OrderCalcRequest req) {
        // JdbcTemplate으로 SP 호출
        // 결과 → OrderAmount VO
    }
}
legacy-db-context.mdstored-procedures[].migration-strategykeep-as-adapter로 표시해두면 /uc-to-skeleton이 이 구조를 자동으로 생성합니다.
D형

부분 마이그레이션 + 신규 기능은 별도 BC

이 경우 전면 재개발을 시도하면 3년 뒤에도 끝나지 않습니다.
현실적 전략은 기존을 유지하고 신규만 새 BC로 분리하는 것입니다.

1
기존 시스템은 그대로 유지
유지보수만 수행. 리팩터링 시도 금지
2
신규 요구사항은 새 BC로
레거시 DB에 직접 쓰지 않고, CDC(Change Data Capture) 또는 이벤트 기반 동기화로 데이터를 받음
3
레거시 → 신규 단방향 데이터 흐름
Debezium 등으로 레거시 테이블 변경을 Kafka로 흘리고, 새 BC가 자신의 DB로 투영(projection)
# legacy-db-context.md
cdc-mapping:
  - source-table: TB_ORDER_MST
    event: LegacyOrderChanged
    topic: legacy.order.changed
    consumers: [new-sales-bc, analytics-bc]

Claude Code 세션 운영

레거시 리버스는 일반 개발보다 세션 분리가 더 중요합니다.
컨텍스트 오염이 도메인 모델의 신뢰성을 직접 해치기 때문입니다.

세션입력산출비고
진단 세션 스키마 덤프 (DDL) legacy-db-context.md의 Summary + Classification 사람 확인 필수
테이블 분석 세션 DDL + sample data + grep 결과 tables[], implicit-references[] 대용량 → 배치로 분할
매핑 결정 세션 위 산출 + usecases/ mapping[] 섹션 반드시 사람이 검토·확정
DDD 모델링 세션 legacy-db-context.md + usecases/ domain-context.md 기존 A-JADM 그대로
구현 세션 모든 계약 파일 코드 BC별 분리
주의: Claude Code에게 "레거시 DB 분석하고 DDD 모델까지 한 번에 만들어줘"라고 요청하면 반드시 망합니다.
컨텍스트가 오염되어 "적당히 정규화된 척하는" 도메인 모델이 나옵니다. 세션 분리는 타협할 수 없는 규칙입니다.

실무 주의점

실전에서 자주 놓치는 세 가지 포인트.

주의 1

ENUM 값 의미 복원

ORD_STS = '9'가 "취소"인지 "보류"인지 코드만 봐서는 모릅니다.

Claude Code가 절대 혼자 못 합니다. 도메인 전문가 인터뷰 결과legacy-db-context.mdcolumns[].semantic 필드에 사람이 직접 채워 넣어야 합니다.

주의 2

배치 = 숨은 비즈니스 로직

야간 정산 배치, 월마감 프로시저 같은 것들이 실제로는 도메인 규칙의 절반을 차지합니다.

UC 추출 시 화면뿐 아니라 크론/스케줄러 목록도 반드시 입력에 포함시킵니다.

주의 3

Shadow Write 검증

새 BC가 동일 입력에 대해 레거시와 같은 결과를 내는지 확인하려면, 한동안 둘 다 실행하고 결과를 비교하는 shadow write 기간이 필요합니다.

최소 1개 월마감 주기는 돌려봐야 합니다.

부록 · 확장 스킬 · 향후 과제

본 확장 문서는 진단 프레임과 계약 파일 스키마 초안까지 정립한 수준입니다.
아래는 실전 적용을 통해 완성해 나갈 항목입니다.

추가 스킬 (초안)

reverse-schema
레거시 DB에서 스키마 추출·분류 → legacy-db-context.md
legacy-uc-extract (예정)
레거시 화면·배치로부터 UC 도출 → usecases/UCxxx.md

추가 계약 파일

legacy-db-context.md
생성자: reverse-schema
소비자: ddd-modeling, arch-design, uc-to-skeleton

추가 커맨드 (초안)

/reverse-schema --source postgres --conn "jdbc:postgresql://..."
/reverse-schema --classify-only             # 진단만 수행
/ddd-modeling --with-legacy                 # legacy-db-context.md 참조
/arch-design --legacy-strategy strangler    # 전략 명시

향후 과제

reverse-schema 스킬의 SKILL.md 작성
legacy-db-context.md YAML 스키마 완전 정의
☐ Strangler 패턴 Java 구현 레퍼런스 확보
☐ 컨설팅 레퍼런스 1~2건 이상 실전 적용 → 패턴 누적
☐ 유형별 실제 케이스 스터디 문서화
이 영역은 선행 설계 비용 > 실전 학습 비용인 대표 영역입니다.
실전 적용을 통해 패턴을 식별한 뒤 스킬로 체계화하는 순서를 따릅니다. 고객 만남 → 경험 → 패턴 식별 → 스킬화.