Files
ga-commission-system/HANDOFF.md
T
GA Pro bfc724669b feat: incar 갭 반영 수수료/운영 14개 도메인 풀스택 + 배치통합 + 신입교육자료
incar CMS(116메뉴) 벤치마킹 갭분석으로 ga-pro에 없던 14개 도메인을 풀스택 추가.

P7 수수료 계산 7종 (V88~V96):
- 수입수수료+수지차, 소개·이관수수료, 정착지원금, 인정실적+환산율,
  시상, 생보운영지원수수료, 지점장수당
- 각 rule/ledger + 공식(보험료×요율, 수입−지급 등) Service 내장

P8 운영/정산 7종 (V97~V105):
- 단계마감, 본사정산, 등급평가, 공동계약, 보증보험, 적립금, 수수료시뮬레이터
- close/reopen/apply/simulate/upsert 등 액션 + 손익·잔액·등급 산정 로직

배치 통합 (V106):
- settle_master.other_commission_total 컬럼 추가(DEFAULT 0, 백필)
- 설계사 지급성 9종 원장 aggregateByAgent → AggregateStep gross 합산
- 빈 원장 0 → 기존 정산결과·무결성 불변(비파괴적)

테스트: P7/P8 Service 공식 단위테스트 21건(ga-api:test 57건 전체 통과)
프론트: 화면 14개 + React.lazy 코드 스플리팅(단일 4MB → 130+ 청크)
문서: DOMAIN_GAP_P7/P8.md, 신입교육_보험과수수료_완전기초.md, DOMAIN_KNOWLEDGE/HANDOFF 갱신

검증: 전체 ./gradlew build(test 포함) SUCCESSFUL, Flyway V88~V106 success,
GET+액션POST smoke 5xx 0건, 9개 집계쿼리 SQL 유효성 확인, 공식 런타임 실측.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:45:10 +09:00

38 KiB
Raw Blame History

Handoff Note — 최신 갱신 2026-05-29 (계약 승계 V86 §3-16) / Round 2 기준 2026-05-14

다음 세션에서 이어가기 위한 핸드오프 문서. Agent Teams 기반 자동 체인으로 진행 중인 작업의 현재 상태와 잔여 작업, 재개 절차를 기록한다.

1. 핸드오프 시점 상태

  • 마지막 커밋: P1-3-c (ChargebackCalculator + ChargebackGradeService + Controller)
  • 빌드 상태: ./gradlew :ga-common:compileJava :ga-core:compileJava :ga-api:compileJava 모두 BUILD SUCCESSFUL
  • 이번 세션 누적 커밋 수: 26개 (Agent Teams 설정 1 + 코드 품질 7 + 도메인 P0 9 + 도메인 P1 11)
  • 변경 사항 없음 (working tree clean)

2. 완료된 작업

2-1. 코드 품질 자동 체인 (7 커밋)

초기 ga-api 5명 리뷰 결과를 바탕으로 P0 이슈 일괄 해결.

단계 커밋 내용
1 5e58a54 OrganizationSaveReq + 상태 Enum 4종 (Agent/Contract/Settle/Approve)
2 a8f0b6e Controller→Service 분리 5개 + 매직 스트링 Enum 교체 + @DataChangeLog 17곳
3a 693275e UserStatus + ExceptionLedgerStatus Enum
3b af3c6b9 Map→SaveReq DTO 4종 + 입력 검증 + 잔여 Enum 적용
4 498d0d4 LookupCache 동시성 + UploadService/TransferFile 트랜잭션 분해 + readOnly 일괄
5a 4b211ec AgentOrgHistoryResp + MapStruct mapper 3종 + AgentMapper.xml 조인
5b b16e678 MapStruct 적용 + Resp 적용 + PaymentService/UploadTemplateService 신설 + PageHelper 통일

2-2. 도메인 P0 — 세무·공제·명세서 (9 커밋)

도메인 a (DB) b (core) c (api)
P0-1 세무 ad5d62e V23 92f3217 f8c7d84 TaxCalculator
P0-2 공제 0e73eb6 V24 a3ba7fa b8f33f1 DeductionService
P0-3 명세서 930335c V25 4f5a2f0 f5efcaa ExcelStatementGenerator

핵심 산출물:

  • 표준 세율 system_config 외부화 (3.3%/8.8%/10%/10%)
  • agent에 tax_type/business_no/vat_type 컬럼
  • payment에 income_tax_amount/local_tax_amount/vat_amount/net_amount
  • agent_deduction (월별 차감 마스터) + payment_deduction_detail (이체 명세)
  • commission_statement (명세서 발급 마스터, agent+settle_month+statement_type UNIQUE)
  • TaxCalculator pure 도메인 + ExcelStatementGenerator (PDF는 향후)

2-3. 도메인 P1 — 1200%룰·분급·차등환수 (11 커밋)

도메인 a (DB) b (core) c (api)
P1-1 1200%룰 6a6ba9f V26 1109253 bba4a33 RegulatoryLimitChecker
P1-2 분급 434e778 V27 5029bc5 (P1-2-c 커밋) InstallmentPlanGenerator
P1-3 차등환수 cc5d0e5 V28 44dbbf3 (P1-3-c 커밋) ChargebackCalculator

P1 보강 (병렬 트랙):

  • 1aa31de RecruitLedger/MaintainLedger sumByContract 메서드 추가
  • f6142a1 RegulatoryLimitChecker TODO 해소 (실제 누적값 사용)

핵심 산출물:

  • regulatory_limit 마스터 (1200%/35%/7년 한도, effective_from/to 이력 추적)
  • installment_ratio + installment_plan (분급 7년 비율 35/25/15/10/7/5/3)
  • chargeback_grade (5단계: 112개월 100% → 85개월 0%)
  • RegulatoryLimitChecker / InstallmentPlanGenerator / ChargebackCalculator pure 도메인

2-4. 병렬화 적용 (P1 후반부)

초반 직렬 a→b→c 패턴에서 후반에 도메인 간 sub-step 병렬로 전환.

  • P1-2-c(api) + P1-3-a(dba) + P1 보강(core) 3명 동시 활성화
  • P1-3-b(core) + P1-1-c TODO 교체(api) 2명 동시
  • 모듈이 다른 사람끼리 묶어 머지 충돌 없음

3. Round 2 완료 사항 (2026-05-14)

3-1. P2 — 8 도메인 (V29~V36, ~50 VO·Mapper, 18 Service+Controller)

# 도메인 마이그레이션 상태
P2-1 마감 (Period Close) V29 완료
P2-2 정정 (Correction) V30 완료
P2-3 이의신청 (Dispute) V31 완료
P2-4 보류사유 (Hold Reasons) V32 완료
P2-5 시책·인센티브 V33 완료
P2-6 해촉정산 (Termination) V34 완료
P2-7 세금계산서 (Tax Invoice) V35 완료
P2-8 외부회신 V36 완료

화면: ga-frontend Wave 4 에서 P4-B 화면과 함께 작성됨.

3-2. P3 — KPI 대시보드 (V37~V39)

  • dba: mv_monthly_summary / mv_org_summary / mv_retention_rate / mv_cumulative (V37)
  • dba: 리포트 메뉴 + KPI 메뉴 INSERT (V38, V39)
  • core: KpiMapper.java + XML
  • api: KpiService + KpiController (/api/kpi/*)
  • frontend: KpiMonthlySummary / KpiOrgSummary / KpiRetention / KpiCumulative (4개 화면)

3-3. P4-A — 수수료 종류 보강 (V40~V44, 5 도메인)

# 도메인 마이그레이션 상태
P4-A-1 commission_type 코드 + commission_master 통합 V40 완료
P4-A-2 수금수수료 (collection_commission_rule/ledger) V41 완료
P4-A-3 갱신·부활수수료 (renewal/reinstatement) V42 완료
P4-A-4 유지보수수당 (persistency_bonus_rule/bonus) V43 완료
P4-A-5 오버라이드 원장 (override_ledger/summary) V44 완료

3-4. P4-B — 운영 필수 9 도메인 (V45~V53)

# 도메인 마이그레이션 상태
P4-B-1 위촉계약 (agent_contract) V45 완료
P4-B-2 자격관리 (agent_license) V46 완료
P4-B-3 보수교육 (agent_training) V47 완료
P4-B-4 계약변경 (contract_endorsement) V48 완료
P4-B-5 민원관리 (complaint) V49 완료
P4-B-6 결재워크플로우 (approval_line/request) V50 완료
P4-B-7 공지·알림 (notice/user_notification) V51 완료
P4-B-8 첨부파일 (attachment) V52 완료
P4-B-9 출금신청 (withdraw_request) V53 완료

3-5. V54~V55 + 프론트

  • V54: P4 메뉴 INSERT (P4-A + P4-B 14개 메뉴)
  • V55: P4 공통코드 추가 (approval_type, complaint_type, license_type, withdraw_status 등)
  • 프론트: ErrorBoundary 전역 적용 + 토스 디자인 시스템 토큰 (RADIUS, GRAY, SHADOW) 일관 적용
  • 프론트 화면 추가: AgentContracts / AgentLicenses / AgentTrainings / Complaints / ContractEndorsements / CollectionCommission / CommissionMaster / OverrideLedger / PersistencyBonus / ReinstatementCommission / RenewalCommission / WithdrawRequest / Approvals / Notices (14개)

3-6. P1 정산 통합 (batch-engineer 완료)

  • ga-batch 8 Step에 TaxCalculator / DeductionService / RegulatoryLimitChecker / InstallmentPlanGenerator / ChargebackCalculator 통합 완료

3-7. P5 Wave 1 — agent + contract 회계 진입점 (V63~V66)

항목 마이그레이션 상태
agent_annual_income 테이블 V63 완료
contract_accounting_entry 테이블 V64 완료
P5-W1 테스트 데이터 + 메뉴 INSERT (ANNUAL_INCOME / ACCOUNTING_JOURNAL) V65 완료
account_code 마스터 + 4건 초기 데이터 V66 완료
rule 감사 컬럼 보강 (commission_rate/payout_rule/override_rule 등) V70 완료
payment 세무 컬럼 보강 (income_tax_amount/local_tax_amount/vat_amount/net_amount) V71 완료

핵심 산출물:

  • agent_annual_income (agentId+settleYear 복합PK, 연도별 사업소득 집계)
  • contract_accounting_entry (분개 엔트리, debit/credit 계정코드 + journalNo 마감)
  • account_code 마스터 (account_type DEBIT/CREDIT/BOTH 구분, 초기 4건: 8350/2530/1130/4110)
  • AgentAnnualIncomeService + AgentAnnualIncomeController (/api/agent-annual-incomes)
  • ContractAccountingService + ContractAccountingController (/api/accounting-entries)
  • AccountCodeService + AccountCodeController (/api/common/account-codes)
  • YearEndIncomeAggregateStep (Step 10, 12월 마감 시만 실행, settleMonth.endsWith("12") 조건)
  • AccountingEntryGenerateStep (Step 9, 매월 정산 후 분개 생성, ledger UNION 기반 contract_id 확보, journal_no=NULL 미전기)
  • 프론트 화면: AgentAnnualIncomeList (/commission/annual-income) + AccountingJournalList (/commission/accounting-journal)

통합 검증 결과 (2026-05-15):

  • Flyway: V63/V64/V65/V66/V70/V71 적용 확인 (현재 ga schema V71)
  • GET /api/agent-annual-incomes → 200 OK
  • POST /api/agent-annual-incomes/regenerate?year=2025 → 200 OK, affected=0 (payment 데이터 없음, 정상)
  • GET /api/accounting-entries → 200 OK
  • GET /api/common/account-codes?accountType=DEBIT → 200 OK, 2 items

보정 사항 (통합 검증 중 발견):

  • App.tsx 라우트와 V65 menu_path 모두 /commission/annual-income, /commission/accounting-journal로 일치
  • ANNUAL_INCOME EXECUTE 권한 누락 → DB 직접 INSERT로 보정
  • payment 세무 컬럼 미적용 (V23 체크섬 충돌 이력) → V71로 idempotent 보강

운영 DB 적용: ga-api 재시작 → Flyway V18~V71 일괄 자동 적용.

3-8. P5 Wave 2 — 원천세/부가세 분기신고 (V67~V69)

항목 마이그레이션 상태
withholding_tax_report 테이블 (원천세 분기신고) V67 완료
vat_report 테이블 (부가세 분기신고) V68 완료
P5-W2 테스트 데이터 + 메뉴 INSERT (WITHHOLDING_TAX / VAT_REPORT) V69 완료
EXECUTE 권한 보강 (WITHHOLDING_TAX / VAT_REPORT) V73 완료

핵심 산출물:

  • withholding_tax_report (분기별 원천세 신고 집계: settleQuarter/agentId/totalIncome/totalWithheld/totalLocalTax)
  • vat_report (분기별 부가세 신고 집계: settleQuarter/totalSales/totalPurchase/totalVatPayable/taxInvoiceCount)
  • WithholdingTaxReportMapper + WithholdingTaxReportMapper.xml (selectList/selectDetailById/upsertQuarterlyAggregate/updateFileStatusToFiled)
  • VatReportMapper + VatReportMapper.xml (selectList/selectDetailById/upsertQuarterlyAggregate/updateFileStatusToFiled)
  • WithholdingTaxReportService + WithholdingTaxReportController (/api/withholding-tax-reports)
  • VatReportService + VatReportController (/api/vat-reports)
  • WithholdingTaxQuarterlyStep (batch Step 11, 분기 마지막 월 실행)
  • VatReportQuarterlyStep (batch Step 12, 분기 마지막 월 실행)
  • 프론트 API 모듈: ga-frontend/src/api/withholdingTax.ts + ga-frontend/src/api/vat.ts

통합 검증 결과 (2026-05-17):

  • Flyway: 운영 DB ga schema V73 (V67/V68/V69/V73 모두 적용 확인)
  • GET /api/withholding-tax-reports → 200 OK, list 반환 (집계 데이터 있음)
  • GET /api/withholding-tax-reports/1 → 200 OK, 단건 상세 반환
  • POST /api/withholding-tax-reports/regenerate {"settleQuarter":"2025-Q4"} → 200 OK, affected=0 (이미 존재, 정상)
  • PUT /api/withholding-tax-reports/complete {"ids":[1]} → 200 OK
  • GET /api/vat-reports → 200 OK, list 반환
  • GET /api/vat-reports/1 → 200 OK, 단건 상세 반환
  • POST /api/vat-reports/regenerate {"settleQuarter":"2025-Q4"} → 200 OK, affected=1
  • PUT /api/vat-reports/complete {"ids":[1]} → 200 OK

보정 사항 (통합 검증 중 발견):

  • frontend build 실패: ChargebackGradeList/InstallmentRatioList/DeductionList validator 파라미터 implicit any, CommissionDispute/IncentiveProgram/TaxInvoiceList parser 반환타입 불일치 → 명시적 타입 어노테이션으로 수정 (6개 파일)
  • complete 엔드포인트 요청 바디: {"ids":[id]} 형식 (단일 reportId 아님)

운영 DB 최신 상태: ga schema V73 적용 완료.

3-11. P6 PoC — 외부 연동 어댑터 모듈 (V81~V84, 2026-05-24 라이브 적용)

P6 본 작업의 PoC가 완료되어 어댑터 추상화 + Mock 구현 + SDK 분기 + 운영 잔여 기능까지 들어감. 실제 KFTC/Toast SDK 통합 시 어댑터 구현체만 바꾸면 ga-api/ga-common 무변경.

항목 마이그레이션 상태
분급 비율 회차(월) 단위 등록 지원 V81 완료
withdraw_request 펌뱅킹 추적 컬럼 (transfer_tx_id/transfer_status/transfer_at) V82 완료
WITHDRAW_REQUESTS EXECUTE 권한 보강 (오기 — 0건 적용) V83 완료(no-op)
WITHDRAW 메뉴 EXECUTE 권한 (V83 보정본) V84 완료

핵심 산출물:

  • 신규 모듈 ga-external-adapter — 외부 SDK 의존성 격리, settings.gradle 등록
  • ga-common.adapter.bank: BankTransferAdapter 인터페이스 + Request/Response/Status
  • ga-common.adapter.message: MessageAdapter 인터페이스 + Channel(SMS/KAKAO)/Request/Response
  • MockBankTransferAdapter / MockMessageAdapter (로그 + 계좌·전화 마스킹, 기본 활성)
  • KftcBankTransferAdapter / ToastMessageAdapter 스켈레톤 (UnsupportedOperationException, SDK 통합 시 구현)
  • @ConditionalOnProperty 프로파일 분기: app.adapter.bank=mock|kftc, app.adapter.message=mock|toast
  • ApprovalService.handleApprovalCallback(WITHDRAW) — 최종 승인 시 펌뱅킹 어댑터 호출 → 성공 시 APPROVED→SENT 전이 + transfer_tx_id 영속화, 실패 시 APPROVED 유지 + fail_reason 기록
  • ApprovalService.sendStepNotification — DB 알림 외 SMS 어댑터 호출 (UserMapper.selectFirstActiveContactByRoleCode 로 실제 결재자 phone 매핑, phone 미확보 시 SMS skip)
  • WithdrawBankRetryService + BankRetryController (POST /api/internal/bank-retry) — 실패 transfer 재시도 배치 + @EnableScheduling + cron 옵션(기본 비활성) + app.bank-retry.limit
  • WithdrawRequestMapper.selectFailedTransfers / updateTransferResult 추가
  • WithdrawRequestVO/Resp + WithdrawRequestMapper.xml + 화면 WithdrawRequest.tsx 에 transfer_tx_id/transfer_status/transfer_at 노출

설정값(application.yml):

app:
  adapter:
    bank:    ${ADAPTER_BANK:mock}      # mock | kftc
    message: ${ADAPTER_MESSAGE:mock}   # mock | toast
  bank-retry:
    limit: ${BANK_RETRY_LIMIT:50}
    cron:  ${BANK_RETRY_CRON:0 0 0 1 1 ?}  # 사실상 비활성. 운영 활성화 예 "0 */10 * * * *"

통합 검증 결과 (2026-05-24):

  • 5모듈 컴파일 BUILD SUCCESSFUL (ga-common/ga-core/ga-external-adapter/ga-api/ga-batch)
  • 운영 DB v82~v84 적용 완료, ga-api 8082 가동
  • POST /api/auth/login → 200 OK, accessToken 발급
  • GET /api/withdraw-requests → 200 OK, 2건 모두 status=SENT / transfer_status=ACCEPTED / transferTxId="MOCK-…" 영속화
  • POST /api/internal/bank-retry → 200 OK, data=0 (실패 건 없음 — 정상)
  • GET /api/approvals/requests/mine → 200 OK, WITHDRAW APPROVED 이력 정상
  • GET /api/forecast-kpi → 200 OK
  • GET /api/year-end-statements → 200 OK

보정 사항:

  • V83 작성 시 menu_code 오기로 0건 적용 → V84 로 정정 보강

4. 잔여 작업 (P6 실연동)

P5 전체 + P6 PoC(어댑터 모듈 + Mock 구현 + SDK 분기 + 재시도 배치 + DB 추적 컬럼) 완료. 잔여는 외부 스펙 확정 후 적용 가능한 항목 + 모바일.

P6 실연동

항목 설명 선행 조건
KFTC 펌뱅킹 실 SDK 통합 KftcBankTransferAdapter 본체 구현 + 인증·엔드포인트 주입 KFTC API 키·계약·테스트망
Toast(NHN) SMS 실 SDK 통합 ToastMessageAdapter 본체 구현 계정·App Key·발신번호·템플릿 코드
카카오 알림톡 실 SDK 통합 동일 (채널만 KAKAO) 비즈 채널·템플릿 승인
펌뱅킹 SETTLED 동기화 ACCEPTED→SETTLED 폴링 스케줄 (queryStatus 사용) 실 SDK 통합 이후
펌뱅킹/메시지 호출 감사 이력 external_call_log 테이블 + 영속화 + 조회 화면 독립 진행 가능
모바일 셀프 조회 PWA 설계사 전용. 정산 명세서/출금 신청/공지 등 화면 우선순위 협의

3-12. P6 PoC — 외부연동 호출 감사 이력 (V85, 2026-05-24 라이브 적용)

P6 PoC 의 운영성 보강. 펌뱅킹/SMS·카카오 어댑터 모든 호출이 external_call_log 테이블에 AOP 로 자동 영속화. 외부 SDK 통합 후에도 그대로 재사용.

항목 위치 상태
external_call_log 테이블 + 인덱스 + SYSTEM_EXTERNAL_CALL 메뉴/권한 V85 완료
ExternalCallLogVO / Resp / SearchParam ga-core 완료
ExternalCallLogMapper(.java/.xml) — insertOne + selectList + selectById ga-core 완료
ExternalCallLogService — recordCall @Transactional(REQUIRES_NEW) ga-api 완료
ExternalCallLogController — GET /api/external-call-logs[/{id}] ga-api 완료
ExternalCallLoggingAspect — BankTransferAdapter+/MessageAdapter+ 모든 메서드 @Around ga-api/aop 완료
ExternalCallLogList.tsx + api/externalCallLog.ts + App.tsx 라우트 ga-frontend 완료

핵심 동작:

  • 어댑터 호출 전/후·예외 모두 external_call_log INSERT (호출자 트랜잭션과 분리 — REQUIRES_NEW)
  • request/response 는 PII 마스킹된 JSON 한 줄로 요약 (계좌·전화 뒤 4자리, 이름 첫글자만)
  • 영속화 자체가 실패해도 비즈니스 흐름은 절대 깨지 않음 (warn 로그만)
  • target_ref_type/id 자동 추출: WITHDRAW(BankTransferRequest.withdrawRequestId), MessageRequest.refType/refId

통합 검증 결과 (2026-05-24):

  • Flyway: V85 자동 적용 → 운영 DB schema v85
  • 5모듈 컴파일 BUILD SUCCESSFUL + ga-frontend TS 타입 검증 통과
  • 라이브 시나리오: 신규 withdraw#3 → 결재 1단계 + 2단계 APPROVE → 최종 승인 → 펌뱅킹 호출
    • withdraw#3 status=SENT, transferTxId=MOCK-…, transferStatus=ACCEPTED
    • GET /api/external-call-logs → total=1, log#1: BANK/MockBankTransferAdapter/requestTransfer/success=true/durMs=3/target=WITHDRAW#3
    • 상세: requestSummary 의 accountNoMasked="****0123" — PII 마스킹 정상 (평문 비노출)

4. 잔여 작업 (P6 실연동)

P5 전체 + P6 PoC(어댑터 모듈 + Mock 구현 + SDK 분기 + 재시도 배치 + DB 추적 컬럼 + 호출 감사 이력) 완료. 잔여는 외부 스펙 확정 후 적용 가능한 항목 + 모바일.

P6 실연동

항목 설명 선행 조건
KFTC 펌뱅킹 실 SDK 통합 KftcBankTransferAdapter 본체 구현 + 인증·엔드포인트 주입 KFTC API 키·계약·테스트망
Toast(NHN) SMS 실 SDK 통합 ToastMessageAdapter 본체 구현 계정·App Key·발신번호·템플릿 코드
카카오 알림톡 실 SDK 통합 동일 (채널만 KAKAO) 비즈 채널·템플릿 승인
펌뱅킹 SETTLED 동기화 ACCEPTED→SETTLED 폴링 스케줄 (queryStatus 사용) 실 SDK 통합 이후
모바일 셀프 조회 PWA 설계사 전용. 정산 명세서/출금 신청/공지 등 화면 우선순위 협의

3-13. P6 — 모바일 셀프 조회 PWA 스캐폴드 (ga-mobile-pwa, 2026-05-24)

설계사 본인 정산명세서·출금·공지를 모바일에서 조회하는 PWA 신규 모듈. 데스크탑 admin UI(ga-frontend)와 별도. 백엔드 무변경 — 기존 ga-api(8082) 그대로 재사용.

구성

항목 위치 비고
신규 모듈 ga-mobile-pwa/ Vite + React 18 + TS + antd-mobile@5
PWA 인프라 vite-plugin-pwa manifest + service worker autoUpdate, /api/** NetworkFirst(5s/5min)
인증 src/stores/authStore.ts (zustand persist) 토큰 키: mobile.accessToken (ga-frontend 와 분리)
API 클라이언트 src/api/{request,auth,statement,withdraw,notice}.ts axios + 401 자동 리다이렉트
화면 5종 src/pages/{Login,Home,StatementList,WithdrawList,NoticeList}.tsx NavBar + List/Card + PullToRefresh + TabBar
라우팅 src/App.tsx + RequireAuth 가드 + MobileTabBar /login 외 모두 보호

화면 ↔ 백엔드 API

경로 백엔드
/login POST /api/auth/login + GET /api/auth/me
/ (홈) 세 도메인 카운트 조회
/statement GET /api/statements?agentId={me.agentId}
/withdraw GET /api/withdraw-requests?agentId={me.agentId}
/notice GET /api/notices/active

본인 데이터 필터: 클라이언트에서 profile.agentId 를 쿼리 파라미터로 전달. 본격 운영에는 백엔드 /api/mobile/me/* 전용 endpoint (토큰 → agentId 서버측 강제 필터) 권장 — 잔여 항목.

빌드 / 실행

cd ga-mobile-pwa
npm install
npm run dev      # http://localhost:3001 (vite dev — /api → 8082 proxy)
npm run build    # tsc --noEmit + vite build → dist/ (manifest + sw 자동 생성)
npm run preview  # 3001 에서 production 번들 서빙

통합 검증 (2026-05-24)

  • npm install 407 packages 성공
  • tsc --noEmit PASS (@types/node 추가)
  • npm run build PASS — manifest.webmanifest + sw.js + workbox-*.js 자동 생성, precache 8 entries (528 KiB), 번들 516KB(gzip 173KB)
  • 라이브: vite dev :3001 LISTENING, ga-api :8082 정상
  • API 응답 확인: /api/statements total=112, /api/withdraw-requests total=3, /api/notices/active 0건(정상), /api/auth/me 정상

잔여 (스캐폴드 이후)

  • 백엔드 /api/mobile/me/* 전용 endpoint → §3-14 에서 완료
  • 본인 알림(user_notification) 표시 → §3-14 에서 완료
  • 출금 신청 작성 폼 (현재 이력 조회만)
  • 정산 명세서 상세 + PDF 다운로드
  • 토큰 만료 자동 refresh
  • 푸시 알림 (FCM 등) — 외부 채널 결정 후
  • 실 아이콘 PNG (192/512/512 maskable)
  • 번들 코드 스플리팅 (manualChunks)

3-14. P6 — 모바일 PWA 보안 강화: /api/mobile/me/* 전용 endpoint (2026-05-24)

PWA 스캐폴드에서 클라이언트가 agentId 를 쿼리 파라미터로 보내 본인 데이터를 필터하던 변조 가능 구조를 서버측 강제 필터로 교체. 토큰 → user → agentId 자동 추출.

백엔드 신규

위치 내용
ga-api/service/mobile/MobileMeService 토큰에서 userId 추출, userMapper.selectById 로 agentId 결정. 클라이언트 param 의 agentId/userId 는 무시하고 덮어쓰기. agentId 미연결 시 BizException(FORBIDDEN)
ga-api/controller/mobile/MobileMeController GET /api/mobile/me/{summary,statements,withdraw-requests,notifications} + POST /api/mobile/me/notifications/{id}/read. 별도 메뉴 권한 없이 인증만 통과

PWA 측 전환

  • src/api/statement.ts / withdraw.ts/api/mobile/me/* 호출, agentId 인터페이스에서 제거
  • src/api/notification.ts 신규 — list/markRead/summary
  • Home.tsxnotificationApi.summary() 한 번으로 3 카운트 동시 표시, 안 읽음 뱃지
  • NoticeList.tsxTabs내 알림 / 공지사항 두 탭. 알림 클릭 시 markRead + summary 무효화
  • StatementList.tsx / WithdrawList.tsx — agentId 의존 제거, E403(설계사 미연결) 시 ErrorBlock 안내

라이브 검증 (2026-05-24)

  • ga-api 컴파일 BUILD SUCCESSFUL · 재기동 OK
  • PWA tsc --noEmit PASS · npm run build PASS (precache 8 entries 538 KiB, gzip 176KB)
  • GET /api/mobile/me/summary (admin, agentId=null) → 200 OK, 세 카운트 모두 0
  • GET /api/mobile/me/statements (admin) → E403 차단 (보안 강제 동작 확인)
  • GET /api/mobile/me/notifications (admin) → 200 OK total=0

4. 잔여 작업 (P6 실연동)

P5 전체 + P6 PoC + P6 후속(2026-05-27) 완료. 잔여는 외부 SDK 차단 항목만.

운영 DB 마이그레이션 상태

운영 DB 192.168.0.60:55432/trading_ai/ga 현재 V96 적용 완료 (P7 incar 갭 반영 7도메인). ga-api 8082 / ga-frontend 3000 / ga-mobile-pwa 3001 모두 라이브.

단위/통합 테스트: SqlSessionFactory 환경 문제 해결 완료 (2026-05-29, §3-17). ./gradlew test → 112건 전부 PASS (ga-common 57 / ga-api 36 / ga-batch 19).

3-15. P6 후속 (2026-05-27, 커밋 2c9bf71 + dfce9e5 + 후속 1)

영역 결과
PWA 출금 신청 작성 폼 POST /api/mobile/me/withdraw-requests + settle-master 드롭다운, net_amount 잔여 한도 검증 포함
PWA 명세서 상세 + 엑셀 다운로드 GET /api/mobile/me/statements/{id}[/download], 본인 검증 + 기존 download 흐름 재사용
PWA 토큰 자동 refresh 401 → /api/auth/refresh 단일화 재시도, refreshToken localStorage 영속
펌뱅킹 SETTLED 동기화 폴러 WithdrawBankSettleService + POST /api/internal/bank-settle, ACCEPTED→COMPLETED/APPROVED 전이
사용자 phone 등록 (admin) PUT /api/system/users/{id}/phone, UserList 인라인 Modal
PWA 본인 프로필 Profile.tsx(phone 자기수정 + 비번변경 + 로그아웃), TabBar 5탭
PWA 출금 신청 취소 POST /api/mobile/me/withdraw-requests/{id}/cancel, REQUESTED 만 SwipeAction
SETTLED/FAILED 본인 알림 자동 발행 user_notification INSERT, PWA NoticeList 내알림 탭 즉시 확인
PWA 알림 모두 읽음 POST /api/mobile/me/notifications/read-all, NoticeList 헤더 버튼
출금 잔여 한도 검증 settle_master.net_amount - sum(활성 출금합계) 초과 시 E411

e2e 검증 (2026-05-27 23:40, agent01/userId=4/agentId=1):

  • 로그인 → AGENT role → /api/mobile/me/summary statementCount=3 / withdrawRequestCount=3
  • /api/mobile/me/settle-masters → 3건 응답 (admin은 E403이었음)
  • POST /api/mobile/me/withdraw-requests → 성공 (requestId=4)
  • /api/mobile/me/statements/1 → 200 (본인) · /statements/2 → E403 (타인) ← 본인 검증 강제
  • PUT /api/mobile/me/profile/phone → 200, /api/auth/me 즉시 반영
  • POST /api/internal/bank-settle → SETTLED 동기화 + user_notification 자동 발행 코드 경로 검증
  • POST /api/auth/refresh → 새 accessToken 발급

잔여 — 외부 SDK 차단:

항목 차단 사유
KftcBankTransferAdapter 본체 API 키·계약·테스트망
ToastMessageAdapter 본체 App Key·발신번호·템플릿 코드
카카오 알림톡 본체 비즈채널·템플릿 승인
푸시 알림 (FCM 등) 외부 채널 결정

3-18. P7 — incar 갭 반영 수수료 계산 7개 도메인 풀스택 (V88~V96 — 완료, 2026-05-31 라이브 적용)

incar CMS 갭 분석 결과 GA 시스템에 없던 수수료 계산 7개 도메인을 DB→Core→API→Frontend 전 레이어로 추가. 운영 DB schema v96.

도메인 마이그레이션 테이블 API 경로
수입수수료+수지차 (INCOME_COMM) V88 income_commission_ledger, margin_summary /api/income-commissions/*
소개·이관수수료 (REFERRAL_COMM) V89 referral_commission_rule, referral_commission_ledger /api/referral-commissions/*
정착지원금 (SETTLING_SUPPORT) V90 settling_support_rule, settling_support_ledger /api/settling-supports/*
인정실적+환산보험료 (RECOGNIZED_PERF) V91 product_conversion_rate, recognized_performance_ledger /api/recognized-performances/*
시상 (AWARD) V92 award_rule, award_ledger /api/awards/*
생보운영지원수수료 (LIFE_OP_SUPPORT) V93 life_support_rule, life_support_ledger /api/life-supports/*
지점장수당 (BRANCH_MGR_ALLOWANCE) V94 branch_mgr_allowance_rule, branch_mgr_allowance_ledger /api/branch-allowances/*
메뉴 7개 V95 GRP_COMMISSION 하위 sort 80~140
공통코드 5그룹 V96 INCOME_STATUS/REFERRAL_TYPE/CALC_TYPE/SUPPORT_STATUS/AWARD_TYPE

통합 검증 (2026-05-31):

  • ./gradlew build -x test BUILD SUCCESSFUL
  • Flyway V88~V96 9건 모두 success=True, 운영 DB schema v96
  • 신규 14테이블 전부 존재 확인, 신규 메뉴 7건 등록 확인
  • 기존 smoke_allmenus.py 67엔드포인트: PASS=66 FAIL=0 SKIP=1(settle/summary 필수파라미터, 기존 동일)
  • P7 신규 8엔드포인트(인정실적은 /rates+/ledgers 분리): PASS=8 FAIL=0
  • 주의: /api/recognized-performances/rules 는 없음. D4는 /rates(환산율)와 /ledgers(원장) 두 경로로 분리됨.

3-16. 계약 승계 (contract_succession, V86 — 완료, 2026-05-29 라이브 적용)

설계사 해촉/이관 시 보험계약의 유지수수료(maintain) 귀속 설계사를 승계월 기준 후임으로 전환. 모집수수료(1회차)는 원 모집설계사 귀속 유지 — 한국 고아계약 승계 표준. 후임 설계사는 본인 등급의 payout 비율로 수령.

레이어 산출물
DB (V86) contract_succession(contract_id/from_agent_id/to_agent_id/succession_date/reason/status) + 인덱스 3 + 메뉴 CONTRACT_SUCCESSION(GRP_PRD, path /contracts/successions) + 권한 READ/CREATE/UPDATE/DELETE + 역할부여(SUPER_ADMIN·ADMIN 전체 / MANAGER READ) + 공통코드 SUCCESSION_REASON·SUCCESSION_STATUS + 테스트데이터 1건
core ContractSuccession VO/Resp/SaveReq/SearchParam + Mapper(.java/.xml). 핵심 selectEffectiveSuccessions(settleMonth) = 계약별 최근 ACTIVE 승계 1건(DISTINCT ON, TO_CHAR(succession_date,'YYYYMM') <= settleMonth)
api ContractSuccessionService/Controller (/api/contract-successions) CRUD + /{id}/cancel. from==to 가드(E411)
batch CalcMaintainStep: effective 승계 Map 사전로드(N+1 회피) → effectiveAgentId = getOrDefault(contractId, c.agentId)CommissionCalculator.calcMaintainLedger(c, month, year, effectiveAgentId) 오버로드(후임 등급 payout + led.setAgentId(effectiveAgentId)). AggregateStep 무변경 — ledger의 agent_id로 집계하므로 후임 귀속 자동 전파
frontend api/succession.ts + pages/contracts/ContractSuccessions.tsx + App.tsx 라우트 contracts/successions

핵심 산출물 커밋: bd5ff4e.

부수 버그 수정 (커밋 4dbc688, 승계 무관):

  • AgentDeductionMapper.updateStatusBatch 가 인터페이스 선언만 있고 XML 구문 누락 → AggregateStep(Step8) 이 PENDING 차감 일괄 APPLIED 전이 시 Invalid bound statement (not found) 로 aggregate 전체 FAILED 되던 잠복 버그(P0-2부터 존재). updateStatus 동일 패턴 + foreach IN 으로 보강. e2e 도중 발견.

통합 검증 (2026-05-29):

  • 5모듈 compileJava BUILD SUCCESSFUL + ga-frontend tsc 0 오류
  • Flyway V86 자동 적용 → 운영 DB schema v86
  • API CRUD + cancel + from==to 가드(E411) 라이브 PASS
  • 배치 풀잡 e2e: ga-batch 8081 기동, POST /internal/jobs/monthly-settlement(JWT 필요 — ga-api admin 토큰 재사용). 테스트 승계(계약59 agent39→agent2, 2026-01-01) 등록 후 202605 정산 실행:
    • maintain_ledger 계약59 = agent2(후임) / 5520(후임 등급 재계산, 원래 agent39/19320)
    • settle_master agent2 maintain = 142700 = ledger 합 / agent39 = 191100 = ledger 합
    • 무결성 ledger 총합 == settle_master maintain 총합 = 17,556,135 정확 일치
    • (202604는 전 행 status=CONFIRMED → upsert 불변 가드 WHERE status NOT IN ('CONFIRMED','PAID') 로 settle_master 미갱신 = 정상 동작)
  • e2e 테스트 DB 부산물(승계 레코드 / 202605 정산데이터 / 회계분개 / 차감 전이 / batch_job_log)은 단일 트랜잭션으로 전부 롤백 완료, pre-e2e 상태 복원 검증.
  • 주의: AccountingEntryGenerateStep(Step8b)은 멱등이 아님 — 동일 월 재실행 시 분개 중복 INSERT(주석상 existsByPaymentId 중복방어 언급되나 코드 미구현). 동일 월 재정산 전 기존 분개 정리 필요.

3-17. 테스트 환경 — SqlSessionFactory 문제 해결 (2026-05-29, 완료)

증상: ./gradlew :ga-api:test 시 Spring 컨텍스트 로딩 실패 — Error creating bean 'externalCallLogMapper' ... Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required.

원인:

  • application-test.yml 이 MybatisAutoConfiguration / DataSourceAutoConfiguration 을 제외 → 컨텍스트에 SqlSessionFactory 없음.
  • 그러나 MyBatisConfig@MapperScan(basePackages = "com.ga") 가 모든 @Mapper 의 MapperFactoryBean 을 생성, 이들은 SqlSessionFactory 를 요구.
  • AbstractControllerTest 는 매퍼를 하나씩 @MockBean 으로 막아 회피했으나, 이후 추가된 매퍼(ExternalCallLog/Succession/AccountingEntry 등)가 목록에 누락 → 실제 MapperFactoryBean 생성 시도 → 깨짐. 매퍼 추가될 때마다 목록 갱신해야 하는 취약 구조.
  • (부수) SettleControllerTest 가 구버전 hold(id, reason) 2인자 stub 사용 → 현재 hold(id, reason, reasonCode) 3인자와 불일치로 testCompile 실패.

해결:

  • TestMyBatisConfig(@TestConfiguration) 신규 — 미접속 PGSimpleDataSource + SqlSessionFactory 한 쌍 제공. 모든 MapperFactoryBean 이 생성되므로 매퍼 목록 갱신 불필요. 컨트롤러 테스트는 Service 를 mock 하므로 실제 쿼리 미실행(연결 안 함).
  • AbstractControllerTest @ImportTestMyBatisConfig 추가.
  • SettleControllerTest hold stub 3인자로 정정.

검증: ./gradlew test --rerun-tasksBUILD SUCCESSFUL, 112건 PASS (ga-common 57 / ga-api 36 / ga-batch 19, 실패·에러 0). DB 무관(미접속 DataSource).

3-9. P5-W3 메뉴/권한 전수 정합 보강 (V74 — 완료, 2026-05-21 라이브 적용)

항목 상태
AUDIT_P5W3.md §4 갭 리포트 작성 완료 (2026-05-17, infra)
V74 SQL 작성 완료 (2026-05-17, dba)
V74 정적검증 (PL) 완료 (멱등·ON CONFLICT·스코프 확인)
4모듈 compileJava PASS (BUILD SUCCESSFUL)
ga-frontend npm run build PASS (TS 오류 0)
운영 DB 접속·Flyway V74 적용 완료 (2026-05-21, ga-api 재기동 시 자동 적용)
스모크 검증 완료 (verify_v74.py 20/20 PASS)

갭 요약 (총 27건):

  • (a) menu 없음: 0건
  • (b) menu_permission 없음: 3건 (NOTICE/DELETE, SYSTEM_DATA_DICT/DELETE, TERM_SETTLE/APPROVE)
  • (c) grant 없음: 6건 (위 3 perm × SUPER_ADMIN+ADMIN, ADMIN/SYSTEM_* 7건은 보안상 의도적 제외)
  • route/menu_path 불일치: 11건 (V74에서 UPDATE 처리)

갭 재검증 (V74 적용 후): python verify_v74.py20/20 PASS 확인 완료 (2026-05-21).

3-10. P5-W3 본론 — 결산 / 이상치·예측 / 연말명세서 (V75~V78, 2026-05-21 완료)

항목 마이그레이션 상태
accounting_close + account_balance_snapshot (회계 결산) V75 완료
retention_anomaly_alert + forecast_kpi_monthly (이상치·예측 KPI) V76 완료
year_end_statement (연말 지급명세서) V77 완료
P5-W3 본론 메뉴 4개 + 권한 + 공통코드 7그룹 + 테스트데이터 V78 완료

핵심 산출물:

  • AccountingCloseService/Controller (/api/accounting-closes) — DRAFT 생성 / 마감 실행(분개 채번+잔액 스냅샷) / 재오픈
  • RetentionAnomalyAlertService/Controller (/api/retention-anomalies) — 13M/25M 이상 탐지 / 검토 처리
  • ForecastKpiMonthlyService/Controller (/api/forecast-kpi) — 이동평균(SMA3/6/NAIVE) 차월 예측
  • YearEndStatementService/Controller (/api/year-end-statements) — 연 사업소득 명세서 재집계/발급/발송/엑셀
  • 배치: BatchConfig 에 Step 11(WithholdingTaxQuarterly) / Step 12(VatReportQuarterly) 연결
  • 프론트: AccountingCloseList / YearEndStatementList / RetentionAnomalyList / ForecastKpiList 4화면 + API 모듈 4개 + App.tsx 라우트 4개
  • 공통: usePermission/PermissionButtonEXECUTE 권한 누락 보강 (canExecute)

통합 검증 (2026-05-21):

  • Flyway: 운영 DB V74~V78 자동 적용 → 현재 v78
  • 4모듈 compileJava + ga-frontend npm run build PASS (TS 오류 0)
  • 스모크: 신규 4 + 회귀 4 = GET 8/8 PASS, 쓰기(regenerate/detect/draft) 4/4 PASS
  • 보정: YearEndStatementMapper.xml 의 a.agent_code 미존재 컬럼 참조 제거 (agent 테이블에 agent_code 없음)

5. 재개 절차 (다음 세션)

  1. Claude Code 시작 시 cwd 확인: C:\Users\kyu\Desktop\ga_pro
  2. HANDOFF.md 읽기: 본 문서 §3-16(계약 승계) + §3-15(P6 후속) + 최근 git log
  3. 운영 DB 상태: V96 적용 완료 (P7 incar 7도메인). ga-api 8082 가동. ga-frontend 3000 / ga-mobile-pwa 3001 은 필요 시 기동. origin/main 동기화 완료(HEAD 4dbc688, 2026-05-29 push). P7 코드(V88~V96)는 로컬 커밋 아직 없음.
  4. 재기동이 필요한 경우:
    • cd ga-commission-system && nohup ./gradlew :ga-api:bootRun --args='--spring.profiles.active=trading_ai' > ../ga-api-bootrun.log 2>&1 & disown
    • cd ga-commission-system/ga-frontend && npm run dev (3000)
    • cd ga-commission-system/ga-mobile-pwa && npm run dev (3001)
    • 정산 배치: nohup ./gradlew :ga-batch:bootRun --args='--spring.profiles.active=trading_ai' > ../ga-batch-bootrun.log 2>&1 & (8081). 트리거 POST :8081/internal/jobs/monthly-settlement {"settleMonth":"yyyyMM"}JWT 필요(ga-api /api/auth/login admin 토큰 재사용). 동기 실행.
  5. 테스트 계정 / DB: admin/admin1234!(SUPER_ADMIN), agent01/agent1234!(userId=4·agentId=1·AGENT). 로그인 필드명 loginId. DB: psql17 192.168.0.60:55432/trading_ai user kyu/pw 7895123, SET search_path TO ga.
  6. 사용자 결정 필요 항목 (외부 SDK 차단 사유):
    • KFTC 펌뱅킹 API 계약·인증 정보 → KftcBankTransferAdapter 본체 구현
    • NHN Toast / 카카오 비즈채널 계정 + 템플릿 코드 → ToastMessageAdapter 본체
    • 푸시 알림 채널 (FCM 등) 결정 → PWA 통합

6. PL 가이드 (다음 세션 유의사항)

  • 시간차 중복 메시지: 팀원이 SendMessage로 같은 보고를 2~3번 보내는 경우 있음. 짧게 acknowledge만.
  • idle 알림: 팀원이 작업 끝낼 때마다 idle 알림 보냄. 정상. 액션 필요 없음.
  • 책임 경계: dba=Flyway/SQL, core=VO/Mapper/Enum, api=Service/Controller. api-developer가 가끔 ga-core Mapper에 메서드 추가하는데 작은 변경이면 통과 가능, 큰 변경은 core-architect에 위임.
  • TODO 잔존: api-developer가 부족한 ga-core 메서드 발견 시 TODO로 남기고 보고 → PL이 후속 보강 디스패치. 같은 단계에서 멈추지 말고 흐름 유지.
  • 자동 정책:
    • 각 단계 완료 후 자동 커밋 (push 안 함)
    • 빌드/테스트 실패 시 담당 팀원에 재지시
    • 컴파일은 PL이 직접 검증. 테스트는 SqlSessionFactory 환경 문제 해결 후 ./gradlew test 112건 PASS (§3-17)

7. 표준 가정 (도메인 작업 진행 시 채택한 값)

도메인 룰은 한국 보험업감독규정 표준 가정을 외부화 가능한 구조로 등록. 실제 회사 정책과 다르면 system_config / regulatory_limit / installment_ratio / chargeback_grade의 데이터만 수정하면 됨.

항목 표준값 근거
사업소득 원천징수 3.3% 한국 표준
기타소득 원천징수 8.8% 한국 표준
지방소득세 원천세의 10% 한국 표준
부가세 10% 한국 표준
1200% 룰 총 한도 1200% 보험업감독규정 제5-15조의5
1차년 한도 35% 2024년 기준
분급 연한 7년 2024년 기준
분급 비율 35/25/15/10/7/5/3 (합 100%) 가정값
차등 환수 112: 100%, 1324: 50%, 2536: 25%, 3784: 10%, 85~: 0% 가정값

가정값과 실제가 다르면 운영 데이터 수정으로 해결 가능. 코드 변경 불필요.