From c4fbd276b9ae14c69d2e7c4a9cf00b26f9ca51d9 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Wed, 17 Dec 2025 17:02:53 +0900 Subject: [PATCH] =?UTF-8?q?GwMetric=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- http/obp-GwMetric.http | 1 + kjb-docs/jbr.md | 4 + .../entity/onl/apim/obp/ObpGwMetricDAO.java | 44 ++++++-- .../onl/apim/common/BaseRestController.java | 101 ++++++++++++++++++ .../rms/onl/apim/obp/ObpGwMetricAfterDTO.java | 5 + .../onl/apim/obp/ObpGwMetricController.java | 54 ++-------- .../eai/rms/onl/apim/obp/ObpGwMetricDTO.java | 4 + .../onl/apim/obp/ObpGwMetricManService.java | 15 ++- .../eai/apim/obp/ObpGwMetric-oracle.xml | 7 +- 9 files changed, 172 insertions(+), 63 deletions(-) create mode 100644 src/main/java/com/eactive/eai/rms/onl/apim/common/BaseRestController.java diff --git a/http/obp-GwMetric.http b/http/obp-GwMetric.http index 2cc592d..4522ac3 100644 --- a/http/obp-GwMetric.http +++ b/http/obp-GwMetric.http @@ -2,5 +2,6 @@ GET {{emsUrl}}/kjb/gw-metrics/after-timeslice/2025-12-02_01:00:00.json + ### Pretty Print GET {{emsUrl}}/kjb/gw-metrics/after-timeslice/2025-12-02_01:00:00.json?pretty=true \ No newline at end of file diff --git a/kjb-docs/jbr.md b/kjb-docs/jbr.md index dbc5d35..120c380 100644 --- a/kjb-docs/jbr.md +++ b/kjb-docs/jbr.md @@ -33,6 +33,10 @@ # Spring Bean 설정 및 리소스 실시간 반영 (경로 주의) -javaagent:C:\path\to\hotswap-agent.jar + + +-XX:HotswapAgent=fatjar +-Dhotswap.agent.disablePlugin=org.hotswap.agent.plugin.ibatis.IBatisPlugin,org.hotswap.agent.plugin.mybatis.MyBatisPlugin ``` ----- diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java index 18f7fff..b1cc50f 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/apim/obp/ObpGwMetricDAO.java @@ -61,18 +61,39 @@ public class ObpGwMetricDAO extends SqlMapClientTemplateDao { } /** - * URI 조회 (HTTP_ADAPTER_EXTRA_LOG) + * URI 및 HTTP Method 조회 결과 + */ + public static class UriMethodResult { + private final Map uriMap; + private final Map methodMap; + + public UriMethodResult(Map uriMap, Map methodMap) { + this.uriMap = uriMap; + this.methodMap = methodMap; + } + + public Map getUriMap() { + return uriMap; + } + + public Map getMethodMap() { + return methodMap; + } + } + + /** + * URI 및 HTTP Method 조회 (HTTP_ADAPTER_EXTRA_LOG) * - GUID = TSEAILG1X.EAISVCSERNO와 매핑 * - SERVICE_PROCESS_NUMBER = 100 (인바운드 요청) * * @param schemaId DB 스키마 ID * @param eaiSvcSernos EAI 서비스 일련번호 목록 (GUID) - * @return eaiSvcSerno → URI 맵 (예: "POST /api/v1/transfer") + * @return UriMethodResult (eaiSvcSerno → URI 맵, eaiSvcSerno → METHOD 맵) */ @SuppressWarnings("unchecked") - public Map selectUris(String schemaId, Set eaiSvcSernos) { + public UriMethodResult selectUrisAndMethods(String schemaId, Set eaiSvcSernos) { if (eaiSvcSernos == null || eaiSvcSernos.isEmpty()) { - return new HashMap<>(); + return new UriMethodResult(new HashMap<>(), new HashMap<>()); } HashMap paramMap = new HashMap<>(); @@ -81,15 +102,22 @@ public class ObpGwMetricDAO extends SqlMapClientTemplateDao { List> resultList = this.template.queryForList("ObpGwMetric.selectUris", paramMap); - Map result = new HashMap<>(); + Map uriMap = new HashMap<>(); + Map methodMap = new HashMap<>(); for (Map m : resultList) { String eaiSvcSerno = m.get("EAI_SVC_SERNO"); String uri = m.get("URI"); - if (eaiSvcSerno != null && uri != null) { - result.put(eaiSvcSerno, uri); + String method = m.get("METHOD"); + if (eaiSvcSerno != null) { + if (uri != null) { + uriMap.put(eaiSvcSerno, uri); + } + if (method != null) { + methodMap.put(eaiSvcSerno, method); + } } } - return result; + return new UriMethodResult(uriMap, methodMap); } /** diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/common/BaseRestController.java b/src/main/java/com/eactive/eai/rms/onl/apim/common/BaseRestController.java new file mode 100644 index 0000000..b5847c4 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/onl/apim/common/BaseRestController.java @@ -0,0 +1,101 @@ +package com.eactive.eai.rms.onl.apim.common; + +import com.eactive.eai.rms.common.interceptor.InterceptorSkipController; +import com.google.gson.GsonBuilder; + +import java.nio.charset.StandardCharsets; + +/** + * REST API Controller 기본 클래스 + * + *

기능:

+ *
    + *
  • 세션 체크 우회 (InterceptorSkipController)
  • + *
  • JSON 변환 유틸리티 (pretty print 지원)
  • + *
  • 응답 크기 포맷팅
  • + *
+ * + *

사용 예:

+ *
+ * {@code
+ * @Controller
+ * public class MyApiController extends BaseRestController {
+ *     @RequestMapping(value = "/api/something.json", produces = JSON_CONTENT_TYPE)
+ *     @ResponseBody
+ *     public String getSomething(@RequestParam(value = "pretty", required = false) Boolean pretty) {
+ *         List data = service.getData();
+ *         return toJson(data, pretty);
+ *     }
+ * }
+ * }
+ * 
+ */ +public abstract class BaseRestController implements InterceptorSkipController { + + /** + * JSON 응답 Content-Type + */ + protected static final String JSON_CONTENT_TYPE = "application/json;charset=utf-8"; + + /** + * 객체를 JSON 문자열로 변환 + * + * @param object 변환할 객체 + * @param pretty true면 들여쓰기 적용 (null이면 false로 처리) + * @return JSON 문자열 + */ + protected String toJson(Object object, Boolean pretty) { + GsonBuilder builder = new GsonBuilder().serializeNulls(); + if (Boolean.TRUE.equals(pretty)) { + builder.setPrettyPrinting(); + } + return builder.create().toJson(object); + } + + /** + * 객체를 JSON 문자열로 변환 (기본: 한 줄 출력) + * + * @param object 변환할 객체 + * @return JSON 문자열 + */ + protected String toJson(Object object) { + return toJson(object, false); + } + + /** + * 바이트 크기를 읽기 쉬운 형식으로 변환 + * 예: 1024 -> "1.0 KB", 1048576 -> "1.0 MB" + * + * @param bytes 바이트 수 + * @return 포맷된 문자열 + */ + protected String formatByteSize(long bytes) { + if (bytes < 1024) { + return bytes + " B"; + } else if (bytes < 1024 * 1024) { + return String.format("%.1f KB", bytes / 1024.0); + } else { + return String.format("%.1f MB", bytes / (1024.0 * 1024.0)); + } + } + + /** + * JSON 문자열의 바이트 크기 계산 (UTF-8 기준) + * + * @param json JSON 문자열 + * @return 바이트 수 + */ + protected int getJsonByteSize(String json) { + return json.getBytes(StandardCharsets.UTF_8).length; + } + + /** + * JSON 문자열의 포맷된 바이트 크기 반환 + * + * @param json JSON 문자열 + * @return 포맷된 크기 문자열 + */ + protected String getFormattedJsonSize(String json) { + return formatByteSize(getJsonByteSize(json)); + } +} 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 index 2852cf6..c7c2626 100644 --- 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 @@ -16,6 +16,7 @@ import java.time.format.DateTimeFormatter; * "apiId": "1ba56b82-a9c9-41e2-b3ab-ff9377667d56", * "apiName": "[해외송금] 가상계좌 입금실패 정보조회", * "apiRoutingUri": "/api/obs/remittance/remittancefail*", + * "httpMethod": "POST", * "orgId": "ORG001", * "partnerCode": "000008-01", * "appId": "APP001", @@ -42,6 +43,9 @@ public class ObpGwMetricAfterDTO { @SerializedName("apiRoutingUri") private String apiRoutingUri; + @SerializedName("httpMethod") + private String httpMethod; + @SerializedName("orgId") private String orgId; @@ -74,6 +78,7 @@ public class 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()); 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 164bc3f..81dc55b 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 @@ -1,8 +1,6 @@ package com.eactive.eai.rms.onl.apim.obp; -import com.eactive.eai.rms.common.interceptor.InterceptorSkipController; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import com.eactive.eai.rms.onl.apim.common.BaseRestController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; @@ -11,7 +9,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; @@ -20,10 +17,10 @@ import java.util.List; * ObpGwMetric REST Controller * API Gateway 시간별 거래량 지표 조회 API * - *

세션 체크를 우회하기 위해 InterceptorSkipController 인터페이스 구현

+ * @see BaseRestController 세션 체크 우회 및 JSON 유틸리티 제공 */ @Controller -public class ObpGwMetricController implements InterceptorSkipController { +public class ObpGwMetricController extends BaseRestController { private static final Logger log = LoggerFactory.getLogger(ObpGwMetricController.class); @@ -45,7 +42,7 @@ public class ObpGwMetricController implements InterceptorSkipController { */ @RequestMapping( value = "/kjb/gw-metrics/{datetime}.json", - produces = "application/json;charset=utf-8" + produces = JSON_CONTENT_TYPE ) @ResponseBody public String getGwMetrics( @@ -65,13 +62,9 @@ public class ObpGwMetricController implements InterceptorSkipController { // JSON 변환 (GSON) String json = toJson(metrics, pretty); - // 응답 크기 계산 (UTF-8 기준) - int jsonBytes = json.getBytes(StandardCharsets.UTF_8).length; - String jsonSize = formatByteSize(jsonBytes); - long elapsed = System.currentTimeMillis() - startTime; log.info("GwMetric 조회 완료: datetime={}, count={}, size={}, elapsed={}ms", - datetime, metrics.size(), jsonSize, elapsed); + datetime, metrics.size(), getFormattedJsonSize(json), elapsed); return json; @@ -90,7 +83,7 @@ public class ObpGwMetricController implements InterceptorSkipController { */ @RequestMapping( value = "/kjb/gw-metrics/after-timeslice/{datetime}.json", - produces = "application/json;charset=utf-8" + produces = JSON_CONTENT_TYPE ) @ResponseBody public String getGwMetricsAfterTimeslice( @@ -110,13 +103,9 @@ public class ObpGwMetricController implements InterceptorSkipController { // JSON 변환 (GSON) String json = toJson(metrics, pretty); - // 응답 크기 계산 (UTF-8 기준) - int jsonBytes = json.getBytes(StandardCharsets.UTF_8).length; - String jsonSize = formatByteSize(jsonBytes); - long elapsed = System.currentTimeMillis() - startTime; log.info("GwMetric after-timeslice 조회 완료: datetime={}, count={}, size={}, elapsed={}ms", - datetime, metrics.size(), jsonSize, elapsed); + datetime, metrics.size(), getFormattedJsonSize(json), elapsed); return json; @@ -125,33 +114,4 @@ public class ObpGwMetricController implements InterceptorSkipController { throw e; } } - - /** - * 객체를 JSON 문자열로 변환 - * - * @param object 변환할 객체 - * @param pretty true면 들여쓰기 적용 - * @return JSON 문자열 - */ - private String toJson(Object object, Boolean pretty) { - GsonBuilder builder = new GsonBuilder().serializeNulls(); - if (Boolean.TRUE.equals(pretty)) { - builder.setPrettyPrinting(); - } - return builder.create().toJson(object); - } - - /** - * 바이트 크기를 읽기 쉬운 형식으로 변환 - * 예: 1024 -> "1.0 KB", 1048576 -> "1.0 MB" - */ - private String formatByteSize(long bytes) { - if (bytes < 1024) { - return bytes + " B"; - } else if (bytes < 1024 * 1024) { - return String.format("%.1f KB", bytes / 1024.0); - } else { - return String.format("%.1f MB", bytes / (1024.0 * 1024.0)); - } - } } 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 02e8785..c0d0a0a 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 @@ -32,6 +32,9 @@ public class ObpGwMetricDTO { @SerializedName("apiRoutingUri") private String uri; + @SerializedName("httpMethod") + private String method; + @SerializedName("attemptedCount") private Long attemptedCount; @@ -74,6 +77,7 @@ public class ObpGwMetricDTO { dto.setTimeslice(formatTimeslice(entity.getTimeslice())); dto.setApiName(entity.getApiName()); dto.setUri(entity.getUri()); + dto.setMethod(entity.getMethod()); dto.setAttemptedCount(entity.getAttemptedCount()); dto.setCompletedCount(entity.getCompletedCount()); dto.setApiId(entity.getApiId()); 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 86c6167..66f9419 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 @@ -165,7 +165,9 @@ public class ObpGwMetricManService extends BaseService { .collect(Collectors.toSet()); Map apiNameMap = obpGwMetricDAO.selectApiNames(apigwDataSourceType.getSchema(), apiIds); - Map uriMap = obpGwMetricDAO.selectUris(apigwDataSourceType.getSchema(), eaiSvcSernos); + ObpGwMetricDAO.UriMethodResult uriMethodResult = obpGwMetricDAO.selectUrisAndMethods(apigwDataSourceType.getSchema(), eaiSvcSernos); + Map uriMap = uriMethodResult.getUriMap(); + Map methodMap = uriMethodResult.getMethodMap(); log.info("[3단계] 부가정보 조회 완료: apiCount={}, eaiSvcSernoCount={}, elapsed={}ms", apiIds.size(), eaiSvcSernos.size(), System.currentTimeMillis() - step3Start); @@ -202,7 +204,8 @@ public class ObpGwMetricManService extends BaseService { entity.setAttemptedCount(vo.getAttemptedCount()); entity.setCompletedCount(vo.getCompletedCount()); entity.setApiName(apiNameMap.get(vo.getApiId())); - entity.setUri(uriMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 URI 조회 + entity.setUri(uriMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 URI 조회 + entity.setMethod(methodMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 HTTP Method 조회 entity.setAppId(appIdMap.get(vo.getOrgId())); entity.setUpdateCount(entity.getUpdateCount() + 1); // 보정 횟수 증가 entity.setUpdatedAt(LocalDateTime.now()); @@ -211,7 +214,7 @@ public class ObpGwMetricManService extends BaseService { updateCount++; } else { // INSERT: 신규 데이터 생성 - ObpGwMetric entity = convertToEntity(vo, apiNameMap, uriMap, appIdMap); + ObpGwMetric entity = convertToEntity(vo, apiNameMap, uriMap, methodMap, appIdMap); obpGwMetricService.save(entity); insertCount++; } @@ -249,7 +252,8 @@ public class ObpGwMetricManService extends BaseService { * VO → Entity 변환 */ private ObpGwMetric convertToEntity(ObpGwMetricVO vo, Map apiNameMap, - Map uriMap, Map appIdMap) { + Map uriMap, Map methodMap, + Map appIdMap) { ObpGwMetric entity = new ObpGwMetric(); entity.setClientId(vo.getClientId()); entity.setHostname(vo.getHostname()); @@ -259,7 +263,8 @@ public class ObpGwMetricManService extends BaseService { entity.setAttemptedCount(vo.getAttemptedCount()); entity.setCompletedCount(vo.getCompletedCount()); entity.setApiName(apiNameMap.get(vo.getApiId())); - entity.setUri(uriMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 URI 조회 + entity.setUri(uriMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 URI 조회 + entity.setMethod(methodMap.get(vo.getEaiSvcSerno())); // eaiSvcSerno로 HTTP Method 조회 entity.setAppId(appIdMap.get(vo.getOrgId())); entity.setUpdateCount(0); entity.setCreatedAt(LocalDateTime.now()); diff --git a/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml b/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml index 05d5b0d..6da8ddb 100644 --- a/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml +++ b/src/main/resources/com/eactive/eai/apim/obp/ObpGwMetric-oracle.xml @@ -66,15 +66,16 @@ SELECT GUID AS EAI_SVC_SERNO, - HTTP_METHOD || ' ' || URL AS URI + URL AS URI, + HTTP_METHOD AS METHOD FROM $schemaId$.HTTP_ADAPTER_EXTRA_LOG WHERE SERVICE_PROCESS_NUMBER = 100 AND GUID IN