feat: P6 외부연동 호출 감사 이력 (V85)
펌뱅킹/SMS·카카오 어댑터 모든 호출을 external_call_log 테이블에 AOP 자동 영속화.
외부 SDK 통합 후에도 그대로 재사용. 운영 감사·장애 추적·SDK 검증 용도.
- V85: external_call_log 테이블 + 인덱스 4종 (called_at desc / type / target / failures)
+ SYSTEM_EXTERNAL_CALL 메뉴 + READ 권한 (SUPER_ADMIN/ADMIN)
- ga-core: ExternalCallLogVO/Resp/SearchParam + Mapper (insertOne / selectList / selectById)
- ga-api/aop: ExternalCallLoggingAspect — BankTransferAdapter+/MessageAdapter+
모든 메서드 @Around 영속화. 호출자 트랜잭션과 분리(REQUIRES_NEW),
request/response 는 PII 마스킹된 JSON 한 줄 요약(계좌·전화 뒤 4자리, 이름 첫글자만),
영속화 자체가 실패해도 비즈니스 흐름 비차단(warn 로그만), 예외는 그대로 propagate.
- ga-api: ExternalCallLogService + Controller GET /api/external-call-logs[/{id}]
- ga-frontend: ExternalCallLogList.tsx + api/externalCallLog.ts + App.tsx 라우트
/system/external-call-log (ProTable + Drawer 상세)
라이브 검증:
- Flyway V85 자동 적용 → 운영 DB schema v85
- 5모듈 컴파일 BUILD SUCCESSFUL + ga-frontend tsc --noEmit 통과
- 시나리오: withdraw#3 신규 → 결재 advance ×2 → 펌뱅킹 호출
external_call_log #1: BANK/MockBankTransferAdapter/requestTransfer/
success=true/durMs=3/target=WITHDRAW#3/accountNoMasked="****0123"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
-- V85: 외부 연동(펌뱅킹/SMS·카카오) 호출 감사 이력
|
||||
--
|
||||
-- 목적: BankTransferAdapter / MessageAdapter 모든 호출을 자동 영속화.
|
||||
-- 운영 감사·장애 추적·외부 SDK 통합 검증에 사용. AOP(@Around)가 INSERT.
|
||||
-- PII 는 요약 필드에 어댑터에서 이미 마스킹된 값만 들어간다 (계좌·전화 뒤 4자리만).
|
||||
|
||||
CREATE TABLE IF NOT EXISTS external_call_log (
|
||||
log_id BIGSERIAL PRIMARY KEY,
|
||||
call_type VARCHAR(20) NOT NULL, -- BANK / MESSAGE
|
||||
adapter_class VARCHAR(200) NOT NULL, -- 실제 호출된 구현체 (Mock/Kftc/Toast)
|
||||
method_name VARCHAR(50) NOT NULL, -- requestTransfer / queryStatus / send
|
||||
target_ref_type VARCHAR(30), -- WITHDRAW / APPROVAL 등 (가능 시)
|
||||
target_ref_id BIGINT, -- withdrawRequestId / refId 등
|
||||
request_summary TEXT, -- 요청 요약 (마스킹된 JSON 한 줄)
|
||||
response_summary TEXT, -- 응답 요약
|
||||
result_code VARCHAR(20), -- 외부 응답 코드 (정상 시)
|
||||
result_message TEXT,
|
||||
success BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
duration_ms INTEGER, -- 호출 소요 시간 (ms)
|
||||
error_class VARCHAR(200), -- 예외 발생 시 클래스명
|
||||
error_message TEXT, -- 예외 메시지
|
||||
called_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by BIGINT -- 시도자 (가능 시)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE external_call_log IS '외부 연동 호출 감사 이력 (AOP 자동 영속화)';
|
||||
COMMENT ON COLUMN external_call_log.call_type IS 'BANK / MESSAGE';
|
||||
COMMENT ON COLUMN external_call_log.adapter_class IS '실제 호출된 어댑터 클래스 (Mock/Kftc/Toast)';
|
||||
COMMENT ON COLUMN external_call_log.method_name IS 'requestTransfer / queryStatus / send';
|
||||
COMMENT ON COLUMN external_call_log.success IS 'true=정상응답, false=예외/실패';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ecl_called_at_desc
|
||||
ON external_call_log(called_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ecl_type_called_at
|
||||
ON external_call_log(call_type, called_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ecl_target
|
||||
ON external_call_log(target_ref_type, target_ref_id)
|
||||
WHERE target_ref_type IS NOT NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_ecl_failures
|
||||
ON external_call_log(called_at DESC)
|
||||
WHERE success = FALSE;
|
||||
|
||||
-- ============================================================
|
||||
-- 메뉴 등록 (시스템 그룹 하위)
|
||||
-- ============================================================
|
||||
INSERT INTO menu (menu_code, menu_name, menu_type, menu_path, component_path, menu_icon,
|
||||
menu_level, sort_order, is_visible, is_active, parent_menu_id, description)
|
||||
SELECT
|
||||
'SYSTEM_EXTERNAL_CALL',
|
||||
'외부연동 호출이력',
|
||||
'PAGE',
|
||||
'/system/external-call-log',
|
||||
'system/ExternalCallLogList',
|
||||
'api',
|
||||
2,
|
||||
20,
|
||||
'Y',
|
||||
'Y',
|
||||
(SELECT menu_id FROM menu WHERE menu_code = 'GRP_SYSTEM'),
|
||||
'펌뱅킹/SMS·카카오 어댑터 호출 감사 이력 (AOP 자동 영속화)'
|
||||
WHERE EXISTS (SELECT 1 FROM menu WHERE menu_code = 'GRP_SYSTEM')
|
||||
ON CONFLICT (menu_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 권한 정의 (READ 만 — 감사 로그는 수동 등록 없음)
|
||||
-- ============================================================
|
||||
INSERT INTO menu_permission (menu_id, perm_code, perm_name, sort_order)
|
||||
SELECT menu_id, 'READ', '조회', 1 FROM menu WHERE menu_code = 'SYSTEM_EXTERNAL_CALL'
|
||||
ON CONFLICT (menu_id, perm_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 역할 부여 (SUPER_ADMIN / ADMIN)
|
||||
-- ============================================================
|
||||
INSERT INTO role_menu_permission (role_id, menu_id, perm_code, is_granted)
|
||||
SELECT r.role_id, m.menu_id, p.perm_code, 'Y'
|
||||
FROM role r
|
||||
CROSS JOIN menu m
|
||||
JOIN menu_permission p ON p.menu_id = m.menu_id
|
||||
WHERE r.role_code IN ('SUPER_ADMIN', 'ADMIN')
|
||||
AND m.menu_code = 'SYSTEM_EXTERNAL_CALL'
|
||||
AND p.perm_code = 'READ'
|
||||
ON CONFLICT (role_id, menu_id, perm_code) DO NOTHING;
|
||||
Reference in New Issue
Block a user