# Handoff Note — 2026-05-14 (Round 2 완료) 다음 세션에서 이어가기 위한 핸드오프 문서. 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단계: 1~12개월 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): ```yaml 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 서버측 강제 필터) 권장 — 잔여 항목. ### 빌드 / 실행 ```bash 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.tsx` — `notificationApi.summary()` 한 번으로 3 카운트 동시 표시, 안 읽음 뱃지 - `NoticeList.tsx` — `Tabs` 로 `내 알림` / `공지사항` 두 탭. 알림 클릭 시 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(어댑터 모듈 + Mock + SDK 분기 + 재시도 + 추적 컬럼 + 호출 감사 이력 + **모바일 PWA 스캐폴드**) 완료. 잔여는 **외부 스펙 확정** 후 적용 가능한 항목. ### 운영 DB 마이그레이션 상태 운영 DB `192.168.0.60:55432/trading_ai/ga` 현재 **V85 적용 완료** (2026-05-24). V79~V85 Flyway 자동 적용 확인. ga-api 8082 가동 중. 단위/통합 테스트는 SqlSessionFactory 환경 설정 문제로 아직 미작성 상태입니다. ## 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.py` → **20/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`/`PermissionButton` 에 `EXECUTE` 권한 누락 보강 (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. 재개 절차 (다음 세션 — P6 실연동 라운드) 1. **Claude Code 시작 시 cwd 확인**: `C:\Users\kyu\Desktop\ga_pro` (현재 세션과 동일) 2. **Agent Teams 활성화 확인**: `.claude/settings.json` 커밋되어 있음 3. **HANDOFF.md 읽기**: 본 문서 + 최근 git log로 컨텍스트 확보 4. **운영 DB 상태**: **V85 적용 완료**. ga-api 8082 가동 중 (필요 시 `python verify_v74.py` / `smoke_v78.py` 재실행) 5. **권장 시작 지점 (외부 스펙 미정 시)**: - 모바일 PWA 후속: 백엔드 `/api/mobile/me/*` 전용 endpoint, 출금 신청 폼, 명세서 상세 - 펌뱅킹 SETTLED 동기화 폴러 (Mock 검증 → 실 SDK 통합 시 가치) - 알림 채널 SMS phone 매핑 보강 (현재 user.phone 미설정 시 SMS skip — phone 등록 화면) 6. **사용자 결정 필요 항목 (P6 실연동 차단 사유)**: - KFTC 펌뱅킹 API 계약·인증 정보 (실 SDK 통합 차단) - NHN Toast / 카카오 비즈채널 계정 + 템플릿 코드 (실 메시지 통합 차단) - 모바일 PWA 화면 우선순위 (잔여 27 빈 화면 중 어떤 것부터) - P2-1 마감 도메인의 마감 후 수정 차단 AOP 구현 방식 (전역 AOP vs Service-level 가드) ## 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)로 통과 안 되는 게 알려진 상태 ## 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%) | 가정값 | | 차등 환수 | 1~12: 100%, 13~24: 50%, 25~36: 25%, 37~84: 10%, 85~: 0% | 가정값 | 가정값과 실제가 다르면 운영 데이터 수정으로 해결 가능. 코드 변경 불필요.