diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index a0309dd..1d75181 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -3,11 +3,11 @@ 이 파일은 AI Agent(Antigravity)가 이 프로젝트에서 작업할 때 항상 명심하고 지켜야 할 규칙을 정의하는 파일입니다. 작업 시 유의해야 할 사항이 생기면 언제든지 이 아래에 자유롭게 내용을 추가해 주세요! -## 💡 개발 가이드라인 +## 개발 가이드라인 * 패키지명은 `controller` 대신 `presentation`을 사용합니다. * MapStruct 사용 시, 테스트 환경 에러를 방지하기 위해 생성자(`new ...Impl()`) 대신 `Mappers.getMapper(인터페이스명.class)` 방식으로 인스턴스를 가져옵니다. -## 📌 명심해야 할 규칙 추가란 +## 명심해야 할 규칙 추가란 * 이모지는 무조건 넣지 않는다 * import 할것 무조건 한다 * 자바 만들때는 무조건 아래 내용을 넣는다 diff --git a/README.md b/README.md index 331ee25..385f549 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Spring Boot 기반 AXHUB 관리자 백엔드 API 서버 및 MCP(Model Context Pr --- -## 🚀 아키텍처 개요 (Architecture Overview) +## 아키텍처 개요 (Architecture Overview) AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다: @@ -14,7 +14,7 @@ AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다: --- -## 🛠 환경 (Environment) +## 환경 (Environment) | 항목 | 버전 | |------|------| @@ -29,7 +29,7 @@ AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다: --- -## ▶️ 실행 방법 (How to Run) +## ▶ 실행 방법 (How to Run) ### 1. Gateway & Tool 서버 실행 (MCP 연동용) - **Gateway 서버 기동:** @@ -46,7 +46,7 @@ AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다: - `HR`: 휴가 등록, 연차 갯수 조회 - `CONTRACT`: 계약 상태, 계약 상세 조회 - `CUSTOMER`: 고객 등급, 고객 상세 정보 조회 - - IntelliJ IDEA: `Run/Debug Configurations` ➔ `AxHubToolApplication` ➔ `Program arguments` 에 `--mcp.tool.target=NOTIFICATION` 입력 + - IntelliJ IDEA: `Run/Debug Configurations` `AxHubToolApplication` `Program arguments` 에 `--mcp.tool.target=NOTIFICATION` 입력 ### 2. Admin 관리자 서버 실행 - **Admin 서버 기동:** @@ -54,7 +54,7 @@ AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다: --- -## 🤖 Gemini MCP 연동 (Integration) +## Gemini MCP 연동 (Integration) 본 시스템은 REST API(JSON-RPC)를 사용하므로, **표준 MCP 통신(Stdio)**을 지원하기 위한 브릿지 스크립트를 내장하고 있습니다. 이를 통해 제미나이(Gemini Code Assist, Gemini CLI 등)와 코드 수정 없이 완벽히 연동됩니다. @@ -73,7 +73,7 @@ AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다: --- -## 🔒 비공개 Tool 관리 및 Fallback 연동 (Visibility & Routing) +## 비공개 Tool 관리 및 Fallback 연동 (Visibility & Routing) 저희 시스템은 MSA 보안 및 아키텍처 원칙에 따라 Tool의 **레지스트리 등록 여부(라우팅)**와 **API 노출 여부(가시성)**를 완벽히 분리하여 관리합니다. @@ -93,7 +93,7 @@ AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다: --- -## 🛡️ 안정성 및 트래픽 제어 (Resilience4j) +## 안정성 및 트래픽 제어 (Resilience4j) MSA(Microservices Architecture) 환경의 안정성을 위해 완벽한 2-Track 방어막을 구축했습니다. 1. **Gateway 계층 (동적 방어):** Tool이 등록할 때 제출한 메타데이터(SLA)를 기반으로 Gateway 내에서 동적 CircuitBreaker 및 RateLimiter를 가동합니다. 한계치 초과 시 트래픽을 Kafka 큐로 비동기 전환합니다. @@ -101,11 +101,11 @@ MSA(Microservices Architecture) 환경의 안정성을 위해 완벽한 2-Track --- -## 🏗️ 모듈(Pod) 및 Tool 코드 자동 생성 (Scaffolders) +## 모듈(Pod) 및 Tool 코드 자동 생성 (Scaffolders) 새로운 도메인의 기능을 추가할 때 발생하는 반복적인 설정(보일러플레이트, 설정 파일 복사 등)을 1초 만에 자동화하기 위해 **AXHUB Developer Portal (Web UI)** 및 **CLI 스캐폴더 2종**을 제공합니다. -### 🌟 1. AXHUB Developer Portal (Web UI) - 가장 추천하는 방식! +### 1. AXHUB Developer Portal (Web UI) - 가장 추천하는 방식! 이제 더 이상 터미널에서 명령어를 칠 필요가 없습니다. Gateway 모듈에 내장된 웹 화면에서 빈칸만 채우면 코드가 마법처럼 찍혀 나옵니다. 1. **접속 방법**: Gateway 서버 기동 후 브라우저에서 `http://localhost:8081/admin/scaffold.html` 접속 @@ -115,7 +115,7 @@ MSA(Microservices Architecture) 환경의 안정성을 위해 완벽한 2-Track ### 2. CLI 스캐폴더 (기존 터미널 방식) 웹 화면을 사용할 수 없는 환경이거나 터미널이 익숙한 경우, 아래 명령어를 통해 CLI 마법사를 사용할 수 있습니다. -### 1️⃣ 새로운 Pod(모듈) 전체를 생성할 때: `PodScaffolder` +### 1⃣ 새로운 Pod(모듈) 전체를 생성할 때: `PodScaffolder` 새로운 도메인(예: 결제, HR)을 위한 완전히 독립적인 Spring Boot 모듈을 생성합니다. 폴더 구조, 빌드 스크립트, 각종 프로퍼티 및 도커 설정까지 완벽하게 세팅됩니다. ```bash @@ -127,7 +127,7 @@ java -cp axhub-common/src/main/java io.shinhanlife.axhub.common.util.PodScaffold java -cp axhub-common/src/main/java io.shinhanlife.axhub.common.util.PodScaffolder hr 8086 ``` -### 2️⃣ 생성된 모듈에 새로운 툴(Function)을 추가할 때: `ToolScaffolder` +### 2⃣ 생성된 모듈에 새로운 툴(Function)을 추가할 때: `ToolScaffolder` 어노테이션(`@McpTool`, `@McpFunction`)이 완벽히 달린 Service와 입출력 DTO 코드를 지정된 모듈 패키지 룰에 맞춰 자동 생성합니다. ```bash @@ -141,11 +141,11 @@ java -cp axhub-common/src/main/java io.shinhanlife.axhub.common.util.ToolScaffol --- -## 📂 패키지 구조 (Package Structure) +## 패키지 구조 (Package Structure) ```text axhub-backend-main (Root) -├── axhub-gateway # 💡 MCP 라우팅 허브 서버 (외부 LLM과 통신 및 Tool 분배) +├── axhub-gateway # MCP 라우팅 허브 서버 (외부 LLM과 통신 및 Tool 분배) ├── axhub-common # 공통 모듈 (Security, Session, Config 등) ├── axhub-tool-core # Tool 공통 기능 (AbstractMcpToolService, Annotation, Scaffolder) ├── axhub-tool-email # [Tool] 이메일 발송 특화 어댑터 모듈 diff --git a/axhub-common/src/main/java/io/shinhanlife/axhub/common/mcp/exception/GlobalExceptionHandler.java b/axhub-common/src/main/java/io/shinhanlife/axhub/common/mcp/exception/GlobalExceptionHandler.java index 0772c0c..18866e2 100644 --- a/axhub-common/src/main/java/io/shinhanlife/axhub/common/mcp/exception/GlobalExceptionHandler.java +++ b/axhub-common/src/main/java/io/shinhanlife/axhub/common/mcp/exception/GlobalExceptionHandler.java @@ -45,7 +45,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity handleAllException(Exception e) { - log.error("💥 [Gateway Fatal Error] 치명적 오류 발생", e); + log.error(" [Gateway Fatal Error] 치명적 오류 발생", e); return buildErrorResponse(-32000, "Server error: 시스템 관리자에게 문의하세요."); } diff --git a/axhub-common/src/main/java/io/shinhanlife/axhub/common/util/PodScaffolder.java b/axhub-common/src/main/java/io/shinhanlife/axhub/common/util/PodScaffolder.java index 1faff42..62d3bb3 100644 --- a/axhub-common/src/main/java/io/shinhanlife/axhub/common/util/PodScaffolder.java +++ b/axhub-common/src/main/java/io/shinhanlife/axhub/common/util/PodScaffolder.java @@ -244,7 +244,7 @@ public class PodScaffolder { } log.append("\n=========================================\n"); - log.append(" 🎉 Pod Scaffolding Complete! \n"); + log.append(" Pod Scaffolding Complete! \n"); log.append("=========================================\n"); log.append("1. [새로운 모듈] ").append(moduleName).append(" 폴더가 생성되었습니다.\n"); log.append("2. [ToolScaffolder]를 사용해 이 모듈 안에 툴을 추가하세요.\n"); diff --git a/axhub-common/src/test/java/io/shinhanlife/glow/util/GlowMciParserTest.java b/axhub-common/src/test/java/io/shinhanlife/glow/util/GlowMciParserTest.java index 1ead27b..a0358bf 100644 --- a/axhub-common/src/test/java/io/shinhanlife/glow/util/GlowMciParserTest.java +++ b/axhub-common/src/test/java/io/shinhanlife/glow/util/GlowMciParserTest.java @@ -36,10 +36,10 @@ public class GlowMciParserTest { // then System.out.println("=================================================="); - System.out.println("✅ [원본 MCI 전문] : [" + rawMciString + "]"); - System.out.println("✅ [파싱된 ID (10자리)] : [" + result.getCustomerId() + "]"); - System.out.println("✅ [파싱된 Name (15자리)] : [" + result.getName() + "]"); - System.out.println("✅ [파싱된 Age (3자리)] : [" + result.getAge() + "]"); + System.out.println(" [원본 MCI 전문] : [" + rawMciString + "]"); + System.out.println(" [파싱된 ID (10자리)] : [" + result.getCustomerId() + "]"); + System.out.println(" [파싱된 Name (15자리)] : [" + result.getName() + "]"); + System.out.println(" [파싱된 Age (3자리)] : [" + result.getAge() + "]"); System.out.println("=================================================="); assertNotNull(result); diff --git a/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/messaging/KafkaProducerService.java b/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/messaging/KafkaProducerService.java index 94420da..088317e 100644 --- a/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/messaging/KafkaProducerService.java +++ b/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/messaging/KafkaProducerService.java @@ -49,7 +49,7 @@ public class KafkaProducerService { String jsonPayload = jsonMapper.writeValueAsString(payload); // 실제 Kafka 서버로 전송 kafkaTemplate.send(topic, ticketId, jsonPayload); - log.info("🎫 [Kafka Queue] 요청 대기열 등록 완료 - Topic: {}, Ticket ID: {}", topic, ticketId); + log.info(" [Kafka Queue] 요청 대기열 등록 완료 - Topic: {}, Ticket ID: {}", topic, ticketId); } catch (Exception e) { // 로컬 테스트 시 실제 Kafka 브로커가 없어서 나는 예외를 방어합니다. // 실제 상용 환경에서는 Dead Letter Queue 등을 태우거나 예외 처리 정책에 따릅니다. diff --git a/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/service/ToolPlanner.java b/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/service/ToolPlanner.java index 36b09c6..84e2f02 100644 --- a/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/service/ToolPlanner.java +++ b/axhub-gateway/src/main/java/io/shinhanlife/axhub/biz/mcp/gateway/service/ToolPlanner.java @@ -38,7 +38,7 @@ public class ToolPlanner { * 요청(payload)을 분석하여 실행해야 할 Tool의 계획을 생성합니다. */ public Object createPlan(Map payload, String tenantId) { - log.info("📝 [Planner] 요청 분석 및 실행 계획 수립 시작"); + log.info(" [Planner] 요청 분석 및 실행 계획 수립 시작"); // 1. 요청에서 호출하려는 툴 이름 추출 (JSON-RPC params.name) Map params = (Map) payload.get("params"); diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/ExternalApiConnector.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/ExternalApiConnector.java index c2da059..cd09b85 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/ExternalApiConnector.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/ExternalApiConnector.java @@ -55,7 +55,7 @@ public class ExternalApiConnector { Object payload = isFixedLength ? payloadBuilder.buildFixedLengthString(spec, data) : data; String contentType = isFixedLength ? "application/x-www-form-urlencoded;charset=EUC-KR" : "application/json"; - log.info("🌐 외부 API 호출 [{}] 시작 (FixedLength: {})", apiName, isFixedLength); + log.info(" 외부 API 호출 [{}] 시작 (FixedLength: {})", apiName, isFixedLength); return restClient.post() .uri(endpoint) diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/LegacyDbConnector.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/LegacyDbConnector.java index f718cc3..2d2b959 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/LegacyDbConnector.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/LegacyDbConnector.java @@ -37,7 +37,7 @@ public class LegacyDbConnector { @RateLimiter(name = "legacyDb", fallbackMethod = "fallbackForDb") @CircuitBreaker(name = "legacyDb", fallbackMethod = "fallbackForDb") public List> executeDynamicQuery(String queryId, String sql, Map params) { - log.info("🗄 Legacy DB 조회 시작 [QueryID: {}]", queryId); + log.info(" Legacy DB 조회 시작 [QueryID: {}]", queryId); // 1. SQL 쿼리 실행 (오라클 등) List> rawResults = jdbcTemplate.queryForList(sql, params); diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/ThirdPartySecurityConnector.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/ThirdPartySecurityConnector.java index 9671495..bb7c585 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/ThirdPartySecurityConnector.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/connector/ThirdPartySecurityConnector.java @@ -32,7 +32,7 @@ public class ThirdPartySecurityConnector { * 3rd Party 보안 모듈(DRM, BM 등)과 연동하기 위한 전용 메서드입니다. */ public String executeSecurityModule(String interfaceId, Map data) throws Exception { - log.info("🔐 [3rd Party Security] 보안 모듈 연동 시작 - Interface: {}", interfaceId); + log.info(" [3rd Party Security] 보안 모듈 연동 시작 - Interface: {}", interfaceId); // 보안 모듈 통신을 위한 특수 페이로드 조립 (예시) // 실제로는 RestClient나 WebClient를 통해 보안 VM의 전용 엔드포인트로 호출합니다. @@ -49,7 +49,7 @@ public class ThirdPartySecurityConnector { "data", data != null ? data : Map.of() )); } else if (interfaceId.startsWith("BM_")) { - log.info("👆 [BM 처리] 바이오 인증 모듈과 통신 중..."); + log.info(" [BM 처리] 바이오 인증 모듈과 통신 중..."); resultJson = jsonMapper.writeValueAsString(Map.of( "status", "SUCCESS", "module", "Bio-Metric", diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/EaiEimsSender.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/EaiEimsSender.java index 79d0183..8f795bf 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/EaiEimsSender.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/EaiEimsSender.java @@ -35,7 +35,7 @@ public class EaiEimsSender implements EimsSender { // 실전 코드: 스프링이 제공하는 카프카 템플릿 주입 private final KafkaTemplate kafkaTemplate; - // 🎫 비동기 티켓 매니저 + // 비동기 티켓 매니저 private final TicketManager ticketManager; @Override @@ -47,12 +47,12 @@ public class EaiEimsSender implements EimsSender { String xmlData = xmlMapper.writeValueAsString(jsonNode); String esbStandardXml = wrapWithEaiHeader(interfaceId, xmlData); - log.info("📦 [EAI 어댑터] Kafka 토픽(eai-topic)으로 전송 시도..."); + log.info(" [EAI 어댑터] Kafka 토픽(eai-topic)으로 전송 시도..."); try { // 실전 코드 적용: Kafka로 메시지 발행 kafkaTemplate.send("eai-topic", esbStandardXml); - log.info("📦 [EAI 어댑터] Kafka 전송 완료!"); + log.info(" [EAI 어댑터] Kafka 전송 완료!"); } catch (Exception e) { // 로컬 환경에는 카프카가 없으므로 에러가 날 수 있습니다. 테스트를 위해 로깅만 하고 넘깁니다. log.warn(" 로컬 환경이거나 Ka\n" + @@ -82,7 +82,7 @@ public class EaiEimsSender implements EimsSender { } finally { stopWatch.stop(); - log.info("📊 [SLA 모니터링 - EAI] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); + log.info(" [SLA 모니터링 - EAI] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); } } diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/HttpEimsSender.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/HttpEimsSender.java index c0b40ab..ce31cda 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/HttpEimsSender.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/HttpEimsSender.java @@ -44,7 +44,7 @@ public class HttpEimsSender implements EimsSender { @Override public String send(String interfaceId, String payload) { - log.info("🌐 [HTTP 모드] EIMS API 호출 중... URL: {}", eimsUrl); + log.info(" [HTTP 모드] EIMS API 호출 중... URL: {}", eimsUrl); // EIMS가 요구하는 JSON 포맷으로 래핑해서 전송 (EIMS 규격에 따라 수정 가능) Map requestBody = Map.of( @@ -52,7 +52,7 @@ public class HttpEimsSender implements EimsSender { "data", payload ); - // 📊 4번 항목 적용: MDC에 저장된 traceId를 추출하여 HTTP Header(X-Trace-Id)로 전파 + // 4번 항목 적용: MDC에 저장된 traceId를 추출하여 HTTP Header(X-Trace-Id)로 전파 String traceId = MDC.get("traceId"); if (traceId == null) traceId = "SYSTEM-GENERATED-" + UUID.randomUUID().toString(); diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/JspFormEimsSender.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/JspFormEimsSender.java index 78e450e..51d44cd 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/JspFormEimsSender.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/JspFormEimsSender.java @@ -37,7 +37,7 @@ public class JspFormEimsSender implements EimsSender { @Override public String send(String interfaceId, String payload) { - log.info("🌐 [JSP Form 모드] 레거시 폼 데이터 전송 중... URL: {}", jspUrl); + log.info(" [JSP Form 모드] 레거시 폼 데이터 전송 중... URL: {}", jspUrl); // 1. Form Data 조립 (HTML
태그 전송과 동일한 효과) MultiValueMap formData = new LinkedMultiValueMap<>(); diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/JspJsonEimsSender.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/JspJsonEimsSender.java index 835d31a..4788599 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/JspJsonEimsSender.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/JspJsonEimsSender.java @@ -37,7 +37,7 @@ public class JspJsonEimsSender implements EimsSender { @Override public String send(String interfaceId, String payload) { - log.info("🌐 [JSP JSON 모드] JSON 페이로드 전송 중... URL: {}", jspUrl); + log.info(" [JSP JSON 모드] JSON 페이로드 전송 중... URL: {}", jspUrl); // 1. JSON 객체로 조립 (스프링이 알아서 JSON String으로 변환해 줌) Map jsonBody = Map.of( diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/MciEimsSender.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/MciEimsSender.java index b6915e6..f9511c4 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/MciEimsSender.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/MciEimsSender.java @@ -49,7 +49,7 @@ public class MciEimsSender implements EimsSender { String xmlData = xmlMapper.writer().withRootName("Body").writeValueAsString(jsonNode); String esbStandardXml = wrapWithEsbHeader(interfaceId, xmlData); - log.info("🌐 [ESB 어댑터] 전송 준비 완료 - RestClient 호출 시작"); + log.info(" [ESB 어댑터] 전송 준비 완료 - RestClient 호출 시작"); String responseXml = restClient.post() .uri(mciUrl) @@ -58,14 +58,14 @@ public class MciEimsSender implements EimsSender { .retrieve() .body(String.class); - log.info("🌐 [ESB 어댑터] 응답 수신 완료: {}", responseXml); + log.info(" [ESB 어댑터] 응답 수신 완료: {}", responseXml); JsonNode responseNode = xmlMapper.readTree(responseXml); return jsonMapper.writeValueAsString(responseNode); } finally { stopWatch.stop(); - log.info("📊 [SLA 모니터링 - MCI] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); + log.info(" [SLA 모니터링 - MCI] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); } } diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/MciStringEimsSender.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/MciStringEimsSender.java index b2576c3..342da8b 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/MciStringEimsSender.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/MciStringEimsSender.java @@ -38,7 +38,7 @@ public class MciStringEimsSender implements EimsSender { StopWatch stopWatch = new StopWatch(); stopWatch.start(); try { - log.info("🌐 [ESB 어댑터(String)] 전송 준비 완료 - RestClient 호출 시작 (Interface: {})", interfaceId); + log.info(" [ESB 어댑터(String)] 전송 준비 완료 - RestClient 호출 시작 (Interface: {})", interfaceId); String response = restClient.post() .uri(mciUrl) @@ -47,12 +47,12 @@ public class MciStringEimsSender implements EimsSender { .retrieve() .body(String.class); - log.info("🌐 [ESB 어댑터(String)] 응답 수신 완료: {}", response); + log.info(" [ESB 어댑터(String)] 응답 수신 완료: {}", response); return response != null ? response : ""; } finally { stopWatch.stop(); - log.info("📊 [SLA 모니터링 - MCI(String)] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); + log.info(" [SLA 모니터링 - MCI(String)] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); } } } diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/TcpEimsSender.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/TcpEimsSender.java index c562b34..334ff9c 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/TcpEimsSender.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/sender/TcpEimsSender.java @@ -39,7 +39,7 @@ public class TcpEimsSender implements EimsSender { @Override public String send(String interfaceId, String payload) throws Exception { - log.info("🔌 [TCP 소켓 모드] EIMS 접속 중... {}:{}", host, port); + log.info(" [TCP 소켓 모드] EIMS 접속 중... {}:{}", host, port); // TCP 소켓 자원을 사용 후 안전하게 닫아주는 try-with-resources 구문 try (Socket socket = new Socket()) { diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/support/MciTemplate.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/support/MciTemplate.java index 1a114eb..8fff1ff 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/support/MciTemplate.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/support/MciTemplate.java @@ -42,25 +42,25 @@ public class MciTemplate { * @throws MciCommunicationException 통신 또는 파싱 실패 시 예외 발생 */ public R call(String interfaceId, T requestDto, Class responseType) { - log.info("📞 [MciTemplate] 시작 - Interface ID: {}", interfaceId); + log.info(" [MciTemplate] 시작 - Interface ID: {}", interfaceId); try { String payload = objectMapper.writeValueAsString(requestDto); - log.debug("📤 [MciTemplate] 전송 페이로드: {}", payload); + log.debug(" [MciTemplate] 전송 페이로드: {}", payload); String responseJson = httpEimsSender.send(interfaceId, payload); - log.debug("📥 [MciTemplate] 수신 응답 JSON: {}", responseJson); + log.debug(" [MciTemplate] 수신 응답 JSON: {}", responseJson); R response = objectMapper.readValue(responseJson, responseType); - log.info("✅ [MciTemplate] 완료 - Interface ID: {}", interfaceId); + log.info(" [MciTemplate] 완료 - Interface ID: {}", interfaceId); return response; } catch (JsonProcessingException e) { - log.error("❌ [MciTemplate] JSON 변환 중 오류 발생", e); + log.error(" [MciTemplate] JSON 변환 중 오류 발생", e); throw new MciCommunicationException("MCI 통신 중 JSON 파싱 오류", e); } catch (Exception e) { - log.error("❌ [MciTemplate] 통신 중 오류 발생", e); + log.error(" [MciTemplate] 통신 중 오류 발생", e); throw new MciCommunicationException("MCI 통신 실패", e); } } diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/support/TicketManager.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/support/TicketManager.java index 08cb970..969c026 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/support/TicketManager.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/adapter/support/TicketManager.java @@ -47,7 +47,7 @@ public class TicketManager { ticketStore.put(ticketId, ticket); - log.info("🎫 [TicketManager] 비동기 작업 티켓 발급 완료: {}", ticketId); + log.info(" [TicketManager] 비동기 작업 티켓 발급 완료: {}", ticketId); // 10초 뒤에 자동으로 작업을 완료 상태로 변경하는 백그라운드 시뮬레이터 실행 simulateEaiProcessing(ticketId, interfaceId); @@ -74,7 +74,7 @@ public class TicketManager { // 가짜 최종 결과 데이터 주입 ticket.setResultData("{\"resultCode\":\"0000\", \"interfaceId\":\"" + interfaceId + "\", \"processedRecords\":50000}"); ticketStore.put(ticketId, ticket); - log.info("🏁 [TicketManager] EAI 비동기 작업 시뮬레이션 완료! (Ticket: {})", ticketId); + log.info(" [TicketManager] EAI 비동기 작업 시뮬레이션 완료! (Ticket: {})", ticketId); } }, 10, TimeUnit.SECONDS); // 10초 지연 } diff --git a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/aop/ToolSlaMonitoringAspect.java b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/aop/ToolSlaMonitoringAspect.java index c8be8e2..a88f7f9 100644 --- a/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/aop/ToolSlaMonitoringAspect.java +++ b/axhub-tool-core/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/aop/ToolSlaMonitoringAspect.java @@ -60,9 +60,9 @@ public class ToolSlaMonitoringAspect { // SLA 기준을 초과하면 (예: 2초 이상) 경고 로깅 처리 가능 if (timeMillis > 2000) { - log.warn("🚨 [SLA 경고] Tool: {} | 소요시간: {}ms | 상태: SLOW_RESPONSE", finalName, timeMillis); + log.warn(" [SLA 경고] Tool: {} | 소요시간: {}ms | 상태: SLOW_RESPONSE", finalName, timeMillis); } else { - log.info("📊 [SLA 추적] Tool: {} | 소요시간: {}ms | 상태: SUCCESS", finalName, timeMillis); + log.info(" [SLA 추적] Tool: {} | 소요시간: {}ms | 상태: SUCCESS", finalName, timeMillis); } return result; @@ -74,7 +74,7 @@ public class ToolSlaMonitoringAspect { long timeMillis = stopWatch.getTotalTimeMillis(); // 에러 발생 시 명확하게 실패 로그 기록 - log.error("💥 [SLA 장애] Tool: {} | 소요시간: {}ms | 상태: FAILED | 사유: {}", finalName, timeMillis, e.getMessage()); + log.error(" [SLA 장애] Tool: {} | 소요시간: {}ms | 상태: FAILED | 사유: {}", finalName, timeMillis, e.getMessage()); // 원래 흐름대로 예외를 던져서 게이트웨이나 상위 로직이 에러를 처리하게 함 throw e; diff --git a/axhub-tool-core/src/test/java/io/shinhanlife/axhub/biz/mcp/sample/mapper/SampleMciMapperTest.java b/axhub-tool-core/src/test/java/io/shinhanlife/axhub/biz/mcp/sample/mapper/SampleMciMapperTest.java index eed03d6..67b29b7 100644 --- a/axhub-tool-core/src/test/java/io/shinhanlife/axhub/biz/mcp/sample/mapper/SampleMciMapperTest.java +++ b/axhub-tool-core/src/test/java/io/shinhanlife/axhub/biz/mcp/sample/mapper/SampleMciMapperTest.java @@ -27,9 +27,9 @@ public class SampleMciMapperTest { SampleMciReqDto mciDto = sampleMciMapper.toMciReq(aiDto); // then: 콘솔에 결과 출력 및 값 검증 - System.out.println("====== 🚀 MapStruct 변환 테스트 결과 🚀 ======"); - System.out.println("✅ [변환 전] AI DTO : " + aiDto); - System.out.println("✅ [변환 후] MCI DTO: " + mciDto); + System.out.println("====== MapStruct 변환 테스트 결과 ======"); + System.out.println(" [변환 전] AI DTO : " + aiDto); + System.out.println(" [변환 후] MCI DTO: " + mciDto); System.out.println("==============================================="); assertNotNull(mciDto, "변환된 객체는 null이 아니어야 합니다."); diff --git a/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/service/BillingProcessService.java b/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/service/BillingProcessService.java index 1a0c224..9ab97a0 100644 --- a/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/service/BillingProcessService.java +++ b/axhub-tool-other/src/main/java/io/shinhanlife/axhub/biz/mcp/tool/service/BillingProcessService.java @@ -41,7 +41,7 @@ public class BillingProcessService extends AbstractMcpToolService { } private Object executeBillingLogic(String mappingId, Object data) { - log.info("💰 [Billing] 청구 처리 전용 커스텀 전/후처리 로직 수행 시작"); + log.info(" [Billing] 청구 처리 전용 커스텀 전/후처리 로직 수행 시작"); Map payload; if (data == null) { @@ -53,7 +53,7 @@ public class BillingProcessService extends AbstractMcpToolService { // 커스텀 전처리 payload.put("custom_injected_data", "Billing System Check OK"); - log.info("💰 [Billing] 커스텀 파라미터 주입 완료"); + log.info(" [Billing] 커스텀 파라미터 주입 완료"); // 부모 클래스의 레거시 공통 연동 메서드 호출 (PII 마스킹 포함) Map result = executeLegacy("MCI", mappingId, payload); diff --git a/remove_emojis.py b/remove_emojis.py new file mode 100644 index 0000000..82c990e --- /dev/null +++ b/remove_emojis.py @@ -0,0 +1,34 @@ +import os +import re + +pattern = re.compile(r'[\u2600-\u27BF\U0001F000-\U0001F9FF]+') + +root_dir = "." +target_exts = {'.java', '.html', '.md', '.yml', '.properties', '.xml', '.json', '.js', '.css'} + +count = 0 +for subdir, dirs, files in os.walk(root_dir): + if '.git' in dirs: dirs.remove('.git') + if 'build' in dirs: dirs.remove('build') + if '.gradle' in dirs: dirs.remove('.gradle') + + for file in files: + ext = os.path.splitext(file)[1].lower() + if ext in target_exts: + filepath = os.path.join(subdir, file) + try: + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + new_content = pattern.sub('', content) + new_content = new_content.replace('\uFE0F', '').replace('\u200D', '') + + if new_content != content: + with open(filepath, 'w', encoding='utf-8') as f: + f.write(new_content) + print(f"Cleaned: {filepath}") + count += 1 + except Exception as e: + pass + +print(f"\nTotal files cleaned: {count}")