035175e895
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>
11 KiB
11 KiB
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.yml 은 01_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);
}
}
규칙:
- 클래스 어노테이션:
@RestController @RequestMapping @Tag @RequiredArgsConstructor - 메서드 어노테이션:
@RequirePermission+ (CUD 일 때)@DataChangeLog - 응답:
ApiResponse<T>/ApiResponse<PageResponse<T>>/ApiResponse<Void> - 요청 바디:
@Valid @RequestBody XxxSaveReq - 검색:
XxxSearchParam(쿼리 파라미터로 자동 바인딩) - PathVariable: id 는
Long - 엑셀 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);
}
}
규칙:
- Service 는 Mapper + 다른 Service 만 의존 (Controller 직접 호출 X)
- CUD 메서드는
@Transactional - 공통코드 검증은
codeService.validateCode(group, code) - 중복 검증 →
DUPLICATE_DATA, 미존재 →NOT_FOUND - 엑셀은
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/orgsGET /tree— 트리 (CTE 재귀)GET /{id}/POST/PUT /{id}/DELETE /{id}
AgentController /api/agentsGET(검색+페이징) /GET /{id}/POST/PUT /{id}PATCH /{id}/status/PATCH /{id}/transfer(조직/직급 변경)GET /{id}/history(조직/직급 이력)GET /export(엑셀 다운로드)
보험상품 / 계약
CompanyController /api/companies— 보험사 5 건 CRUDProductController /api/products— 보험사 × 상품ContractController /api/contracts— 검색/CRUD/엑셀,policy_no중복검증
수수료 규정
RuleController /api/rulesGET /commission-rates,POST,PUT /{id},DELETE /{id}GET /payout-rules, ... 동일 패턴GET /override-rules,GET /chargeback-rules,GET /exception-codes
데이터 수신 / 매핑
ReceiveController /api/receivePOST /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/settleGET(월별 검색) /GET /{id}/GET /summary?settleMonth=PATCH /{id}/confirm(@RequirePermission APPROVE) —@Transactional,version체크 →RULE_VERSION_CONFLICT가능PATCH /{id}/hold{reason}
PaymentController /api/paymentsGET검색 /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/usersGET/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(전체 매트릭스 갱신)
- 역할 CRUD +
공통 (ga-common 에 위치하지만 ga-api 가 노출)
MenuController /api/menus—GET /my,GET /tree, CRUDCommonCodeController /api/common/codes— 그룹/상세 CRUDFileController /api/files— 업로드/다운로드/목록NotificationController /api/notifications— 목록/읽음SystemConfigController /api/system/config— 시스템 설정SystemLogController /api/system/logs— 로그인/API/변경 이력
4. 인증 / 권한 흐름
- 로그인:
POST /api/auth/login→ JWT 발급 - 헤더: 모든 요청에
Authorization: Bearer <token> JwtAuthFilter: SecurityContext 에LoginUser주입@RequirePermission:PermissionAspect가role_menu_permission조회 → 없으면FORBIDDEN@DataChangeLog: 변경 전/후 데이터 비교 후data_change_logJSONB 비동기 적재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