2026-05-12 22:16:31 +09:00
2026-05-12 22:16:31 +09:00

GA 수수료 정산 솔루션

법인보험대리점(GA) 수수료 정산 시스템 — Multi-module Spring Boot + React.

기술 스택

영역 기술
Backend Java 17, Spring Boot 3.2, Spring Security, Spring Batch
ORM MyBatis 3 + PageHelper
DB PostgreSQL 16
Cache Redis 7
Build Gradle 멀티모듈
Frontend React 18 + Vite + TypeScript + Ant Design 5 + AG Grid + React Query + Zustand
Infra Docker Compose, Nginx

모듈 구조

ga-commission-system/
├── ga-common/    # 공통 프레임워크 (인증/권한/로그/엑셀/공통코드/메뉴)
├── ga-core/      # 도메인 코어 (VO, Mapper, SQL)
├── ga-api/       # REST API (포트 8080)
├── ga-batch/     # Spring Batch (포트 8081)
├── ga-admin/     # 관리자 라이브러리
└── ga-frontend/  # React SPA (포트 3000)

🚀 실행 가이드

환경 요구사항

  • Java 17+ (java -version 확인)
  • Node.js 18+ + npm
  • PostgreSQL 14+ (운영: 192.168.0.60:55432/trading_ai/ga schema)
  • Redis 7+ (선택 — 공통코드/세션 캐시)
  • Gradle (또는 IDE: IntelliJ IDEA 권장 — 자동으로 Gradle wrapper 생성)

시나리오 A — 운영 DB(trading_ai/ga) 에 연결해서 실행 권장

이미 V1~V17 마이그레이션 + 테스트 데이터(보험사 5 / 상품 20 / 설계사 50 / 계약 200)가 192.168.0.60:55432/trading_ai/ga 스키마에 적용되어 있습니다.

1) Backend (ga-api) 실행

# 1-1. Gradle wrapper 가 없다면 한 번 생성 (Gradle 설치 필요)
gradle wrapper --gradle-version 8.6

# 1-2. 빌드
./gradlew :ga-api:bootJar

# 1-3. 실행 (trading_ai 프로파일 — 운영 DB 연결)
./gradlew :ga-api:bootRun --args='--spring.profiles.active=trading_ai'

# 또는 환경변수로 비밀번호/Redis 호스트 오버라이드
export DB_PASSWORD=your-password
export REDIS_HOST=localhost
java -jar ga-api/build/libs/ga-api.jar --spring.profiles.active=trading_ai

확인:

2) Backend (ga-batch) 실행 — 정산 배치 트리거 처리

./gradlew :ga-batch:bootRun --args='--spring.profiles.active=trading_ai'
# 또는
java -jar ga-batch/build/libs/ga-batch.jar --spring.profiles.active=trading_ai

기본 포트 8081. ga-api 가 /api/batch/settlement/run 호출 시 batch_job_log 에 REQUESTED 상태로 기록되며, ga-batch 가 이를 폴링/직접 호출로 실행합니다.

3) Frontend 실행

cd ga-frontend
npm install
npm run dev    # http://localhost:3000 (Vite dev server)

기본적으로 /api/* 요청은 Vite 프록시로 http://localhost:8080 으로 전달됩니다.

4) 로그인 & 동작 확인

URL : http://localhost:3000
ID  : admin
PW  : admin1234!

로그인 후 사이드바 메뉴 — 좌측에 동적으로 메뉴가 그려져야 합니다. 주요 화면:

메뉴 경로 설명
대시보드 / 정산 요약 카드
설계사 관리 /org/agents 50명 더미 + 검색/등록/수정/엑셀
계약 관리 /contracts 200건 더미 + 상태/보험사 필터
모집수수료 원장 /ledger/recruit AG Grid 호환 테이블 + 엑셀 다운로드
예외금액 원장 /ledger/exception 승인/반려
정산 결과 /settle 월 요약 + 확정/보류
정산 배치 /settle/batch 배치 실행 + 진행률 폴링
지급 관리 /payments 지급 상태 추적
사용자 관리 /system/users 비번 초기화/잠금 해제
역할/권한 /system/roles 권한 매트릭스 (메뉴 × 6권한)
공통코드 /system/codes 그룹별 코드 조회

시나리오 B — 로컬 PostgreSQL 로 처음부터 (개발용)

# 1) PostgreSQL + Redis 기동 (Docker)
docker compose up -d postgres redis

# 2) ga-api 실행 (Flyway 가 자동으로 V1~V17 마이그레이션)
./gradlew :ga-api:bootRun
# 기본 프로파일은 application.yml — localhost:5432/ga 에 연결

# 3) ga-batch 실행
./gradlew :ga-batch:bootRun

# 4) 프론트
cd ga-frontend && npm install && npm run dev

application.yml 의 기본 DB:

url: jdbc:postgresql://localhost:5432/ga
username: ga
password: ga

시나리오 C — Docker Compose 로 전체 기동

docker compose up -d --build
# postgres + redis + api + batch + frontend 모두 컨테이너로 기동
# - 프론트: http://localhost:3000
# - API:    http://localhost:8080/swagger-ui.html
# - Batch:  http://localhost:8081 (내부 통신용)

docker-compose.yml 환경변수:

  • JWT_SECRET — JWT 서명 키 (256bit 이상)
  • ENCRYPT_KEY — AES-256 키 (32바이트)

DB 마이그레이션 직접 실행 (스크립트)

저장소 루트의 migrate.py (이 파일은 ga-commission-system 외부에 있음) — Python + pg8000 으로 V1~V17 SQL을 임의 DB에 일괄 적용:

pip install pg8000
python migrate.py
# host/port/db/user/schema 는 파일 상단 상수로 수정

ga 스키마가 이미 있으면 idempotent guard 로 멈춥니다. 완전 재설치는 DROP SCHEMA ga CASCADE; CREATE SCHEMA ga; 후 재실행.


🧪 데이터 확인 (운영 DB 기준)

테이블 건수
insurance_company 5 (삼성/한화/교보/디비/KB)
product 20 (보험사 × 4상품)
organization 7 (본부 1 / 지점 3 / 팀 3)
agent 50 (GM 3 / DM 3 / SMGR 6 / SFC 12 / FC 26)
contract 200 (ACTIVE 190 / LAPSE 10)
commission_rate 100
payout_rule 120
override_rule 10
chargeback_rule 4
common_code (그룹/상세) 18 / 42
menu / menu_permission 33 / 150
role / role_menu_permission 4 / 323

🔧 트러블슈팅

Q. ./gradlew 가 없다고 함

gradle wrapper --gradle-version 8.6 로 한번 생성하거나, IntelliJ 로 프로젝트 열면 자동 생성.

Q. ga-api 부팅 시 Failed to determine a suitable driver class

JDBC URL 또는 자격증명 누락. --spring.profiles.active=trading_ai 가 적용됐는지 확인.

Q. NoSuchKey: ga (Flyway)

운영 DB 의 ga 스키마를 인식하려면 currentSchema=ga 가 URL 에 있어야 함. application-trading_ai.yml 에서 이미 설정되어 있음.

Q. 프론트에서 401 / "토큰이 만료되었습니다"

localStorageaccessToken 이 만료됨. /login 으로 다시 로그인.

Q. AG Grid 가 빈 화면으로 보임

@ag-grid-community/styles/ag-theme-quartz.css import 가 잘 됐는지 확인.

Q. 비밀번호 변경

-- BCrypt 해시 생성 후 직접 UPDATE
UPDATE ga.users SET password = '$2b$10$...' WHERE login_id = 'admin';

또는 사용자 관리 화면 → "비번초기화" 액션.


📋 주요 기능

백엔드

  • 인증/권한: JWT (HS256), 메뉴×권한 RBAC, BCrypt 비밀번호
  • 공통코드: Redis 캐시, 시스템코드 잠금, REST CRUD
  • 메뉴: 트리 구조, 사용자별 동적 메뉴 + 권한 매트릭스
  • 엑셀: SXSSF+Cursor 다운로드(100만건), SAX+배치 업로드(70만건)
  • 로그: 로그인/API/데이터변경 자동 기록 (비동기)
  • 암호화: AES-256 CBC (resident_no, account_no 자동)

도메인

  • 조직: 트리 구조, CTE 재귀, 인사 이력
  • 계약: 보험사/상품/계약 관리
  • 수수료 규정: 보험사율, 지급율, 오버라이드, 환수, 예외코드
  • 원장: 모집/유지/예외 (PageHelper + AG Grid)
  • 정산: 8 Step 배치 (수신→변환→대사→모집→유지→환수/예외→오버라이드→집계)
  • 지급: 이체파일 생성, 상태 추적

정산 배치 8 Step

  1. receiveData — 보험사별 DataReceiver → raw_commission_data 적재
  2. transformData — MappingEngine 으로 raw → 표준 원장 (TRIM/UPPER/DATE/DIVIDE/CODE_MAP)
  3. reconcile — 보험사 통보액 vs 시스템 계산액 대사
  4. calcRecruit — 모집수수료 (계약시점 commission_rate/payout_rule 적용)
  5. calcMaintain — 유지수수료 (납입주기 매칭, 1~5년차)
  6. chargebackException — 실효 계약 환수 자동생성
  7. calcOverride — 조직 트리 상향 탐색 → 상위 직급에 분배
  8. aggregate — 설계사별 통합 집계 → settle_master UPSERT (3.3% 세금)

🛠 신입 개발자 표준 패턴 (5분 컷)

// 1) VO (XxxVO + XxxResp + XxxSaveReq + XxxSearchParam)
@Data
public class FooSearchParam extends SearchParam { }

// 2) Mapper Interface + XML
@Mapper
public interface FooMapper {
    List<FooResp> selectList(FooSearchParam p);
}

// 3) Service
@Service @RequiredArgsConstructor
public class FooService {
    private final FooMapper mapper;
    public PageResponse<FooResp> list(FooSearchParam p) {
        p.startPage();
        return PageResponse.of(mapper.selectList(p));
    }
}

// 4) Controller
@RestController @RequestMapping("/api/foos") @RequiredArgsConstructor
public class FooController {
    private final FooService service;
    @GetMapping
    @RequirePermission(menu = "FOO", perm = "READ")
    public ApiResponse<PageResponse<FooResp>> list(FooSearchParam p) {
        return ApiResponse.ok(service.list(p));
    }
}

🔒 보안

  • JWT (HS256) — 액세스 2시간, 리프레시 7일
  • AES-256 CBC 암호화 (EncryptTypeHandler 자동 적용)
  • RBAC — role_menu_permission 매트릭스
  • @DataChangeLog — 데이터 변경 이력 AOP 자동
  • API 접근 로그 — 비동기 저장 (api_access_log)

🗄 DB 마이그레이션

버전 내용
V1~V8 도메인 스키마 + 초기 데이터 (직급, 예외코드)
V9~V12 공통코드/메뉴/시스템관리 + 초기 데이터
V13 인덱스 최적화
V14 파티셔닝 helper 함수
V15 통계/리포트 뷰 (v_agent_monthly_summary 등)
V16 관리자 계정 + 메뉴 상세 + 권한 매트릭스
V17 테스트 데이터 (보험사 5 / 상품 20 / 설계사 50 / 계약 200 + 수수료 규정)

진행 상황

  • 1단계 — 프로젝트 뼈대 + DB (V1~V17)
  • ga-common — 공통 프레임워크 일체
  • ga-core — 22개 테이블 VO/Mapper/XML
  • ga-api — Auth + 14개 도메인 컨트롤러
  • ga-batch — MonthlySettlementJob 8 Step (실제 동작 로직 포함)
  • Docker — docker-compose + multi-stage Dockerfile + nginx
  • ga-frontend — 13개 페이지 (대시보드/설계사/계약/원장/정산/지급/배치/시스템관리)
  • 운영 DB 적용 — 192.168.0.60:55432/trading_ai/ga (V1~V17 + 테스트 데이터)
  • 단위/통합 테스트 — 미작성
  • MappingEngine 정밀화 — 일부 변환 규칙은 단순화된 형태

🔮 향후 확장

  • 프론트 페이지 추가: menu 테이블 등록 → React 페이지 구현 → 동적 라우팅 적용
  • 신규 보험사: company_profile + company_field_mapping 등록 + 필요 시 DataReceiver 구현
  • 신규 정산 규정: commission_rate/payout_rule/override_rule/chargeback_ruleeffective_from 만 다른 버전으로 추가 (versioning)
  • 이체파일 포맷: 은행별 EBCDIC/CSV 출력 어댑터 추가

📡 외부 시스템 연결 정보 (요약)

항목
운영 DB 호스트 192.168.0.60
운영 DB 포트 55432
운영 DB 데이터베이스 trading_ai
운영 DB 스키마 ga
운영 DB 사용자 kyu
Git 원격 https://gl.kyleyang.co.kr/kyu/ga-commission-system
초기 관리자 admin / admin1234!
S
Description
GA Commission Settlement Solution
Readme 3.5 MiB
Languages
Java 61.3%
TypeScript 37.6%
CSS 0.6%
Python 0.3%