Files
GA Pro 035175e895 docs: Agent Teams 실행 가이드 + 8개 도메인 spec 추가
00_PROMPTS.md  — Claude Code 실행 프롬프트 1~6
01_DB_SCHEMA.md — V1~V17 Flyway 스키마 + 프로젝트 뼈대
02_COMMON_SPEC.md — ga-common 공통 프레임워크 (모델/예외/MyBatis/AOP/엑셀/공통컴포넌트)
03_CORE_SPEC.md — ga-core VO 설계 원칙 + Mapper 패턴
04_BATCH_SPEC.md — ga-batch 정산 8 Step + DataReceiver/MappingEngine
05_API_SPEC.md — ga-api Controller/Service 표준 패턴 + 엔드포인트 목록
06_FRONTEND_SPEC.md — ga-frontend 13개 화면 + 공통 컴포넌트 사용법
07_INFRA_SPEC.md — Docker/Nginx + README + DEVELOPER_GUIDE
08_DBA_SPEC.md — V13~V15 인덱스/파티셔닝/뷰

실제 구현된 ga-commission-system 기준으로 작성. 코드는 변경 없음.

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

11 KiB
Raw Permalink Blame History

05_API_SPEC — ga-api (REST API)

모든 Controller / Service 가 동일한 패턴으로 작성된다. 신입이 복붙 후 테이블명/필드명만 바꾸면 새 화면이 완성되도록.

패키지 구조

ga-api/src/main/java/com/ga/api/
├── GaApiApplication.java                       # @SpringBootApplication(scanBasePackages = "com.ga")
├── auth/
│   ├── AuthController.java                     # /api/auth/*
│   └── AuthService.java
├── controller/
│   ├── batch/    BatchTriggerController.java
│   ├── ledger/   LedgerController.java          # 모집/유지/예외 통합
│   ├── org/      OrganizationController.java, AgentController.java
│   ├── product/  CompanyController.java, ProductController.java, ContractController.java
│   ├── receive/  ReceiveController.java
│   ├── rule/     RuleController.java
│   ├── settle/   SettleController.java, PaymentController.java
│   └── system/   UserController.java, RoleController.java
└── service/
    └── (controller 미러 구조 — XxxService.java)

build.gradle:

apply plugin: 'org.springframework.boot'
bootJar { archiveFileName = 'ga-api.jar'; mainClass = 'com.ga.api.GaApiApplication' }
jar.enabled = false
dependencies {
    implementation project(':ga-core')
}

application.yml01_DB_SCHEMA.md 의 yml + server.port: 8080.


1. 표준 Controller 패턴

@RestController
@RequestMapping("/api/agents")
@Tag(name = "설계사 관리")
@RequiredArgsConstructor
public class AgentController {

    private final AgentService agentService;

    @GetMapping
    @RequirePermission(menu = "ORG_AGENT", perm = "READ")
    public ApiResponse<PageResponse<AgentResp>> list(AgentSearchParam param) {
        return ApiResponse.ok(agentService.list(param));
    }

    @GetMapping("/{id}")
    @RequirePermission(menu = "ORG_AGENT", perm = "READ")
    public ApiResponse<AgentResp> detail(@PathVariable Long id) {
        return ApiResponse.ok(agentService.detail(id));
    }

    @PostMapping
    @RequirePermission(menu = "ORG_AGENT", perm = "CREATE")
    @DataChangeLog(menu = "ORG_AGENT", table = "agent")
    public ApiResponse<Long> create(@Valid @RequestBody AgentSaveReq req) {
        return ApiResponse.ok(agentService.create(req));
    }

    @PutMapping("/{id}")
    @RequirePermission(menu = "ORG_AGENT", perm = "UPDATE")
    @DataChangeLog(menu = "ORG_AGENT", table = "agent")
    public ApiResponse<Void> update(@PathVariable Long id, @Valid @RequestBody AgentSaveReq req) {
        agentService.update(id, req); return ApiResponse.ok();
    }

    @PatchMapping("/{id}/status")
    @RequirePermission(menu = "ORG_AGENT", perm = "UPDATE")
    @DataChangeLog(menu = "ORG_AGENT", table = "agent")
    public ApiResponse<Void> updateStatus(@PathVariable Long id, @RequestParam String status) {
        agentService.updateStatus(id, status); return ApiResponse.ok();
    }

    @GetMapping("/export")
    @RequirePermission(menu = "ORG_AGENT", perm = "EXPORT")
    public void export(AgentSearchParam p, HttpServletResponse res) {
        agentService.exportExcel(p, res);
    }
}

규칙:

  1. 클래스 어노테이션: @RestController @RequestMapping @Tag @RequiredArgsConstructor
  2. 메서드 어노테이션: @RequirePermission + (CUD 일 때) @DataChangeLog
  3. 응답: ApiResponse<T> / ApiResponse<PageResponse<T>> / ApiResponse<Void>
  4. 요청 바디: @Valid @RequestBody XxxSaveReq
  5. 검색: XxxSearchParam (쿼리 파라미터로 자동 바인딩)
  6. PathVariable: id 는 Long
  7. 엑셀 export 는 void + HttpServletResponse 직접 사용

2. 표준 Service 패턴

@Service @RequiredArgsConstructor
public class AgentService {
    private final AgentMapper mapper;
    private final CommonCodeService codeService;
    private final AgentMapstruct mapstruct;
    private final ExcelService excelService;

    public PageResponse<AgentResp> list(AgentSearchParam param) {
        param.startPage();
        return PageResponse.of(mapper.selectList(param));
    }

    public AgentResp detail(Long id) {
        AgentResp r = mapper.selectDetailById(id);
        if (r == null) throw new BizException(ErrorCode.NOT_FOUND);
        return r;
    }

    @Transactional
    public Long create(AgentSaveReq req) {
        if (req.getLicenseNo() != null && mapper.existsByLicenseNo(req.getLicenseNo()) > 0)
            throw new BizException(ErrorCode.DUPLICATE_DATA);
        AgentVO vo = mapstruct.toVO(req);
        vo.setStatus("ACTIVE");
        mapper.insert(vo);
        return vo.getAgentId();
    }

    @Transactional
    public void update(Long id, AgentSaveReq req) {
        AgentVO vo = mapper.selectById(id);
        if (vo == null) throw new BizException(ErrorCode.NOT_FOUND);
        mapstruct.update(vo, req);
        mapper.update(vo);
    }

    @Transactional
    public void updateStatus(Long id, String status) {
        codeService.validateCode("AGENT_STATUS", status);
        mapper.updateStatus(id, status);
    }

    public void exportExcel(AgentSearchParam p, HttpServletResponse res) {
        excelService.exportLargeExcel("설계사목록", AgentExcelVO.class,
                () -> mapper.selectCursorForExcel(p), res);
    }
}

규칙:

  1. Service 는 Mapper + 다른 Service 만 의존 (Controller 직접 호출 X)
  2. CUD 메서드는 @Transactional
  3. 공통코드 검증은 codeService.validateCode(group, code)
  4. 중복 검증 → DUPLICATE_DATA, 미존재 → NOT_FOUND
  5. 엑셀은 excelService.exportLargeExcel(fileName, ExcelVO.class, cursorSupplier, response)

3. API 엔드포인트 목록 (현재 구현된 것)

인증 (AuthController/api/auth)

  • POST /login{loginId, password}{accessToken, refreshToken, user} + login_log 적재
  • POST /refresh — refresh 토큰으로 access 갱신
  • POST /logout — 클라이언트 토큰 폐기 (서버는 로그만)
  • GET /me — 현재 사용자 + 역할 + 메뉴 트리

조직 / 설계사

  • OrganizationController /api/orgs
    • GET /tree — 트리 (CTE 재귀)
    • GET /{id} / POST / PUT /{id} / DELETE /{id}
  • AgentController /api/agents
    • GET (검색+페이징) / GET /{id} / POST / PUT /{id}
    • PATCH /{id}/status / PATCH /{id}/transfer (조직/직급 변경)
    • GET /{id}/history (조직/직급 이력)
    • GET /export (엑셀 다운로드)

보험상품 / 계약

  • CompanyController /api/companies — 보험사 5 건 CRUD
  • ProductController /api/products — 보험사 × 상품
  • ContractController /api/contracts — 검색/CRUD/엑셀, policy_no 중복검증

수수료 규정

  • RuleController /api/rules
    • GET /commission-rates, POST, PUT /{id}, DELETE /{id}
    • GET /payout-rules, ... 동일 패턴
    • GET /override-rules, GET /chargeback-rules, GET /exception-codes

데이터 수신 / 매핑

  • ReceiveController /api/receive
    • POST /upload?companyCode=&dataType= — multipart 업로드 → raw_commission_data 적재
    • POST /process?companyCode=&settleMonth=MappingEngine 으로 변환 호출
    • GET /status?companyCode=&settleMonth= — 진행 현황
    • GET /errors?companyCode=&resolveStatus= — 에러 목록
    • PATCH /errors/{errorId}/resolve — 에러 해결 처리
    • 매핑 관리: GET /mapping/{companyCode}/fields, POST/PUT/DELETE

원장 (LedgerController /api/ledger — 통합)

  • GET /recruit (검색+페이징) / GET /recruit/export (엑셀)
  • GET /maintain, GET /exception — 동일 패턴
  • POST /exception — 예외금액 등록 (수동)
  • PATCH /exception/{id}/approve — 승인 (@RequirePermission perm=APPROVE)
  • PATCH /exception/{id}/reject — 반려

정산

  • SettleController /api/settle
    • GET (월별 검색) / GET /{id} / GET /summary?settleMonth=
    • PATCH /{id}/confirm (@RequirePermission APPROVE) — @Transactional, version 체크 → RULE_VERSION_CONFLICT 가능
    • PATCH /{id}/hold {reason}
  • PaymentController /api/payments
    • GET 검색 / POST 이체 생성 (settle_master.status='CONFIRMED' 인 것만)
    • POST /{id}/transfer-file 이체파일 생성
    • PATCH /{id}/success, PATCH /{id}/fail

배치 트리거 (BatchTriggerController /api/batch)

  • POST /settlement/run?settleMonth=&companyCode=batch_job_log 적재 + ga-batch 트리거
  • GET /settlement/status?jobLogId= — 진행 상태
  • POST /settlement/restart?jobLogId=&fromStep= — 재실행 (선택 구현)

시스템관리

  • UserController /api/system/users
    • GET / POST / PUT /{id} / DELETE /{id}
    • PATCH /{id}/reset-password — 임시 비밀번호 발급 (이메일/알림)
    • PATCH /{id}/unlock — 잠금 해제
    • PUT /{id}/roles — 역할 매핑
  • RoleController /api/system/roles
    • 역할 CRUD + GET /{id}/permissions (메뉴 × 권한 매트릭스)
    • PUT /{id}/permissions (전체 매트릭스 갱신)

공통 (ga-common 에 위치하지만 ga-api 가 노출)

  • MenuController /api/menusGET /my, GET /tree, CRUD
  • CommonCodeController /api/common/codes — 그룹/상세 CRUD
  • FileController /api/files — 업로드/다운로드/목록
  • NotificationController /api/notifications — 목록/읽음
  • SystemConfigController /api/system/config — 시스템 설정
  • SystemLogController /api/system/logs — 로그인/API/변경 이력

4. 인증 / 권한 흐름

  1. 로그인: POST /api/auth/login → JWT 발급
  2. 헤더: 모든 요청에 Authorization: Bearer <token>
  3. JwtAuthFilter: SecurityContext 에 LoginUser 주입
  4. @RequirePermission: PermissionAspectrole_menu_permission 조회 → 없으면 FORBIDDEN
  5. @DataChangeLog: 변경 전/후 데이터 비교 후 data_change_log JSONB 비동기 적재
  6. ApiLogAspect: 모든 요청 api_access_log 비동기 적재 (민감정보 마스킹)

5. 응답 표준

성공

{
  "success": true, "code": "0000", "message": "OK",
  "data": { ... }, "timestamp": "2026-05-10T12:34:56"
}

페이징

{
  "success": true, "code": "0000",
  "data": {
    "list": [...], "pageNum": 1, "pageSize": 20, "total": 123, "pages": 7
  }
}

실패

{
  "success": false, "code": "B009", "message": "중복된 데이터입니다",
  "data": null, "timestamp": "..."
}

6. 테스트 (선택)

  • MockMvc 기반 Controller 테스트
    • @WebMvcTest(AgentController.class) + @MockBean AgentService
    • 인증/권한 검증 (403 응답)
    • 요청 바디 검증 (@Valid 실패 시 400)
  • WireMock 으로 ga-batch 호출 stub