diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/PodScaffolder.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/PodScaffolder.java
index 09bf3a8e..64125ddd 100644
--- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/PodScaffolder.java
+++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/PodScaffolder.java
@@ -4,6 +4,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.charset.StandardCharsets;
import java.nio.file.StandardOpenOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@@ -72,7 +73,7 @@ public class PodScaffolder {
implementation project(':dap-was-lib')
}
""";
- Files.writeString(modulePath.resolve("build.gradle"), buildGradle);
+ writeUtf8(modulePath.resolve("build.gradle"), buildGradle);
log.append("[3/6] Dockerfile 생성 중...\n");
String dockerfile = """
@@ -84,7 +85,7 @@ public class PodScaffolder {
EXPOSE %s
ENTRYPOINT ["java", "-jar", "app.jar"]
""".formatted(moduleName, portStr);
- Files.writeString(modulePath.resolve("Dockerfile"), dockerfile);
+ writeUtf8(modulePath.resolve("Dockerfile"), dockerfile);
log.append("[4/6] Application 클래스 및 설정 파일 생성 중...\n");
Path srcPath = modulePath.resolve("src/main/java/io/shinhanlife/dap/mcc/" + shortName);
@@ -121,7 +122,7 @@ public class PodScaffolder {
}
}
""".formatted(shortName, shortName, capitalize(shortName), author, createDate, createDate, author, capitalize(shortName), capitalize(shortName));
- Files.writeString(srcPath.resolve("DapWas" + capitalize(shortName) + "Application.java"), appClass);
+ writeUtf8(srcPath.resolve("DapWas" + capitalize(shortName) + "Application.java"), appClass);
Path resPath = modulePath.resolve("src/main/resources");
Files.createDirectories(resPath);
@@ -145,7 +146,7 @@ public class PodScaffolder {
tenant-domains:
TESTER-DEV: ALL
""".formatted(portStr, moduleName, moduleName.replace("dap-", ""));
- Files.writeString(resPath.resolve("application.yml"), applicationYml);
+ writeUtf8(resPath.resolve("application.yml"), applicationYml);
String applicationLocalYml = """
# Local 환경 전용 설정 (H2 메모리 DB 등)
@@ -194,7 +195,7 @@ public class PodScaffolder {
tool:
url: ${AXHUB_TOOL_URL:http://localhost:${server.port}}
""";
- Files.writeString(resPath.resolve("application-local.yml"), applicationLocalYml);
+ writeUtf8(resPath.resolve("application-local.yml"), applicationLocalYml);
String applicationDevYml = """
# OCI 클라우드 환경 전용 설정
@@ -244,7 +245,7 @@ public class PodScaffolder {
externalMci:
url: http://10.176.32.176
""".formatted(portStr, portStr);
- Files.writeString(resPath.resolve("application-dev.yml"), applicationDevYml);
+ writeUtf8(resPath.resolve("application-dev.yml"), applicationDevYml);
String applicationTestYml = """
server:
@@ -264,7 +265,7 @@ public class PodScaffolder {
tool:
url: ${AXHUB_TOOL_URL}
""".formatted(portStr);
- Files.writeString(resPath.resolve("application-test.yml"), applicationTestYml);
+ writeUtf8(resPath.resolve("application-test.yml"), applicationTestYml);
String applicationProdYml = """
server:
@@ -284,7 +285,7 @@ public class PodScaffolder {
tool:
url: ${AXHUB_TOOL_URL}
""".formatted(portStr);
- Files.writeString(resPath.resolve("application-prod.yml"), applicationProdYml);
+ writeUtf8(resPath.resolve("application-prod.yml"), applicationProdYml);
String logbackXml = """
@@ -312,21 +313,21 @@ public class PodScaffolder {
""".formatted(moduleName, moduleName);
- Files.writeString(resPath.resolve("logback-spring.xml"), logbackXml);
+ writeUtf8(resPath.resolve("logback-spring.xml"), logbackXml);
log.append("[5/6] settings.gradle 에 모듈 등록 중...\n");
Path settingsPath = rootDir.resolve(Paths.get("settings.gradle"));
if (Files.exists(settingsPath)) {
- String settings = Files.readString(settingsPath);
+ String settings = Files.readString(settingsPath, StandardCharsets.UTF_8);
if (!settings.contains("include '" + moduleName + "'")) {
- Files.writeString(settingsPath, System.lineSeparator() + "include '" + moduleName + "'" + System.lineSeparator(), StandardOpenOption.APPEND);
+ Files.writeString(settingsPath, System.lineSeparator() + "include '" + moduleName + "'" + System.lineSeparator(), StandardCharsets.UTF_8, StandardOpenOption.APPEND);
}
}
log.append("[6/6] docker-compose.yml 에 서비스 추가 중...\n");
Path dockerComposePath = rootDir.resolve(Paths.get("docker-compose.yml"));
if (Files.exists(dockerComposePath)) {
- String compose = Files.readString(dockerComposePath);
+ String compose = Files.readString(dockerComposePath, StandardCharsets.UTF_8);
String serviceName = moduleName.replace("dap-", ""); // e.g. tool-payment
if (!compose.contains(" " + serviceName + ":")) {
String newService = """
@@ -352,7 +353,7 @@ public class PodScaffolder {
- GLOW_COMMUNICATION_EAI_HOST=http://mci-mock
- GLOW_COMMUNICATION_EAI_PORT=8080
""".formatted(serviceName, moduleName, portStr, portStr, serviceName, portStr);
- Files.writeString(dockerComposePath, System.lineSeparator() + newService, StandardOpenOption.APPEND);
+ Files.writeString(dockerComposePath, System.lineSeparator() + newService, StandardCharsets.UTF_8, StandardOpenOption.APPEND);
}
}
@@ -365,6 +366,10 @@ public class PodScaffolder {
return log.toString();
}
+ private static void writeUtf8(Path path, String content) throws IOException {
+ Files.writeString(path, content, StandardCharsets.UTF_8);
+ }
+
private static String capitalize(String str) {
if (str == null || str.isEmpty()) return str;
return str.substring(0, 1).toUpperCase() + str.substring(1);
diff --git a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java
index 7125d841..d6da6dd5 100644
--- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java
+++ b/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java
@@ -5,6 +5,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashSet;
@@ -14,28 +15,28 @@ import java.util.Set;
import java.util.Scanner;
/**
- * MCP Tool 肄붾뱶瑜??먮룞 ?앹꽦(Scaffolding)?섎뒗 ?좏떥由ы떚 ?대옒??
+ * MCP Tool 코드를 자동 생성(Scaffolding)하는 유틸리티 클래스입니다.
*
- * [?ㅽ뻾 諛⑸쾿]
- * 諛⑸쾿 1. IDE(IntelliJ ???먯꽌 吏곸젒 ?ㅽ뻾 (??뷀삎 紐⑤뱶 異붿쿇 狩?
- * - ???대옒??ToolScaffolder.java)瑜??닿퀬 main 硫붿꽌?쒕? 吏곸젒 ?ㅽ뻾(Run)?⑸땲??
- * - 肄섏넄 李쎌뿉 ?⑤뒗 吏덈Ц??李⑤??濡?媛믪쓣 ?낅젰?섍린留??섎㈃ ?뚯씪???앹꽦?⑸땲??
+ * [실행 방법]
+ * 방법 1. IDE(IntelliJ 등)에서 직접 실행
+ * - ToolScaffolder.java의 main 메서드를 실행합니다.
+ * - 콘솔 질문에 차례대로 값을 입력하면 파일이 생성됩니다.
*
- * 諛⑸쾿 2. 而ㅻ㎤?쒕씪???곕????먯꽌 ?ㅽ뻾 (紐낅졊??湲곕컲)
- * - 而댄뙆?? javac -encoding UTF-8 dap-was-lib/src/main/java/io/shinhanlife/dap/mcc/util/ToolScaffolder.java
- * - ?ㅽ뻾: java -cp dap-was-lib/src/main/java io.shinhanlife.dap.lib.util.ToolScaffolder [?대쫫] [ID] "[?ㅻ챸]" "[洹몃9]" "[?듭떊諛⑹떇]" "[紐⑤뱢紐?"
+ * 방법 2. 명령줄에서 실행
+ * - 컴파일: javac -encoding UTF-8 dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java
+ * - 실행: java -cp dap-was-lib/src/main/java io.shinhanlife.dap.lib.util.ToolScaffolder [이름] [ID] "[설명]" "[그룹]" "[통신방식]" "[모듈명]"
*/
/**
* @package io.shinhanlife.dap.lib.util
* @className ToolScaffolder
- * @description AX HUB ?쒖뒪??泥섎━ ?대옒??
+ * @description AX HUB 시스템 처리 클래스
* @author 0986406
* @create 2026.09.01
*
- * ---------- 媛쒖젙?대젰 ----------
- * ?섏젙?? ?섏젙?? ?섏젙?댁슜
+ * ---------- 개정이력 ----------
+ * 수정일 수정자 수정내용
* ---------- -------- ---------------------------
- * 2026.09.01 0986406 理쒖큹?앹꽦
+ * 2026.09.01 0986406 최초생성
*
*
*/
@@ -65,8 +66,8 @@ public class ToolScaffolder {
System.out.println(" MCP Tool Scaffolder (Java CLI) ");
System.out.println("=========================================\n");
- String baseName = getOrAsk(args, 0, scanner, "1. ?앹꽦??Tool??湲곕낯 ?대쫫 (?? ExchangeRate) [?곷Ц PascalCase]: ");
- String interfaceId = getOrAsk(args, 1, scanner, "2. ?덇굅??API ?명꽣?섏씠??ID (?? EXCH_001): ");
+ String baseName = getOrAsk(args, 0, scanner, "1. 생성할 Tool의 기본 이름 (예: ExchangeRate) [영문 PascalCase]: ");
+ String interfaceId = getOrAsk(args, 1, scanner, "2. 레거시 API 인터페이스 ID (예: EXCH_001): ");
String title = getOrAsk(args, 2, scanner, "3. Tool title: ");
String description = getOrAsk(args, 3, scanner, "4. Tool description for LLM: ");
String group = getOrAsk(args, 4, scanner, "5. Tool category: ");
@@ -83,12 +84,12 @@ public class ToolScaffolder {
String defaultAuthor = System.getProperty("user.name");
String defaultDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));
- String author = getOrAsk(args, 7, scanner, "7. ?묒꽦??(?뷀꽣 ?낅젰 ??'" + defaultAuthor + "'): ");
+ String author = getOrAsk(args, 7, scanner, "8. 작성자(Enter 입력 시 '" + defaultAuthor + "'): ");
if (author.trim().isEmpty()) author = defaultAuthor;
- String createDate = getOrAsk(args, 8, scanner, "8. ?묒꽦??(?뷀꽣 ?낅젰 ??'" + defaultDate + "'): ");
+ String createDate = getOrAsk(args, 8, scanner, "9. 작성일(Enter 입력 시 '" + defaultDate + "'): ");
if (createDate.trim().isEmpty()) createDate = defaultDate;
- String useSchemaResourceStr = getOrAsk(args, 9, scanner, "9. input/output JSON Schema ?뚯씪 ?먮룞 ?앹꽦 ?щ? (y/N): ");
+ String useSchemaResourceStr = getOrAsk(args, 9, scanner, "10. input/output JSON Schema 파일 자동 생성 여부 (y/N): ");
boolean useSchemaResource = "y".equalsIgnoreCase(useSchemaResourceStr.trim());
String schemaResourceDirectory = "classpath:tool-schemas/" + group.toLowerCase() + "/";
@@ -161,7 +162,7 @@ public class ToolScaffolder {
Path legacyDtoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "legacy"));
Path converterDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "converter"));
- // schema resource ?뚯씪 寃쎈줈 (useSchemaResource=true ???뚮쭔 ?앹꽦)
+ // Schema Resource 파일 경로(useSchemaResource=true일 때만 생성)
boolean useSchemaResource = (inputSchemaResource != null && !inputSchemaResource.trim().isEmpty()) || (outputSchemaResource != null && !outputSchemaResource.trim().isEmpty());
String schemaBaseName = toKebabCase(baseName);
String inputSchemaFileName = schemaBaseName + "-resource-input-schema.json";
@@ -222,25 +223,25 @@ public class ToolScaffolder {
/**
* @package %s.dto
* @className %sRequest
- * @description AX HUB ?쒖뒪??泥섎━ ?대옒??
+ * @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
*
- * ---------- 媛쒖젙?대젰 ----------
- * ?섏젙?? ?섏젙?? ?섏젙?댁슜
+ * ---------- 개정이력 ----------
+ * 수정일 수정자 수정내용
* ---------- -------- ---------------------------
- * %s %s 理쒖큹?앹꽦
+ * %s %s 최초생성
*
*
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class %sRequest {
- @McpToolParam(description = "?섏떊???꾪솕踰덊샇", required = true)
+ @McpToolParam(description = "수신자 전화번호", required = true)
-(?:\\\\d{3}|\\\\d{4})-\\\\d{4}$", examples = {"010-1234-5678"})
private String phoneNumber;
- @McpToolParam(description = "?꾩넚??硫붿떆吏 ?댁슜", required = true)
+ @McpToolParam(description = "전송할 메시지 내용", required = true)
private String message;
}
""".formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName);
@@ -250,9 +251,9 @@ public class ToolScaffolder {
.replaceAll("(?m)^\\s*@McpToolParam\\([^\\r\\n]*\\)\\R", "")
.replaceAll("(?m)^\\s*-\\(\\?:[^\\r\\n]*\\R", "")
.replace("private String phoneNumber;", "@Schema(example = \"01012345678\")\n private String phoneNumber;")
- .replace("private String message;", "@Schema(example = \"?뚯뒪??硫붿떆吏?낅땲??\")\n private String message;");
+ .replace("private String message;", "@Schema(example = \"테스트 메시지입니다.\")\n private String message;");
reqContent = dtoContent(bizPackage + ".dto", baseName + "Request", inputFields, author, createDate, true);
- Files.writeString(dtoDir.resolve(baseName + "Request.java"), reqContent);
+ writeUtf8(dtoDir.resolve(baseName + "Request.java"), reqContent);
// Generate Response DTO
String resContent = """
@@ -264,14 +265,14 @@ public class ToolScaffolder {
/**
* @package %s.dto
* @className %sResponse
- * @description AX HUB ?쒖뒪??泥섎━ ?대옒??
+ * @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
*
- * ---------- 媛쒖젙?대젰 ----------
- * ?섏젙?? ?섏젙?? ?섏젙?댁슜
+ * ---------- 개정이력 ----------
+ * 수정일 수정자 수정내용
* ---------- -------- ---------------------------
- * %s %s 理쒖큹?앹꽦
+ * %s %s 최초생성
*
*
*/
@@ -286,7 +287,7 @@ public class ToolScaffolder {
}
""".formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName);
resContent = dtoContent(bizPackage + ".dto", baseName + "Response", outputFields, author, createDate, false);
- Files.writeString(dtoDir.resolve(baseName + "Response.java"), resContent);
+ writeUtf8(dtoDir.resolve(baseName + "Response.java"), resContent);
String toolName = toToolName(moduleName, group, baseName);
@@ -310,14 +311,14 @@ public class ToolScaffolder {
/**
* @package %s.usecase
* @className %sUseCase
- * @description AX HUB ?쒖뒪??泥섎━ ?대옒??
+ * @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
*
- * ---------- 媛쒖젙?대젰 ----------
- * ?섏젙?? ?섏젙?? ?섏젙?댁슜
+ * ---------- 개정이력 ----------
+ * 수정일 수정자 수정내용
* ---------- -------- ---------------------------
- * %s %s 理쒖큹?앹꽦
+ * %s %s 최초생성
*
*
*/
@@ -338,7 +339,7 @@ public class ToolScaffolder {
baseName, baseName
);
- Files.writeString(usecaseDir.resolve(baseName + "UseCase.java"), serviceInterfaceContent);
+ writeUtf8(usecaseDir.resolve(baseName + "UseCase.java"), serviceInterfaceContent);
String serviceImplContent;
@@ -361,14 +362,14 @@ public class ToolScaffolder {
/**
* @package %s.usecase.impl
* @className %sUseCaseImpl
- * @description AX HUB ?쒖뒪??泥섎━ ?대옒??
+ * @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
*
- * ---------- 媛쒖젙?대젰 ----------
- * ?섏젙?? ?섏젙?? ?섏젙?댁슜
+ * ---------- 개정이력 ----------
+ * 수정일 수정자 수정내용
* ---------- -------- ---------------------------
- * %s %s 理쒖큹?앹꽦
+ * %s %s 최초생성
*
*
*/
@@ -382,9 +383,9 @@ public class ToolScaffolder {
@Override
public %sResponse execute(%sRequest req) {
- log.info("[MCI Tool] {} ?붿껌 ?섏떊.", "%s");
+ log.info("[MCI Tool] {} 요청 수신.", "%s");
try {
- // MapStruct瑜??댁슜???먮룞 留ㅽ븨 (AI DTO -> MCI DTO)
+ // MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO)
%s_I mciReq = converter.toLegacyRequest(req);
Transfer