diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/aop/EimsMonitoringAspect.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/aop/EimsMonitoringAspect.java deleted file mode 100644 index bc2ff8a7..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/aop/EimsMonitoringAspect.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.aop; - -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.springframework.stereotype.Component; - -import java.util.Arrays; - -/** - * @package io.shinhanlife.dap.lib.adapter.aop - * @className EimsMonitoringAspect - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Aspect -@Component -public class EimsMonitoringAspect { - - // 포인트컷: io.shinhanlife.dap.lib.adapter.sender 패키지 내의 EimsSender를 구현한 모든 클래스의 메서드를 타겟으로 지정합니다. - @Around("execution(* io.shinhanlife.dap.lib.adapter.sender.*EimsSender.*(..))") - public Object monitorEimsCommunication(ProceedingJoinPoint joinPoint) throws Throwable { - - // 1. 호출되는 클래스와 메서드 이름 추출 - String className = joinPoint.getTarget().getClass().getSimpleName(); - String methodName = joinPoint.getSignature().getName(); - Object[] args = joinPoint.getArgs(); // 파라미터 - - long startTime = System.currentTimeMillis(); - - // 2. [요청 로깅] EIMS망으로 요청이 나가기 직전 - log.info(" [EIMS 요청] {} - {}() | Params: {}", className, methodName, Arrays.toString(args)); - - try { - // 실제 레거시 통신 로직 실행 (이 코드가 없으면 통신이 진행되지 않습니다) - Object result = joinPoint.proceed(); - - // 3. [응답 로깅] 정상적으로 통신이 완료된 후 - long executionTime = System.currentTimeMillis() - startTime; - log.info("◀ [EIMS 응답] {} - {}() | 소요시간: {}ms | Result: {}", className, methodName, executionTime, result); - - return result; - - } catch (Exception e) { - // 4. [에러 로깅] 레거시 통신 중 장애(타임아웃 등) 발생 시 - long executionTime = System.currentTimeMillis() - startTime; - log.error(" [EIMS 에러] {} - {}() | 소요시간: {}ms | Error: {}", className, methodName, executionTime, e.getMessage()); - - // 에러를 삼키지 않고 다시 던져서 기존 예외 처리 로직(서킷 브레이커 등)이 작동하게 합니다. - throw e; - } - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/exception/MciCommunicationException.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/exception/MciCommunicationException.java deleted file mode 100644 index 2e45f94a..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/exception/MciCommunicationException.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.exception; - -/** - * @package io.shinhanlife.dap.lib.adapter.exception - * @className MciCommunicationException - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public class MciCommunicationException extends RuntimeException { - - public MciCommunicationException(String message) { - super(message); - } - - public MciCommunicationException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/EaiEimsSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/EaiEimsSender.java deleted file mode 100644 index 2ddbc1c2..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/EaiEimsSender.java +++ /dev/null @@ -1,104 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import io.shinhanlife.dap.lib.adapter.support.TicketManager; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.stereotype.Service; -import org.springframework.util.StopWatch; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className EaiEimsSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Service("eaiEimsSender") -@RequiredArgsConstructor -public class EaiEimsSender implements EimsSender { - - private final ObjectMapper jsonMapper; - private final XmlMapper xmlMapper; - - // 실전 코드: 스프링이 제공하는 카프카 템플릿 주입 - private final KafkaTemplate kafkaTemplate; - - // 비동기 티켓 매니저 - private final TicketManager ticketManager; - - @Override - public String send(String interfaceId, String jsonPayload) throws Exception { - StopWatch stopWatch = new StopWatch(); stopWatch.start(); - - try { - JsonNode jsonNode = jsonMapper.readTree(jsonPayload); - String xmlData = xmlMapper.writeValueAsString(jsonNode); - String esbStandardXml = wrapWithEaiHeader(interfaceId, xmlData); - - log.info(" [EAI 어댑터] Kafka 토픽(eai-topic)으로 전송 시도..."); - - try { - // 실전 코드 적용: Kafka로 메시지 발행 - kafkaTemplate.send("eai-topic", esbStandardXml); - log.info(" [EAI 어댑터] Kafka 전송 완료!"); - } catch (Exception e) { - // 로컬 환경에는 카프카가 없으므로 에러가 날 수 있습니다. 테스트를 위해 로깅만 하고 넘깁니다. - log.warn(" 로컬 환경이거나 Ka\n" + - "\n" + - "
\n" + - " MCP_GATEWAY_ASYNC\n" + - " EAI_BATCH_JOB\n" + - " 1782705901850\n" + - " ASYNC\n" + - "
\n" + - " \n" + - " \n" + - " BATCH_20260629_001\n" + - " GLOBAL_MINIMUM_TAX_SYS\n" + - " 50000\n" + - " \n" + - " \n" + - "
fka 서버에 연결할 수 없습니다. (메시지 출력으로 대체합니다) \n전송하려던 메시지: {}", esbStandardXml); - } - - // 비동기 폴링을 위한 티켓 발급 - String ticketId = ticketManager.issueTicket(interfaceId, jsonPayload); - - return String.format( - "{\"status\":\"PROCESSING\", \"interfaceId\":\"%s\", \"ticketId\":\"%s\", \"message\":\"비동기 작업이 접수되었습니다. 상태 조회 API를 통해 결과를 확인하세요.\"}", - interfaceId, ticketId); - - } finally { - stopWatch.stop(); - log.info(" [SLA 모니터링 - EAI] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); - } - } - - private String wrapWithEaiHeader(String interfaceId, String xmlData) { - // 비동기 EAI는 추적을 위해 TransferType이나 Batch ID 같은 속성이 추가로 들어가는 경우가 많습니다. - return String.format( - "" + - "
" + - "MCP_GATEWAY_ASYNC" + - "%s" + - "%d" + - "ASYNC" + - "
" + - "%s" + - "
", - interfaceId, System.currentTimeMillis(), xmlData - ); - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/EimsSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/EimsSender.java deleted file mode 100644 index ee6afe1b..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/EimsSender.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className EimsSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -public interface EimsSender { - // 프로토콜에 상관없이 이 메서드 하나로 통일합니다. - String send(String interfaceId, String payload) throws Exception; -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/HttpEimsSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/HttpEimsSender.java deleted file mode 100644 index 9ba08a23..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/HttpEimsSender.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClient; -import org.springframework.http.client.SimpleClientHttpRequestFactory; - -import java.util.Map; -import java.util.UUID; - -import org.slf4j.MDC; - -import io.shinhanlife.dap.lib.config.GlowCommunicationProperties; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className HttpEimsSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Component -public class HttpEimsSender implements EimsSender { - - private final RestClient restClient; - private final String eimsUrl; - private final GlowCommunicationProperties glowProps; - - public HttpEimsSender(GlowCommunicationProperties glowProps) { - this.glowProps = glowProps; - // yml의 대내 MCI host, port, uri를 조합하여 EIMS 호출 주소 생성 - this.eimsUrl = glowProps.getMci().getHost() + ":" + glowProps.getMci().getPort() + glowProps.getMci().getUri(); - - /* - *********************************************** 중요 ************************************************** - this.restClient = RestClient.create(); - 보통 금융권(신한라이프 등 은행/보험사)의 내부 레거시 시스템이나 MCI(Message Channel Integration) 솔루션은 HTTP/2를 기본으로 지원하지 않는 경우가 훨씬 많습니다. - *********************************************** 중요 ************************************************** - */ - - // HTTP/2 통신 시 Stream Cancelled(RST_STREAM) 에러 방지를 위해 HTTP/1.1 전용 Factory 사용 - SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); - factory.setConnectTimeout(3000); - factory.setReadTimeout(5000); - - this.restClient = RestClient.builder().requestFactory(factory).build(); - } - - @Override - public String send(String interfaceId, String payload) { - log.info(" [HTTP 모드] EIMS API 호출 중... URL: {}", eimsUrl); - - // EIMS가 요구하는 JSON 포맷으로 래핑해서 전송 (EIMS 규격에 따라 수정 가능) - Map requestBody = Map.of( - "interfaceId", interfaceId, - "data", payload - ); - - // 4번 항목 적용: MDC에 저장된 traceId를 추출하여 HTTP Header(X-Trace-Id)로 전파 - String traceId = MDC.get("traceId"); - if (traceId == null) traceId = "SYSTEM-GENERATED-" + UUID.randomUUID().toString(); - - return restClient.post() - .uri(eimsUrl) - .header("X-Trace-Id", traceId) - .header("X-Shinhan-Global-ID", traceId) - .body(requestBody) - .retrieve() - .body(String.class); // 응답 결과를 String으로 받음 - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/JspFormEimsSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/JspFormEimsSender.java deleted file mode 100644 index c05cfbb1..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/JspFormEimsSender.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClient; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className JspFormEimsSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Component -public class JspFormEimsSender implements EimsSender { - - private final RestClient restClient; - private final String jspUrl; - - public JspFormEimsSender(@Value("${eims.jsp.form.url}") String jspUrl) { - this.jspUrl = jspUrl; - this.restClient = RestClient.builder().build(); - } - - @Override - public String send(String interfaceId, String payload) { - log.info(" [JSP Form 모드] 레거시 폼 데이터 전송 중... URL: {}", jspUrl); - - // 1. Form Data 조립 (HTML
태그 전송과 동일한 효과) - MultiValueMap formData = new LinkedMultiValueMap<>(); - formData.add("interfaceId", interfaceId); - formData.add("data", payload); - - // 2. HTTP 전송 (Content-Type: application/x-www-form-urlencoded) - String rawResponse = restClient.post() - .uri(jspUrl) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .body(formData) - .retrieve() - .body(String.class); - - // 3. JSP 특유의 앞뒤 공백 및 엔터 제거 - return rawResponse != null ? rawResponse.trim() : ""; - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/JspJsonEimsSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/JspJsonEimsSender.java deleted file mode 100644 index a89dd8f1..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/JspJsonEimsSender.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClient; - -import java.util.Map; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className JspJsonEimsSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Component -public class JspJsonEimsSender implements EimsSender { - - private final RestClient restClient; - private final String jspUrl; - - // 여기서 eims.jsp.json.url 딱 하나만 깔끔하게 받아옵니다! - public JspJsonEimsSender(@Value("${eims.jsp.json.url}") String jspUrl) { - this.jspUrl = jspUrl; - this.restClient = RestClient.builder().build(); - } - - @Override - public String send(String interfaceId, String payload) { - log.info(" [JSP JSON 모드] JSON 페이로드 전송 중... URL: {}", jspUrl); - - // 1. JSON 객체로 조립 (스프링이 알아서 JSON String으로 변환해 줌) - Map jsonBody = Map.of( - "interfaceId", interfaceId, - "data", payload - ); - - // 2. HTTP 전송 (Content-Type: application/json) - String rawResponse = restClient.post() - .uri(jspUrl) - .contentType(MediaType.APPLICATION_JSON) - .body(jsonBody) - .retrieve() - .body(String.class); - - // 3. JSP 응답 정제 - return rawResponse != null ? rawResponse.trim() : ""; - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/MciEimsSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/MciEimsSender.java deleted file mode 100644 index 71acbfef..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/MciEimsSender.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.MediaType; -import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.stereotype.Service; -import org.springframework.util.StopWatch; -import org.springframework.web.client.RestClient; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className MciEimsSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Service("mciEimsSender") -public class MciEimsSender implements EimsSender { - - private final ObjectMapper jsonMapper; - private final XmlMapper xmlMapper; - private final RestClient restClient; // Spring Boot 3.2+ 최신 HTTP 클라이언트 - private final String mciUrl; - - public MciEimsSender(ObjectMapper jsonMapper, XmlMapper xmlMapper, @Value("${eims.mci.url}") String mciUrl) { - this.jsonMapper = jsonMapper; - this.xmlMapper = xmlMapper; - this.mciUrl = mciUrl; - this.restClient = RestClient.create(); // 클라이언트 초기화 - - /* - *********************************************** 중요 ************************************************** - SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); - factory.setConnectTimeout(3000); - factory.setReadTimeout(5000); - this.restClient = RestClient.builder().requestFactory(factory).build(); - 보통 금융권(신한라이프 등 은행/보험사)의 내부 레거시 시스템이나 MCI(Message Channel Integration) 솔루션은 HTTP/2를 기본으로 지원하지 않는 경우가 훨씬 많습니다. - *********************************************** 중요 ************************************************** - */ - } - - @Override - public String send(String interfaceId, String jsonPayload) throws Exception { - StopWatch stopWatch = new StopWatch(); stopWatch.start(); - - try { - JsonNode jsonNode = jsonMapper.readTree(jsonPayload); - String xmlData = xmlMapper.writer().withRootName("Body").writeValueAsString(jsonNode); - String esbStandardXml = wrapWithEsbHeader(interfaceId, xmlData); - - log.info(" [ESB 어댑터] 전송 준비 완료 - RestClient 호출 시작"); - - String responseXml = restClient.post() - .uri(mciUrl) - .contentType(MediaType.APPLICATION_XML) - .body(esbStandardXml) - .retrieve() - .body(String.class); - - log.info(" [ESB 어댑터] 응답 수신 완료: {}", responseXml); - - JsonNode responseNode = xmlMapper.readTree(responseXml); - return jsonMapper.writeValueAsString(responseNode); - - } finally { - stopWatch.stop(); - log.info(" [SLA 모니터링 - MCI] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); - } - } - - private String wrapWithEsbHeader(String interfaceId, String xmlData) { - return String.format("
%s
%s
", interfaceId, xmlData); - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/MciStringEimsSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/MciStringEimsSender.java deleted file mode 100644 index c6bd3ae0..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/MciStringEimsSender.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; -import org.springframework.util.StopWatch; -import org.springframework.web.client.RestClient; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className MciStringEimsSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Service("mciStringEimsSender") -public class MciStringEimsSender implements EimsSender { - - private final RestClient restClient; - private final String mciUrl; - - public MciStringEimsSender(@Value("${eims.mcistring.url}") String mciUrl) { - this.mciUrl = mciUrl; - this.restClient = RestClient.create(); - /* - *********************************************** 중요 ************************************************** - SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); - factory.setConnectTimeout(3000); - factory.setReadTimeout(5000); - this.restClient = RestClient.builder().requestFactory(factory).build(); - 보통 금융권(신한라이프 등 은행/보험사)의 내부 레거시 시스템이나 MCI(Message Channel Integration) 솔루션은 HTTP/2를 기본으로 지원하지 않는 경우가 훨씬 많습니다. - *********************************************** 중요 ************************************************** - */ - - } - - @Override - public String send(String interfaceId, String payload) throws Exception { - StopWatch stopWatch = new StopWatch(); stopWatch.start(); - - try { - log.info(" [ESB 어댑터(String)] 전송 준비 완료 - RestClient 호출 시작 (Interface: {})", interfaceId); - - String response = restClient.post() - .uri(mciUrl) - .contentType(MediaType.TEXT_PLAIN) - .body(payload != null ? payload : "") - .retrieve() - .body(String.class); - - log.info(" [ESB 어댑터(String)] 응답 수신 완료: {}", response); - return response != null ? response : ""; - - } finally { - stopWatch.stop(); - log.info(" [SLA 모니터링 - MCI(String)] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); - } - } -} diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/ShinhanMciSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/ShinhanMciSender.java deleted file mode 100644 index 599ba442..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/ShinhanMciSender.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dap.lib.integration.mci.config.ShinhanIntegrationProperties; -import io.shinhanlife.dap.lib.integration.mci.dto.MciRequestWrapper; -import io.shinhanlife.dap.lib.integration.mci.dto.ShinhanCommonHeaderDto; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; -import org.springframework.util.StopWatch; -import org.springframework.web.client.RestClient; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className ShinhanMciSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -import org.springframework.boot.context.properties.EnableConfigurationProperties; - -@Slf4j -@Service -@EnableConfigurationProperties(ShinhanIntegrationProperties.class) -public class ShinhanMciSender { - - private final ObjectMapper jsonMapper; - private final RestClient restClient; - private final ShinhanIntegrationProperties properties; - private final AtomicInteger sequenceGenerator = new AtomicInteger(1); - - public ShinhanMciSender(ObjectMapper jsonMapper, ShinhanIntegrationProperties properties) { - this.jsonMapper = jsonMapper; - this.properties = properties; - this.restClient = RestClient.create(); - } - - public String send(String targetUrl, MciRequestWrapper requestWrapper) throws Exception { - StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - - try { - ShinhanCommonHeaderDto header = requestWrapper.getTgrmCmnnhddValu(); - if (header == null) { - header = new ShinhanCommonHeaderDto(); - requestWrapper.setTgrmCmnnhddValu(header); - } - - // 필수 헤더 자동 세팅 로직 - header.setEnvrTypeCd(properties.getEnvrTypeCd()); - header.setReqRspnScCd("S"); // S: 요청 - header.setAppliDutjCd("DAP"); // 어플리케이션업무코드 - - if (header.getGlbId() == null || header.getGlbId().isEmpty()) { - header.setGlbId(generateGlbId()); - } - - int currentSeq = sequenceGenerator.getAndIncrement(); - header.setPgrsSriaNo(String.format("%03d", currentSeq)); // 3자리 - - if (header.getReqTgrmTnsmDtptDt() == null) { - header.setReqTgrmTnsmDtptDt(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"))); - } - - String jsonPayload = jsonMapper.writeValueAsString(requestWrapper); - - log.info(" [신한 통합 MCI 어댑터] 전송 준비 완료 - URL: {}", targetUrl); - log.debug(" [신한 통합 MCI 어댑터] 요청 Payload: {}", jsonPayload); - - String responseJson = restClient.post() - .uri(targetUrl) - .contentType(MediaType.APPLICATION_JSON) - .body(jsonPayload) - .retrieve() - .body(String.class); - - log.info(" [신한 통합 MCI 어댑터] 응답 수신 완료"); - log.debug(" [신한 통합 MCI 어댑터] 응답 Payload: {}", responseJson); - - return responseJson; - - } finally { - stopWatch.stop(); - log.info(" [SLA 모니터링 - MCI 연계] 소요시간: {} ms", stopWatch.getTotalTimeMillis()); - } - } - - private String generateGlbId() { - // 전사공통키 (37 Byte) - // 전문생성상세일시(17) + 전문생성시스템명(9) + 어플리케이션업무코드(3) + 전문세션번호(8) - String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); // 17 byte - String systemName = String.format("%-9s", "AXHUB"); // 9 byte, left-aligned padded with spaces - String appCode = "DAP"; // 3 byte - String sessionNo = UUID.randomUUID().toString().substring(0, 8).toUpperCase(); // 8 byte - return timestamp + systemName + appCode + sessionNo; - } -} diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/TcpEimsSender.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/TcpEimsSender.java deleted file mode 100644 index a6f9a1a7..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/sender/TcpEimsSender.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.sender; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Component; - -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.Socket; - -/** - * @package io.shinhanlife.dap.lib.adapter.sender - * @className TcpEimsSender - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Component -public class TcpEimsSender implements EimsSender { - - @Value("${eims.tcp.host}") - private String host; - - @Value("${eims.tcp.port}") - private int port; - - @Value("${eims.tcp.timeout}") - private int timeout; - - @Override - public String send(String interfaceId, String payload) throws Exception { - log.info(" [TCP 소켓 모드] EIMS 접속 중... {}:{}", host, port); - - // TCP 소켓 자원을 사용 후 안전하게 닫아주는 try-with-resources 구문 - try (Socket socket = new Socket()) { - // 1. 타임아웃 및 연결 설정 - socket.connect(new InetSocketAddress(host, port), timeout); - socket.setSoTimeout(timeout); // 읽기 타임아웃 - - OutputStream os = socket.getOutputStream(); - InputStream is = socket.getInputStream(); - - // 2. 데이터 송신 (EUC-KR 인코딩 필수) - // 보통 금융권 TCP 통신은 맨 앞에 전체 길이나 인터페이스 ID를 헤더로 붙입니다. - String sendData = interfaceId + payload; - os.write(sendData.getBytes("EUC-KR")); - os.flush(); - - // 3. 데이터 수신 - byte[] buffer = new byte[4096]; - int readByte = is.read(buffer); - - if (readByte == -1) { - throw new RuntimeException("EIMS 서버가 응답 없이 연결을 종료했습니다."); - } - - // 받은 바이트를 다시 한글(EUC-KR) 문자열로 복원 - return new String(buffer, 0, readByte, "EUC-KR"); - } - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/DynamicPayloadBuilder.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/DynamicPayloadBuilder.java deleted file mode 100644 index 62f4090b..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/DynamicPayloadBuilder.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.support; - -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; - -/** - * @package io.shinhanlife.dap.lib.adapter.support - * @className DynamicPayloadBuilder - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Service -public class DynamicPayloadBuilder { - - public String buildFixedLengthString(List> specList, Map data) throws Exception { - StringBuilder sb = new StringBuilder(); - - for (Map spec : specList) { - String name = (String) spec.get("name"); - // EIMS가 고정장을 요구할 경우를 대비한 len 파라미터 체크 (기본값 0 방어) - int len = spec.get("len") != null ? (Integer) spec.get("len") : 0; - String type = (String) spec.get("type"); - String rawValue = String.valueOf(data.getOrDefault(name, "")); - - // len 값이 없으면 변환 없이 바로 이어붙임 (JSON 통신용) - if (len == 0) { - sb.append(rawValue); - continue; - } - - // len 값이 있으면 고정장 통신 규칙 적용 - byte[] rawBytes = rawValue.getBytes("EUC-KR"); - if (rawBytes.length > len) { - throw new IllegalArgumentException(name + " 길이가 " + len + " 바이트를 초과합니다."); - } - - int padLength = len - rawBytes.length; - StringBuilder paddedValue = new StringBuilder(rawValue); - - if ("NUMBER".equalsIgnoreCase(type)) { - for (int i = 0; i < padLength; i++) paddedValue.insert(0, "0"); - } else { - for (int i = 0; i < padLength; i++) paddedValue.append(" "); - } - sb.append(paddedValue.toString()); - } - return sb.toString(); - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/DynamicSchemaValidator.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/DynamicSchemaValidator.java deleted file mode 100644 index 71167483..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/DynamicSchemaValidator.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.support; - -import org.springframework.stereotype.Service; -import java.util.List; -import java.util.Map; - -/** - * @package io.shinhanlife.dap.lib.adapter.support - * @className DynamicSchemaValidator - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Service -public class DynamicSchemaValidator { - - public boolean validate(List> specList, Map data, StringBuilder errorLog) { - for (Map spec : specList) { - String name = (String) spec.get("name"); - String type = (String) spec.get("type"); - boolean isRequired = spec.get("required") != null && (Boolean) spec.get("required"); - Object value = data.get(name); - - // 1. 필수값 체크 - if (isRequired && (value == null || String.valueOf(value).trim().isEmpty())) { - errorLog.append(String.format("[%s] 필드는 필수 입력 항목입니다. ", name)); - return false; - } - - // 2. 타입 체크 (값이 있을 때만) - if (value != null && !String.valueOf(value).trim().isEmpty()) { - if ("NUMBER".equalsIgnoreCase(type) && !String.valueOf(value).matches("-?\\d+(\\.\\d+)?")) { - errorLog.append(String.format("[%s] 필드는 숫자여야 합니다. ", name)); - return false; - } - } - } - return true; - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/MciTemplate.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/MciTemplate.java deleted file mode 100644 index a808d9f0..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/MciTemplate.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.support; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.shinhanlife.dap.lib.adapter.exception.MciCommunicationException; -import io.shinhanlife.dap.lib.adapter.sender.EimsSender; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * @package io.shinhanlife.dap.lib.adapter.support - * @className MciTemplate - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- *
- * 
- */ -@Slf4j -@Component -@RequiredArgsConstructor -public class MciTemplate { - - private final EimsSender httpEimsSender; - private final ObjectMapper objectMapper; - - /** - * MCI 인터페이스를 호출하고 결과를 지정된 타입으로 반환합니다. - * - * @param interfaceId 호출할 MCI 인터페이스 ID - * @param requestDto 요청 데이터 객체 - * @param responseType 응답을 매핑할 클래스 타입 - * @param 요청 객체 타입 - * @param 응답 객체 타입 - * @return 매핑된 응답 객체 - * @throws MciCommunicationException 통신 또는 파싱 실패 시 예외 발생 - */ - public R call(String interfaceId, T requestDto, Class responseType) { - log.info(" [MciTemplate] 시작 - Interface ID: {}", interfaceId); - - try { - String payload = objectMapper.writeValueAsString(requestDto); - log.debug(" [MciTemplate] 전송 페이로드: {}", payload); - - String responseJson = httpEimsSender.send(interfaceId, payload); - log.debug(" [MciTemplate] 수신 응답 JSON: {}", responseJson); - - R response = objectMapper.readValue(responseJson, responseType); - log.info(" [MciTemplate] 완료 - Interface ID: {}", interfaceId); - - return response; - - } catch (JsonProcessingException e) { - log.error(" [MciTemplate] JSON 변환 중 오류 발생", e); - throw new MciCommunicationException("MCI 통신 중 JSON 파싱 오류", e); - } catch (Exception e) { - log.error(" [MciTemplate] 통신 중 오류 발생", e); - throw new MciCommunicationException("MCI 통신 실패", e); - } - } -} diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/ResultStandardizer.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/ResultStandardizer.java deleted file mode 100644 index f3105928..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/ResultStandardizer.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.support; - -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.Map; - -/** - * @package io.shinhanlife.dap.lib.adapter.support - * @className ResultStandardizer - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Component -public class ResultStandardizer { - - // 오라클 조회 결과(Map)의 키 값을 카멜 케이스로 변환 (스키마 매핑) - public Map standardize(Map rawData) { - Map standardMap = new HashMap<>(); - - rawData.forEach((key, value) -> { - String camelKey = convertToCamelCase(key); - standardMap.put(camelKey, value); - }); - - return standardMap; - } - - private String convertToCamelCase(String snakeCase) { - if (snakeCase == null || snakeCase.isEmpty()) return snakeCase; - - StringBuilder result = new StringBuilder(); - boolean nextIsUpper = false; - - for (char c : snakeCase.toLowerCase().toCharArray()) { - if (c == '_') { - nextIsUpper = true; - } else { - result.append(nextIsUpper ? Character.toUpperCase(c) : c); - nextIsUpper = false; - } - } - return result.toString(); - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/TicketManager.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/TicketManager.java deleted file mode 100644 index 4339f9c9..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/support/TicketManager.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.support; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -/** - * @package io.shinhanlife.dap.lib.adapter.support - * @className TicketManager - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@Component -public class TicketManager { - - // 로컬 시뮬레이션을 위한 인메모리 저장소 (운영 환경에서는 Redis 등을 사용) - private final Map ticketStore = new ConcurrentHashMap<>(); - - // 가짜 EAI 작업(10초 대기)을 실행할 백그라운드 쓰레드 풀 - private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5); - - /** - * 새로운 비동기 작업을 접수하고 티켓을 발급합니다. - */ - public String issueTicket(String interfaceId, String payload) { - String ticketId = "TICKET-" + UUID.randomUUID().toString(); - - Ticket ticket = new Ticket(); - ticket.setTicketId(ticketId); - ticket.setStatus("PROCESSING"); - ticket.setMessage("EAI 시스템에서 데이터 처리 중입니다..."); - - ticketStore.put(ticketId, ticket); - - log.info(" [TicketManager] 비동기 작업 티켓 발급 완료: {}", ticketId); - - // 10초 뒤에 자동으로 작업을 완료 상태로 변경하는 백그라운드 시뮬레이터 실행 - simulateEaiProcessing(ticketId, interfaceId); - - return ticketId; - } - - /** - * 특정 티켓의 현재 상태를 조회합니다. - */ - public Ticket getTicketStatus(String ticketId) { - return ticketStore.getOrDefault(ticketId, new Ticket("NOT_FOUND", "해당 티켓을 찾을 수 없습니다.")); - } - - /** - * 10초 뒤에 상태를 COMPLETED로 변경하여 진짜 EAI 배치가 끝난 것처럼 흉내냅니다. - */ - private void simulateEaiProcessing(String ticketId, String interfaceId) { - scheduler.schedule(() -> { - Ticket ticket = ticketStore.get(ticketId); - if (ticket != null) { - ticket.setStatus("COMPLETED"); - ticket.setMessage("EAI 배치가 정상적으로 완료되었습니다."); - // 가짜 최종 결과 데이터 주입 - ticket.setResultData("{\"resultCode\":\"0000\", \"interfaceId\":\"" + interfaceId + "\", \"processedRecords\":50000}"); - ticketStore.put(ticketId, ticket); - log.info(" [TicketManager] EAI 비동기 작업 시뮬레이션 완료! (Ticket: {})", ticketId); - } - }, 10, TimeUnit.SECONDS); // 10초 지연 - } - - // 티켓 상태를 담을 내부 DTO 클래스 - @lombok.Data - public static class Ticket { - private String ticketId; - private String status; // PROCESSING, COMPLETED, FAILED, NOT_FOUND - private String message; - private String resultData; // 최종 완료 시 담길 데이터 - - public Ticket() {} - public Ticket(String status, String message) { - this.status = status; - this.message = message; - } - } -} diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockEimsHttpServer.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockEimsHttpServer.java deleted file mode 100644 index 5fa174ce..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockEimsHttpServer.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.test; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.core.io.ClassPathResource; -import java.util.Map; - -/** - * @package io.shinhanlife.dap.lib.adapter.test - * @className MockEimsHttpServer - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@RestController -@RequestMapping("/api") -public class MockEimsHttpServer { - - private final ObjectMapper objectMapper; - - public MockEimsHttpServer(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - @PostMapping("/gateway") - public Map mockEimsReceiver( - @RequestHeader(value = "X-Trace-Id", required = false) String traceId, - @RequestBody Map request) { - - String interfaceId = (String) request.get("interfaceId"); - String data = (String) request.get("data"); - - log.info(" [가짜 EIMS 서버] HTTP 요청 수신 완료!"); - log.info(" 수신된 Trace-ID (거래고유번호): {}", traceId != null ? traceId : "없음"); - log.info(" 인터페이스ID: {}, 데이터: [{}]", interfaceId, data); - - // No-Code Mock 응답 생성 (JSON 설정 파일 기반) - try { - ClassPathResource resource = new ClassPathResource("mock-responses.json"); - Map mockDataMap = objectMapper.readValue(resource.getInputStream(), Map.class); - - if (mockDataMap.containsKey(interfaceId)) { - return (Map) mockDataMap.get(interfaceId); - } - } catch (Exception e) { - log.warn("JSON 파싱 에러 또는 mock 파일 읽기 실패 (기본 응답 반환)", e); - } - - // 기본 응답 - return Map.of( - "status", "404", - "message", "MOCK 데이터가 정의되지 않았습니다.", - "receivedLength", data.length() - ); - } - - @PostMapping("/mock/esb/api") - public String mockEsbReceiver(@RequestBody String xmlPayload) { - log.info(" [가짜 ESB 서버] MCI/ESB 요청 수신 완료!"); - log.info(" 수신된 XML 전문: {}", xmlPayload); - - // MciEimsSender가 기대하는 JSON 변환용 XML 포맷 응답 - return "SUCCESSMOCK_MCI_EIMS_RECEIVE_SUCCESS정상 처리되었습니다."; - } - - @PostMapping("/mock/esb/string") - public String mockEsbStringReceiver(@RequestBody(required = false) String payload) { - log.info(" [가짜 ESB 서버] MCI String 요청 수신 완료!"); - log.info(" 수신된 String 전문: {}", payload); - - // MciSampleStringResponse 에 맞게 고정 길이 응답 생성 - // name (10), age (3), joinDate (8), statusCode (2) - // targetList (30) -> MciSampleTargetDto (itemCode 5, itemValue 5) x 3 - return "홍길동 03020260901OKA0001B0001A0002B0002A0003B0003"; - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockEimsTcpServer.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockEimsTcpServer.java deleted file mode 100644 index 32f9d342..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockEimsTcpServer.java +++ /dev/null @@ -1,128 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.test; - -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.event.EventListener; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; - -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ServerSocket; -import java.net.Socket; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.core.io.ClassPathResource; -import java.util.Map; - -@Slf4j -@Component -// 대신 "local" 환경(application-local.properties)에서만 가짜 소켓 서버가 켜지도록 보장합니다. -/** - * @package io.shinhanlife.dap.lib.adapter.test - * @className MockEimsTcpServer - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Profile("local") -public class MockEimsTcpServer { - - private final ObjectMapper objectMapper; - - public MockEimsTcpServer(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - @Value("${eims.tcp.port:8090}") - private int port; - - @Value("${server.port:8081}") - private int serverPort; - - private ServerSocket serverSocket; - private boolean running = true; - - @EventListener(ApplicationReadyEvent.class) - public void startTcpServer() { - // MSA 환경에서는 각 툴 Pod 내부에서 독립적인 가짜 EIMS 서버가 실행되도록 허용 - log.info(" [가짜 EIMS 서버] 로컬 테스트 환경용 EIMS TCP 서버 가동을 준비합니다."); - - // Java 21 가상 스레드를 사용하여 메인 서버 가동에 방해 없이 백그라운드에서 실행 - Thread.ofVirtual().start(() -> { - try { - serverSocket = new ServerSocket(port); - log.info(" [가짜 EIMS 서버] 로컬 TCP 소켓 서버 가동 완료 (Port: {})", port); - - while (running) { - Socket clientSocket = serverSocket.accept(); - - // 연결된 요청을 별도 스레드로 처리 - Thread.ofVirtual().start(() -> handleClient(clientSocket)); - } - } catch (Exception e) { - if (running) log.error(" 가짜 TCP 서버 에러: {}", e.getMessage()); - } - }); - } - - private void handleClient(Socket socket) { - try (socket) { - InputStream is = socket.getInputStream(); - OutputStream os = socket.getOutputStream(); - - byte[] buffer = new byte[4096]; - int readByte = is.read(buffer); - - if (readByte != -1) { - String receivedData = new String(buffer, 0, readByte, "EUC-KR"); - log.info(" [가짜 EIMS 서버] TCP 데이터 수신 완료!"); - log.info(" 수신된 전문: [{}]", receivedData); - - String responseData = "{\"status\":\"404\",\"message\":\"MOCK 데이터가 정의되지 않았습니다.\"}"; - try { - int braceIndex = receivedData.indexOf('{'); - String interfaceId = ""; - if (braceIndex > 0) { - interfaceId = receivedData.substring(0, braceIndex).trim(); - } else if (braceIndex == -1) { - interfaceId = receivedData.trim(); - } - - ClassPathResource resource = new ClassPathResource("mock-responses.json"); - Map mockDataMap = objectMapper.readValue(resource.getInputStream(), Map.class); - - if (mockDataMap.containsKey(interfaceId)) { - responseData = objectMapper.writeValueAsString(mockDataMap.get(interfaceId)); - } - } catch (Exception e) { - log.warn("TCP JSON 파싱 에러 또는 파일 읽기 실패", e); - } - - // 응답 데이터 송신 - os.write(responseData.getBytes("EUC-KR")); - os.flush(); - } - } catch (Exception e) { - log.error(" 가짜 TCP 클라이언트 처리 에러: {}", e.getMessage()); - } - } - - @PreDestroy - public void stopTcpServer() { - this.running = false; - try { - if (serverSocket != null) serverSocket.close(); - } catch (Exception ignored) {} - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockJspServer.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockJspServer.java deleted file mode 100644 index f1939d1e..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/test/MockJspServer.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.test; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.*; - -import java.util.Map; - -/** - * @package io.shinhanlife.dap.lib.adapter.test - * @className MockJspServer - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -@RestController -@RequestMapping("/mock") -public class MockJspServer { - - // ========================================== - // 1. Form Data 방식 테스트 수신부 (jsp-form) - // ========================================== - @PostMapping(value = "/jsp-form", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) - public String mockJspFormReceiver( - @RequestParam("interfaceId") String interfaceId, - @RequestParam("data") String data) { - - log.info(" [가짜 JSP 서버] 폼 데이터(Form) 수신 완료!"); - log.info(" 파라미터 파싱 확인 - ID: {}, DATA: [{}]", interfaceId, data); - - // 실제 JSP 서버처럼 앞뒤에 의미 없는 줄바꿈(엔터)과 공백을 잔뜩 넣어서 리턴합니다. - return "\n\n SUCCESS_FROM_MOCK_JSP_FORM \n\n"; - } - - // ========================================== - // 2. JSON 방식 테스트 수신부 (jsp-json) - // ========================================== - @PostMapping(value = "/jsp-json", consumes = MediaType.APPLICATION_JSON_VALUE) - public String mockJspJsonReceiver(@RequestBody Map request) { - - String interfaceId = request.get("interfaceId"); - String data = request.get("data"); - - log.info(" [가짜 JSP 서버] 제이슨(JSON) 수신 완료!"); - log.info(" JSON 파싱 확인 - ID: {}, DATA: [{}]", interfaceId, data); - - // 여기도 마찬가지로 쓰레기 여백을 넣어줍니다. - return "\n\n SUCCESS_FROM_MOCK_JSP_JSON \n\n"; - } -} \ No newline at end of file diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/util/LegacyDataTransformer.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/util/LegacyDataTransformer.java deleted file mode 100644 index 88aef4e0..00000000 --- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/adapter/util/LegacyDataTransformer.java +++ /dev/null @@ -1,92 +0,0 @@ -package io.shinhanlife.dap.lib.adapter.util; - -import lombok.extern.slf4j.Slf4j; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @package io.shinhanlife.dap.lib.adapter.util - * @className LegacyDataTransformer - * @description AX HUB 시스템 처리 클래스 - * @author 0986406 - * @create 2026.09.01 - *
- * ---------- 개정이력 ----------
- * 수정일      수정자    수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01  0986406    최초생성
- * 
- * 
- */ -@Slf4j -public class LegacyDataTransformer { - - /** - * AI 모델이 보낸 자유로운 형태의 data를 레거시 시스템 규격(spec)에 맞게 강제 변환합니다. - * - * @param data AI가 보낸 파라미터 맵 - * @param spec 레거시 시스템이 요구하는 파라미터 스펙 (name, type, maxLength, defaultValue, required 등) - * @return 엄격하게 정제된 파라미터 맵 - */ - public static Map transform(Map data, List> spec) { - if (spec == null || spec.isEmpty()) { - return data != null ? data : new HashMap<>(); - } - - Map transformedData = new HashMap<>(); - - for (Map fieldSpec : spec) { - String fieldName = (String) fieldSpec.get("name"); - if (fieldName == null) continue; - - Object rawValue = data != null ? data.get(fieldName) : null; - Object finalValue = rawValue; - - // 1. 기본값(Default Value) 주입 - if (finalValue == null && fieldSpec.containsKey("defaultValue")) { - finalValue = fieldSpec.get("defaultValue"); - log.debug(" [DataTransformer] '{}' 필드 누락 -> 기본값 '{}' 주입", fieldName, finalValue); - } - - // 2. 강제 형변환 (Type Coercion) - String type = (String) fieldSpec.getOrDefault("type", "string"); - if (finalValue != null) { - if ("string".equalsIgnoreCase(type) && !(finalValue instanceof String)) { - finalValue = String.valueOf(finalValue); - log.debug(" [DataTransformer] '{}' 필드 강제 String 형변환", fieldName); - } else if ("number".equalsIgnoreCase(type) && finalValue instanceof String) { - try { - finalValue = Long.parseLong((String) finalValue); - log.debug(" [DataTransformer] '{}' 필드 강제 Number 형변환", fieldName); - } catch (NumberFormatException e) { - log.warn(" [DataTransformer] '{}' 필드 Number 형변환 실패. 기존 값 유지", fieldName); - } - } - } - - // 3. 길이 제한 (Truncation / MaxLength) - if (finalValue instanceof String && fieldSpec.containsKey("maxLength")) { - int maxLength = (Integer) fieldSpec.get("maxLength"); - String strVal = (String) finalValue; - if (strVal.length() > maxLength) { - finalValue = strVal.substring(0, maxLength); - log.warn(" [DataTransformer] '{}' 필드 길이 초과! {}자로 강제 자름 (Truncated)", fieldName, maxLength); - } - } - - // 4. 필수값(Required) 누락 체크 (에러를 던지지 않고 빈 문자열 강제 주입하여 레거시 팅김 방지) - boolean isRequired = (Boolean) fieldSpec.getOrDefault("required", false); - if (isRequired && finalValue == null) { - log.error(" [DataTransformer] 필수 필드 '{}' 누락! 강제 공백 주입하여 시스템 장애 방지", fieldName); - finalValue = "string".equalsIgnoreCase(type) ? "" : 0; - } - - if (finalValue != null) { - transformedData.put(fieldName, finalValue); - } - } - - return transformedData; - } -} \ No newline at end of file diff --git a/dap-was-oth/src/main/resources/application-dev.yml b/dap-was-oth/src/main/resources/application-dev.yml index b1a82a17..658693ab 100644 --- a/dap-was-oth/src/main/resources/application-dev.yml +++ b/dap-was-oth/src/main/resources/application-dev.yml @@ -13,20 +13,3 @@ spring: import: - classpath:glow/application-glow.yml - classpath:glow/application-glow-dev.yml - -eims: - http: - url: http://localhost:${server.port}/api/gateway - tcp: - host: 127.0.0.1 - port: 8090 - timeout: 5000 - jsp: - form: - url: http://localhost:${server.port}/mock/jsp-form - json: - url: http://localhost:${server.port}/mock/jsp-json - mci: - url: http://localhost:${server.port}/api/mock/esb/api - mcistring: - url: http://localhost:${server.port}/api/mock/esb/string diff --git a/dap-was-oth/src/main/resources/application-local.yml b/dap-was-oth/src/main/resources/application-local.yml index 1e73a06d..20256677 100644 --- a/dap-was-oth/src/main/resources/application-local.yml +++ b/dap-was-oth/src/main/resources/application-local.yml @@ -15,23 +15,6 @@ spring: console: enabled: true -eims: - http: - url: http://localhost:${server.port}/api/gateway - tcp: - host: 127.0.0.1 - port: 8090 - timeout: 5000 - jsp: - form: - url: http://localhost:${server.port}/mock/jsp-form - json: - url: http://localhost:${server.port}/mock/jsp-json - mci: - url: http://localhost:${server.port}/api/mock/esb/api - mcistring: - url: http://localhost:${server.port}/api/mock/esb/string - mcp: security: tenant-domains: diff --git a/dap-was-sms/src/main/resources/application-dev.yml b/dap-was-sms/src/main/resources/application-dev.yml index 638b9e58..dfee070e 100644 --- a/dap-was-sms/src/main/resources/application-dev.yml +++ b/dap-was-sms/src/main/resources/application-dev.yml @@ -13,20 +13,3 @@ spring: import: - classpath:glow/application-glow.yml - classpath:glow/application-glow-dev.yml - -eims: - http: - url: http://localhost:${server.port}/api/gateway - tcp: - host: 127.0.0.1 - port: 8090 - timeout: 5000 - jsp: - form: - url: http://localhost:${server.port}/mock/jsp-form - json: - url: http://localhost:${server.port}/mock/jsp-json - mci: - url: http://localhost:${server.port}/api/mock/esb/api - mcistring: - url: http://localhost:${server.port}/api/mock/esb/string diff --git a/dap-was-sms/src/main/resources/application-local.yml b/dap-was-sms/src/main/resources/application-local.yml index caa93c7f..d4add358 100644 --- a/dap-was-sms/src/main/resources/application-local.yml +++ b/dap-was-sms/src/main/resources/application-local.yml @@ -15,23 +15,6 @@ spring: console: enabled: true -eims: - http: - url: http://localhost:${server.port}/api/gateway - tcp: - host: 127.0.0.1 - port: 8090 - timeout: 5000 - jsp: - form: - url: http://localhost:${server.port}/mock/jsp-form - json: - url: http://localhost:${server.port}/mock/jsp-json - mci: - url: http://localhost:${server.port}/api/mock/esb/api - mcistring: - url: http://localhost:${server.port}/api/mock/esb/string - mcp: security: tenant-domains: