diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/service/ExecuteService.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/service/ExecuteService.java index 368dc3b..c735d8a 100644 --- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/service/ExecuteService.java +++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/service/ExecuteService.java @@ -232,6 +232,10 @@ public class ExecuteService { } String executeApiUrl = targetUrl + "/mcp/" + metadata.getName(); + Map headers = new java.util.HashMap<>(); + headers.put("trace-id", java.util.UUID.randomUUID().toString()); + headers.put("request-id", context.requestId()); + ObjectNode pageArguments = paginationValidator.normalize(arguments); LargeToolResponseService.Collector collector = largeResponses.newCollector(metadata.getName(), context.requestId()); @@ -244,7 +248,7 @@ public class ExecuteService { JsonNode data = null; try { - data = toolInvoker.invoke(pagePayload, executeApiUrl); + data = toolInvoker.invoke(pagePayload, executeApiUrl, headers); } catch (org.springframework.web.client.RestClientResponseException e) { // HTTP 4xx, 5xx 에러는 연결 오류가 아니라 비즈니스 로직 오류이거나 검증 실패이므로 원본 에러를 그대로 반환 throw new ToolExecutionException(FailureType.SERVER_ERROR, "Tool Pod HTTP 에러 (" + e.getStatusCode() + "): " + e.getResponseBodyAsString()); @@ -253,7 +257,7 @@ public class ExecuteService { String fallbackUrl = executeApiUrl.replaceAll("http://tool-[a-zA-Z0-9-]+", "http://localhost"); log.warn(" [ExecuteService] 호스트를 찾을 수 없어 localhost로 재시도합니다: {}", fallbackUrl); try { - data = toolInvoker.invoke(pagePayload, fallbackUrl); + data = toolInvoker.invoke(pagePayload, fallbackUrl, headers); } catch (Exception ex) { throw new ToolExecutionException(FailureType.SERVER_ERROR, "Tool Pod 호출 실패 (localhost 재시도 포함): " + ex.getMessage()); } diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/transport/HttpToolInvoker.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/transport/HttpToolInvoker.java index c6718b2..01f5317 100644 --- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/transport/HttpToolInvoker.java +++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/transport/HttpToolInvoker.java @@ -40,14 +40,17 @@ public class HttpToolInvoker implements ToolInvoker { } @Override - public JsonNode invoke(Map payload, String targetUrl) { + public JsonNode invoke(Map payload, String targetUrl, Map headers) { try { - Object httpResult = restClient.post() + RestClient.RequestBodySpec requestSpec = restClient.post() .uri(targetUrl) - .contentType(MediaType.APPLICATION_JSON) - // TODO: Use actual tenant's key - .header("X-Trace-Id", UUID.randomUUID().toString()) - .body(payload) + .contentType(MediaType.APPLICATION_JSON); + + if (headers != null) { + headers.forEach(requestSpec::header); + } + + Object httpResult = requestSpec.body(payload) .retrieve() .body(Object.class); diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/transport/ToolInvoker.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/transport/ToolInvoker.java index 823577c..bb0b988 100644 --- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/transport/ToolInvoker.java +++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/transport/ToolInvoker.java @@ -22,5 +22,5 @@ import java.util.Map; * Tool 서버 호출 transport의 최소 공통 인터페이스입니다. */ public interface ToolInvoker { - JsonNode invoke(Map payload, String targetUrl); + JsonNode invoke(Map payload, String targetUrl, Map headers); } diff --git a/dap-tool-core/src/main/java/io/shinhanlife/dap/mcc/presentation/BusinessToolController.java b/dap-tool-core/src/main/java/io/shinhanlife/dap/mcc/presentation/BusinessToolController.java index b66c71f..e5bfea5 100644 --- a/dap-tool-core/src/main/java/io/shinhanlife/dap/mcc/presentation/BusinessToolController.java +++ b/dap-tool-core/src/main/java/io/shinhanlife/dap/mcc/presentation/BusinessToolController.java @@ -70,10 +70,13 @@ public class BusinessToolController { public ResponseEntity executeDynamicTool( @PathVariable("name") String functionName, @RequestHeader(value = "X-Request-Id", required = false) String headerRequestId, + @RequestHeader(value = "trace-id", required = false) String traceId, + @RequestHeader(value = "request-id", required = false) String requestId, @RequestBody(required = false) Map arguments) { String finalRequestId = headerRequestId; + log.info(" [Tool] IN - trace-id: {}, request-id: {}", traceId, requestId); log.info("\n [Tool] 동적 툴 실행 요청 수신 (함수명): {}", functionName); if (arguments != null) { try { @@ -198,7 +201,13 @@ public class BusinessToolController { log.info("[Tool -> MCP Gateway] 동적 툴 실행 결과 반환: {}", methodResult); } - return ResponseEntity.ok(methodResult); + log.info(" [Tool] OUT - trace-id: {}, request-id: {}", traceId, requestId); + + ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.ok(); + if (traceId != null) responseBuilder.header("trace-id", traceId); + if (requestId != null) responseBuilder.header("request-id", requestId); + + return responseBuilder.body(methodResult); } catch (Exception e) { log.error("[Tool] 리플렉션 실행 중 예외 발생: {}", e.getMessage());