diff --git a/src/main/java/com/eactive/eai/adapter/handler/TemplateAdapterErrorMsgHandler.java b/src/main/java/com/eactive/eai/adapter/handler/TemplateAdapterErrorMsgHandler.java
new file mode 100644
index 0000000..2d1956f
--- /dev/null
+++ b/src/main/java/com/eactive/eai/adapter/handler/TemplateAdapterErrorMsgHandler.java
@@ -0,0 +1,197 @@
+package com.eactive.eai.adapter.handler;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.eactive.eai.adapter.AdapterPropManager;
+import com.eactive.eai.common.util.Logger;
+import com.eactive.eai.message.StandardItem;
+import com.eactive.eai.message.StandardMessage;
+
+/**
+ * 템플릿 기반 어댑터 에러 메시지 핸들러.
+ *
+ * [프로퍼티 설정]
+ * AdapterPropManager 의 "AdapterErrorMessageHandler" 프로퍼티 그룹에서
+ * "{adapterGroupId}.template" 키로 등록된 템플릿 문자열을 읽어
+ * StandardMessage 의 필드 값으로 치환한 결과를 반환한다.
+ *
+ * [변수 치환 규칙]
+ * 1. "${callprop.키}" → callProp.getProperty("키") 로 치환
+ * 2. "${경로}" → StandardMessage.findItemValue("경로") 로 치환
+ * callprop 접두어가 있으면 callProp 을 우선 참조하고,
+ * 없으면 StandardMessage 경로로 해석한다.
+ *
+ * [배열 반복 문법] (MSG_LIST 등 GRID 타입)
+ * {{#foreach MSG.MSG_LIST}}
+ * { "code":"${outp_msg_cd}", "msg":"${outp_msg_ctnt}" }
+ * {{/foreach}}
+ * → MSG_LIST 각 행을 반복하며 행 내 필드명(접두어 없이)으로 치환,
+ * 반복 결과를 쉼표(,)로 이어 붙임
+ *
+ * [사용 예 – JSON 템플릿]
+ * {
+ * "adapterName": "${callprop.ADAPTER_NAME}",
+ * "resultCode": "${MSG.MAIN_MSG.outp_msg_cd}",
+ * "resultMsg": "${MSG.MAIN_MSG.outp_msg_ctnt}",
+ * "errors": [
+ * {{#foreach MSG.MSG_LIST}}
+ * {
+ * "code": "${outp_msg_cd}",
+ * "msg": "${outp_msg_ctnt}",
+ * "desc": "${outp_msg_desc}",
+ * "field": "${err_occu_item_nm}"
+ * }
+ * {{/foreach}}
+ * ]
+ * }
+ *
+ * [사용 예 – XML 템플릿]
+ *
+ * ${callprop.ADAPTER_NAME}
+ * ${MSG.MAIN_MSG.outp_msg_cd}
+ *
+ * {{#foreach MSG.MSG_LIST}}
+ * ${outp_msg_cd}${outp_msg_ctnt}
+ * {{/foreach}}
+ *
+ *
+ */
+public class TemplateAdapterErrorMsgHandler implements AdapterErrorMessageHandler {
+
+ private static final Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
+
+ /** AdapterPropManager 에서 템플릿을 조회할 프로퍼티 그룹 이름 */
+ static final String PROP_GROUP = "AdapterErrorMessageHandler";
+
+ /** callProp 참조 변수 접두어: ${callprop.키} */
+ static final String CALLPROP_PREFIX = "callprop.";
+
+ /** ${path} 또는 ${callprop.key} 스칼라 변수 패턴 */
+ private static final Pattern VAR_PATTERN =
+ Pattern.compile("\\$\\{([^}]+)\\}");
+
+ /** {{#foreach path}}...{{/foreach}} 배열 반복 블록 패턴 */
+ private static final Pattern FOREACH_PATTERN =
+ Pattern.compile("\\{\\{#foreach\\s+([^}]+)\\}\\}(.*?)\\{\\{/foreach\\}\\}",
+ Pattern.DOTALL);
+
+ @Override
+ public Object generateNonStandardErrorResponseMessage(
+ String inboundAdapterGroupName,
+ String inboundAdapterName,
+ Properties callProp,
+ Object outboundRequestData,
+ StandardMessage resStandardMessage) throws Exception {
+
+ String templateKey = inboundAdapterGroupName + ".template";
+ String template = AdapterPropManager.getInstance().getProperty(PROP_GROUP, templateKey);
+
+ if (StringUtils.isBlank(template)) {
+ if (logger.isWarn()) {
+ logger.warn("TemplateAdapterErrorMsgHandler] template not found. group=" + PROP_GROUP
+ + ", key=" + templateKey);
+ }
+ return null;
+ }
+
+ return render(template, resStandardMessage, callProp);
+ }
+
+ /**
+ * 템플릿에 StandardMessage 및 callProp 값을 치환해 최종 문자열을 반환한다.
+ * package-private: 단위 테스트에서 직접 호출 가능
+ */
+ String render(String template, StandardMessage msg, Properties callProp) {
+ // 1단계: {{#foreach path}}...{{/foreach}} 배열 반복 처리
+ StringBuffer sb = new StringBuffer();
+ Matcher foreachMatcher = FOREACH_PATTERN.matcher(template);
+ while (foreachMatcher.find()) {
+ String arrayPath = foreachMatcher.group(1).trim();
+ String blockContent = foreachMatcher.group(2);
+ String rendered = renderForeach(arrayPath, blockContent, msg);
+ foreachMatcher.appendReplacement(sb, Matcher.quoteReplacement(rendered));
+ }
+ foreachMatcher.appendTail(sb);
+
+ // 2단계: 나머지 ${path} / ${callprop.키} 스칼라 변수 치환
+ String intermediate = sb.toString();
+ StringBuffer result = new StringBuffer();
+ Matcher varMatcher = VAR_PATTERN.matcher(intermediate);
+ while (varMatcher.find()) {
+ String expr = varMatcher.group(1).trim();
+ String value = resolveScalar(expr, msg, callProp);
+ varMatcher.appendReplacement(result, Matcher.quoteReplacement(value));
+ }
+ varMatcher.appendTail(result);
+ return result.toString();
+ }
+
+ /**
+ * 변수 표현식을 해석해 값을 반환한다.
+ * - "callprop.키" → callProp.getProperty("키")
+ * - 그 외 → StandardMessage.findItemValue(expr)
+ */
+ private String resolveScalar(String expr, StandardMessage msg, Properties callProp) {
+ if (expr.startsWith(CALLPROP_PREFIX)) {
+ String key = expr.substring(CALLPROP_PREFIX.length());
+ if (callProp != null) {
+ return StringUtils.defaultString(callProp.getProperty(key));
+ }
+ return "";
+ }
+ return StringUtils.defaultString(msg.findItemValue(expr));
+ }
+
+ /**
+ * GRID 타입 배열 아이템을 반복하며 blockContent 를 각 행으로 치환,
+ * 결과를 쉼표로 이어 반환한다. (foreach 블록 내부는 callProp 참조 미지원)
+ */
+ private String renderForeach(String arrayPath, String blockContent, StandardMessage msg) {
+ StandardItem arrayItem = msg.findItem(arrayPath);
+ if (arrayItem == null) {
+ if (logger.isWarn()) logger.warn("TemplateAdapterErrorMsgHandler] foreach path not found: " + arrayPath);
+ return "";
+ }
+
+ List> rows = arrayItem.getList();
+ if (rows == null || rows.isEmpty()) {
+ return "";
+ }
+
+ List renderedItems = new ArrayList<>();
+ for (LinkedHashMap row : rows) {
+ String rowText = renderRow(blockContent, row);
+ if (StringUtils.isNotBlank(rowText)) {
+ renderedItems.add(rowText);
+ }
+ }
+ return String.join(",", renderedItems);
+ }
+
+ /**
+ * 배열 한 행(LinkedHashMap) 기준으로 blockContent 의 ${fieldName} 을 치환한다.
+ * fieldName 은 경로 없이 해당 행의 컬럼명만 사용한다.
+ */
+ private String renderRow(String blockContent, LinkedHashMap row) {
+ StringBuffer result = new StringBuffer();
+ Matcher varMatcher = VAR_PATTERN.matcher(blockContent);
+ while (varMatcher.find()) {
+ String fieldName = varMatcher.group(1).trim();
+ String value = "";
+ StandardItem item = row.get(fieldName);
+ if (item != null) {
+ value = StringUtils.defaultString(item.getValue());
+ }
+ varMatcher.appendReplacement(result, Matcher.quoteReplacement(value));
+ }
+ varMatcher.appendTail(result);
+ return result.toString().trim();
+ }
+}
diff --git a/src/main/java/com/eactive/eai/adapter/http/client/HttpClient5AdapterServiceSupport.java b/src/main/java/com/eactive/eai/adapter/http/client/HttpClient5AdapterServiceSupport.java
index 601b163..046ace8 100644
--- a/src/main/java/com/eactive/eai/adapter/http/client/HttpClient5AdapterServiceSupport.java
+++ b/src/main/java/com/eactive/eai/adapter/http/client/HttpClient5AdapterServiceSupport.java
@@ -40,7 +40,9 @@ import com.eactive.eai.adapter.AdapterManager;
import com.eactive.eai.adapter.Keys;
import com.eactive.eai.adapter.http.secure.HttpClient5SSLContextFactory;
import com.eactive.eai.common.TransactionContextKeys;
+import com.eactive.eai.common.message.EAIMessageKeys;
import com.eactive.eai.common.property.PropManager;
+import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.HttpAdapterExtraLogUtil;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.httpouttlsinfo.HttpOutTlsInfoVO;
@@ -566,4 +568,10 @@ public abstract class HttpClient5AdapterServiceSupport implements HttpClientAdap
}
public abstract Object execute(Properties prop, Object message, Properties tempProp) throws Exception;
+
+ protected boolean isTas(Properties tempProp) {
+ String simYn = tempProp.getProperty("SIM_YN");
+ return EAIServerManager.getInstance().isTASEnabledEAIServer()
+ && StringUtils.equals(simYn, EAIMessageKeys.TRANTYPE_TAS);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceBody.java b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceBody.java
index 2468b2f..691a213 100644
--- a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceBody.java
+++ b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceBody.java
@@ -41,14 +41,11 @@ public class HttpClient5AdapterServiceBody extends HttpClient5AdapterServiceSupp
boolean useForwardProxy = StringUtils.equalsIgnoreCase(prop.getProperty("FORWARD_PROXY_USE_YN", "N"), "Y");
String forwardProxyUrl = prop.getProperty("FORWARD_PROXY_URL");
- if(useForwardProxy) {
+ if(useForwardProxy && !isTas(tempProp)) {
java.net.URL url = new java.net.URL(forwardProxyUrl);
// 프로토콜, 호스트, 포트 추출
- String protocol = url.getProtocol();
- String host = url.getHost();
- int port = url.getPort();
- HttpHost proxy = new HttpHost(protocol,host, port);
+ HttpHost proxy = new HttpHost(url.getProtocol(), url.getHost(), url.getPort());
requestConfigBuilder.setProxy(proxy);
}
diff --git a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java
index 88558bb..cefe58d 100644
--- a/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java
+++ b/src/main/java/com/eactive/eai/adapter/http/client/impl/HttpClient5AdapterServiceRest.java
@@ -11,6 +11,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
+import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -294,14 +295,11 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
- if(useForwardProxy) {
+ if(useForwardProxy && !isTas(tempProp)) {
java.net.URL url = new java.net.URL(forwardProxyUrl);
// 프로토콜, 호스트, 포트 추출
- String protocol = url.getProtocol();
- String host = url.getHost();
- int port = url.getPort();
- HttpHost proxy = new HttpHost(protocol,host, port);
+ HttpHost proxy = new HttpHost(url.getProtocol(), url.getHost(), url.getPort());
requestConfigBuilder.setProxy(proxy);
}
@@ -708,12 +706,19 @@ public class HttpClient5AdapterServiceRest extends HttpClient5AdapterServiceSupp
return new String(responseMessage, encode);
}
- String[] relayKeyArr = org.springframework.util.StringUtils.tokenizeToStringArray(relayHeaderKeys, ",");
JSONObject headerJson = new JSONObject();
- for (String key : relayKeyArr) {
- String value = responseHeaderProp.getProperty(key);
- if (StringUtils.isNotBlank(value)) {
- headerJson.put(key, value);
+ if (StringUtils.equalsIgnoreCase(relayHeaderKeys, "ALL")) {
+ for (Enumeration