From d4fb11deb2d8f1b3e4089c458bf194bdb4d42b75 Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 30 Jan 2026 16:44:21 +0900 Subject: [PATCH 1/2] =?UTF-8?q?RestTemplateConfig=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resttemplate/ExternalHttpClient.java | 68 +++++++++++++++---- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/config/resttemplate/ExternalHttpClient.java b/src/main/java/com/kamco/cd/kamcoback/config/resttemplate/ExternalHttpClient.java index 048625a4..fdaef5a7 100644 --- a/src/main/java/com/kamco/cd/kamcoback/config/resttemplate/ExternalHttpClient.java +++ b/src/main/java/com/kamco/cd/kamcoback/config/resttemplate/ExternalHttpClient.java @@ -1,10 +1,14 @@ package com.kamco.cd.kamcoback.config.resttemplate; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.nio.charset.StandardCharsets; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.HttpStatusCodeException; @@ -16,24 +20,16 @@ import org.springframework.web.client.RestTemplate; public class ExternalHttpClient { private final RestTemplate restTemplate; - private final com.fasterxml.jackson.databind.ObjectMapper objectMapper; + private final ObjectMapper objectMapper; + /** responseType(예: DTO.class, String.class 등)로 그대로 역직렬화해서 받는 호출. - 응답 타입이 명확히 고정일 때 사용 */ public ExternalCallResult call( String url, HttpMethod method, Object body, HttpHeaders headers, Class responseType) { - if (headers == null) headers = new HttpHeaders(); - headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON); - headers.setAccept(java.util.List.of(org.springframework.http.MediaType.APPLICATION_JSON)); + HttpHeaders resolvedHeaders = resolveJsonHeaders(headers); + logRequestBody(body); - try { - if (body != null) { - log.info("[HTTP-REQ-BODY-JSON] {}", objectMapper.writeValueAsString(body)); - } - } catch (Exception e) { - log.warn("[HTTP-REQ-BODY-JSON] serialize failed: {}", e.getMessage()); - } - - HttpEntity entity = new HttpEntity<>(body, headers); + HttpEntity entity = new HttpEntity<>(body, resolvedHeaders); try { ResponseEntity res = restTemplate.exchange(url, method, entity, responseType); @@ -44,5 +40,51 @@ public class ExternalHttpClient { } } + /** + * 응답이 배열/객체/문자열/숫자 등 무엇이든 상관없이 "원문(JSON 문자열)"로 안전하게 받는 호출. - 응답 구조가 유동적이거나, String.class로 받았다가 + * Jackson 컨버터 때문에 깨지는 케이스에 사용 + */ + public ExternalCallResult callRaw( + String url, HttpMethod method, Object body, HttpHeaders headers) { + + HttpHeaders resolvedHeaders = resolveJsonHeaders(headers); + logRequestBody(body); + + HttpEntity entity = new HttpEntity<>(body, resolvedHeaders); + + try { + ResponseEntity res = restTemplate.exchange(url, method, entity, byte[].class); + String raw = + (res.getBody() == null) ? null : new String(res.getBody(), StandardCharsets.UTF_8); + return new ExternalCallResult<>(res.getStatusCodeValue(), true, raw, null); + } catch (HttpStatusCodeException e) { + return new ExternalCallResult<>( + e.getStatusCode().value(), false, null, e.getResponseBodyAsString()); + } + } + + private HttpHeaders resolveJsonHeaders(HttpHeaders headers) { + HttpHeaders h = (headers == null) ? new HttpHeaders() : headers; + + // 이미 세팅돼 있으면 존중하고, 없으면 JSON 기본값 세팅 + if (h.getContentType() == null) { + h.setContentType(MediaType.APPLICATION_JSON); + } + if (h.getAccept() == null || h.getAccept().isEmpty()) { + h.setAccept(List.of(MediaType.APPLICATION_JSON)); + } + return h; + } + + private void logRequestBody(Object body) { + try { + if (body != null) { + log.info("[HTTP-REQ-BODY-JSON] {}", objectMapper.writeValueAsString(body)); + } + } catch (Exception e) { + log.warn("[HTTP-REQ-BODY-JSON] serialize failed: {}", e.getMessage()); + } + } + public record ExternalCallResult(int statusCode, boolean success, T body, String errBody) {} } -- 2.49.1 From 86e8408f278e938441240eb61328b833ec052330 Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 30 Jan 2026 16:51:27 +0900 Subject: [PATCH 2/2] =?UTF-8?q?RestTemplateConfig=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resttemplate/ExternalHttpClient.java | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/config/resttemplate/ExternalHttpClient.java b/src/main/java/com/kamco/cd/kamcoback/config/resttemplate/ExternalHttpClient.java index fdaef5a7..8c2e36b1 100644 --- a/src/main/java/com/kamco/cd/kamcoback/config/resttemplate/ExternalHttpClient.java +++ b/src/main/java/com/kamco/cd/kamcoback/config/resttemplate/ExternalHttpClient.java @@ -22,7 +22,10 @@ public class ExternalHttpClient { private final RestTemplate restTemplate; private final ObjectMapper objectMapper; - /** responseType(예: DTO.class, String.class 등)로 그대로 역직렬화해서 받는 호출. - 응답 타입이 명확히 고정일 때 사용 */ + /** + * - responseType이 DTO면: DTO로 역직렬화 - responseType이 String.class면: 응답을 byte[]로 받아 UTF-8 원문 문자열로 반환 + * (배열/객체 등 유동 JSON 안전) + */ public ExternalCallResult call( String url, HttpMethod method, Object body, HttpHeaders headers, Class responseType) { @@ -32,31 +35,19 @@ public class ExternalHttpClient { HttpEntity entity = new HttpEntity<>(body, resolvedHeaders); try { + if (responseType == String.class) { + ResponseEntity res = restTemplate.exchange(url, method, entity, byte[].class); + String raw = + (res.getBody() == null) ? null : new String(res.getBody(), StandardCharsets.UTF_8); + + @SuppressWarnings("unchecked") + T casted = (T) raw; // responseType == String.class 이므로 안전 + return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null); + } + ResponseEntity res = restTemplate.exchange(url, method, entity, responseType); return new ExternalCallResult<>(res.getStatusCodeValue(), true, res.getBody(), null); - } catch (HttpStatusCodeException e) { - return new ExternalCallResult<>( - e.getStatusCode().value(), false, null, e.getResponseBodyAsString()); - } - } - /** - * 응답이 배열/객체/문자열/숫자 등 무엇이든 상관없이 "원문(JSON 문자열)"로 안전하게 받는 호출. - 응답 구조가 유동적이거나, String.class로 받았다가 - * Jackson 컨버터 때문에 깨지는 케이스에 사용 - */ - public ExternalCallResult callRaw( - String url, HttpMethod method, Object body, HttpHeaders headers) { - - HttpHeaders resolvedHeaders = resolveJsonHeaders(headers); - logRequestBody(body); - - HttpEntity entity = new HttpEntity<>(body, resolvedHeaders); - - try { - ResponseEntity res = restTemplate.exchange(url, method, entity, byte[].class); - String raw = - (res.getBody() == null) ? null : new String(res.getBody(), StandardCharsets.UTF_8); - return new ExternalCallResult<>(res.getStatusCodeValue(), true, raw, null); } catch (HttpStatusCodeException e) { return new ExternalCallResult<>( e.getStatusCode().value(), false, null, e.getResponseBodyAsString()); @@ -66,7 +57,6 @@ public class ExternalHttpClient { private HttpHeaders resolveJsonHeaders(HttpHeaders headers) { HttpHeaders h = (headers == null) ? new HttpHeaders() : headers; - // 이미 세팅돼 있으면 존중하고, 없으면 JSON 기본값 세팅 if (h.getContentType() == null) { h.setContentType(MediaType.APPLICATION_JSON); } -- 2.49.1