fix: preserve Korean text in scaffold output
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 2m28s

This commit is contained in:
jade
2026-08-12 17:29:10 +09:00
parent 017812cd29
commit 6aa62a5499
8 changed files with 183 additions and 131 deletions

View File

@@ -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 = """
<?xml version="1.0" encoding="UTF-8"?>
@@ -312,21 +313,21 @@ public class PodScaffolder {
<logger name="io.shinhanlife" level="DEBUG" />
</configuration>
""".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);

View File

@@ -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
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 0986406 理쒖큹?앹꽦
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
@@ -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
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@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
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -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
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -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
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -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<Object> resTransfer = mci.callTo(
@@ -400,7 +401,7 @@ public class ToolScaffolder {
: "MCI call completed without a response body.");
return response;
} catch (Exception e) {
log.error("[MCI Tool] ?곕룞 以??ㅻ쪟 諛쒖깮: {}", e.getMessage(), e);
log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e);
%sResponse response = new %sResponse();
response.setResultCode("ERROR");
response.setResultMessage(e.getMessage() != null ? e.getMessage() : "Unknown error");
@@ -460,14 +461,14 @@ public class ToolScaffolder {
/**
* @package %s.usecase.impl
* @className %sUseCaseImpl
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -513,7 +514,7 @@ public class ToolScaffolder {
);
}
Files.writeString(usecaseImplDir.resolve(baseName + "UseCaseImpl.java"), serviceImplContent);
writeUtf8(usecaseImplDir.resolve(baseName + "UseCaseImpl.java"), serviceImplContent);
if (isMci) {
String mciReqContent = """
@@ -524,32 +525,32 @@ public class ToolScaffolder {
/**
* @package %s.%s.io
* @className %s_I
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@Data
public class %s_I {
/**
* EAI ?쒖뒪?쒖씠 ?붽뎄?섎뒗 ?섏떊??踰덊샇 ?뚮씪誘명꽣紐?
* EAI 시스템이 요구하는 수신자 번호 파라미터명
*/
private String phone;
/**
* EAI ?쒖뒪?쒖씠 ?붽뎄?섎뒗 硫붿떆吏€ ?댁슜 ?뚮씪誘명꽣紐?
* EAI 시스템이 요구하는 메시지 내용 파라미터명
*/
private String content;
}
""".formatted(BASE_PACKAGE, mciGroupPath.replace("/", "."), BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, author, createDate, createDate, author, interfaceId);
mciReqContent = mciIoContent(mciGroupPath.replace("/", "."), interfaceId + "_I", inputFields, author, createDate);
Files.writeString(mciIoDir.resolve(interfaceId + "_I.java"), mciReqContent);
writeUtf8(mciIoDir.resolve(interfaceId + "_I.java"), mciReqContent);
String mciResContent = """
package %s.%s.io;
@@ -559,14 +560,14 @@ public class ToolScaffolder {
/**
* @package %s.%s.io
* @className %s_O
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -576,7 +577,7 @@ public class ToolScaffolder {
}
""".formatted(BASE_PACKAGE, mciGroupPath.replace("/", "."), BASE_PACKAGE, mciGroupPath.replace("/", "."), interfaceId, author, createDate, createDate, author, interfaceId);
mciResContent = mciIoContent(mciGroupPath.replace("/", "."), interfaceId + "_O", outputFields, author, createDate);
Files.writeString(mciIoDir.resolve(interfaceId + "_O.java"), mciResContent);
writeUtf8(mciIoDir.resolve(interfaceId + "_O.java"), mciResContent);
String converterContent = """
package %s.converter;
@@ -592,14 +593,14 @@ public class ToolScaffolder {
/**
* @package %s.converter
* @className %sConverter
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -628,7 +629,7 @@ public class ToolScaffolder {
baseName, interfaceId
);
converterContent = mciConverterContent(bizPackage, baseName, mciGroupPath.replace("/", "."), interfaceId);
Files.writeString(converterDir.resolve(baseName + "Converter.java"), converterContent);
writeUtf8(converterDir.resolve(baseName + "Converter.java"), converterContent);
log.append("\n=========================================\n");
log.append(" Scaffolding Complete! (Routing: " + routingType + ")\n");
@@ -653,14 +654,14 @@ public class ToolScaffolder {
/**
* @package %s.%s
* @className Mci%sClient
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -677,7 +678,7 @@ public class ToolScaffolder {
BASE_PACKAGE, mciGroupPath.replace("/", "."),
BASE_PACKAGE, mciGroupPath.replace("/", "."), clientPrefixCap, author, createDate, createDate, author, clientPrefixCap
);
Files.writeString(mciClientDir.resolve("Mci" + clientPrefixCap + "Client.java"), mciClientContent);
writeUtf8(mciClientDir.resolve("Mci" + clientPrefixCap + "Client.java"), mciClientContent);
log.append("[MCI Client] ").append(mciClientDir.resolve("Mci" + clientPrefixCap + "Client.java")).append("\n");
}
@@ -687,13 +688,13 @@ public class ToolScaffolder {
String httpResponseClass = baseName + "HttpResponse";
String httpClientClass = httpApiClass + "Client";
Files.writeString(httpIoDir.resolve(httpRequestClass + ".java"),
writeUtf8(httpIoDir.resolve(httpRequestClass + ".java"),
dtoContent(httpPackage + ".io", httpRequestClass, inputFields, author, createDate, true));
Files.writeString(httpIoDir.resolve(httpResponseClass + ".java"),
writeUtf8(httpIoDir.resolve(httpResponseClass + ".java"),
dtoContent(httpPackage + ".io", httpResponseClass, outputFields, author, createDate, false));
Files.writeString(httpClientDir.resolve(httpClientClass + ".java"),
writeUtf8(httpClientDir.resolve(httpClientClass + ".java"),
httpClientContent(httpPackage, httpClientClass, httpApiName));
Files.writeString(converterDir.resolve(baseName + "Converter.java"),
writeUtf8(converterDir.resolve(baseName + "Converter.java"),
httpConverterContent(bizPackage, baseName, httpPackage));
log.append("\n=========================================\n");
@@ -716,32 +717,32 @@ public class ToolScaffolder {
/**
* @package %s.legacy
* @className %sLegacyRequest
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@Data
public class %sLegacyRequest {
/**
* EAI ?쒖뒪?쒖씠 ?붽뎄?섎뒗 ?섏떊??踰덊샇 ?뚮씪誘명꽣紐?
* EAI 시스템이 요구하는 수신자 번호 파라미터명
*/
private String phone;
/**
* EAI ?쒖뒪?쒖씠 ?붽뎄?섎뒗 硫붿떆吏€ ?댁슜 ?뚮씪誘명꽣紐?
* EAI 시스템이 요구하는 메시지 내용 파라미터명
*/
private String content;
}
""".formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName);
legacyReqContent = dtoContent(bizPackage + ".legacy", baseName + "LegacyRequest", inputFields, author, createDate, true);
Files.writeString(legacyDtoDir.resolve(baseName + "LegacyRequest.java"), legacyReqContent);
writeUtf8(legacyDtoDir.resolve(baseName + "LegacyRequest.java"), legacyReqContent);
String legacyResContent = """
package %s.legacy;
@@ -751,14 +752,14 @@ public class ToolScaffolder {
/**
* @package %s.legacy
* @className %sLegacyResponse
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -768,7 +769,7 @@ public class ToolScaffolder {
}
""".formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName);
legacyResContent = dtoContent(bizPackage + ".legacy", baseName + "LegacyResponse", outputFields, author, createDate, true);
Files.writeString(legacyDtoDir.resolve(baseName + "LegacyResponse.java"), legacyResContent);
writeUtf8(legacyDtoDir.resolve(baseName + "LegacyResponse.java"), legacyResContent);
String converterContent = """
package %s.converter;
@@ -784,14 +785,14 @@ public class ToolScaffolder {
/**
* @package %s.converter
* @className %sConverter
* @description AX HUB ?쒖뒪??泥섎━ ?대옒??
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 媛쒖젙?대젰 ----------
* ?섏젙?? ?섏젙?? ?섏젙?댁슜
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 理쒖큹?앹꽦
* %s %s 최초생성
*
* </pre>
*/
@@ -818,7 +819,7 @@ public class ToolScaffolder {
baseName, baseName, baseName, baseName, baseName, baseName, baseName
);
converterContent = legacyConverterContent(bizPackage, baseName);
Files.writeString(converterDir.resolve(baseName + "Converter.java"), converterContent);
writeUtf8(converterDir.resolve(baseName + "Converter.java"), converterContent);
log.append("\n=========================================\n");
log.append(" Scaffolding Complete! (Routing: " + routingType + ")\n");
@@ -831,7 +832,7 @@ public class ToolScaffolder {
log.append("[Legacy Response DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyResponse.java")).append("\n");
log.append("[Legacy Converter] ").append(converterDir.resolve(baseName + "Converter.java")).append("\n");
}
// schema resource ?뚯씪 ?앹꽦 (useSchemaResource=true ????
// Schema Resource 파일 생성(useSchemaResource=true일 때)
if (useSchemaResource) {
Files.createDirectories(schemaDir);
String inputSchema = """
@@ -841,7 +842,7 @@ public class ToolScaffolder {
"properties": {
"TODO_FIELD": {
"type": "string",
"description": "TODO: ?뚮씪誘명꽣 ?ㅻ챸???낅젰?섏꽭??"
"description": "TODO: 파라미터 설명을 입력하세요."
}
},
"required": []
@@ -854,19 +855,19 @@ public class ToolScaffolder {
"properties": {
"status": {
"type": "string",
"description": "泥섎━ 寃곌낵 ?곹깭 (SUCCESS / FAILURE)",
"description": "처리 결과 상태 (SUCCESS / FAILURE)",
"enum": ["SUCCESS", "FAILURE"]
},
"message": {
"type": "string",
"description": "泥섎━ 寃곌낵 硫붿떆吏€"
"description": "처리 결과 메시지"
}
},
"required": ["status"]
}
""";
Files.writeString(schemaDir.resolve(inputSchemaFileName), inputSchema);
Files.writeString(schemaDir.resolve(outputSchemaFileName), outputSchema);
writeUtf8(schemaDir.resolve(inputSchemaFileName), inputSchema);
writeUtf8(schemaDir.resolve(outputSchemaFileName), outputSchema);
log.append("[Input Schema] ").append(schemaDir.resolve(inputSchemaFileName)).append("\n");
log.append("[Output Schema] ").append(schemaDir.resolve(outputSchemaFileName)).append("\n");
}
@@ -881,9 +882,9 @@ public class ToolScaffolder {
Files.createDirectories(wireMockBodyPath.getParent());
Files.createDirectories(wireMockMappingPath.getParent());
Files.createDirectories(podMockResponsePath.getParent());
Files.writeString(wireMockBodyPath, mockResponse);
Files.writeString(wireMockMappingPath, wireMockMappingContent(interfaceId, wireMockBodyPath.getFileName().toString()));
Files.writeString(podMockResponsePath, mockResponse);
writeUtf8(wireMockBodyPath, mockResponse);
writeUtf8(wireMockMappingPath, wireMockMappingContent(interfaceId, wireMockBodyPath.getFileName().toString()));
writeUtf8(podMockResponsePath, mockResponse);
ensureLocalHttpApiConfiguration(projectRoot, httpApiName, toolName);
log.append("[WireMock Response] ").append(wireMockBodyPath).append("\n");
log.append("[WireMock Mapping] ").append(wireMockMappingPath).append("\n");
@@ -891,19 +892,19 @@ public class ToolScaffolder {
} else {
Path mockResponsePath = rootDir.resolve(Paths.get(moduleName, "src/main/resources/mock-responses", toolName + ".json"));
Files.createDirectories(mockResponsePath.getParent());
Files.writeString(mockResponsePath, mockResponse);
writeUtf8(mockResponsePath, mockResponse);
log.append("[Mock Response] ").append(mockResponsePath).append("\n");
}
Path generatedTestDir = rootDir.resolve(Paths.get(moduleName, "src/test/java/io/shinhanlife/dap/mcc/biz", group.toLowerCase(), "usecase"));
Files.createDirectories(generatedTestDir);
Path generatedTestPath = generatedTestDir.resolve(baseName + "UseCaseTest.java");
Files.writeString(generatedTestPath, useCaseTestContent(bizPackage, baseName));
writeUtf8(generatedTestPath, useCaseTestContent(bizPackage, baseName));
log.append("[Unit Test] ").append(generatedTestPath).append("\\n");
log.append("[Test Command] .\\gradlew.bat :").append(moduleName.substring(moduleName.lastIndexOf(java.io.File.separator) + 1)).append(":test --tests \"*").append(baseName).append("UseCaseTest\"\\n");
Files.createDirectories(definitionDir);
Path definitionPath = definitionDir.resolve(toolName + ".yml");
Files.writeString(definitionPath, toolDefinitionContentV17(toolName, title, description, group,
writeUtf8(definitionPath, toolDefinitionContentV17(toolName, title, description, group,
interfaceId, inputFields, isMutationTool(baseName), definitionOptions));
log.append("[V17 Tool Definition] ").append(definitionPath).append("\n");
log.append("\n Tip: HTTP Tool은 WireMock 실행 후 생성된 mapping URL로 호출을 확인하세요.\n");
@@ -1089,7 +1090,7 @@ public class ToolScaffolder {
private static void ensureLocalHttpApiConfiguration(Path projectRoot, String httpApiName, String toolName) throws IOException {
Path localConfigPath = projectRoot.resolve("dap-was-lib/src/main/resources/glow/application-glow-local.yml");
Files.createDirectories(localConfigPath.getParent());
String existing = Files.exists(localConfigPath) ? Files.readString(localConfigPath) : "";
String existing = Files.exists(localConfigPath) ? Files.readString(localConfigPath, StandardCharsets.UTF_8) : "";
if (java.util.regex.Pattern.compile("(?m)^\\s*-\\s+name:\\s*"
+ java.util.regex.Pattern.quote(httpApiName) + "\\s*$").matcher(existing).find()) {
return;
@@ -1133,7 +1134,11 @@ public class ToolScaffolder {
enabled: true
""";
}
Files.writeString(localConfigPath, existing);
writeUtf8(localConfigPath, existing);
}
private static void writeUtf8(Path path, String content) throws IOException {
Files.writeString(path, content, StandardCharsets.UTF_8);
}
private static String dtoContent(String packageName, String className, List<FieldDefinition> fields,

View File

@@ -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.util.Comparator;
/** Updates MCP SDK and project-owned metadata in a generated tool source file. */
@@ -27,10 +28,10 @@ public final class ToolSourceUpdater {
throw new IllegalArgumentException("Tool source not found: " + toolName);
}
String content = Files.readString(targetFile);
String content = Files.readString(targetFile, StandardCharsets.UTF_8);
content = updateMcpTool(content, toolName, description);
content = updateToolHint(content, categoryKey, register, requiresApproval);
Files.writeString(targetFile, content);
Files.writeString(targetFile, content, StandardCharsets.UTF_8);
}
private static Path findToolSource(Path rootDirectory, String toolName) throws IOException {
@@ -47,7 +48,7 @@ public final class ToolSourceUpdater {
private static boolean containsMcpTool(Path path, String toolName) {
try {
return annotationArguments(Files.readString(path), "McpTool", toolName) != null;
return annotationArguments(Files.readString(path, StandardCharsets.UTF_8), "McpTool", toolName) != null;
} catch (IOException exception) {
throw new IllegalStateException("Failed to read tool source: " + path, exception);
}