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
deleted file mode 100644
index d8425f648..000000000
--- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/PodScaffolder.java
+++ /dev/null
@@ -1,308 +0,0 @@
-package io.shinhanlife.dap.lib.util;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.Scanner;
-
-public class PodScaffolder {
-
- public static void main(String[] args) throws IOException {
- Scanner scanner = new Scanner(System.in);
-
- System.out.println("=========================================");
- System.out.println(" MCP Tool Pod Scaffolder (Java CLI) ");
- System.out.println("=========================================\n");
-
- String rawModuleName = getOrAsk(args, 0, scanner, "1. 생성할 모듈(Pod) 이름 (예: payment 또는 dap-was-payment): ");
- String moduleName = rawModuleName.startsWith("dap-was-") ? rawModuleName : "dap-was-" + rawModuleName;
- String portStr = getOrAsk(args, 1, scanner, "2. 사용할 포트 번호 (예: 8085): ");
- String shortName = moduleName.replace("dap-was-", "").replace("-", "");
-
- String defaultAuthor = System.getProperty("user.name");
- String defaultDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));
-
- String author = getOrAsk(args, 2, scanner, "3. 작성자 (엔터 입력 시 '" + defaultAuthor + "'): ");
- if (author.trim().isEmpty()) author = defaultAuthor;
- String createDate = getOrAsk(args, 3, scanner, "4. 작성일 (엔터 입력 시 '" + defaultDate + "'): ");
- if (createDate.trim().isEmpty()) createDate = defaultDate;
-
- String result = scaffoldPod(moduleName, portStr, shortName, author, createDate);
- System.out.println(result);
- }
-
- private static String getOrAsk(String[] args, int index, Scanner scanner, String prompt) {
- if (args.length > index) {
- return args[index];
- }
- System.out.print(prompt);
- return scanner.nextLine().trim();
- }
-
- public static String scaffoldPod(String moduleName, String portStr, String shortName, String author, String createDate) throws IOException {
- String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
- Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
-
- Path modulePath = rootDir.resolve(Paths.get(moduleName));
- if (Files.exists(modulePath)) {
- return "[오류] 이미 존재하는 모듈입니다: " + moduleName;
- }
-
- StringBuilder log = new StringBuilder();
- log.append("[1/6] 모듈 디렉터리 생성 중...\n");
- Files.createDirectories(modulePath);
-
- log.append("[2/6] build.gradle 생성 중...\n");
- String buildGradle = """
- plugins {
- id 'org.springframework.boot'
- }
- dependencies {
- implementation project(':dap-was-core')
- }
- dependencies {
- compileOnly 'org.projectlombok:lombok:1.18.32'
- annotationProcessor 'org.projectlombok:lombok:1.18.32'
- }
- """;
- Files.writeString(modulePath.resolve("build.gradle"), buildGradle);
-
- log.append("[3/6] Dockerfile 생성 중...\n");
- String dockerfile = """
- FROM eclipse-temurin:21-jdk-alpine
- WORKDIR /app
- COPY build/libs/%s-0.0.1-SNAPSHOT.jar app.jar
- ENTRYPOINT ["java", "-jar", "app.jar"]
- """.formatted(moduleName);
- Files.writeString(modulePath.resolve("Dockerfile"), dockerfile);
-
- log.append("[4/6] Application 클래스 및 설정 파일 생성 중...\n");
- Path srcPath = modulePath.resolve("src/main/java/io/shinhanlife/dap/mcc/" + shortName);
- Files.createDirectories(srcPath);
-
- String appClass = """
- package io.shinhanlife.dap.mcc.%s;
-
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
- import org.springframework.cache.annotation.EnableCaching;
-
- /**
- * @package io.shinhanlife.dap.mcc.%s
- * @className DapWas%sApplication
- * @description AX HUB 시스템 처리 클래스
- * @author %s
- * @create %s
- *
- * ---------- 개정이력 ----------
- * 수정일 수정자 수정내용
- * ---------- -------- ---------------------------
- * %s %s 최초생성
- *
- *
- */
- @SpringBootApplication(scanBasePackages = {"io.shinhanlife.dap.mcc", "io.shinhanlife.dap.lib.adapter", "io.shinhanlife.dap.lib.mcp", "io.shinhanlife.dap.lib.config", "io.shinhanlife.dap.lib.integration"})
- @ConfigurationPropertiesScan(basePackages = {"io.shinhanlife.dap.mcc", "io.shinhanlife.dap.lib.adapter", "io.shinhanlife.dap.lib.mcp", "io.shinhanlife.dap.lib.config", "io.shinhanlife.dap.lib.integration"})
- @EnableCaching
- public class DapWas%sApplication {
- public static void main(String[] args) {
- SpringApplication.run(DapWas%sApplication.class, args);
- }
- }
- """.formatted(shortName, shortName, capitalize(shortName), author, createDate, createDate, author, capitalize(shortName), capitalize(shortName));
- Files.writeString(srcPath.resolve("DapTool" + capitalize(shortName) + "Application.java"), appClass);
-
- Path resPath = modulePath.resolve("src/main/resources");
- Files.createDirectories(resPath);
- String applicationYml = """
- server:
- port: %s
- spring:
- application:
- name: %s
- profiles:
- active: local
- logging:
- level:
- org.apache.kafka: ERROR
- mcp:
- namespace: ""
- security:
- tenant-domains:
- TESTER-DEV: ALL
- """.formatted(portStr, moduleName);
- Files.writeString(resPath.resolve("application.yml"), applicationYml);
-
- String applicationLocalYml = """
- # Local 환경 전용 설정 (H2 메모리 DB 등)
- spring:
- datasource:
- url: jdbc:p6spy:h2:mem:testdb;DB_CLOSE_DELAY=-1;
- driverClassName: com.p6spy.engine.spy.P6SpyDriver
- username: sa
- password: password
- h2:
- console:
- enabled: true
-
- eims:
- http:
- url: http://localhost:${server.port}/api/gateway
- tcp:
- host: 127.0.0.1
- port: 8090
- timeout: 5000
- jsp:
- form:
- url: http://localhost:${server.port}/mock/jsp-form
- json:
- url: http://localhost:${server.port}/mock/jsp-json
- mci:
- url: http://localhost:${server.port}/api/mock/esb/api
- mcistring:
- url: http://localhost:${server.port}/api/mock/esb/string
-
- mcp:
- security:
- tenant-domains:
- mcp-client-1: CUSTOMER,COMMON
- mcp-client-2: ALL
-
- axhub:
- gateway:
- url: http://localhost:8081
- tool:
- url: ${AXHUB_TOOL_URL:http://localhost:${server.port}}
- """;
- Files.writeString(resPath.resolve("application-local.yml"), applicationLocalYml);
-
- String applicationDevYml = """
- # OCI 클라우드 환경 전용 설정
- server:
- port: ${PORT:%s}
-
- axhub:
- gateway:
- url: https://axhubmcp.devjun.net
- tool:
- url: http://144.24.70.100:%s
-
- eims:
- http:
- url: http://localhost:${server.port}/api/gateway
- tcp:
- host: 127.0.0.1
- port: 8090
- timeout: 5000
- jsp:
- form:
- url: http://localhost:${server.port}/mock/jsp-form
- json:
- url: http://localhost:${server.port}/mock/jsp-json
- mci:
- url: http://localhost:${server.port}/api/mock/esb/api
- mcistring:
- url: http://localhost:${server.port}/api/mock/esb/string
-
- shinhan:
- integration:
- envrTypeCd: D
- eai:
- url: http://10.176.32.181
- internalMci:
- url: http://10.176.32.173
- bancaMci:
- url: http://10.176.32.117
- externalMci:
- url: http://10.176.32.176
- """.formatted(portStr, portStr);
- Files.writeString(resPath.resolve("application-dev.yml"), applicationDevYml);
-
- String logbackXml = """
-
-
-
-
-
- ${LOG_PATTERN}
-
-
-
- logs/%s.log
-
- logs/%s-%%d{yyyy-MM-dd}.log
- 30
-
-
- ${LOG_PATTERN}
-
-
-
-
-
-
-
-
- """.formatted(moduleName, moduleName);
- Files.writeString(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);
- if (!settings.contains("include '" + moduleName + "'")) {
- Files.writeString(settingsPath, System.lineSeparator() + "include '" + moduleName + "'" + System.lineSeparator(), 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 serviceName = moduleName.replace("dap-", ""); // e.g. tool-payment
- if (!compose.contains(" " + serviceName + ":")) {
- String newService = """
- %s:
- build:
- context: .
- dockerfile: %s/Dockerfile
- ports:
- - "%s:%s"
- depends_on:
- - redis
- environment:
- - TZ=Asia/Seoul
- - SPRING_REDIS_HOST=redis
- - SPRING_REDIS_PORT=6379
- - SPRING_DATA_REDIS_PORT=6379
- - AXHUB_TOOL_URL=http://%s:%s
- - GLOW_COMMUNICATION_MCI_HOST=http://mci-mock
- - GLOW_COMMUNICATION_MCI_PORT=8080
- - GLOW_COMMUNICATION_EXTMCI_HOST=http://mci-mock
- - GLOW_COMMUNICATION_EXTMCI_PORT=8080
- - 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);
- }
- }
-
- log.append("\n=========================================\n");
- log.append(" Pod Scaffolding Complete! \n");
- log.append("=========================================\n");
- log.append("1. [새로운 모듈] ").append(moduleName).append(" 폴더가 생성되었습니다.\n");
- log.append("2. [ToolScaffolder]를 사용해 이 모듈 안에 툴을 추가하세요.\n");
- log.append("3. 실행 전 Gradle 동기화(Sync)를 한 번 진행해 주세요.\n");
- return log.toString();
- }
-
- 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
deleted file mode 100644
index ffcaacd53..000000000
--- a/dap-was-lib/src/main/java/io/shinhanlife/dap/lib/util/ToolScaffolder.java
+++ /dev/null
@@ -1,714 +0,0 @@
-package io.shinhanlife.dap.lib.util;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.Locale;
-import java.util.Scanner;
-
-/**
- * MCP Tool 코드를 자동 생성(Scaffolding)하는 유틸리티 클래스
- *
- * [실행 방법]
- * 방법 1. IDE(IntelliJ 등)에서 직접 실행 (대화형 모드 추천 ⭐)
- * - 이 클래스(ToolScaffolder.java)를 열고 main 메서드를 직접 실행(Run)합니다.
- * - 콘솔 창에 뜨는 질문에 차례대로 값을 입력하기만 하면 파일이 생성됩니다.
- *
- * 방법 2. 커맨드라인(터미널)에서 실행 (명령어 기반)
- * - 컴파일: javac -encoding UTF-8 dap-was-core/src/main/java/io/shinhanlife/dap/mcc/util/ToolScaffolder.java
- * - 실행: java -cp dap-was-core/src/main/java io.shinhanlife.dap.lib.util.ToolScaffolder [이름] [ID] "[설명]" "[그룹]" "[통신방식]" "[모듈명]"
- */
-/**
- * @package io.shinhanlife.dap.lib.util
- * @className ToolScaffolder
- * @description AX HUB 시스템 처리 클래스
- * @author 0986406
- * @create 2026.09.01
- *
- * ---------- 개정이력 ----------
- * 수정일 수정자 수정내용
- * ---------- -------- ---------------------------
- * 2026.09.01 0986406 최초생성
- *
- *
- */
-public class ToolScaffolder {
-
- private static final String BASE_PACKAGE = "io.shinhanlife.dap.mcc";
- private static final String BASE_PACKAGE_PATH = "src/main/java/io/shinhanlife/dap/mcc";
-
- public static void main(String[] args) throws IOException {
- Scanner scanner = new Scanner(System.in);
-
- System.out.println("=========================================");
- 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 description = getOrAsk(args, 2, scanner, "3. Tool 기능 설명 (예: 환율 조회): ");
- String group = getOrAsk(args, 3, scanner, "4. Tool 소속 그룹 (예: SAMPLE, NOTIFICATION, CLAIM, POLICY, HR, CONTRACT, CUSTOMER 등): ");
- if (group.isEmpty()) group = "COMMON";
- String routingType = getOrAsk(args, 4, scanner, "5. 통신 프로토콜 (예: HTTP, TCP, MCI, EAI): ");
- if (routingType.trim().isEmpty()) {
- routingType = "HTTP";
- }
- String moduleName = getOrAsk(args, 5, scanner, "6. 코드를 생성할 모듈 (기본: dap-was-oth): ");
- if (moduleName.trim().isEmpty()) {
- moduleName = "dap-was-oth";
- }
-
- String defaultAuthor = System.getProperty("user.name");
- String defaultDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));
-
- String author = getOrAsk(args, 6, scanner, "7. 작성자 (엔터 입력 시 '" + defaultAuthor + "'): ");
- if (author.trim().isEmpty()) author = defaultAuthor;
- String createDate = getOrAsk(args, 7, scanner, "8. 작성일 (엔터 입력 시 '" + defaultDate + "'): ");
- if (createDate.trim().isEmpty()) createDate = defaultDate;
-
- String result = scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, createDate, true, null);
- System.out.println(result);
- }
-
- private static String getOrAsk(String[] args, int index, Scanner scanner, String prompt) {
- if (args.length > index) {
- return args[index];
- }
- System.out.print(prompt);
- return scanner.nextLine().trim();
- }
-
- public static String scaffold(String baseName, String interfaceId, String description, String group, String routingType, String moduleName, String author, String createDate, boolean register, String clientSystemCode) throws IOException {
- baseName = toPascalCase(baseName);
- String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
- Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
-
- Path usecaseDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "usecase"));
- Path usecaseImplDir = usecaseDir.resolve("impl");
- Path dtoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "biz", group.toLowerCase(), "dto"));
-
- 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"));
-
- String bizPackage = BASE_PACKAGE + ".biz." + group.toLowerCase();
- boolean isMci = "MCI".equalsIgnoreCase(routingType);
- String mciGroupPath = "infra/itrf/mci/" + group.toLowerCase();
- String clientPkgSuffix = "";
- String clientPrefixCap = "";
- Path mciClientDir = null;
-
- if (isMci && clientSystemCode != null && clientSystemCode.length() == 4) {
- String clientPrefix = clientSystemCode.toLowerCase();
- clientPkgSuffix = clientPrefix.substring(0, 3) + "." + clientPrefix.substring(3, 4);
- clientPrefixCap = toPascalCase(clientSystemCode);
- mciGroupPath = "infra/itrf/mci/" + clientPrefix.substring(0, 3) + "/" + clientPrefix.substring(3, 4);
- mciClientDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, mciGroupPath));
- }
-
- Path mciIoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, mciGroupPath, "io"));
-
- Files.createDirectories(usecaseDir);
- Files.createDirectories(usecaseImplDir);
- Files.createDirectories(dtoDir);
- if (isMci) {
- Files.createDirectories(mciIoDir);
- if (mciClientDir != null) {
- Files.createDirectories(mciClientDir);
- }
- } else {
- Files.createDirectories(legacyDtoDir);
- }
- Files.createDirectories(converterDir);
-
- StringBuilder log = new StringBuilder();
-
- // Generate Request DTO
- String reqContent = """
- package %s.dto;
-
- import com.fasterxml.jackson.annotation.JsonInclude;
- import io.shinhanlife.dap.lib.annotation.McpParameter;
- import io.shinhanlife.dap.lib.annotation.McpValidation;
- import lombok.Data;
-
- /**
- * @package %s.dto
- * @className %sRequest
- * @description AX HUB 시스템 처리 클래스
- * @author %s
- * @create %s
- *
- * ---------- 개정이력 ----------
- * 수정일 수정자 수정내용
- * ---------- -------- ---------------------------
- * %s %s 최초생성
- *
- *
- */
- @Data
- @JsonInclude(JsonInclude.Include.NON_NULL)
- public class %sRequest {
- @McpParameter(description = "수신자 전화번호", required = true)
- @McpValidation(pattern = "^01(?:0|1|[6-9])-(?:\\\\d{3}|\\\\d{4})-\\\\d{4}$", examples = {"010-1234-5678"})
- private String phoneNumber;
-
- @McpParameter(description = "전송할 메시지 내용", required = true)
- @McpValidation(defaultValue = "안녕하세요.")
- private String message;
- }
- """.formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName);
- Files.writeString(dtoDir.resolve(baseName + "Request.java"), reqContent);
-
- // Generate Response DTO
- String resContent = """
- package %s.dto;
-
- import com.fasterxml.jackson.annotation.JsonInclude;
- import io.shinhanlife.dap.lib.annotation.McpOutputSchema;
- import io.shinhanlife.dap.lib.annotation.McpValidation;
- import lombok.Data;
-
- /**
- * @package %s.dto
- * @className %sResponse
- * @description AX HUB 시스템 처리 클래스
- * @author %s
- * @create %s
- *
- * ---------- 개정이력 ----------
- * 수정일 수정자 수정내용
- * ---------- -------- ---------------------------
- * %s %s 최초생성
- *
- *
- */
- @Data
- @McpOutputSchema
- @JsonInclude(JsonInclude.Include.NON_NULL)
- public class %sResponse {
- @McpValidation(required = true)
- private String resultCode;
-
- @McpValidation(maxLength = 200, nullable = true)
- private String resultMessage;
-
- // TODO: Add response fields here. Do not include PII in the Tool response.
- }
- """.formatted(bizPackage, bizPackage, baseName, author, createDate, createDate, author, baseName);
- Files.writeString(dtoDir.resolve(baseName + "Response.java"), resContent);
-
- String toolName = toToolName(moduleName, group, baseName);
-
- String serviceInterfaceContent = """
- package %s.usecase;
-
- import io.shinhanlife.dap.lib.annotation.McpFunction;
- import io.shinhanlife.dap.lib.annotation.McpTool;
- import %s.dto.%sRequest;
-
- @McpTool(
- routingType = "%s",
- categoryKey = "%s"
- )
- public interface %sUseCase {
- @McpFunction(
- displayName = "%s 툴",
- name = "%s",
- description = "%s",
- prompt = "%s",
- mappingId = "%s",
- register = %s,
- requiresApproval = false,
- openWorldHint = true,
- version = "1.0.0",
- timeoutMillis = 300000L,
- enabled = true
- )
- Object execute(%sRequest req);
- }
- """.formatted(
- bizPackage,
- bizPackage, baseName,
- routingType, group.toLowerCase(),
- baseName,
- baseName, toolName, description, description + " ?줘.", interfaceId, register,
- baseName
- );
-
- Files.writeString(usecaseDir.resolve(baseName + "UseCase.java"), serviceInterfaceContent);
-
- String serviceImplContent;
-
- if (isMci) {
- serviceImplContent = """
- package %s.usecase.impl;
-
- import %s.dto.%sRequest;
- import %s.dto.%sResponse;
- import %s.usecase.%sUseCase;
- import io.shinhanlife.dap.lib.integration.mci.component.AxhubMciComponent;
- import io.shinhanlife.glow.communication.dto.Transfer;
- import org.springframework.stereotype.Service;
- import lombok.RequiredArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import java.util.Map;
- import %s.converter.%sConverter;
- import %s.%s.io.%s_I;
- %s
-
- /**
- * @package %s.usecase.impl
- * @className %sUseCaseImpl
- * @description AX HUB 시스템 처리 클래스
- * @author %s
- * @create %s
- *
- * ---------- 개정이력 ----------
- * 수정일 수정자 수정내용
- * ---------- -------- ---------------------------
- * %s %s 최초생성
- *
- *
- */
- @Slf4j
- @Service
- @RequiredArgsConstructor
- public class %sUseCaseImpl implements %sUseCase {
-
- %s
- private final %sConverter converter;
-
- @Override
- public Object execute(%sRequest req) {
- log.info("[MCI Tool] {} 요청 수신.", "%s");
- try {
- // MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO)
- %s_I mciReq = converter.toLegacyRequest(req);
-
- Transfer