From ffb6dcac66897659181232e6f473789cc1e0918c Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 4 Feb 2026 13:52:04 +0900 Subject: [PATCH 01/22] =?UTF-8?q?OBP=20GwMetric=20=EB=82=A0=EC=A7=9C?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EB=8C=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rms/onl/apim/obp/ObpGwMetricAfterDTO.java | 137 ------------------ .../onl/apim/obp/ObpGwMetricController.java | 88 +---------- .../eai/rms/onl/apim/obp/ObpGwMetricDTO.java | 131 +++++++++++------ .../onl/apim/obp/ObpGwMetricManService.java | 17 +-- 4 files changed, 86 insertions(+), 287 deletions(-) delete mode 100644 src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricAfterDTO.java diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricAfterDTO.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricAfterDTO.java deleted file mode 100644 index 6ea4219..0000000 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricAfterDTO.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.eactive.eai.rms.onl.apim.obp; - -import com.eactive.apim.portal.obp.entity.ObpGwMetric; -import com.google.gson.annotations.SerializedName; -import lombok.Data; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -/** - * ObpGwMetric JSON 응답 DTO (after-timeslice 엔드포인트용) - * - *

예시 출력 형식에 맞춤:

- *
- * {
- *   "apiId": "1ba56b82-a9c9-41e2-b3ab-ff9377667d56",
- *   "apiName": "[해외송금] 가상계좌 입금실패 정보조회",
- *   "apiRoutingUri": "/api/obs/remittance/remittancefail*",
- *   "httpMethod": "POST",
- *   "orgId": "ORG001",
- *   "partnerCode": "000008-01",
- *   "appId": "APP001",
- *   "appKey": "l7xx2fd7efc523794be5a1b126873a5de613",
- *   "requestUri": "-1",
- *   "timeslice": "2025-12-02T03:00:00.000+0000",
- *   "attemptedCount": 3,
- *   "completedCount": 3
- * }
- * 
- */ -@Data -public class ObpGwMetricAfterDTO { - - private static final DateTimeFormatter ISO8601_MILLIS_FORMATTER = - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'+0000'"); - - @SerializedName("apiId") - private String apiId; - - @SerializedName("apiName") - private String apiName; - - @SerializedName("apiRoutingUri") - private String apiRoutingUri; - - @SerializedName("httpMethod") - private String httpMethod; - - @SerializedName("orgId") - private String orgId; - - @SerializedName("partnerCode") - private String partnerCode; - - @SerializedName("appId") - private String appId; - - @SerializedName("appKey") - private String appKey; - - @SerializedName("requestUri") - private String requestUri; - - @SerializedName("timeslice") - private String timeslice; - - @SerializedName("attemptedCount") - private Long attemptedCount; - - @SerializedName("completedCount") - private Long completedCount; - - - // AS-IS 호환을 위한 필드 (null 값으로 리턴) - @SerializedName("clusterHostname") - private String clusterHostname = null; - - @SerializedName("apiDatabaseId") - private Integer apiDatabaseId = null; - - @SerializedName("unauthorizedCount") - private Integer authorizedCount = null; - - @SerializedName("backMinTime") - private Integer backMinTime = null; - - @SerializedName("backMaxTime") - private Integer backMaxTime = null; - - @SerializedName("backSumTime") - private Integer backSumTime = null; - - @SerializedName("frontMinTime") - private Integer frontMinTime = null; - - @SerializedName("frontMaxTime") - private Integer frontMaxTime = null; - - @SerializedName("frontSumTime") - private Integer frontSumTime = null; - - - /** - * Entity → DTO 변환 - */ - public static ObpGwMetricAfterDTO from(ObpGwMetric entity) { - ObpGwMetricAfterDTO dto = new ObpGwMetricAfterDTO(); - dto.setApiId(entity.getApiId()); - dto.setApiName(entity.getApiName()); - dto.setApiRoutingUri(entity.getUri()); - dto.setHttpMethod(entity.getMethod()); - dto.setOrgId(entity.getOrgId()); - dto.setPartnerCode(entity.getPartnerCode()); - dto.setAppId(entity.getAppId()); - dto.setAppKey(entity.getClientId()); - dto.setRequestUri(entity.getRequestUri()); - dto.setTimeslice(formatTimeslice(entity.getTimeslice())); - dto.setAttemptedCount(entity.getAttemptedCount()); - dto.setCompletedCount(entity.getCompletedCount()); - - - // 260224 - AS-IS 개발자포탈에서 쓰이던 ID 필드 값 삭제(OBP 디비 필드 형식과 맞지 않음) - dto.setOrgId(null); - dto.setAppId(null); - - return dto; - } - - /** - * timeslice를 ISO8601 형식 (밀리초 포함) + UTC 시간대로 변환 - * 예: 2025-12-02T03:00:00.000+0000 - */ - private static String formatTimeslice(LocalDateTime timeslice) { - if (timeslice == null) return null; - return timeslice.format(ISO8601_MILLIS_FORMATTER); - } -} diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java index 1e2dbdc..7acd524 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java @@ -85,7 +85,7 @@ public class ObpGwMetricController extends BaseRestController { DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.MONITORING)); // 5. 데이터 조회 - List metrics = obpGwMetricManService.findByTimesliceAfter(timesliceHour); + List metrics = obpGwMetricManService.findByTimesliceAfter(timesliceHour); // 6. JSON 변환 String json = toJson(metrics, pretty); @@ -104,90 +104,4 @@ public class ObpGwMetricController extends BaseRestController { throw new BizException("Failed to retrieve metrics: " + e.getMessage(), e); } } - - /** - * 특정 시간대의 GwMetric 데이터 조회 - * - * @param datetime 조회 시간대 (형식: yyyy-MM-dd_HH:mm:ss) - * @param pretty true면 JSON 들여쓰기 적용 - * @return JSON Array - */ - @RequestMapping( - value = "/kjb/gw-metrics/{datetime}.json", - produces = JSON_CONTENT_TYPE - ) - @ResponseBody - public String getGwMetrics( - @PathVariable("datetime") String datetime, - @RequestParam(value = "pretty", required = false) Boolean pretty) { - log.debug("GwMetric 조회 요청: datetime={}, pretty={}", datetime, pretty); - long startTime = System.currentTimeMillis(); - - try { - // Path 파라미터 파싱 - LocalDateTime targetTime = LocalDateTime.parse(datetime, PATH_FORMATTER); - LocalDateTime timeslice = targetTime.withMinute(0).withSecond(0).withNano(0); - - // 조회 - List metrics = obpGwMetricManService.findByTimeslice(timeslice); - - // JSON 변환 (GSON) - String json = toJson(metrics, pretty); - - long elapsed = System.currentTimeMillis() - startTime; - log.info("GwMetric 조회 완료: datetime={}, count={}, size={}, elapsed={}ms", - datetime, metrics.size(), getFormattedJsonSize(json), elapsed); - - return json; - - } catch (Exception e) { - log.error("GwMetric 조회 실패: datetime={}", datetime, e); - throw e; - } - } - - /** - * 특정 시간대 이후의 GwMetric 데이터 조회 - * - * @param datetime 조회 시작 시간대 (형식: yyyy-MM-dd_HH:mm:ss) - * @param pretty true면 JSON 들여쓰기 적용 - * @return JSON Array (해당 시간대 이후의 모든 지표) - * @deprecated Use {@link #getGwMetricsAfterTimesliceV2(String, String, Boolean)} instead. - * Migrate to /api/metrics/afterTimeslice/{timeslice}/{providerCode} - * Will be removed in next major version. - */ - @Deprecated - @RequestMapping( - value = "/kjb/gw-metrics/after-timeslice/{datetime}.json", - produces = JSON_CONTENT_TYPE - ) - @ResponseBody - public String getGwMetricsAfterTimeslice( - @PathVariable("datetime") String datetime, - @RequestParam(value = "pretty", required = false) Boolean pretty) { - log.debug("GwMetric after-timeslice 조회 요청: datetime={}, pretty={}", datetime, pretty); - long startTime = System.currentTimeMillis(); - - try { - // Path 파라미터 파싱 - LocalDateTime targetTime = LocalDateTime.parse(datetime, PATH_FORMATTER); - LocalDateTime timeslice = targetTime.withMinute(0).withSecond(0).withNano(0); - - // 조회 - List metrics = obpGwMetricManService.findByTimesliceAfter(timeslice); - - // JSON 변환 (GSON) - String json = toJson(metrics, pretty); - - long elapsed = System.currentTimeMillis() - startTime; - log.info("GwMetric after-timeslice 조회 완료: datetime={}, count={}, size={}, elapsed={}ms", - datetime, metrics.size(), getFormattedJsonSize(json), elapsed); - - return json; - - } catch (Exception e) { - log.error("GwMetric after-timeslice 조회 실패: datetime={}", datetime, e); - throw e; - } - } } diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricDTO.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricDTO.java index c0d0a0a..976e678 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricDTO.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricDTO.java @@ -8,32 +8,61 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** - * ObpGwMetric JSON 응답 DTO - * Entity 주석에 명시된 JSON Key 사용 + * ObpGwMetric JSON 응답 DTO (after-timeslice 엔드포인트용) + * + *

예시 출력 형식에 맞춤:

+ *
+ * {
+ *   "apiId": "1ba56b82-a9c9-41e2-b3ab-ff9377667d56",
+ *   "apiName": "[해외송금] 가상계좌 입금실패 정보조회",
+ *   "apiRoutingUri": "/api/obs/remittance/remittancefail*",
+ *   "httpMethod": "POST",
+ *   "orgId": "ORG001",
+ *   "partnerCode": "000008-01",
+ *   "appId": "APP001",
+ *   "appKey": "l7xx2fd7efc523794be5a1b126873a5de613",
+ *   "requestUri": "-1",
+ *   "timeslice": "2025-12-02T03:00:00.000+0000",
+ *   "attemptedCount": 3,
+ *   "completedCount": 3
+ * }
+ * 
*/ @Data public class ObpGwMetricDTO { - private static final DateTimeFormatter ISO8601_FORMATTER = - DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'+0000'"); + private static final DateTimeFormatter ISO8601_MILLIS_FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'+0900'"); - @SerializedName("appKey") - private String clientId; - - @SerializedName("clusterHostname") - private String hostname; - - @SerializedName("timeslice") - private String timeslice; + @SerializedName("apiId") + private String apiId; @SerializedName("apiName") private String apiName; @SerializedName("apiRoutingUri") - private String uri; + private String apiRoutingUri; @SerializedName("httpMethod") - private String method; + private String httpMethod; + + @SerializedName("orgId") + private String orgId; + + @SerializedName("partnerCode") + private String partnerCode; + + @SerializedName("appId") + private String appId; + + @SerializedName("appKey") + private String appKey; + + @SerializedName("requestUri") + private String requestUri; + + @SerializedName("timeslice") + private String timeslice; @SerializedName("attemptedCount") private Long attemptedCount; @@ -41,62 +70,68 @@ public class ObpGwMetricDTO { @SerializedName("completedCount") private Long completedCount; - // EAPIM ID - @SerializedName("apiIdByEapim") - private String apiId; - @SerializedName("appIdByEapim") - private String appId; + // AS-IS 호환을 위한 필드 (null 값으로 리턴) + @SerializedName("clusterHostname") + private String clusterHostname = null; - @SerializedName("orgIdByEapim") - private String orgId; + @SerializedName("apiDatabaseId") + private Integer apiDatabaseId = null; - // AS-IS 호환용 필드 - @SerializedName("appId") - private String appIdByCaPortal; + @SerializedName("unauthorizedCount") + private Integer authorizedCount = null; - @SerializedName("portalOrgId") - private String orgIdByCaPortal; + @SerializedName("backMinTime") + private Integer backMinTime = null; - @SerializedName("portalApiId") - private String apiIdByCaPortal; + @SerializedName("backMaxTime") + private Integer backMaxTime = null; - @SerializedName("partnerCode") - private String partnerCode; + @SerializedName("backSumTime") + private Integer backSumTime = null; + + @SerializedName("frontMinTime") + private Integer frontMinTime = null; + + @SerializedName("frontMaxTime") + private Integer frontMaxTime = null; + + @SerializedName("frontSumTime") + private Integer frontSumTime = null; - @SerializedName("apiId") - private String apiIdByCa; /** * Entity → DTO 변환 */ public static ObpGwMetricDTO from(ObpGwMetric entity) { ObpGwMetricDTO dto = new ObpGwMetricDTO(); - dto.setClientId(entity.getClientId()); - dto.setHostname(entity.getHostname()); - dto.setTimeslice(formatTimeslice(entity.getTimeslice())); + dto.setApiId(entity.getApiId()); dto.setApiName(entity.getApiName()); - dto.setUri(entity.getUri()); - dto.setMethod(entity.getMethod()); + dto.setApiRoutingUri(entity.getUri()); + dto.setHttpMethod(entity.getMethod()); + dto.setOrgId(entity.getOrgId()); + dto.setPartnerCode(entity.getPartnerCode()); + dto.setAppId(entity.getAppId()); + dto.setAppKey(entity.getClientId()); + dto.setRequestUri(entity.getRequestUri()); + dto.setTimeslice(formatTimeslice(entity.getTimeslice())); dto.setAttemptedCount(entity.getAttemptedCount()); dto.setCompletedCount(entity.getCompletedCount()); - dto.setApiId(entity.getApiId()); - dto.setAppId(entity.getAppId()); - dto.setOrgId(entity.getOrgId()); - dto.setAppIdByCaPortal(entity.getAppIdByCaPortal()); - dto.setOrgIdByCaPortal(entity.getOrgIdByCaPortal()); - dto.setApiIdByCaPortal(entity.getApiIdByCaPortal()); - dto.setPartnerCode(entity.getPartnerCode()); - dto.setApiIdByCa(entity.getApiIdByCa()); + + + // 260224 - AS-IS 개발자포탈에서 쓰이던 ID 필드 값 삭제(OBP 디비 필드 형식과 맞지 않음) + dto.setOrgId(null); + dto.setAppId(null); + return dto; } /** - * timeslice를 ISO8601 형식 + UTC 시간대로 변환 - * 예: 2025-12-02T01:00:00+0000 + * timeslice를 ISO8601 형식 (밀리초 포함) + UTC 시간대로 변환 + * 예: 2025-12-02T03:00:00.000+0000 */ private static String formatTimeslice(LocalDateTime timeslice) { if (timeslice == null) return null; - return timeslice.format(ISO8601_FORMATTER); + return timeslice.format(ISO8601_MILLIS_FORMATTER); } } diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java index 814f13c..72e5c65 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java @@ -311,30 +311,17 @@ public class ObpGwMetricManService extends BaseService { return entity; } - /** - * 특정 시간대의 GwMetric 데이터 조회 (REST API용) - */ - public List findByTimeslice(LocalDateTime timeslice) { - DataSourceContextHolder.setDataSourceType( - DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.MONITORING)); - - List entities = obpGwMetricService.findByTimeslice(timeslice); - return entities.stream() - .map(ObpGwMetricDTO::from) - .collect(Collectors.toList()); - } - /** * 특정 시간대 이후의 GwMetric 데이터 조회 (REST API용) * after-timeslice 엔드포인트에서 사용 */ - public List findByTimesliceAfter(LocalDateTime timeslice) { + public List findByTimesliceAfter(LocalDateTime timeslice) { DataSourceContextHolder.setDataSourceType( DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.MONITORING)); List entities = obpGwMetricService.findByTimesliceGreaterThanEqual(timeslice); return entities.stream() - .map(ObpGwMetricAfterDTO::from) + .map(ObpGwMetricDTO::from) .collect(Collectors.toList()); } From 1222a3b0134c37aaf06b184cce5d907a86b6eb63 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 4 Feb 2026 17:49:41 +0900 Subject: [PATCH 02/22] =?UTF-8?q?OBP=20GwMetric=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=EC=A0=9C=ED=95=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onl/apim/obp/ObpGwMetricService.java | 21 +++++++++++++++++++ .../onl/apim/obp/ObpGwMetricController.java | 2 +- .../onl/apim/obp/ObpGwMetricManService.java | 18 +++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java index da1d6ef..4c2b803 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricService.java @@ -2,6 +2,9 @@ package com.eactive.eai.rms.data.entity.onl.apim.obp; import com.eactive.apim.portal.obp.entity.ObpGwMetric; import com.eactive.apim.portal.obp.repository.ObpGwMetricRepository; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -50,6 +53,24 @@ public class ObpGwMetricService { return repository.findByTimesliceGreaterThanEqual(timeslice); } + /** + * 특정 시간대 이후의 GwMetric 데이터 조회 (row 수 제한) + * + * @param timeslice 조회 시작 시간대 + * @param limit 최대 조회 건수 (0이면 무제한) + * @return 조회된 지표 목록 (timeslice ASC 정렬) + */ + public List findByTimesliceGreaterThanEqual(LocalDateTime timeslice, int limit) { + if (limit <= 0) { + // 무제한 조회 (기존 동작 유지) + return repository.findByTimesliceGreaterThanEqual(timeslice); + } else { + // 제한적 조회 (timeslice ASC 정렬 + limit) + Pageable pageable = PageRequest.of(0, limit, Sort.by(Sort.Order.asc("timeslice"))); + return repository.findByTimesliceGreaterThanEqual(timeslice, pageable); + } + } + /** * 엔티티 저장 (INSERT 또는 UPDATE) */ diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java index 7acd524..5f93a4c 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java @@ -55,7 +55,7 @@ public class ObpGwMetricController extends BaseRestController { produces = JSON_CONTENT_TYPE ) @ResponseBody - public String getGwMetricsAfterTimesliceV2( + public String getGwMetricsAfterTimeslice( @PathVariable("timeslice") String timeslice, @PathVariable("providerCode") String providerCode, @RequestParam(value = "pretty", required = false) Boolean pretty) { diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java index 72e5c65..a815b2c 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricManService.java @@ -10,6 +10,7 @@ import com.eactive.eai.rms.common.util.CommonUtil; import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricJpaDAO; import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricService; import com.eactive.eai.rms.data.entity.onl.apim.obp.ObpGwMetricVO; +import com.eactive.ext.kjb.common.KjbPropertyHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataIntegrityViolationException; @@ -314,12 +315,27 @@ public class ObpGwMetricManService extends BaseService { /** * 특정 시간대 이후의 GwMetric 데이터 조회 (REST API용) * after-timeslice 엔드포인트에서 사용 + * + *

조회 범위 제한:

+ *
    + *
  • pageSize 값을 KjbProperty.apiGwMetricPageSize에서 읽어옴 (기본값: 10000)
  • + *
  • 0일 경우 무제한 조회
  • + *
  • 정렬: timeslice ASC (오래된 데이터부터 반환)
  • + *
*/ public List findByTimesliceAfter(LocalDateTime timeslice) { DataSourceContextHolder.setDataSourceType( DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.MONITORING)); - List entities = obpGwMetricService.findByTimesliceGreaterThanEqual(timeslice); + // KjbProperty에서 pageSize 읽기 + Integer pageSize = KjbPropertyHolder.get().getApiGwMetricPageSize(); + if (pageSize == null) { + pageSize = 10000; // 기본값 (null 안전 처리) + } + + // Service 호출 (limit 적용) + List entities = obpGwMetricService.findByTimesliceGreaterThanEqual(timeslice, pageSize); + return entities.stream() .map(ObpGwMetricDTO::from) .collect(Collectors.toList()); From 809b271ba500327f75088ffd10476038b4c3079d Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 4 Feb 2026 18:10:33 +0900 Subject: [PATCH 03/22] =?UTF-8?q?OBP=20GwMetric=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java index 5f93a4c..18a4f48 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpGwMetricController.java @@ -60,7 +60,7 @@ public class ObpGwMetricController extends BaseRestController { @PathVariable("providerCode") String providerCode, @RequestParam(value = "pretty", required = false) Boolean pretty) { - log.debug("GwMetric after-timeslice 조회 요청 (v2): timeslice={}, providerCode={}, pretty={}", + log.debug("GwMetric after-timeslice 조회 요청: timeslice={}, providerCode={}, pretty={}", timeslice, providerCode, pretty); long startTime = System.currentTimeMillis(); From f42af436d3ab98c99654c7934ba190c0c66e7c74 Mon Sep 17 00:00:00 2001 From: daekuk Date: Thu, 5 Feb 2026 10:01:38 +0900 Subject: [PATCH 04/22] =?UTF-8?q?-=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=99=94=EB=A9=B4=EC=97=90=EC=84=9C=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=A0=80=EC=9E=A5=20=ED=9B=84=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=EC=A0=95=EB=B3=B4=EB=A5=BC=20=ED=95=84?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=ED=99=95=EC=9D=B8=20=EB=B0=8F=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D?= =?UTF-8?q?=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?=ED=9B=84=20=EC=9E=90=EB=8F=99=20=ED=8C=9D=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jsp/onl/admin/rule/layoutManDetail.jsp | 12 +++++- .../apim/apiInterfaceManDetail.jsp | 40 +++++++++++++------ 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/WebContent/jsp/onl/admin/rule/layoutManDetail.jsp b/WebContent/jsp/onl/admin/rule/layoutManDetail.jsp index 8bcf3eb..4ab7166 100644 --- a/WebContent/jsp/onl/admin/rule/layoutManDetail.jsp +++ b/WebContent/jsp/onl/admin/rule/layoutManDetail.jsp @@ -375,10 +375,20 @@ url:url, data:postData, success:function(args){ - alert("<%=localeMessage.getString("common.saveMsg")%>"); if(isPop == "true"){ + alert("<%=localeMessage.getString("common.saveMsg")%> \n저장 후 변환정보 수정이 필수입니다. 변환정보를 확인하세요"); + const origin = window.location.origin; + + const layoutName = $("input[name='loutName']").val(); + + parent.postMessage({ + action: 'LAYOUT_SAVED_OPEN_TRANSFORM', + layoutPartName: '${param.layoutPartName}' + }, origin); + window.close(); } else { + alert("<%=localeMessage.getString("common.saveMsg")%>"); goNav(returnUrl); } }, diff --git a/WebContent/jsp/onl/transaction/apim/apiInterfaceManDetail.jsp b/WebContent/jsp/onl/transaction/apim/apiInterfaceManDetail.jsp index 4072679..7450ced 100644 --- a/WebContent/jsp/onl/transaction/apim/apiInterfaceManDetail.jsp +++ b/WebContent/jsp/onl/transaction/apim/apiInterfaceManDetail.jsp @@ -1153,27 +1153,41 @@ addLayoutBtnEvent('outboundErrResponse', '아웃바운드 오류', 'TGTE'); window.addEventListener('message', function(e) { + if (e.origin !== window.location.origin) return; + const popupMessage = e.data; - if('DELETE' == popupMessage.cmd){ - if('layout' == popupMessage.type){ - $('#'+popupMessage.layoutPartName).val(''); + if (!popupMessage) return; + + if ('DELETE' == popupMessage.cmd) { + if ('layout' == popupMessage.type) { + $('#' + popupMessage.layoutPartName).val(''); } - if('transform' == popupMessage.type){ + if ('transform' == popupMessage.type) { const partName = popupMessage.transformName.endsWith('REQ') ? '#requestTransform' : '#responseTransform'; $(partName).val(''); } transformSetStateChange(); + return; } - }); - $('#requestTransformButton').on("click", function(event) { - modifyTransform('request'); - }); - $('#responseTransformButton').on("click", function(event) { - modifyTransform('response'); - }); - $('#errResponseTransformButton').on("click", function(event) { - modifyTransform('errResponse'); + // 레이아웃 저장 후 변환 팝업 자동 실행 로직 + if (popupMessage.action === 'LAYOUT_SAVED_OPEN_TRANSFORM') { + + // 혹시 모를 popup창 닫기 + if ($('.ui-dialog-content').length > 0) { + $('.ui-dialog-content').dialog('close'); + } + + const part = popupMessage.layoutPartName || ""; + + if (part.includes('Request')) { + modifyTransform('request'); + } else if (part.includes('Response')) { + modifyTransform('response'); + } else if (part.includes('ErrResponse')) { + modifyTransform('errResponse'); + } + } }); // modifyLayout('inboundRequestLayout', '인바운드 요청', 'CBSS') From 71a3729e70fdbe4e0d1a4f361ac37214f559aaae Mon Sep 17 00:00:00 2001 From: daekuk Date: Thu, 5 Feb 2026 10:03:20 +0900 Subject: [PATCH 05/22] =?UTF-8?q?-=20tabs4(HTTP)=20=EA=B8=80=EC=9E=90?= =?UTF-8?q?=EA=B0=80=20=EB=84=88=EB=AC=B4=20=EA=B8=B8=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=ED=83=88=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EA=B0=9C=EC=84=A0=20-=20=EB=B0=95?= =?UTF-8?q?=EA=B8=B0=EC=84=AD=20=EC=9D=B4=EC=82=AC=EB=8B=98=20=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=20=EB=B0=98=EC=98=81,=20=ED=97=A4=EB=8D=94=EB=B6=80?= =?UTF-8?q?=20clipboard=20copy=20=EC=8B=9C=20=EC=A2=85=EB=A3=8C=20?= =?UTF-8?q?=EB=B9=84=ED=8A=B8=20@@=20=EC=A0=84=EC=97=90=20=EC=97=85?= =?UTF-8?q?=EB=AC=B4=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=A5=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=ED=95=98=EC=97=AC=20copy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tracking/trackingManDetail.jsp | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp b/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp index 2f5db38..d32d033 100644 --- a/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp +++ b/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp @@ -15,6 +15,14 @@ From 591ffa0c3568d540552ee42d1bcb228eab6dec90 Mon Sep 17 00:00:00 2001 From: daekuk1 Date: Mon, 9 Feb 2026 14:23:36 +0900 Subject: [PATCH 08/22] =?UTF-8?q?=ED=95=9C=EA=B8=80=20=EC=98=A4=EB=A5=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/layoutsync/LayoutSyncController.java | 71 ++++++++----------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/onl/manage/rule/layoutsync/LayoutSyncController.java b/src/main/java/com/eactive/eai/rms/onl/manage/rule/layoutsync/LayoutSyncController.java index 15604d6..b8df9ea 100644 --- a/src/main/java/com/eactive/eai/rms/onl/manage/rule/layoutsync/LayoutSyncController.java +++ b/src/main/java/com/eactive/eai/rms/onl/manage/rule/layoutsync/LayoutSyncController.java @@ -404,7 +404,7 @@ public class LayoutSyncController extends OnlBaseAnnotationController implements String resultS = null; ServletInputStream sis = null; - HashMap param = new HashMap(); + HashMap param = new HashMap<>(); String recvTime = DateUtil.getDateTime("yyyyMMddHHmmssSS").substring(0, 16); // 초기화 @@ -431,43 +431,33 @@ public class LayoutSyncController extends OnlBaseAnnotationController implements byte[] arg = new byte[request.getContentLength()]; int resultSet = 0; int total = 0; - try { - resultSet = sis.readLine(arg, 0, arg.length); - while(resultSet != -1) { - total += resultSet; - resultSet = sis.readLine(arg, total, 1024); - } + + String xmlContent = null; + try { + resultSet = sis.readLine(arg, 0, arg.length); + while (resultSet != -1) { + total += resultSet; + resultSet = sis.readLine(arg, total, 1024); + } - // ===== 인코딩 확인 로그 시작 ===== - logger.info("========== 인코딩 디버깅 정보 =========="); - logger.info("request.getCharacterEncoding(): " + request.getCharacterEncoding()); - logger.info("request.getContentType(): " + request.getContentType()); - logger.info("Content Length: " + arg.length + " bytes"); + xmlContent = new String(arg, "EUC-KR"); + // ===== 인코딩 확인 로그 시작 ===== + logger.info("========== 인코딩 디버깅 정보 =========="); + // 다양한 인코딩으로 읽어보기 + logger.info("request.getCharacterEncoding(): {}", request.getCharacterEncoding()); + logger.info("request.getContentType(): {}", request.getContentType()); + logger.info("Content Length: {} bytes", arg.length); + logger.info("EUC-KR 전체 내용:\n{}", xmlContent); + logger.info("========================================"); + // ===== 인코딩 확인 로그 끝 ===== + param.put("recvData", xmlContent); + } catch (Exception e) { + logger.error("MessageSync.err - don't read the XML File", e); + param.put("recvData", ""); + } - // 다양한 인코딩으로 읽어보기 - String asDefaultEncoding = new String(arg); - String asUtf8 = new String(arg, "UTF-8"); - String asEucKr = new String(arg, "EUC-KR"); - - logger.info("Default Encoding 전체 내용:\n" + asDefaultEncoding); - logger.info("UTF-8 전체 내용:\n" + asUtf8); - logger.info("EUC-KR 전체 내용:\n" + asEucKr); - logger.info("========================================"); - // ===== 인코딩 확인 로그 끝 ===== - - param.put("recvData", new String(arg, "EUC-KR")); - } catch(Exception e) { - logger.error("MessageSync.err - don't read the XML File - " + new String(arg, "EUC-KR"), e); - param.put("recvData", ""); - } - - // byte[]로부터 XML 파싱 (BOM 제거) - EUC-KR로 읽기 - String xmlContent = new String(arg, "EUC-KR").trim(); - // BOM 제거 (UTF-8 BOM: EF BB BF) - if (xmlContent.startsWith("\uFEFF")) { - xmlContent = xmlContent.substring(1); - } - Document doc = builder.build(new ByteArrayInputStream(xmlContent.getBytes("EUC-KR"))); + // byte[]로부터 XML 파싱 - EUC-KR로 읽기 + Document doc = builder.build(new StringReader(xmlContent)); param.put("logPrcssSeqno", UUIDGenerator.getUUID()); // xml 1.1 @@ -485,9 +475,9 @@ public class LayoutSyncController extends OnlBaseAnnotationController implements param.put("recvAmndHMS", recvTime); logger.info(layoutName + " command - [" + command +"]"); - HashMap returnMap = new HashMap(); + HashMap returnMap = new HashMap<>(); if( command.equals("delete") ) { - HashMap vo = new HashMap(); + HashMap vo = new HashMap<>(); vo.put("loutName",layoutName); service.deleteLayout(vo); @@ -513,10 +503,10 @@ public class LayoutSyncController extends OnlBaseAnnotationController implements // } String layout = body.getChild("data").getText(); - logger.info(layoutName + " recv Data - [" + layout + "]"); + logger.info("{} recv Data - [{}]]", layoutName, layout); param.put("recvData", layout); - Document doc2 = builder.build(new ByteArrayInputStream(layout.getBytes())); + Document doc2 = builder.build(new StringReader(layout)); HashMap vo = new HashMap(); @@ -601,7 +591,6 @@ public class LayoutSyncController extends OnlBaseAnnotationController implements } catch( Exception e ) { logger.error("",e); - logger.error("",e.getStackTrace()[0]); try { if( results != null ) { if (e instanceof UseSystemException) { From f53fefcb2e7ca6afc0ac89907ceab1cc90bb62cd Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 9 Feb 2026 14:25:14 +0900 Subject: [PATCH 09/22] =?UTF-8?q?UMS=20=EC=97=B0=EB=8F=99=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- .../com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 818a536..759e8ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -org.gradle.jvmargs=-Xmx2g +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true org.gradle.caching=true org.gradle.configureondemand=true \ No newline at end of file diff --git a/src/main/java/com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java b/src/main/java/com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java index 4769cc8..6a0b7c1 100644 --- a/src/main/java/com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java +++ b/src/main/java/com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java @@ -7,6 +7,7 @@ import com.eactive.ext.kjb.common.KjbPropertyHolder; import com.eactive.ext.kjb.common.KjbUmsException; import com.eactive.ext.kjb.ums.KjbUmsService; import com.eactive.ext.kjb.ums.UmsBizWorkCode; +import com.eactive.ext.kjb.ums.gson.GsonUtil; import com.eactive.ext.kjb.ums.gson.IUmsGsonObject; import com.eactive.ext.kjb.ums.gson.VerifyPhoneUmsTemplate; import lombok.extern.slf4j.Slf4j; @@ -124,7 +125,7 @@ public class SmsAuthService { IUmsGsonObject content = null; content = VerifyPhoneUmsTemplate.builder().authNumber(authCode).build(); - getUmsService().sendKakaoAlimTalk(guid, UmsBizWorkCode.VERIFY_PHONE, cleanedPhone, content); + getUmsService().sendKakaoAlimTalk(guid, UmsBizWorkCode.VERIFY_PHONE, cleanedPhone, GsonUtil.toJsonObject(content)); log.info("SMS 인증번호 발송 완료 - guid: {}, phone: {}****{}", guid, cleanedPhone.substring(0, 3), cleanedPhone.substring(cleanedPhone.length() - 4)); log.debug("SMS 인증번호 발송 - authCode: {}", authCode); From de2aea658d2f60e52a5b909403b143acb901f3aa Mon Sep 17 00:00:00 2001 From: Rinjae Date: Mon, 9 Feb 2026 14:41:50 +0900 Subject: [PATCH 10/22] =?UTF-8?q?UMS=20=EC=97=B0=EB=8F=99=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java b/src/main/java/com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java index 6a0b7c1..ea0a1fa 100644 --- a/src/main/java/com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java +++ b/src/main/java/com/eactive/eai/rms/ext/kjb/smsauth/SmsAuthService.java @@ -8,8 +8,8 @@ import com.eactive.ext.kjb.common.KjbUmsException; import com.eactive.ext.kjb.ums.KjbUmsService; import com.eactive.ext.kjb.ums.UmsBizWorkCode; import com.eactive.ext.kjb.ums.gson.GsonUtil; -import com.eactive.ext.kjb.ums.gson.IUmsGsonObject; import com.eactive.ext.kjb.ums.gson.VerifyPhoneUmsTemplate; +import com.google.gson.JsonObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -122,10 +122,9 @@ public class SmsAuthService { String guid = String.format("EAPIM_EMS-%s", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now())); - IUmsGsonObject content = null; - content = VerifyPhoneUmsTemplate.builder().authNumber(authCode).build(); + JsonObject content = GsonUtil.toJsonObject(VerifyPhoneUmsTemplate.builder().authNumber(authCode).build()); - getUmsService().sendKakaoAlimTalk(guid, UmsBizWorkCode.VERIFY_PHONE, cleanedPhone, GsonUtil.toJsonObject(content)); + getUmsService().sendKakaoAlimTalk(guid, UmsBizWorkCode.VERIFY_PHONE, cleanedPhone, content); log.info("SMS 인증번호 발송 완료 - guid: {}, phone: {}****{}", guid, cleanedPhone.substring(0, 3), cleanedPhone.substring(cleanedPhone.length() - 4)); log.debug("SMS 인증번호 발송 - authCode: {}", authCode); From ae286cb6b7eafaf401e52e0757867d96cf707435 Mon Sep 17 00:00:00 2001 From: daekuk1 Date: Mon, 9 Feb 2026 16:45:46 +0900 Subject: [PATCH 11/22] =?UTF-8?q?function=20=EC=9C=84=EC=B9=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tracking/trackingManDetail.jsp | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp b/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp index 66bece0..2e2a48c 100644 --- a/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp +++ b/WebContent/jsp/onl/transaction/tracking/trackingManDetail.jsp @@ -914,40 +914,41 @@ $(document).ready(function() { // console.log("변환 완료:", parsedList); } - /** - * 문자열의 바이트 수 계산 (한글 2바이트, 영문/숫자 1바이트) - */ - function getByteLength(str) { - let byteCount = 0; - for (let i = 0; i < str.length; i++) { - const charCode = str.charCodeAt(i); - byteCount += (charCode >= 0xAC00 && charCode <= 0xD7A3) ? 2 : 1; - } - return byteCount; - } - - /** - * 문자열을 주어진 바이트 수만큼 패딩 - * @param {string} str - 원본 문자열 - * @param {number} targetBytes - 목표 바이트 수 - * @param {string} position - 'left' 또는 'right' (기본: 'right') - * @param {string} padChar - 패딩 문자 (기본: 공백) - * @returns {string} - 패딩된 문자열 - */ - function padByteString(str, targetBytes, position = 'right', padChar = ' ') { - const currentBytes = getByteLength(str); - - if (currentBytes >= targetBytes) { - return str; // 이미 목표 바이트 이상이면 그대로 반환 - } - - const paddingBytes = targetBytes - currentBytes; - const padding = padChar.repeat(paddingBytes); - - return position === 'left' ? padding + str : str + padding; - } }); +/** + * 문자열의 바이트 수 계산 (한글 2바이트, 영문/숫자 1바이트) + */ +function getByteLength(str) { + let byteCount = 0; + for (let i = 0; i < str.length; i++) { + const charCode = str.charCodeAt(i); + byteCount += (charCode >= 0xAC00 && charCode <= 0xD7A3) ? 2 : 1; + } + return byteCount; +} + +/** + * 문자열을 주어진 바이트 수만큼 패딩 + * @param {string} str - 원본 문자열 + * @param {number} targetBytes - 목표 바이트 수 + * @param {string} position - 'left' 또는 'right' (기본: 'right') + * @param {string} padChar - 패딩 문자 (기본: 공백) + * @returns {string} - 패딩된 문자열 + */ +function padByteString(str, targetBytes, position = 'right', padChar = ' ') { + const currentBytes = getByteLength(str); + + if (currentBytes >= targetBytes) { + return str; // 이미 목표 바이트 이상이면 그대로 반환 + } + + const paddingBytes = targetBytes - currentBytes; + const padding = padChar.repeat(paddingBytes); + + return position === 'left' ? padding + str : str + padding; +} +