Refactor: rename modules to was and remove MCP gateway integration

This commit is contained in:
jade
2026-08-04 22:13:51 +09:00
parent b698c0e8f8
commit 6637c90021
268 changed files with 62 additions and 307 deletions

View File

@@ -59,7 +59,7 @@ jobs:
# 4. 마운트된 /app 디렉토리로 이동하여 호스트의 도커 컴포즈 제어!
cd /app
docker system prune -f
ACTIVE_PROFILE=dev docker compose up -d --build --remove-orphans gateway redis mci-mock dozzle tool-sms tool-oth
ACTIVE_PROFILE=dev docker compose up -d --build --remove-orphans redis mci-mock dozzle was-sms was-oth
# 5. 배포 후 대롱대롱 매달려 있는 가비지 이미지 자동 소거 청소!
docker image prune -f

View File

@@ -1,103 +0,0 @@
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.regex.*;
public class AddJavadoc {
static final String ROOT_DIR = ".";
static final String TEMPLATE =
"/**\n" +
" * @package %s\n" +
" * @className %s\n" +
" * @description AX HUB ?쒖뒪??泥섎━ ?대옒??n" +
" * @author 源€?뺤떇\n" +
" * @create 2026.09.01\n" +
" * <pre>\n" +
" * ---------- 媛쒖젙?대젰 ----------\n" +
" * ?섏젙?? ?섏젙?? ?섏젙?댁슜\n" +
" * ---------- -------- ---------------------------\n" +
" * 2026.09.01 源€?뺤떇 理쒖큹?앹꽦\n" +
" * \n" +
" * </pre>\n" +
" */";
public static void main(String[] args) throws Exception {
Files.walkFileTree(Paths.get(ROOT_DIR), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
String name = dir.getFileName().toString();
if (name.equals(".git") || name.equals("build") || name.equals(".gradle") || name.equals("scratch") || name.equals("out") || name.equals("bin")) {
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toString().endsWith(".java") && !file.getFileName().toString().equals("AddJavadoc.java")) {
processJavaFile(file);
}
return FileVisitResult.CONTINUE;
}
});
System.out.println("Done.");
}
private static void processJavaFile(Path file) throws IOException {
List<String> lines = Files.readAllLines(file);
String content = String.join("\n", lines);
if (content.contains("---------- 媛쒖젙?대젰 ----------") || content.contains("@className")) {
System.out.println("Skipping (already has javadoc): " + file);
return;
}
String packageName = "unknown";
String className = "unknown";
Matcher pkgMatcher = Pattern.compile("(?m)^\\s*package\\s+([\\w\\.]+)\\s*;").matcher(content);
if (pkgMatcher.find()) {
packageName = pkgMatcher.group(1);
}
int classDeclIdx = -1;
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
Matcher classMatcher = Pattern.compile("^\\s*(?:public\\s+|protected\\s+|private\\s+|abstract\\s+|final\\s+|static\\s+)*(class|interface|enum|record)\\s+(\\w+)").matcher(line);
if (classMatcher.find()) {
classDeclIdx = i;
className = classMatcher.group(2);
break;
}
}
if (classDeclIdx == -1) {
System.out.println("Skipping (no class declaration found): " + file);
return;
}
int insertIdx = classDeclIdx;
for (int i = classDeclIdx - 1; i >= 0; i--) {
String line = lines.get(i).trim();
if (line.isEmpty()) {
continue;
}
if (line.startsWith("@")) {
insertIdx = i;
} else if (line.startsWith("//") || line.startsWith("/*") || line.startsWith("*")) {
break;
} else {
break;
}
}
String javadoc = String.format(TEMPLATE, packageName, className);
lines.add(insertIdx, javadoc);
Files.write(file, String.join("\n", lines).getBytes("UTF-8"));
System.out.println("Updated: " + file);
}
}

31
HELP.md
View File

@@ -1,31 +0,0 @@
# Read Me First
The following was discovered as part of building this project:
* The original package name 'io.shinhanlife.chat-backend' is invalid and this project uses 'io.shinhanlife.chatbackend' instead.
# Getting Started
### Reference Documentation
For further reference, please consider the following sections:
* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/3.5.3/maven-plugin)
* [Create an OCI image](https://docs.spring.io/spring-boot/3.5.3/maven-plugin/build-image.html)
* [Spring Web](https://docs.spring.io/spring-boot/3.5.3/reference/web/servlet.html)
* [Spring Session for Spring Data Redis](https://docs.spring.io/spring-session/reference/)
* [Spring Data JPA](https://docs.spring.io/spring-boot/3.5.3/reference/data/sql.html#data.sql.jpa-and-spring-data)
### Guides
The following guides illustrate how to use some features concretely:
* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/)
### Maven Parent overrides
Due to Maven's design, elements are inherited from the parent POM to the project POM.
While most of the inheritance is fine, it also inherits unwanted elements like `<license>` and `<developers>` from the parent.
To prevent this, the project POM contains empty overrides for these elements.
If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides.

View File

@@ -1,57 +0,0 @@
/**
* @package io.shinhanlife
* @className McpBridge
* @description AX HUB 시스템 처리 클래스
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class McpBridge {
public static void main(String[] args) {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (line.isEmpty()) continue;
try {
HttpRequest req = HttpRequest.newBuilder()
// Spring AI MCP Server??怨듭떇 ?⑥씪 ?붾뱶?ъ씤??
.uri(URI.create("http://localhost:8081/mcp"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(line))
.build();
HttpResponse<String> response = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
System.out.flush();
} catch (Exception e) {
// MCP ?쒖? ?먮윭 ?щ㎎?쇰줈 諛섑솚 (?꾩쓽濡?id 異붿텧 ?쒖쇅, 理쒖냼?쒖쓽 ?먮윭 ?묐떟)
System.out.println("{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32603,\"message\":\"" + e.getMessage().replace("\"", "\\\"") + "\"}}");
System.out.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -17,7 +17,7 @@ MCP Gateway (dap-gateway)
└─ 대상 Tool 서버로 라우팅
Tool Server (dap-tool-sms / dap-tool-oth)
Tool Server (dap-was-sms / dap-was-oth)
└─ BusinessToolController
@@ -33,16 +33,16 @@ UseCase → Converter → MCI/EAI Client → 레거시 시스템
목표: Gateway + 고객 Pod + 영업 Pod + 지급/납입 Pod + 알림 Pod + 인사 Pod + 공통 Pod
```
`dap-tool-oth`에는 여러 업무 카테고리가 함께 있습니다. AA 협의 후에는 부서·업무 소유권 단위로 Tool 서버, 이미지, Pod, 배포 파이프라인을 분리합니다. 이 목표 구조는 향후 전환 방향이며 현재 구현 완료 상태가 아닙니다.
`dap-was-oth`에는 여러 업무 카테고리가 함께 있습니다. AA 협의 후에는 부서·업무 소유권 단위로 Tool 서버, 이미지, Pod, 배포 파이프라인을 분리합니다. 이 목표 구조는 향후 전환 방향이며 현재 구현 완료 상태가 아닙니다.
## Gradle 멀티모듈
| 모듈 | 역할 | 실행 포트 |
|---|---|---:|
| `dap-gateway` | MCP 진입점, Tool Registry, 라우팅, 권한·가드레일, Chat API | 8081 |
| `dap-tool-core` | 공통 어노테이션, Controller, JSON Schema, MCI/EAI 지원, 보안·로깅 공통 기능 | 라이브러리 |
| `dap-tool-sms` | SMS/알림 Tool 서버 | 8082 |
| `dap-tool-oth` | 공통·업무·샘플·MCI Tool 서버 | 8084 |
| `dap-was-lib` | 공통 어노테이션, Controller, JSON Schema, MCI/EAI 지원, 보안·로깅 공통 기능 | 라이브러리 |
| `dap-was-sms` | SMS/알림 Tool 서버 | 8082 |
| `dap-was-oth` | 공통·업무·샘플·MCI Tool 서버 | 8084 |
기술 기준은 Java 21, Spring Boot 4, Gradle, Spring AI MCP Server, Redis, MapStruct, MyBatis, Resilience4j입니다.
@@ -83,7 +83,7 @@ $env:OPENROUTER_API_KEY = '<개발용 비밀 저장소의 키>'
$env:SPRING_PROFILES_ACTIVE = 'local'
$env:AXHUB_GATEWAY_URL = 'http://localhost:8081'
$env:AXHUB_TOOL_URL = 'http://localhost:8084'
.\gradlew.bat :dap-tool-oth:bootRun
.\gradlew.bat :dap-was-oth:bootRun
```
Tool 서버가 기동된 뒤 아래 URL로 등록된 Tool 목록을 확인합니다.
@@ -98,7 +98,7 @@ SMS Tool도 함께 확인하려면 별도 PowerShell에서 아래 명령을 실
$env:SPRING_PROFILES_ACTIVE = 'local'
$env:AXHUB_GATEWAY_URL = 'http://localhost:8081'
$env:AXHUB_TOOL_URL = 'http://localhost:8082'
.\gradlew.bat :dap-tool-sms:bootRun
.\gradlew.bat :dap-was-sms:bootRun
```
전체 컨테이너 환경이 필요하면 개별 실행 대신 다음 한 줄을 사용합니다.
@@ -211,7 +211,7 @@ CLCNNB00001_O : CLCNNB00001 응답 DTO
- [ ] `MciXxxClient``INTERFACE_ID_I`, `INTERFACE_ID_O`를 인터페이스 ID 기준으로 만든다.
- [ ] DTO에 Bean Validation을 선언하고, 중첩 DTO가 있으면 입력 Schema와 검증 대상에 포함되는지 확인한다.
- [ ] 조회·변경 작업 특성에 따라 `readOnlyHint`, `requiresApproval`, `idempotentHint`를 설정한다.
- [ ] 단위 테스트를 작성하고 `:dap-tool-core:test` 또는 대상 모듈 테스트를 실행한다.
- [ ] 단위 테스트를 작성하고 `:dap-was-lib:test` 또는 대상 모듈 테스트를 실행한다.
- [ ] Tool 서버 기동 후 `/mcp/api/v1/tools/list`에서 이름, 설명, category, JSON Schema가 맞는지 확인한다.
- [ ] 요청·응답 로그에 개인정보나 인증값이 남지 않는지 확인한다.
### 등록·노출 제어
@@ -238,8 +238,8 @@ CLCNNB00001_O : CLCNNB00001 응답 DTO
```powershell
.\gradlew.bat clean build
.\gradlew.bat :dap-tool-core:test
.\gradlew.bat :dap-tool-core:compileJava
.\gradlew.bat :dap-was-lib:test
.\gradlew.bat :dap-was-lib:compileJava
```
### 애플리케이션 실행
@@ -251,17 +251,17 @@ CLCNNB00001_O : CLCNNB00001 응답 DTO
.\gradlew.bat :dap-gateway:bootRun
# SMS Tool
.\gradlew.bat :dap-tool-sms:bootRun
.\gradlew.bat :dap-was-sms:bootRun
# 기타 업무 Tool
.\gradlew.bat :dap-tool-oth:bootRun
.\gradlew.bat :dap-was-oth:bootRun
```
기본 프로필은 `local`입니다. 개발 서버 설정이 필요하면 실행 환경에 프로필을 지정합니다.
```powershell
$env:SPRING_PROFILES_ACTIVE = 'dev'
.\gradlew.bat :dap-tool-oth:bootRun
.\gradlew.bat :dap-was-oth:bootRun
```
## Docker Compose 실행
@@ -340,7 +340,7 @@ $env:SPRING_PROFILES_ACTIVE = 'dev'
| 변수 | 적용 대상 | 설명 | 로컬 기본값/예시 |
|---|---|---|---|
| `PORT` | `dap-tool-sms`, `dap-tool-oth` | 개발 프로필에서 Tool 서버 포트 변경 | SMS `8082`, OTH `8084` |
| `PORT` | `dap-was-sms`, `dap-was-oth` | 개발 프로필에서 Tool 서버 포트 변경 | SMS `8082`, OTH `8084` |
| `AXHUB_GATEWAY_URL` | 모든 Tool | Tool 등록·Heartbeat 대상 Gateway 주소 | 로컬 `http://localhost:8081`, Docker `http://gateway:8081` |
| `AXHUB_TOOL_URL` | 모든 Tool | Gateway가 해당 Tool Pod를 호출할 주소 | 로컬 `http://localhost:{server.port}` |
| `GLOW_COMMUNICATION_MCI_HOMT` | Docker Tool 컨테이너 | MCI 대상 호스트 | 로컬 Compose는 `mci-mock` |
@@ -371,7 +371,7 @@ $env:OPENROUTER_API_KEY = '<개인 또는 개발용 비밀 저장소의 키>'
$env:SPRING_PROFILES_ACTIVE = 'local'
$env:AXHUB_GATEWAY_URL = 'http://localhost:8081'
$env:AXHUB_TOOL_URL = 'http://localhost:8084'
.\gradlew.bat :dap-tool-oth:bootRun
.\gradlew.bat :dap-was-oth:bootRun
```
### 권한 도메인 설정
@@ -420,7 +420,7 @@ Gateway에는 민감 키와 일부 형식을 마스킹하는 공통 기능이
### 부서별 Tool Pod와 저장소 경계
현재 `dap-tool-oth`에 함께 있는 업무 Tool을 부서·업무 소유권 단위로 분리합니다. 각 Pod는 독립 이미지, 독립 배포, 독립 장애 범위를 갖도록 구성합니다. 실제 분리는 AA가 확정한 Tool 소유 부서와 운영 책임 매핑을 기준으로 수행합니다.
현재 `dap-was-oth`에 함께 있는 업무 Tool을 부서·업무 소유권 단위로 분리합니다. 각 Pod는 독립 이미지, 독립 배포, 독립 장애 범위를 갖도록 구성합니다. 실제 분리는 AA가 확정한 Tool 소유 부서와 운영 책임 매핑을 기준으로 수행합니다.
### Agent별 Tool 노출 수 제한
@@ -457,11 +457,11 @@ MCP SDK 표준 tools/list, tools/call
|---|---|
| Gateway Tool API | `dap-gateway/.../presentation/McpRouterController.java` |
| 동적 MCP SSE/호출 경로 | `dap-gateway/.../sync/DynamicMcpController.java` |
| Tool 실행 Controller | `dap-tool-core/.../presentation/BusinessToolController.java` |
| Tool 자동 등록 | `dap-tool-core/.../usecase/ToolRegistryHeartbeatSender.java` |
| Tool 어노테이션 | `dap-tool-core/.../annotation/McpTool.java`, `McpFunction.java` |
| Tool 예시 | `dap-tool-oth/.../biz/oth`, `biz/sol`, `biz/smp` |
| SMS Tool 예시 | `dap-tool-sms/.../biz/sms` |
| Tool 실행 Controller | `dap-was-lib/.../presentation/BusinessToolController.java` |
| Tool 자동 등록 | `dap-was-lib/.../usecase/ToolRegistryHeartbeatSender.java` |
| Tool 어노테이션 | `dap-was-lib/.../annotation/McpTool.java`, `McpFunction.java` |
| Tool 예시 | `dap-was-oth/.../biz/oth`, `biz/sol`, `biz/smp` |
| SMS Tool 예시 | `dap-was-sms/.../biz/sms` |
| Docker 환경 | `docker-compose.yml` |
---
@@ -568,14 +568,14 @@ Tool 실행이 끝나면 아래 로그는 Schema 정의가 아니라 **검증을
스키마 리소스와 DTO 기반 자동 Schema는 아래 테스트로 함께 검증할 수 있습니다.
```powershell
.\gradlew.bat :dap-tool-oth:test --tests "io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchRequestSchemaTest"
.\gradlew.bat :dap-was-oth:test --tests "io.shinhanlife.dap.mcc.biz.cmm.dto.ClaimSearchRequestSchemaTest"
```
### Tool Naming Convention
All Tool names use the four-level lowercase format `pod.domain.service.action`. Do not use underscores or CamelCase; use a hyphen (`-`) only when a single level has multiple words.
- `pod`: deployment Tool Pod/module (`dap-tool-oth``oth`, `dap-tool-sms``sms`)
- `pod`: deployment Tool Pod/module (`dap-was-oth``oth`, `dap-was-sms``sms`)
- `domain`: business-domain package (`cmm`, `smp`, `sol`, etc.)
- `service`: business service or resource
- `action`: the requested operation (`search`, `list`, `detail`, `issue`, `inquiry`, etc.)
@@ -587,7 +587,7 @@ oth.sol.request.list
oth.smp.weather.inquiry
```
When Scaffold receives `dap-tool-oth`, `cmm`, and `ClaimSearch`, it generates `oth.cmm.claim.search`. The `validateMcpToolNames` Gradle task rejects both a duplicate name and any name outside this format before packaging, including its source file and line number.
When Scaffold receives `dap-was-oth`, `cmm`, and `ClaimSearch`, it generates `oth.cmm.claim.search`. The `validateMcpToolNames` Gradle task rejects both a duplicate name and any name outside this format before packaging, including its source file and line number.
### Tool Test Console

View File

@@ -50,7 +50,7 @@ subprojects {
}
}
def toolCoreProject = project(':dap-tool-core')
def toolCoreProject = project(':dap-was-lib')
tasks.register('validateMcpToolNames', JavaExec) {
group = 'verification'

View File

@@ -280,7 +280,6 @@ public class PodScaffolder {
- SPRING_REDIS_HOST=redis
- SPRING_REDIS_PORT=6379
- SPRING_DATA_REDIS_PORT=6379
- AXHUB_GATEWAY_URL=http://gateway:8081
- AXHUB_TOOL_URL=http://%s:%s
- GLOW_COMMUNICATION_MCI_HOST=http://mci-mock
- GLOW_COMMUNICATION_MCI_PORT=8080

View File

@@ -3,7 +3,7 @@ package io.shinhanlife.dap.mcc.manifest;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.shinhanlife.dap.lib.config.McpProperties;
import io.shinhanlife.dap.mcc.dto.ToolMetadata;
import io.shinhanlife.dap.mcc.usecase.ToolRegistryHeartbeatSender;
import io.shinhanlife.dap.mcc.usecase.LocalToolScanner;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Comparator;
@@ -30,9 +30,9 @@ public class ToolManifestService {
private String lastRevision;
@Autowired
public ToolManifestService(ToolRegistryHeartbeatSender heartbeatSender, ObjectMapper objectMapper,
public ToolManifestService(LocalToolScanner localToolScanner, ObjectMapper objectMapper,
McpProperties properties) {
this(heartbeatSender::getAllScannedTools, objectMapper, properties);
this(localToolScanner::getAllScannedTools, objectMapper, properties);
}
ToolManifestService(Supplier<List<ToolMetadata>> toolSupplier, ObjectMapper objectMapper,

Some files were not shown because too many files have changed in this diff Show More