diff --git a/WebContent/jsp/onl/adapter/socket/socketStatus.jsp b/WebContent/jsp/onl/adapter/socket/socketStatus.jsp index 2c060a3..eabc18a 100644 --- a/WebContent/jsp/onl/adapter/socket/socketStatus.jsp +++ b/WebContent/jsp/onl/adapter/socket/socketStatus.jsp @@ -303,8 +303,8 @@
-     -    +<%--     --%> +<%--    --%> <%-- " id="btn_excel" level="W"/> --%> diff --git a/WebContent/jsp/onl/admin/rule/transform2/transform2ManDetail.jsp b/WebContent/jsp/onl/admin/rule/transform2/transform2ManDetail.jsp index d37ab64..577526e 100644 --- a/WebContent/jsp/onl/admin/rule/transform2/transform2ManDetail.jsp +++ b/WebContent/jsp/onl/admin/rule/transform2/transform2ManDetail.jsp @@ -1313,6 +1313,8 @@ $('#functionComboDialog').val($('#functionCombo').val()); + setSearchable("functionComboDialog"); + mouseoverEndpoint(endpoint, 'click-endpoint'); moveDialogPosition(false); }, diff --git a/http/obp.http b/http/obp.http index 0b3e6fe..31c5dbc 100644 --- a/http/obp.http +++ b/http/obp.http @@ -17,7 +17,7 @@ GET {{emsUrl}}/api/apis/KJB GET {{emsUrl}}/api/apis/000002-02/KJB -### 트랜잭션 로그 조회 +### 트랜잭션 로그 조회 - POST POST {{emsUrl}}/api/transaction/log Content-Type: application/json; application/json; charset=UTF-8 @@ -27,3 +27,8 @@ Content-Type: application/json; application/json; charset=UTF-8 } + +### 트랜잭션 로그 조회 - GET +GET {{emsUrl}}/api/transaction/log?apiServiceNumber=asdasd&txDate=asdasdasd + + diff --git a/src/main/java/com/eactive/eai/rms/data/entity/onl/adapter/AdapterGroupService.java b/src/main/java/com/eactive/eai/rms/data/entity/onl/adapter/AdapterGroupService.java index 9d0ab0e..1ec9d3c 100644 --- a/src/main/java/com/eactive/eai/rms/data/entity/onl/adapter/AdapterGroupService.java +++ b/src/main/java/com/eactive/eai/rms/data/entity/onl/adapter/AdapterGroupService.java @@ -245,4 +245,8 @@ public class AdapterGroupService extends AbstractDataService findByBzwksvckeynameStartingWith(String adapterName) { + QStandardMessageInfo qStandardMessageInfo = QStandardMessageInfo.standardMessageInfo; + + return getJPAQueryFactory() + .selectFrom(qStandardMessageInfo) + .where(qStandardMessageInfo.bzwksvckeyname.startsWith(adapterName)) + .fetch(); + } + + public StandardMessageInfo saveAndFlush(StandardMessageInfo stdMsgInfo) { + return repository.saveAndFlush(stdMsgInfo); + } } diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpApiDTO.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpApiDTO.java index e978927..e76bc19 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpApiDTO.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpApiDTO.java @@ -12,10 +12,15 @@ import lombok.Getter; @Builder public class ObpApiDTO { + /** + * 고정 ID (항상 null) + */ + private final Integer id; + /** * API ID (TSEAIHE01.EAISVCNAME) */ - private final String id; + private final String apiId; /** * API 이름 (TSEAIHE01.EAISVCDESC) @@ -28,9 +33,9 @@ public class ObpApiDTO { private final boolean enabled; /** - * API URL (TSEAIHS04.APIFULLPATH에서 '|' 이후 부분) + * API URL 패턴 (TSEAIHS04.APIFULLPATH에서 '|' 이후 부분) */ - private final String url; + private final String urlPattern; /** * HTTP Method (TSEAIHS04.APIFULLPATH에서 '|' 이전 부분) @@ -38,7 +43,51 @@ public class ObpApiDTO { private final String method; /** - * 인증 타입 (TSEAIHE01.AUTHTYPE) + * 요구되는 인증 정보 */ - private final String authType; + private final RequiredAuthentication requiredAuthentication; + + /** + * API 상태 정보 + */ + private final Status status; + + /** + * 요구되는 인증 정보 DTO + */ + @Getter + @Builder + public static class RequiredAuthentication { + /** + * 인증 타입 코드 (API_KEY, OAUTH2, NONE) + */ + private final String key; + + /** + * 인증 타입 표시명 + */ + private final String name; + } + + /** + * API 상태 정보 DTO + */ + @Getter + @Builder + public static class Status { + /** + * 상태 코드 (RUNNING, SUSPEND) + */ + private final String key; + + /** + * 상태 표시명 + */ + private final String name; + + /** + * 배포 여부 (enabled와 동일) + */ + private final boolean deployed; + } } diff --git a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpApiService.java b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpApiService.java index e896067..83ed71e 100644 --- a/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpApiService.java +++ b/src/main/java/com/eactive/eai/rms/onl/apim/obp/ObpApiService.java @@ -95,16 +95,71 @@ public class ObpApiService { // USEYN이 "1"이면 enabled = true boolean enabled = "1".equals(useyn); + // RequiredAuthentication 매핑 + ObpApiDTO.RequiredAuthentication reqAuth = mapAuthType(authtype); + + // Status 매핑 + ObpApiDTO.Status status = mapStatus(enabled); + return ObpApiDTO.builder() - .id(eaisvcname) + .id(null) + .apiId(eaisvcname) .name(eaisvcdesc) .enabled(enabled) - .url(url) + .urlPattern(url) .method(method) - .authType(authtype) + .requiredAuthentication(reqAuth) + .status(status) .build(); } + /** + * 인증 타입을 RequiredAuthentication으로 매핑 + * + * @param authtype TSEAIHE01.AUTHTYPE 값 + * @return RequiredAuthentication 객체 + */ + private ObpApiDTO.RequiredAuthentication mapAuthType(String authtype) { + if ("api_key".equalsIgnoreCase(authtype)) { + return ObpApiDTO.RequiredAuthentication.builder() + .key("API_KEY") + .name("APP키를 이용한 인증") + .build(); + } else if ("oauth".equalsIgnoreCase(authtype) || "ca".equalsIgnoreCase(authtype)) { + return ObpApiDTO.RequiredAuthentication.builder() + .key("OAUTH2") + .name("OAuth Ver.2 인증") + .build(); + } else { + return ObpApiDTO.RequiredAuthentication.builder() + .key("NONE") + .name("인증없이 사용") + .build(); + } + } + + /** + * enabled 값을 Status로 매핑 + * + * @param enabled 활성화 여부 + * @return Status 객체 + */ + private ObpApiDTO.Status mapStatus(boolean enabled) { + if (enabled) { + return ObpApiDTO.Status.builder() + .key("RUNNING") + .name("서비스 중") + .deployed(true) + .build(); + } else { + return ObpApiDTO.Status.builder() + .key("SUSPEND") + .name("서비스 일시 중지") + .deployed(false) + .build(); + } + } + /** * 기관코드에 해당하는 법인이 이용하는 API 목록 조회 * 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 c7c2626..af7feb1 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 @@ -70,6 +70,36 @@ public class ObpGwMetricAfterDTO { @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 변환 */ diff --git a/src/main/java/com/eactive/eai/rms/onl/manage/adapter/adapter/AdapterController.java b/src/main/java/com/eactive/eai/rms/onl/manage/adapter/adapter/AdapterController.java index 64129af..77dd89d 100644 --- a/src/main/java/com/eactive/eai/rms/onl/manage/adapter/adapter/AdapterController.java +++ b/src/main/java/com/eactive/eai/rms/onl/manage/adapter/adapter/AdapterController.java @@ -248,7 +248,7 @@ public class AdapterController extends OnlBaseAnnotationController { synchronized (obj) { service.update(adapterGroupUI); - Map resultMap = service.reloadSync(adapterGroupUI); + Map resultMap = service.reloadSyncStdMsgAndAdapter(adapterGroupUI); return ResponseEntity.ok(resultMap); } diff --git a/src/main/java/com/eactive/eai/rms/onl/manage/adapter/adapter/AdapterManService.java b/src/main/java/com/eactive/eai/rms/onl/manage/adapter/adapter/AdapterManService.java index 75c4f22..d55875d 100644 --- a/src/main/java/com/eactive/eai/rms/onl/manage/adapter/adapter/AdapterManService.java +++ b/src/main/java/com/eactive/eai/rms/onl/manage/adapter/adapter/AdapterManService.java @@ -23,10 +23,15 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import com.eactive.eai.agent.command.Command; import com.eactive.eai.agent.command.CommonCommand; +import com.eactive.eai.common.stdmessage.STDMessageManager; import com.eactive.eai.data.entity.onl.adapter.Adapter; import com.eactive.eai.data.entity.onl.adapter.AdapterGroup; +import com.eactive.eai.data.entity.onl.adapter.AdapterProp; import com.eactive.eai.data.entity.onl.adapter.AdapterPropGroup; +import com.eactive.eai.data.entity.onl.stdmessage.StandardMessageInfo; +import com.eactive.eai.data.jpa.BaseRepository; import com.eactive.eai.rms.common.acl.monitoringCode.MonitoringCodeManService; import com.eactive.eai.rms.common.base.OnlBaseService; import com.eactive.eai.rms.common.comDomain.CommonDomainManService; @@ -42,6 +47,7 @@ import com.eactive.eai.rms.data.entity.onl.adapter.AdapterTypePropertyService; import com.eactive.eai.rms.data.entity.onl.adapter.CommonDomain; import com.eactive.eai.rms.data.entity.onl.code.CodeService; import com.eactive.eai.rms.data.entity.onl.server.EAIServerService; +import com.eactive.eai.rms.data.entity.onl.stdmessage.StandardMessageInfoService; import com.eactive.eai.rms.onl.common.exception.BizException; import com.eactive.eai.rms.onl.common.service.AdapterChangeThreadPool; import com.eactive.eai.rms.onl.manage.adapter.adapter.mapper.AdapterGroupUIMapper; @@ -52,6 +58,7 @@ import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUIMixIn; import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUISearch; import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterPropUI; import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterUI; +import com.ext.eai.agent.stdmessage.ReloadSTDMessageCommand; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; @@ -106,7 +113,10 @@ public class AdapterManService extends OnlBaseService { @Autowired private EAIServerService eaiServerService; - + + @Autowired + private StandardMessageInfoService standardmessageinfoService; + public Page selectList(Pageable pageable, AdapterGroupUISearch adapterGroupUISearch) { Page page = adapterGroupService.findPage(pageable, adapterGroupUISearch); return page.map(adapterGroupUIMapper::toVoWithoutChildrenMapping); @@ -234,11 +244,133 @@ public class AdapterManService extends OnlBaseService { public void update(AdapterGroupUI adapterGroupUI) { - AdapterGroup adapterGroup = adapterGroupService.getById(adapterGroupUI.getAdptrbzwkgroupname()); - adapterGroupUIMapper.updateEntity(adapterGroupUI, adapterGroup); + AdapterGroup adapterGroup = adapterGroupService.getById(adapterGroupUI.getAdptrbzwkgroupname()); + + Map oldApiPathMap = extractApiPaths(adapterGroup); + + adapterGroupUIMapper.updateEntity(adapterGroupUI, adapterGroup); + + // NonUniqueObjectException 대응 + adapterGroupService.saveAndFlush(adapterGroup); - adapterGroupService.save(adapterGroup); + + // HS04 - API_FULL_PATH 동기화 + syncIfApiPathChanged(adapterGroup, oldApiPathMap); } + + private Map extractApiPaths(AdapterGroup adapterGroup) { + + Map pathMap = new HashMap<>(); + + if (adapterGroup == null || adapterGroup.getAdapters() == null) { + return pathMap; + } + for (Adapter adapter : adapterGroup.getAdapters()) { + + if (adapter.getAdapterPropGroup() == null + || adapter.getAdapterPropGroup().getAdapterProps() == null) { + continue; + } + + for (AdapterProp prop : adapter.getAdapterPropGroup().getAdapterProps()) { + String propName = prop.getId().getPrptyname(); + + if ("API_PATH".equals(propName)) { + String adapterName = adapter.getId().getAdptrbzwkgroupname(); + + String apiPathValue = prop.getPrpty2val(); + + pathMap.put(adapterName, apiPathValue); + + break; + + } + + } + } + + + return pathMap; + + } + + private void syncIfApiPathChanged(AdapterGroup newGroup, Map oldPathMap) { + + if (newGroup.getAdapters() == null) return; + + for (Adapter newAdapter : newGroup.getAdapters()) { // adapterData 분리 + String adapterName = newAdapter.getId().getAdptrbzwkgroupname(); // 업데이트할때 들어온 어댑터 이름 + String newPath = findApiPathValue(newAdapter); + + String oldPath = oldPathMap.get(adapterName); + + if (StringUtils.isNotBlank(oldPath) + && StringUtils.isNotBlank(newPath) + && !StringUtils.equals(oldPath, newPath)) { + + updateStdMessageInfoApiFullPath(adapterName, oldPath, newPath); + } + } + + + } + + private String findApiPathValue(Adapter adapter) { + if (adapter.getAdapterPropGroup() == null + || adapter.getAdapterPropGroup().getAdapterProps() == null) { + return null; + } + + return adapter.getAdapterPropGroup().getAdapterProps().stream() + .filter(prop -> prop.getId() != null && "API_PATH".equals(prop.getId().getPrptyname())) + .findFirst() + .map(AdapterProp::getPrpty2val) + .orElse(null); + } + + private void updateStdMessageInfoApiFullPath(String adapterName, String oldPath, String newPath) { + + List stdMsgInfoList = standardmessageinfoService.findByBzwksvckeynameStartingWith(adapterName); + + // 변경 사항이 있었는지 체크해서 HS04 Reload 함. + for (StandardMessageInfo stdMsgInfo : stdMsgInfoList) { + String fullPath = stdMsgInfo.getApifullpath(); + + // 기존 경로(oldPath)를 포함하고 있을 때만 교체 + if (StringUtils.isNotBlank(fullPath) && fullPath.contains(oldPath)) { + + int pipeIndex = fullPath.indexOf("|"); + + if (pipeIndex != -1) { + String methodPart = fullPath.substring(0, pipeIndex + 1); + String uriPart = fullPath.substring(pipeIndex + 1); + + if (uriPart.startsWith(oldPath)) { + String newUriPart = newPath + uriPart.substring(oldPath.length()); + String updatedFullPath = methodPart + newUriPart; + + stdMsgInfo.setApifullpath(updatedFullPath); + + + try { + // DB에 즉시 반영 + standardmessageinfoService.saveAndFlush(stdMsgInfo); + + } catch (Exception e) { + + log.error("StandardMessageInfo APIFULLPATH SAVE FAIL key = {}", stdMsgInfo.getBzwksvckeyname()); + } + + } + } else { + // 파이프가 없는 경우 로그 생성 + log.warn("[StandardMessageInfo APIFULLPATH Sync Skip] Abnormal data format found. Adapter: {}, FullPath: {}", adapterName, fullPath); + } + + } + } + + } public void delete(AdapterGroupUI adapterGroupUI) { adapterGroupService.deleteById(adapterGroupUI.getAdptrbzwkgroupname()); @@ -407,7 +539,55 @@ public class AdapterManService extends OnlBaseService { adapterGroup.setMoniuseyn(adapterGroupUI.getMoniuseyn()); } } + + public Map reloadSyncStdMsgAndAdapter(AdapterGroupUI adapterGroupUI) throws Exception { + Map receive = null; + Map result = new HashMap<>(); + String status = "success"; + try { + log.info(">>>>>> [Reload AdapterManService Start] 리로드 시작"); + + for (Entry entry : adapterGroupUI.getAdapterData().entrySet()) { + AdapterUI adapterUI = entry.getValue(); + String prptyGroupName = adapterUI.getPrptygroupname(); + + CommonCommand pcommand = new CommonCommand( + "com.eactive.eai.agent.adapter.ReloadAdapterPropGroupCommand", + prptyGroupName + ); + agentUtilService.broadcast(pcommand); + } + + // 어댑터 그룹 리로드 + CommonCommand acommand = new CommonCommand( + "com.eactive.eai.agent.adapter.ReloadAdapterGroupCommand", + adapterGroupUI.getAdptrbzwkgroupname() + ); + receive = agentUtilService.broadcast(acommand); + + // HS04 API_FULL_PATH + Command stdcommand = new ReloadSTDMessageCommand(); + stdcommand.setArgs("ALL"); + agentUtilService.broadcast(stdcommand); + + log.info(">>>>>> [Reload AdapterManService End] 리로드 완료"); + + } catch (Exception e) { + status = "error"; + log.error("리로드 실패", e); + } + + if (receive != null && receive.values().stream().anyMatch(value -> value == null || !"success".equals(value))) { + status = "error"; + } + + result.put("status", status); + if(receive != null) result.put("message", receive.toString()); + + return result; + } + public Map reloadSync(AdapterGroupUI adapterGroupUI) throws Exception { Map receive = null; Map result = new HashMap<>(); @@ -419,19 +599,19 @@ public class AdapterManService extends OnlBaseService { prptyGroupName); agentUtilService.broadcast(pcommand); } - + CommonCommand acommand = new CommonCommand("com.eactive.eai.agent.adapter.ReloadAdapterGroupCommand", adapterGroupUI.getAdptrbzwkgroupname()); receive = agentUtilService.broadcast(acommand); - + if (receive.values().stream().anyMatch(value -> value == null || !"success".equals(value))) { status = "error"; } - + result.put("status", status); result.put("message", receive.toString()); return result; - + } public Map deleteSync(AdapterGroupUI map, List list) throws Exception { diff --git a/src/main/java/com/eactive/eai/rms/onl/manage/control/control/ControlApiController.java b/src/main/java/com/eactive/eai/rms/onl/manage/control/control/ControlApiController.java new file mode 100644 index 0000000..5355e18 --- /dev/null +++ b/src/main/java/com/eactive/eai/rms/onl/manage/control/control/ControlApiController.java @@ -0,0 +1,38 @@ +package com.eactive.eai.rms.onl.manage.control.control; + +import com.eactive.eai.rms.onl.apim.common.BaseRestController; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +@Controller +public class ControlApiController extends BaseRestController { + ControlManController controlManController; + + + public ControlApiController(ControlManController controlManController) { + this.controlManController = controlManController; + } + + + @RequestMapping(value = "/control/controlMan.json", params = "cmd=LIST") + public ModelAndView selectList(HttpServletRequest request, ControlManUISearch uiSearch) { + return controlManController.selectList(request, uiSearch); + } + + @RequestMapping(value = "/control/controlMan.json", params = "cmd=INSERT") + private String insert(HttpServletRequest request, HttpServletResponse response, ControlManUIDetail uiDetail) + throws Exception { + return controlManController.insert(request, response, uiDetail); + } + + @RequestMapping(value = "/control/controlMan.json", params = "cmd=DELETE") + public String delete(HttpServletRequest request, HttpServletResponse response, ControlManUIDetail uiDetail) + throws Exception { + return controlManController.delete(request, response, uiDetail); + } +} diff --git a/src/main/java/com/eactive/eai/rms/onl/manage/control/control/ControlManController.java b/src/main/java/com/eactive/eai/rms/onl/manage/control/control/ControlManController.java index 3522889..10a95ef 100644 --- a/src/main/java/com/eactive/eai/rms/onl/manage/control/control/ControlManController.java +++ b/src/main/java/com/eactive/eai/rms/onl/manage/control/control/ControlManController.java @@ -17,11 +17,12 @@ import org.springframework.web.servlet.ModelAndView; import com.eactive.eai.agent.command.CommonCommand; import com.eactive.eai.rms.common.base.OnlBaseAnnotationController; +import com.eactive.eai.rms.common.interceptor.InterceptorSkipController; import com.eactive.eai.rms.common.login.SessionManager; import com.google.gson.Gson; @Controller -public class ControlManController extends OnlBaseAnnotationController { +public class ControlManController extends OnlBaseAnnotationController { private static final String RELOAD_RESTRICT_INFO_COMMAND = "com.eactive.eai.agent.restrict.ReloadRestrictInfoCommand"; private static final String ERROR_RESULT_STATUS = "에러"; @@ -49,7 +50,7 @@ public class ControlManController extends OnlBaseAnnotationController { // 통제 등록 @RequestMapping(value = "/onl/admin/control/controlMan.json", params = "cmd=INSERT") - private String insert(HttpServletRequest request, HttpServletResponse response, ControlManUIDetail uiDetail) + public String insert(HttpServletRequest request, HttpServletResponse response, ControlManUIDetail uiDetail) throws Exception { String userId = SessionManager.getUserId(request); service.insert(uiDetail, userId); diff --git a/src/main/java/com/eactive/ext/kjb/web/ApiTransactionLogController.java b/src/main/java/com/eactive/ext/kjb/web/ApiTransactionLogController.java index 431ac88..977838e 100644 --- a/src/main/java/com/eactive/ext/kjb/web/ApiTransactionLogController.java +++ b/src/main/java/com/eactive/ext/kjb/web/ApiTransactionLogController.java @@ -10,11 +10,11 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.RequiredArgsConstructor; -import lombok.extern.java.Log; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -33,9 +33,17 @@ public class ApiTransactionLogController extends BaseRestController { private final EAILogService eaiLogService; private final ObjectMapper objectMapper = new ObjectMapper(); + + @GetMapping("/transaction/log") + public ResponseEntity getApiLog(String txDate, String apiServiceNumber) { + return getApiLog(new LogRequestBody() {{ + setTxDate(txDate); + setApiServiceNumber(apiServiceNumber); + }}); + } + // 이중 매핑 문제를 해결하기 위해 (예: /api/transaction/log.do 와 /transaction/log.do) '.do' 확장자를 제거함. // 이제 이 엔드포인트는 /api/transaction/log 를 통해서만 접근 가능하며, API 컨벤션에 맞춰 단일하고 명확한 접근 지점을 보장함. -// @GetMapping("/transaction/log") @PostMapping("/transaction/log") public ResponseEntity getApiLog(@RequestBody LogRequestBody requestBody) { DataSourceContextHolder.setDataSourceType(DataSourceTypeManager.getDataSourceType(DataSourceTypeManager.APIGW));