style: remove all emojis from source code as per user rule

This commit is contained in:
jade
2026-07-13 16:15:17 +09:00
parent fc1e8489cd
commit 7e591e8421
23 changed files with 92 additions and 58 deletions

View File

@@ -3,11 +3,11 @@
이 파일은 AI Agent(Antigravity)가 이 프로젝트에서 작업할 때 항상 명심하고 지켜야 할 규칙을 정의하는 파일입니다. 이 파일은 AI Agent(Antigravity)가 이 프로젝트에서 작업할 때 항상 명심하고 지켜야 할 규칙을 정의하는 파일입니다.
작업 시 유의해야 할 사항이 생기면 언제든지 이 아래에 자유롭게 내용을 추가해 주세요! 작업 시 유의해야 할 사항이 생기면 언제든지 이 아래에 자유롭게 내용을 추가해 주세요!
## 💡 개발 가이드라인 ## 개발 가이드라인
* 패키지명은 `controller` 대신 `presentation`을 사용합니다. * 패키지명은 `controller` 대신 `presentation`을 사용합니다.
* MapStruct 사용 시, 테스트 환경 에러를 방지하기 위해 생성자(`new ...Impl()`) 대신 `Mappers.getMapper(인터페이스명.class)` 방식으로 인스턴스를 가져옵니다. * MapStruct 사용 시, 테스트 환경 에러를 방지하기 위해 생성자(`new ...Impl()`) 대신 `Mappers.getMapper(인터페이스명.class)` 방식으로 인스턴스를 가져옵니다.
## 📌 명심해야 할 규칙 추가란 ## 명심해야 할 규칙 추가란
* 이모지는 무조건 넣지 않는다 * 이모지는 무조건 넣지 않는다
* import 할것 무조건 한다 * import 할것 무조건 한다
* 자바 만들때는 무조건 아래 내용을 넣는다 * 자바 만들때는 무조건 아래 내용을 넣는다

View File

@@ -4,7 +4,7 @@ Spring Boot 기반 AXHUB 관리자 백엔드 API 서버 및 MCP(Model Context Pr
--- ---
## 🚀 아키텍처 개요 (Architecture Overview) ## 아키텍처 개요 (Architecture Overview)
AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다: 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 연동용) ### 1. Gateway & Tool 서버 실행 (MCP 연동용)
- **Gateway 서버 기동:** - **Gateway 서버 기동:**
@@ -46,7 +46,7 @@ AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다:
- `HR`: 휴가 등록, 연차 갯수 조회 - `HR`: 휴가 등록, 연차 갯수 조회
- `CONTRACT`: 계약 상태, 계약 상세 조회 - `CONTRACT`: 계약 상태, 계약 상세 조회
- `CUSTOMER`: 고객 등급, 고객 상세 정보 조회 - `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 관리자 서버 실행 ### 2. Admin 관리자 서버 실행
- **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 등)와 코드 수정 없이 완벽히 연동됩니다. 본 시스템은 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 노출 여부(가시성)**를 완벽히 분리하여 관리합니다. 저희 시스템은 MSA 보안 및 아키텍처 원칙에 따라 Tool의 **레지스트리 등록 여부(라우팅)**와 **API 노출 여부(가시성)**를 완벽히 분리하여 관리합니다.
@@ -93,7 +93,7 @@ AXHUB Backend는 3개의 주요 애플리케이션으로 분리 운영됩니다:
--- ---
## 🛡️ 안정성 및 트래픽 제어 (Resilience4j) ## 안정성 및 트래픽 제어 (Resilience4j)
MSA(Microservices Architecture) 환경의 안정성을 위해 완벽한 2-Track 방어막을 구축했습니다. MSA(Microservices Architecture) 환경의 안정성을 위해 완벽한 2-Track 방어막을 구축했습니다.
1. **Gateway 계층 (동적 방어):** Tool이 등록할 때 제출한 메타데이터(SLA)를 기반으로 Gateway 내에서 동적 CircuitBreaker 및 RateLimiter를 가동합니다. 한계치 초과 시 트래픽을 Kafka 큐로 비동기 전환합니다. 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)** 및 **CLI 스캐폴더 2종**을 제공합니다.
### 🌟 1. AXHUB Developer Portal (Web UI) - 가장 추천하는 방식! ### 1. AXHUB Developer Portal (Web UI) - 가장 추천하는 방식!
이제 더 이상 터미널에서 명령어를 칠 필요가 없습니다. Gateway 모듈에 내장된 웹 화면에서 빈칸만 채우면 코드가 마법처럼 찍혀 나옵니다. 이제 더 이상 터미널에서 명령어를 칠 필요가 없습니다. Gateway 모듈에 내장된 웹 화면에서 빈칸만 채우면 코드가 마법처럼 찍혀 나옵니다.
1. **접속 방법**: Gateway 서버 기동 후 브라우저에서 `http://localhost:8081/admin/scaffold.html` 접속 1. **접속 방법**: Gateway 서버 기동 후 브라우저에서 `http://localhost:8081/admin/scaffold.html` 접속
@@ -115,7 +115,7 @@ MSA(Microservices Architecture) 환경의 안정성을 위해 완벽한 2-Track
### 2. CLI 스캐폴더 (기존 터미널 방식) ### 2. CLI 스캐폴더 (기존 터미널 방식)
웹 화면을 사용할 수 없는 환경이거나 터미널이 익숙한 경우, 아래 명령어를 통해 CLI 마법사를 사용할 수 있습니다. 웹 화면을 사용할 수 없는 환경이거나 터미널이 익숙한 경우, 아래 명령어를 통해 CLI 마법사를 사용할 수 있습니다.
### 1⃣ 새로운 Pod(모듈) 전체를 생성할 때: `PodScaffolder` ### 1⃣ 새로운 Pod(모듈) 전체를 생성할 때: `PodScaffolder`
새로운 도메인(예: 결제, HR)을 위한 완전히 독립적인 Spring Boot 모듈을 생성합니다. 폴더 구조, 빌드 스크립트, 각종 프로퍼티 및 도커 설정까지 완벽하게 세팅됩니다. 새로운 도메인(예: 결제, HR)을 위한 완전히 독립적인 Spring Boot 모듈을 생성합니다. 폴더 구조, 빌드 스크립트, 각종 프로퍼티 및 도커 설정까지 완벽하게 세팅됩니다.
```bash ```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 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 코드를 지정된 모듈 패키지 룰에 맞춰 자동 생성합니다. 어노테이션(`@McpTool`, `@McpFunction`)이 완벽히 달린 Service와 입출력 DTO 코드를 지정된 모듈 패키지 룰에 맞춰 자동 생성합니다.
```bash ```bash
@@ -141,11 +141,11 @@ java -cp axhub-common/src/main/java io.shinhanlife.axhub.common.util.ToolScaffol
--- ---
## 📂 패키지 구조 (Package Structure) ## 패키지 구조 (Package Structure)
```text ```text
axhub-backend-main (Root) axhub-backend-main (Root)
├── axhub-gateway # 💡 MCP 라우팅 허브 서버 (외부 LLM과 통신 및 Tool 분배) ├── axhub-gateway # MCP 라우팅 허브 서버 (외부 LLM과 통신 및 Tool 분배)
├── axhub-common # 공통 모듈 (Security, Session, Config 등) ├── axhub-common # 공통 모듈 (Security, Session, Config 등)
├── axhub-tool-core # Tool 공통 기능 (AbstractMcpToolService, Annotation, Scaffolder) ├── axhub-tool-core # Tool 공통 기능 (AbstractMcpToolService, Annotation, Scaffolder)
├── axhub-tool-email # [Tool] 이메일 발송 특화 어댑터 모듈 ├── axhub-tool-email # [Tool] 이메일 발송 특화 어댑터 모듈

View File

@@ -45,7 +45,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
public ResponseEntity<JsonRpcResponse> handleAllException(Exception e) { public ResponseEntity<JsonRpcResponse> handleAllException(Exception e) {
log.error("💥 [Gateway Fatal Error] 치명적 오류 발생", e); log.error(" [Gateway Fatal Error] 치명적 오류 발생", e);
return buildErrorResponse(-32000, "Server error: 시스템 관리자에게 문의하세요."); return buildErrorResponse(-32000, "Server error: 시스템 관리자에게 문의하세요.");
} }

View File

@@ -244,7 +244,7 @@ public class PodScaffolder {
} }
log.append("\n=========================================\n"); log.append("\n=========================================\n");
log.append(" 🎉 Pod Scaffolding Complete! \n"); log.append(" Pod Scaffolding Complete! \n");
log.append("=========================================\n"); log.append("=========================================\n");
log.append("1. [새로운 모듈] ").append(moduleName).append(" 폴더가 생성되었습니다.\n"); log.append("1. [새로운 모듈] ").append(moduleName).append(" 폴더가 생성되었습니다.\n");
log.append("2. [ToolScaffolder]를 사용해 이 모듈 안에 툴을 추가하세요.\n"); log.append("2. [ToolScaffolder]를 사용해 이 모듈 안에 툴을 추가하세요.\n");

View File

@@ -36,10 +36,10 @@ public class GlowMciParserTest {
// then // then
System.out.println("=================================================="); System.out.println("==================================================");
System.out.println(" [원본 MCI 전문] : [" + rawMciString + "]"); System.out.println(" [원본 MCI 전문] : [" + rawMciString + "]");
System.out.println(" [파싱된 ID (10자리)] : [" + result.getCustomerId() + "]"); System.out.println(" [파싱된 ID (10자리)] : [" + result.getCustomerId() + "]");
System.out.println(" [파싱된 Name (15자리)] : [" + result.getName() + "]"); System.out.println(" [파싱된 Name (15자리)] : [" + result.getName() + "]");
System.out.println(" [파싱된 Age (3자리)] : [" + result.getAge() + "]"); System.out.println(" [파싱된 Age (3자리)] : [" + result.getAge() + "]");
System.out.println("=================================================="); System.out.println("==================================================");
assertNotNull(result); assertNotNull(result);

View File

@@ -49,7 +49,7 @@ public class KafkaProducerService {
String jsonPayload = jsonMapper.writeValueAsString(payload); String jsonPayload = jsonMapper.writeValueAsString(payload);
// 실제 Kafka 서버로 전송 // 실제 Kafka 서버로 전송
kafkaTemplate.send(topic, ticketId, jsonPayload); 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) { } catch (Exception e) {
// 로컬 테스트 시 실제 Kafka 브로커가 없어서 나는 예외를 방어합니다. // 로컬 테스트 시 실제 Kafka 브로커가 없어서 나는 예외를 방어합니다.
// 실제 상용 환경에서는 Dead Letter Queue 등을 태우거나 예외 처리 정책에 따릅니다. // 실제 상용 환경에서는 Dead Letter Queue 등을 태우거나 예외 처리 정책에 따릅니다.

View File

@@ -38,7 +38,7 @@ public class ToolPlanner {
* 요청(payload)을 분석하여 실행해야 할 Tool의 계획을 생성합니다. * 요청(payload)을 분석하여 실행해야 할 Tool의 계획을 생성합니다.
*/ */
public Object createPlan(Map<String, Object> payload, String tenantId) { public Object createPlan(Map<String, Object> payload, String tenantId) {
log.info("📝 [Planner] 요청 분석 및 실행 계획 수립 시작"); log.info(" [Planner] 요청 분석 및 실행 계획 수립 시작");
// 1. 요청에서 호출하려는 툴 이름 추출 (JSON-RPC params.name) // 1. 요청에서 호출하려는 툴 이름 추출 (JSON-RPC params.name)
Map<String, Object> params = (Map<String, Object>) payload.get("params"); Map<String, Object> params = (Map<String, Object>) payload.get("params");

View File

@@ -55,7 +55,7 @@ public class ExternalApiConnector {
Object payload = isFixedLength ? payloadBuilder.buildFixedLengthString(spec, data) : data; Object payload = isFixedLength ? payloadBuilder.buildFixedLengthString(spec, data) : data;
String contentType = isFixedLength ? "application/x-www-form-urlencoded;charset=EUC-KR" : "application/json"; 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() return restClient.post()
.uri(endpoint) .uri(endpoint)

View File

@@ -37,7 +37,7 @@ public class LegacyDbConnector {
@RateLimiter(name = "legacyDb", fallbackMethod = "fallbackForDb") @RateLimiter(name = "legacyDb", fallbackMethod = "fallbackForDb")
@CircuitBreaker(name = "legacyDb", fallbackMethod = "fallbackForDb") @CircuitBreaker(name = "legacyDb", fallbackMethod = "fallbackForDb")
public List<Map<String, Object>> executeDynamicQuery(String queryId, String sql, Map<String, Object> params) { public List<Map<String, Object>> executeDynamicQuery(String queryId, String sql, Map<String, Object> params) {
log.info("🗄 Legacy DB 조회 시작 [QueryID: {}]", queryId); log.info(" Legacy DB 조회 시작 [QueryID: {}]", queryId);
// 1. SQL 쿼리 실행 (오라클 등) // 1. SQL 쿼리 실행 (오라클 등)
List<Map<String, Object>> rawResults = jdbcTemplate.queryForList(sql, params); List<Map<String, Object>> rawResults = jdbcTemplate.queryForList(sql, params);

View File

@@ -32,7 +32,7 @@ public class ThirdPartySecurityConnector {
* 3rd Party 보안 모듈(DRM, BM 등)과 연동하기 위한 전용 메서드입니다. * 3rd Party 보안 모듈(DRM, BM 등)과 연동하기 위한 전용 메서드입니다.
*/ */
public String executeSecurityModule(String interfaceId, Map<String, Object> data) throws Exception { public String executeSecurityModule(String interfaceId, Map<String, Object> data) throws Exception {
log.info("🔐 [3rd Party Security] 보안 모듈 연동 시작 - Interface: {}", interfaceId); log.info(" [3rd Party Security] 보안 모듈 연동 시작 - Interface: {}", interfaceId);
// 보안 모듈 통신을 위한 특수 페이로드 조립 (예시) // 보안 모듈 통신을 위한 특수 페이로드 조립 (예시)
// 실제로는 RestClient나 WebClient를 통해 보안 VM의 전용 엔드포인트로 호출합니다. // 실제로는 RestClient나 WebClient를 통해 보안 VM의 전용 엔드포인트로 호출합니다.
@@ -49,7 +49,7 @@ public class ThirdPartySecurityConnector {
"data", data != null ? data : Map.of() "data", data != null ? data : Map.of()
)); ));
} else if (interfaceId.startsWith("BM_")) { } else if (interfaceId.startsWith("BM_")) {
log.info("👆 [BM 처리] 바이오 인증 모듈과 통신 중..."); log.info(" [BM 처리] 바이오 인증 모듈과 통신 중...");
resultJson = jsonMapper.writeValueAsString(Map.of( resultJson = jsonMapper.writeValueAsString(Map.of(
"status", "SUCCESS", "status", "SUCCESS",
"module", "Bio-Metric", "module", "Bio-Metric",

View File

@@ -35,7 +35,7 @@ public class EaiEimsSender implements EimsSender {
// 실전 코드: 스프링이 제공하는 카프카 템플릿 주입 // 실전 코드: 스프링이 제공하는 카프카 템플릿 주입
private final KafkaTemplate<String, String> kafkaTemplate; private final KafkaTemplate<String, String> kafkaTemplate;
// 🎫 비동기 티켓 매니저 // 비동기 티켓 매니저
private final TicketManager ticketManager; private final TicketManager ticketManager;
@Override @Override
@@ -47,12 +47,12 @@ public class EaiEimsSender implements EimsSender {
String xmlData = xmlMapper.writeValueAsString(jsonNode); String xmlData = xmlMapper.writeValueAsString(jsonNode);
String esbStandardXml = wrapWithEaiHeader(interfaceId, xmlData); String esbStandardXml = wrapWithEaiHeader(interfaceId, xmlData);
log.info("📦 [EAI 어댑터] Kafka 토픽(eai-topic)으로 전송 시도..."); log.info(" [EAI 어댑터] Kafka 토픽(eai-topic)으로 전송 시도...");
try { try {
// 실전 코드 적용: Kafka로 메시지 발행 // 실전 코드 적용: Kafka로 메시지 발행
kafkaTemplate.send("eai-topic", esbStandardXml); kafkaTemplate.send("eai-topic", esbStandardXml);
log.info("📦 [EAI 어댑터] Kafka 전송 완료!"); log.info(" [EAI 어댑터] Kafka 전송 완료!");
} catch (Exception e) { } catch (Exception e) {
// 로컬 환경에는 카프카가 없으므로 에러가 날 수 있습니다. 테스트를 위해 로깅만 하고 넘깁니다. // 로컬 환경에는 카프카가 없으므로 에러가 날 수 있습니다. 테스트를 위해 로깅만 하고 넘깁니다.
log.warn(" 로컬 환경이거나 Ka<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + log.warn(" 로컬 환경이거나 Ka<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" +
@@ -82,7 +82,7 @@ public class EaiEimsSender implements EimsSender {
} finally { } finally {
stopWatch.stop(); stopWatch.stop();
log.info("📊 [SLA 모니터링 - EAI] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); log.info(" [SLA 모니터링 - EAI] 소요시간: {} ms", stopWatch.getTotalTimeMillis());
} }
} }

View File

@@ -44,7 +44,7 @@ public class HttpEimsSender implements EimsSender {
@Override @Override
public String send(String interfaceId, String payload) { public String send(String interfaceId, String payload) {
log.info("🌐 [HTTP 모드] EIMS API 호출 중... URL: {}", eimsUrl); log.info(" [HTTP 모드] EIMS API 호출 중... URL: {}", eimsUrl);
// EIMS가 요구하는 JSON 포맷으로 래핑해서 전송 (EIMS 규격에 따라 수정 가능) // EIMS가 요구하는 JSON 포맷으로 래핑해서 전송 (EIMS 규격에 따라 수정 가능)
Map<String, String> requestBody = Map.of( Map<String, String> requestBody = Map.of(
@@ -52,7 +52,7 @@ public class HttpEimsSender implements EimsSender {
"data", payload "data", payload
); );
// 📊 4번 항목 적용: MDC에 저장된 traceId를 추출하여 HTTP Header(X-Trace-Id)로 전파 // 4번 항목 적용: MDC에 저장된 traceId를 추출하여 HTTP Header(X-Trace-Id)로 전파
String traceId = MDC.get("traceId"); String traceId = MDC.get("traceId");
if (traceId == null) traceId = "SYSTEM-GENERATED-" + UUID.randomUUID().toString(); if (traceId == null) traceId = "SYSTEM-GENERATED-" + UUID.randomUUID().toString();

View File

@@ -37,7 +37,7 @@ public class JspFormEimsSender implements EimsSender {
@Override @Override
public String send(String interfaceId, String payload) { public String send(String interfaceId, String payload) {
log.info("🌐 [JSP Form 모드] 레거시 폼 데이터 전송 중... URL: {}", jspUrl); log.info(" [JSP Form 모드] 레거시 폼 데이터 전송 중... URL: {}", jspUrl);
// 1. Form Data 조립 (HTML <form> 태그 전송과 동일한 효과) // 1. Form Data 조립 (HTML <form> 태그 전송과 동일한 효과)
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();

View File

@@ -37,7 +37,7 @@ public class JspJsonEimsSender implements EimsSender {
@Override @Override
public String send(String interfaceId, String payload) { 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으로 변환해 줌) // 1. JSON 객체로 조립 (스프링이 알아서 JSON String으로 변환해 줌)
Map<String, String> jsonBody = Map.of( Map<String, String> jsonBody = Map.of(

View File

@@ -49,7 +49,7 @@ public class MciEimsSender implements EimsSender {
String xmlData = xmlMapper.writer().withRootName("Body").writeValueAsString(jsonNode); String xmlData = xmlMapper.writer().withRootName("Body").writeValueAsString(jsonNode);
String esbStandardXml = wrapWithEsbHeader(interfaceId, xmlData); String esbStandardXml = wrapWithEsbHeader(interfaceId, xmlData);
log.info("🌐 [ESB 어댑터] 전송 준비 완료 - RestClient 호출 시작"); log.info(" [ESB 어댑터] 전송 준비 완료 - RestClient 호출 시작");
String responseXml = restClient.post() String responseXml = restClient.post()
.uri(mciUrl) .uri(mciUrl)
@@ -58,14 +58,14 @@ public class MciEimsSender implements EimsSender {
.retrieve() .retrieve()
.body(String.class); .body(String.class);
log.info("🌐 [ESB 어댑터] 응답 수신 완료: {}", responseXml); log.info(" [ESB 어댑터] 응답 수신 완료: {}", responseXml);
JsonNode responseNode = xmlMapper.readTree(responseXml); JsonNode responseNode = xmlMapper.readTree(responseXml);
return jsonMapper.writeValueAsString(responseNode); return jsonMapper.writeValueAsString(responseNode);
} finally { } finally {
stopWatch.stop(); stopWatch.stop();
log.info("📊 [SLA 모니터링 - MCI] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); log.info(" [SLA 모니터링 - MCI] 소요시간: {} ms", stopWatch.getTotalTimeMillis());
} }
} }

View File

@@ -38,7 +38,7 @@ public class MciStringEimsSender implements EimsSender {
StopWatch stopWatch = new StopWatch(); stopWatch.start(); StopWatch stopWatch = new StopWatch(); stopWatch.start();
try { try {
log.info("🌐 [ESB 어댑터(String)] 전송 준비 완료 - RestClient 호출 시작 (Interface: {})", interfaceId); log.info(" [ESB 어댑터(String)] 전송 준비 완료 - RestClient 호출 시작 (Interface: {})", interfaceId);
String response = restClient.post() String response = restClient.post()
.uri(mciUrl) .uri(mciUrl)
@@ -47,12 +47,12 @@ public class MciStringEimsSender implements EimsSender {
.retrieve() .retrieve()
.body(String.class); .body(String.class);
log.info("🌐 [ESB 어댑터(String)] 응답 수신 완료: {}", response); log.info(" [ESB 어댑터(String)] 응답 수신 완료: {}", response);
return response != null ? response : ""; return response != null ? response : "";
} finally { } finally {
stopWatch.stop(); stopWatch.stop();
log.info("📊 [SLA 모니터링 - MCI(String)] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); log.info(" [SLA 모니터링 - MCI(String)] 소요시간: {} ms", stopWatch.getTotalTimeMillis());
} }
} }
} }

View File

@@ -39,7 +39,7 @@ public class TcpEimsSender implements EimsSender {
@Override @Override
public String send(String interfaceId, String payload) throws Exception { 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 구문 // TCP 소켓 자원을 사용 후 안전하게 닫아주는 try-with-resources 구문
try (Socket socket = new Socket()) { try (Socket socket = new Socket()) {

View File

@@ -42,25 +42,25 @@ public class MciTemplate {
* @throws MciCommunicationException 통신 또는 파싱 실패 시 예외 발생 * @throws MciCommunicationException 통신 또는 파싱 실패 시 예외 발생
*/ */
public <T, R> R call(String interfaceId, T requestDto, Class<R> responseType) { public <T, R> R call(String interfaceId, T requestDto, Class<R> responseType) {
log.info("📞 [MciTemplate] 시작 - Interface ID: {}", interfaceId); log.info(" [MciTemplate] 시작 - Interface ID: {}", interfaceId);
try { try {
String payload = objectMapper.writeValueAsString(requestDto); String payload = objectMapper.writeValueAsString(requestDto);
log.debug("📤 [MciTemplate] 전송 페이로드: {}", payload); log.debug(" [MciTemplate] 전송 페이로드: {}", payload);
String responseJson = httpEimsSender.send(interfaceId, payload); String responseJson = httpEimsSender.send(interfaceId, payload);
log.debug("📥 [MciTemplate] 수신 응답 JSON: {}", responseJson); log.debug(" [MciTemplate] 수신 응답 JSON: {}", responseJson);
R response = objectMapper.readValue(responseJson, responseType); R response = objectMapper.readValue(responseJson, responseType);
log.info(" [MciTemplate] 완료 - Interface ID: {}", interfaceId); log.info(" [MciTemplate] 완료 - Interface ID: {}", interfaceId);
return response; return response;
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
log.error(" [MciTemplate] JSON 변환 중 오류 발생", e); log.error(" [MciTemplate] JSON 변환 중 오류 발생", e);
throw new MciCommunicationException("MCI 통신 중 JSON 파싱 오류", e); throw new MciCommunicationException("MCI 통신 중 JSON 파싱 오류", e);
} catch (Exception e) { } catch (Exception e) {
log.error(" [MciTemplate] 통신 중 오류 발생", e); log.error(" [MciTemplate] 통신 중 오류 발생", e);
throw new MciCommunicationException("MCI 통신 실패", e); throw new MciCommunicationException("MCI 통신 실패", e);
} }
} }

View File

@@ -47,7 +47,7 @@ public class TicketManager {
ticketStore.put(ticketId, ticket); ticketStore.put(ticketId, ticket);
log.info("🎫 [TicketManager] 비동기 작업 티켓 발급 완료: {}", ticketId); log.info(" [TicketManager] 비동기 작업 티켓 발급 완료: {}", ticketId);
// 10초 뒤에 자동으로 작업을 완료 상태로 변경하는 백그라운드 시뮬레이터 실행 // 10초 뒤에 자동으로 작업을 완료 상태로 변경하는 백그라운드 시뮬레이터 실행
simulateEaiProcessing(ticketId, interfaceId); simulateEaiProcessing(ticketId, interfaceId);
@@ -74,7 +74,7 @@ public class TicketManager {
// 가짜 최종 결과 데이터 주입 // 가짜 최종 결과 데이터 주입
ticket.setResultData("{\"resultCode\":\"0000\", \"interfaceId\":\"" + interfaceId + "\", \"processedRecords\":50000}"); ticket.setResultData("{\"resultCode\":\"0000\", \"interfaceId\":\"" + interfaceId + "\", \"processedRecords\":50000}");
ticketStore.put(ticketId, ticket); ticketStore.put(ticketId, ticket);
log.info("🏁 [TicketManager] EAI 비동기 작업 시뮬레이션 완료! (Ticket: {})", ticketId); log.info(" [TicketManager] EAI 비동기 작업 시뮬레이션 완료! (Ticket: {})", ticketId);
} }
}, 10, TimeUnit.SECONDS); // 10초 지연 }, 10, TimeUnit.SECONDS); // 10초 지연
} }

View File

@@ -60,9 +60,9 @@ public class ToolSlaMonitoringAspect {
// SLA 기준을 초과하면 (예: 2초 이상) 경고 로깅 처리 가능 // SLA 기준을 초과하면 (예: 2초 이상) 경고 로깅 처리 가능
if (timeMillis > 2000) { if (timeMillis > 2000) {
log.warn("🚨 [SLA 경고] Tool: {} | 소요시간: {}ms | 상태: SLOW_RESPONSE", finalName, timeMillis); log.warn(" [SLA 경고] Tool: {} | 소요시간: {}ms | 상태: SLOW_RESPONSE", finalName, timeMillis);
} else { } else {
log.info("📊 [SLA 추적] Tool: {} | 소요시간: {}ms | 상태: SUCCESS", finalName, timeMillis); log.info(" [SLA 추적] Tool: {} | 소요시간: {}ms | 상태: SUCCESS", finalName, timeMillis);
} }
return result; return result;
@@ -74,7 +74,7 @@ public class ToolSlaMonitoringAspect {
long timeMillis = stopWatch.getTotalTimeMillis(); 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; throw e;

View File

@@ -27,9 +27,9 @@ public class SampleMciMapperTest {
SampleMciReqDto mciDto = sampleMciMapper.toMciReq(aiDto); SampleMciReqDto mciDto = sampleMciMapper.toMciReq(aiDto);
// then: 콘솔에 결과 출력 및 값 검증 // then: 콘솔에 결과 출력 및 값 검증
System.out.println("====== 🚀 MapStruct 변환 테스트 결과 🚀 ======"); System.out.println("====== MapStruct 변환 테스트 결과 ======");
System.out.println(" [변환 전] AI DTO : " + aiDto); System.out.println(" [변환 전] AI DTO : " + aiDto);
System.out.println(" [변환 후] MCI DTO: " + mciDto); System.out.println(" [변환 후] MCI DTO: " + mciDto);
System.out.println("==============================================="); System.out.println("===============================================");
assertNotNull(mciDto, "변환된 객체는 null이 아니어야 합니다."); assertNotNull(mciDto, "변환된 객체는 null이 아니어야 합니다.");

View File

@@ -41,7 +41,7 @@ public class BillingProcessService extends AbstractMcpToolService {
} }
private Object executeBillingLogic(String mappingId, Object data) { private Object executeBillingLogic(String mappingId, Object data) {
log.info("💰 [Billing] 청구 처리 전용 커스텀 전/후처리 로직 수행 시작"); log.info(" [Billing] 청구 처리 전용 커스텀 전/후처리 로직 수행 시작");
Map<String, Object> payload; Map<String, Object> payload;
if (data == null) { if (data == null) {
@@ -53,7 +53,7 @@ public class BillingProcessService extends AbstractMcpToolService {
// 커스텀 전처리 // 커스텀 전처리
payload.put("custom_injected_data", "Billing System Check OK"); payload.put("custom_injected_data", "Billing System Check OK");
log.info("💰 [Billing] 커스텀 파라미터 주입 완료"); log.info(" [Billing] 커스텀 파라미터 주입 완료");
// 부모 클래스의 레거시 공통 연동 메서드 호출 (PII 마스킹 포함) // 부모 클래스의 레거시 공통 연동 메서드 호출 (PII 마스킹 포함)
Map<String, Object> result = executeLegacy("MCI", mappingId, payload); Map<String, Object> result = executeLegacy("MCI", mappingId, payload);

34
remove_emojis.py Normal file
View File

@@ -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}")