From b5ff930556fda3796f0af201120ffa16cd818712 Mon Sep 17 00:00:00 2001 From: jade Date: Thu, 30 Jul 2026 10:16:25 +0900 Subject: [PATCH] docs: refresh project README --- README.md | 623 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 469 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index 4028631a..cc9b178b 100644 --- a/README.md +++ b/README.md @@ -1,154 +1,469 @@ -# DAP Backend - -Spring Boot 기반 DAP 관리자 백엔드 API 서버 및 MCP(Model Context Protocol) Gateway / Tool 분산 서버 프로젝트 입니다. - ---- - -## 아키텍처 개요 (Architecture Overview) - -DAP Backend는 2개의 주요 애플리케이션으로 분리 운영됩니다: - -1. **MCP Gateway (`DapGatewayApplication`)**: 외부 LLM(Claude, GPT 등) 서버의 MCP 통신을 받아, 내부 Tool 서버들로 분배(라우팅)하는 허브 서버이자 관리자 웹(Scaffolder)을 제공하는 통합 서버 (포트: 8081) -2. **MCP Tool (`DapTool*Application`)**: 실제 레거시 시스템(MCI, EAI 등)과 통신하여 비즈니스 로직(결제, 휴가신청 등)을 수행하는 어댑터 서버 (포트: 8082~8085 등 분산 구성 가능) - ---- - -## 환경 (Environment) - -| 항목 | 버전 | -|------|------| -| Java | 21 | -| Spring Boot | 4.0.5 | -| Build Tool | Gradle | -| 주요 기술 스택 | MyBatis, Lombok, MapStruct, P6Spy | -| 데이터베이스 | H2 (in-memory, 로컬 개발용) | -| 세션/캐시 저장소 | Redis | -| **장애 격리 / 제어** | **Resilience4j (RateLimiter, CircuitBreaker, Retry)** | -| **메시지 큐** | **Kafka (트래픽 폭주 시 대기열 전환용)** | - ---- - -## ▶ 실행 방법 (How to Run) - -### 1. Gateway & Tool 서버 실행 (MCP 연동용) -- **Gateway 서버 기동:** - - `./gradlew :dap-gateway:bootRun` -- **Tool 서버 기동:** - - `./gradlew :dap-tool-oth:bootRun` (또는 dap-tool-payment 등) - - Tool 서버가 기동되면 자동으로 Gateway(8081)에 자신을 등록(Auto-Registration)합니다. - - **(선택) 특정 Tool 그룹만 실행하기:** - - 업무 특성에 따라 세분화된 그룹에 속한 Tool만 띄우고 싶다면, 실행 인수에 `--mcp.tool.target=그룹명`을 추가합니다. - - **지원되는 그룹명:** - - `NOTIFICATION`: 이메일, SMS 발송 - - `CLAIM`: 청구 처리, 심사 상태 조회 - - `POLICY`: 증권 발행, 발행 가능 여부 조회 - - `HR`: 휴가 등록, 연차 갯수 조회 - - `CONTRACT`: 계약 상태, 계약 상세 조회 - - `CUSTOMER`: 고객 등급, 고객 상세 정보 조회 - - `SAMPLE`: 날씨, 환율, 명언 조회 등 외부 연동 샘플 - - IntelliJ IDEA: `Run/Debug Configurations`에서 `DapTool*Application` 의 `Program arguments` 에 `--mcp.tool.target=NOTIFICATION` 입력 - - ---- - -## 🤖 AI Agent 연동 아키텍처 (MCP & Agent Builder) - -본 시스템은 **투트랙(Two-Track) AI 연동 아키텍처**를 제공하여 로컬 개발 환경과 프로덕션 환경 모두를 완벽하게 지원합니다. - -### 1. 로컬 코딩 AI (Antigravity, Cursor, Claude Desktop 등) 연동 -표준 MCP 통신(Stdio)을 요구하는 로컬 AI 에이전트를 위해 자바 기반의 브릿지 스크립트(`McpBridge.java`)를 내장하고 있습니다. 브릿지가 Stdio 요청을 HTTP로 변환하여 로컬 환경의 Gateway(포트: 8281)로 전달합니다. - -- **설정 방법**: IDE의 `mcp_config.json` 설정 파일에 아래와 같이 등록합니다. - ```json - "mcpServers": { - "dap-gateway": { - "command": "java", - "args": ["C:/절대경로/dap-backend-main/McpBridge.java"] - } - } - ``` -- **특정 카테고리 툴 필터링**: `McpBridge.java` 내부의 URI 파라미터(`?categoryKey=sample` 등)를 수정하여 원하는 도메인의 툴만 선택적으로 AI에게 학습시킬 수 있습니다. - -### 2. 프로덕션 클라우드 AI (Google Cloud Agent Builder 등) 연동 -실제 라이브 서비스에서 동작하는 클라우드 Agent Builder는 REST API 기반의 OpenAPI Spec을 요구합니다. -`dap-gateway`는 이미 **Agent Builder 규격의 REST API(`/mcp/api/v1/tools/call`)를 네이티브로 제공**하므로, 별도의 브릿지나 어댑터 없이 Endpoint URL과 Swagger(OpenAPI) 문서만 클라우드 콘솔에 등록하면 즉시 라이브 챗봇/에이전트로 서비스할 수 있습니다. - ---- - -## 비공개 Tool 관리 및 Fallback 연동 (Visibility & Routing) - -저희 시스템은 MSA 보안 및 아키텍처 원칙에 따라 Tool의 **레지스트리 등록 여부(라우팅)**와 **API 노출 여부(가시성)**를 완벽히 분리하여 관리합니다. - -1. **`visible = false`**: - 레지스트리에 정상적으로 등록되어 게이트웨이가 동적으로 라우팅하지만, 클라이언트에게 제공되는 `/tools/list` API 목록에서는 숨겨집니다. -2. **`register = false`**: - 내부 레지스트리(Redis)에 툴 정보를 등록하지 않습니다 (외부 레지스트리를 독자적으로 사용할 경우 등). - 이 경우 게이트웨이는 `application.yml`의 `mcp.gateway.fallback.routes` 설정을 참조하여 **Fallback 정적 라우팅**을 수행하므로 연동이 100% 보장됩니다. - -```java -@McpFunction( - name = "secret_tool", - visible = false, // 목록 숨김 여부 (기본값: true) - register = false // 내부 Redis 등록 여부 (기본값: true) -) -``` - ---- - -## 🛡️ 시스템 안정성 및 네트워크 제어 (Resilience & Network) - -MSA 및 외부 시스템(MCI) 연동 환경의 안정성을 위해 완벽한 3-Tier 방어 체계를 구축했습니다. - -1. **Gateway 라우팅 방어 (Timeout & Fallback):** - - MCP 라우터(`McpRouterController`) 단에 1초 타임아웃을 강제 적용하여 특정 Tool Pod의 응답 지연이 전체 시스템 장애로 이어지는 것을 방지하고 신속하게 정적 Fallback 라우팅으로 전환합니다. -2. **MCI 네트워크 안정화 (HTTP/1.1 Downgrade):** - - 기존 HTTP/2 사용 시 레거시 시스템 연동 중 간헐적으로 발생하던 `RST_STREAM` 오류를 원천 차단하기 위해, MCI 전용 `HttpEimsSender`에는 고도로 최적화된 **HTTP/1.1 전용 커넥션 풀(Factory)**이 고정 적용되어 네트워크 단절을 방지합니다. -3. **Resilience4j 기반 트래픽 제어:** - - **Gateway 계층 (동적 방어):** Tool 등록 시 제출된 SLA 메타데이터를 기반으로 동적 CircuitBreaker 및 RateLimiter를 가동하며, 한계치 초과 시 Kafka 큐로 비동기 전환합니다. - - **Tool 계층 (정적 방어):** 레거시 커넥터 내부에 `@CircuitBreaker`, `@RateLimiter` 어노테이션 기반의 장애 전파 차단 로직이 2차적으로 가동됩니다. - ---- - -## 모듈(Pod) 및 Tool 코드 자동 생성 (Scaffolders) - -새로운 도메인의 기능을 추가할 때 발생하는 반복적인 설정(보일러플레이트, 설정 파일 복사 등)을 1초 만에 자동화하기 위해 **DAP Developer Portal (Web UI)** 및 **CLI 스캐폴더 2종**을 제공합니다. - -### 1. DAP Developer Portal (Web UI) - 가장 추천하는 방식! -이제 더 이상 터미널에서 명령어를 칠 필요가 없습니다. Gateway 모듈에 내장된 웹 화면에서 빈칸만 채우면 신한라이프 패키지 개발 가이드에 맞춘 코드가 마법처럼 찍혀 나옵니다. - -1. **접속 방법**: Gateway 서버 기동 후 브라우저에서 `http://localhost:8081/admin/scaffold.html` 접속 -2. **Pod (모듈) 생성 탭**: 모듈명(예: hr)과 포트만 입력하면 독립적인 Spring Boot 모듈이 디렉토리부터 빌드 스크립트까지 완벽히 생성됩니다. -3. **Tool (기능) 생성 탭**: 생성된 모듈에 새로운 툴 코드를 자동으로 주입합니다. - - **MCI 연동 기반 툴 생성**: 4자리 시스템 코드(예: `nclg`)를 기반으로 알맞은 패키지에 `MciNclgClient`, `Converter`, `_I`, `_O` 파일이 정확하게 생성됩니다. - - **완벽한 보일러플레이트 자동화**: `UseCaseImpl` 내부에 컴포넌트(`Client`, `Converter`)가 자동으로 의존성 주입되며, Java 15 Text Block을 활용해 들여쓰기(Indentation)까지 완벽히 정렬된 코드를 제공합니다. - -### 2. CLI 스캐폴더 (기존 터미널 방식) -웹 화면을 사용할 수 없는 환경이거나 터미널이 익숙한 경우, 아래 명령어를 통해 CLI 마법사를 사용할 수 있습니다. - -### 1⃣ 새로운 Pod(모듈) 전체를 생성할 때: `PodScaffolder` -새로운 도메인(예: 결제, HR)을 위한 완전히 독립적인 Spring Boot 모듈을 생성합니다. 폴더 구조, 빌드 스크립트, 각종 프로퍼티 및 도커 설정까지 완벽하게 세팅됩니다. - -```bash -# 사용법: javac로 컴파일 후 실행 -javac -encoding UTF-8 dap-common/src/main/java/io/shinhanlife/dap/common/util/PodScaffolder.java -java -cp dap-common/src/main/java io.shinhanlife.dap.lib.util.PodScaffolder [모듈명] [포트번호] - -# 실행 예시 (dap-tool-hr 모듈을 8086 포트로 생성) -java -cp dap-common/src/main/java io.shinhanlife.dap.lib.util.PodScaffolder hr 8086 -``` - -### 2⃣ 생성된 모듈에 새로운 툴(Function)을 추가할 때: `ToolScaffolder` -어노테이션(`@McpTool`, `@McpFunction`)이 완벽히 달린 Service와 입출력 DTO 코드를 지정된 모듈 패키지 룰에 맞춰 자동 생성합니다. - -```bash -# 사용법: javac로 컴파일 후 실행 -javac -encoding UTF-8 dap-common/src/main/java/io/shinhanlife/dap/common/util/ToolScaffolder.java -java -cp dap-common/src/main/java io.shinhanlife.dap.lib.util.ToolScaffolder [Tool이름] [인터페이스ID] "[기능설명]" "[그룹명]" "[통신방식]" "[모듈명]" - -# 실행 예시 (payment 모듈에 결제 승인 기능 추가) -java -cp dap-common/src/main/java io.shinhanlife.dap.lib.util.ToolScaffolder PaymentApproval PAY_001 "결제 승인 처리 기능" "COMMON" "HTTP" "dap-tool-payment" -``` - ---- - - +# AX HUB MCP Tool + +신한라이프 업무 시스템과 AI Agent를 연결하는 MCP(Model Context Protocol) Gateway 및 Tool 서버 프로젝트입니다. + +Agent는 Gateway에서 Tool 목록과 입력 명세를 받고, Gateway는 권한과 정책을 확인한 뒤 Tool 서버로 요청을 전달합니다. 업무 Tool은 `DTO → UseCase → Converter → MCI/EAI Client` 구조로 레거시 시스템을 호출합니다. + +## 전체 흐름 + +```text +AI Agent / MCP Client + │ + ▼ +MCP Gateway (dap-gateway) + ├─ Tool 목록·스키마 제공 + ├─ Tool 권한·승인·가드레일 확인 + ├─ Redis Registry 및 실행 추적 + └─ 대상 Tool 서버로 라우팅 + │ + ▼ +Tool Server (dap-tool-sms / dap-tool-oth) + └─ BusinessToolController + │ + ▼ +UseCase → Converter → MCI/EAI Client → 레거시 시스템 +``` + +## 현재 구조와 목표 구조 + +현재는 Gateway와 두 개의 Tool 애플리케이션으로 구성됩니다. + +```text +현재: Gateway + SMS Tool Pod + OTH Tool Pod +목표: Gateway + 고객 Pod + 영업 Pod + 지급/납입 Pod + 알림 Pod + 인사 Pod + 공통 Pod +``` + +`dap-tool-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 | + +기술 기준은 Java 21, Spring Boot 4, Gradle, Spring AI MCP Server, Redis, MapStruct, MyBatis, Resilience4j입니다. + +## 환경 (Environment) + +| 항목 | 버전 / 기준 | +|---|---| +| Java | 21 | +| Spring Boot | 4.0.5 | +| Gradle Wrapper | 8.14.3 | +| Spring AI BOM | 2.0.0 | +| Spring AI MCP Server | `spring-ai-starter-mcp-server-webmvc` | +| Redis Client | Lettuce 6.6.0.RELEASE | +| Resilience | Resilience4j 2.2.0 | +| MyBatis Spring Boot Starter | 3.0.3 | +| MapStruct | 1.5.5.Final | +| Lombok | 1.18.32 | +| JSON Schema Validator | networknt 1.4.0 | +| OpenAPI UI | springdoc 2.5.0 | +| 컨테이너 실행 | Docker Compose | + +프로젝트는 JDK 21을 기준으로 컴파일됩니다. IntelliJ에서는 Project SDK, Gradle JVM, Run Configuration JRE를 모두 JDK 21로 맞춰야 합니다. +## 5분 빠른 시작 + +Docker와 JDK 21이 준비된 로컬 개발 환경 기준입니다. + +```powershell +# 1. Redis와 MCI Mock만 먼저 실행 +$env:ACTIVE_PROFILE = 'local' +docker compose up -d redis mci-mock + +# 2. Gateway 실행 (새 PowerShell) +$env:SPRING_PROFILES_ACTIVE = 'local' +$env:OPENROUTER_API_KEY = '<개발용 비밀 저장소의 키>' +.\gradlew.bat :dap-gateway:bootRun + +# 3. OTH Tool 실행 (또 다른 PowerShell) +$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 +``` + +Tool 서버가 기동된 뒤 아래 URL로 등록된 Tool 목록을 확인합니다. + +```text +http://localhost:8081/mcp/api/v1/tools/list +``` + +SMS Tool도 함께 확인하려면 별도 PowerShell에서 아래 명령을 실행합니다. + +```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 +``` + +전체 컨테이너 환경이 필요하면 개별 실행 대신 다음 한 줄을 사용합니다. + +```powershell +$env:ACTIVE_PROFILE = 'local' +docker compose up -d --build +``` +## Tool 등록과 실행 + +### 등록과 목록 제공 + +1. Tool 서버 기동 시 `ToolRegistryHeartbeatSender`가 `@McpTool`, `@McpFunction`을 스캔합니다. +2. Tool 이름, 설명, 입력 JSON Schema, `categoryKey`, 연동 방식, 실행 URL을 메타데이터로 생성합니다. +3. Gateway Redis Registry에 등록·Heartbeat 정보를 전송합니다. +4. Agent와 관리 화면은 Gateway에서 Tool 목록과 명세를 조회합니다. + +### 실행 + +1. Agent가 Gateway에 Tool 이름과 입력값을 보냅니다. +2. Gateway가 Tool 존재 여부, 허용 Tool, 쓰기 승인, 가드레일을 확인합니다. +3. Gateway가 Tool 서버의 `/mcp/{toolName}`으로 요청을 전달합니다. +4. `BusinessToolController`가 Tool 메서드를 찾아 DTO로 변환하고 JSON Schema를 검증합니다. +5. UseCase가 업무 흐름을 수행합니다. +6. Converter가 업무 DTO를 인터페이스 ID 기반 MCI 요청 DTO로 변환합니다. +7. MCI/EAI Client가 레거시를 호출하고 결과를 Tool 응답으로 반환합니다. + +## 주요 URL + +로컬에서 Gateway를 직접 실행할 때의 기준입니다. Docker Compose를 사용하면 Gateway 호스트 포트는 `8281`입니다. + +| 용도 | 메서드 | URL | +|---|---|---| +| Tool 목록 | `GET` | `http://localhost:8081/mcp/api/v1/tools/list` | +| Tool 실행 | `POST` | `http://localhost:8081/mcp/api/v1/tools/call` | +| Tool Markdown 문서 | `GET` | `http://localhost:8081/mcp/api/v1/tools/docs/markdown` | +| 카테고리별 SSE MCP 연결 | `GET` | `http://localhost:8081/mcp/sse/{categoryKey}` | +| 카테고리별 MCP 호출 채널 | `POST` | `http://localhost:8081/mcp/custom/{categoryKey}` | +| Chat 스트리밍 API | `POST` | `http://localhost:8081/api/chat` | +| Scaffold API | `POST` | `http://localhost:8081/api/v1/scaffold/pod` 또는 `/tool` | + +`/mcp/sse/{categoryKey}`는 SSE 연결을 여는 전송 경로이고, `/mcp/custom/{categoryKey}`는 같은 카테고리의 MCP 요청을 처리하는 호출 경로입니다. 두 URL은 역할이 다릅니다. + +## Tool 개발 표준 + +| 구성 요소 | 책임 | +|---|---| +| `XxxRequest`, `XxxResponse` | Agent/Tool 관점의 입력·응답 DTO | +| `XxxUseCase` | Tool 계약과 MCP 메타데이터 선언 | +| `XxxUseCaseImpl` | 업무 흐름 조합과 Client 호출 | +| `XxxConverter` | 업무 DTO와 레거시 인터페이스 DTO 사이 변환 | +| `MciXxxClient` | Glow/MCI 또는 EAI 통신 호출 | +| `INTERFACE_ID_I`, `INTERFACE_ID_O` | 인터페이스 ID 기준 MCI 요청·응답 DTO | + +### Tool 선언 + +Tool 그룹에는 `@McpTool`, Agent가 호출하는 함수에는 `@McpFunction`을 사용합니다. + +```java +@McpTool(routingType = "MCI", categoryKey = "claim") +public interface ClaimInquiryUseCase { + + @McpFunction( + name = "claim_inquiry", + displayName = "보험금 청구 조회", + description = "청구 번호로 보험금 청구 상태를 조회합니다.", + mappingId = "CLM00000001" + ) + ClaimInquiryResponse inquire(ClaimInquiryRequest request); +} +``` + +`categoryKey`는 Tool의 업무 그룹입니다. Gateway의 목록 필터링, Agent 권한, 동적 MCP 서버 구분에 사용하므로 합의된 업무 키를 사용합니다. + +- `MCI`: 사내 MCI 인터페이스 호출 +- `EAI`: EAI 연동 +- `DIRECT`: 외부 HTTP 또는 내부 직접 연동 + +### 변환 원칙 + +UseCase 구현체는 Tool 요청을 레거시 요청과 섞어 쓰지 않습니다. Converter에서 변환한 뒤 Client에 전달합니다. + +```java +@Override +public ClaimInquiryResponse inquire(ClaimInquiryRequest request) { + CLM00000001_I mciRequest = converter.toMciRequest(request); + CLM00000001_O mciResponse = mciClmClient.callClm00000001(mciRequest); + return converter.toResponse(mciResponse); +} +``` + +MCI 입출력 객체는 업무 이름이 아니라 인터페이스 ID를 기준으로 둡니다. + +```text +CLCNNB00001_I : CLCNNB00001 요청 DTO +CLCNNB00001_O : CLCNNB00001 응답 DTO +``` + +예를 들어 `Onnba3011Request`와 `CLCNNB00001_I`는 같은 업무 데이터를 담을 수 있지만 같은 객체가 아닙니다. 둘 사이의 변환 책임은 `Onnba3011Converter`에 둡니다. + +### 새 Tool 추가 체크리스트 + +새 업무 Tool을 추가할 때는 아래 순서로 확인합니다. + +- [ ] 소속 모듈과 `categoryKey`를 업무 소유 조직 기준으로 결정한다. +- [ ] Agent 입력·응답 DTO인 `XxxRequest`, `XxxResponse`를 만든다. +- [ ] `XxxUseCase`에 `@McpTool`을 선언하고, 호출 메서드에 `@McpFunction`의 이름·설명·연동 ID를 선언한다. +- [ ] `XxxUseCaseImpl`에서 업무 흐름만 조합한다. +- [ ] `XxxConverter`에 업무 DTO ↔ 인터페이스 ID DTO 변환을 둔다. +- [ ] `MciXxxClient`와 `INTERFACE_ID_I`, `INTERFACE_ID_O`를 인터페이스 ID 기준으로 만든다. +- [ ] DTO에 Bean Validation을 선언하고, 중첩 DTO가 있으면 입력 Schema와 검증 대상에 포함되는지 확인한다. +- [ ] 조회·변경 작업 특성에 따라 `readOnlyHint`, `requiresApproval`, `idempotentHint`를 설정한다. +- [ ] 단위 테스트를 작성하고 `:dap-tool-core:test` 또는 대상 모듈 테스트를 실행한다. +- [ ] Tool 서버 기동 후 `/mcp/api/v1/tools/list`에서 이름, 설명, category, JSON Schema가 맞는지 확인한다. +- [ ] 요청·응답 로그에 개인정보나 인증값이 남지 않는지 확인한다. +### 등록·노출 제어 + +| 속성 | 의미 | +|---|---| +| `register` | Redis Registry와 Gateway 카탈로그에 등록할지 여부 | +| `visible` | Agent/클라이언트 목록에 표시할지 여부 | +| `requiresApproval` | 쓰기·고위험 작업의 승인 요구 여부 | +| `readOnlyHint`, `destructiveHint`, `idempotentHint` | Agent 호출 특성 힌트 | + +`register = false`인 Tool은 자동 카탈로그 등록 대상이 아닙니다. 별도 실행 목적이 있는 경우에만 사용하고, 필요한 fallback 경로를 운영 설정으로 확인합니다. + +## 빌드·테스트·로컬 실행 + +### 사전 조건 + +- JDK 21 +- Gradle Wrapper 사용 권장 +- 로컬 Redis 또는 Docker Compose 환경 +- 필요 시 MCI Mock 또는 사내 MCI/EAI 접근 환경 + +### 전체 빌드와 대표 검증 + +```powershell +.\gradlew.bat clean build +.\gradlew.bat :dap-tool-core:test +.\gradlew.bat :dap-tool-core:compileJava +``` + +### 애플리케이션 실행 + +각 애플리케이션은 별도 PowerShell에서 실행합니다. + +```powershell +# Gateway +.\gradlew.bat :dap-gateway:bootRun + +# SMS Tool +.\gradlew.bat :dap-tool-sms:bootRun + +# 기타 업무 Tool +.\gradlew.bat :dap-tool-oth:bootRun +``` + +기본 프로필은 `local`입니다. 개발 서버 설정이 필요하면 실행 환경에 프로필을 지정합니다. + +```powershell +$env:SPRING_PROFILES_ACTIVE = 'dev' +.\gradlew.bat :dap-tool-oth:bootRun +``` + +## Docker Compose 실행 + +Docker Compose는 Redis, Gateway, MCI Mock, SMS Tool, OTH Tool을 함께 기동합니다. + +```powershell +$env:ACTIVE_PROFILE = 'local' +docker compose up -d --build +docker compose ps +``` + +| 서비스 | 컨테이너 포트 | 호스트 포트 | +|---|---:|---:| +| Redis | 6379 | 6379 | +| Gateway | 8081 | 8281 | +| MCI Mock | 8080 | 8089 | +| SMS Tool | 8082 | 8282 | +| OTH Tool | 8084 | 8284 | + +컨테이너 내부와 PC 브라우저의 접속 주소는 다릅니다. + +```text +컨테이너 내부: http://gateway:8081, http://tool-oth:8084 +PC 브라우저: http://localhost:8281, http://localhost:8284 +``` + +## 환경 설정 + +### 프로필 + +기본 프로필은 `local`입니다. Tool 서버와 Gateway 모두 `local`, `dev` 프로필 파일을 사용합니다. + +| 프로필 | 목적 | 주요 설정 파일 | +|---|---|---| +| `local` | PC 개발·MCI Mock·H2 메모리 DB 기반 실행 | `application-local.yml` | +| `dev` | 개발 서버 배포 실행 | `application-dev.yml` | + +프로필은 환경 변수로 지정합니다. + +```powershell +# 로컬 개발 +$env:SPRING_PROFILES_ACTIVE = 'local' + +# 개발 서버 설정으로 실행 +$env:SPRING_PROFILES_ACTIVE = 'dev' +``` + +### 환경별 연결 주소 비교 + +| 항목 | `local` 프로세스 실행 | Docker Compose | `dev` 프로필 | +|---|---|---|---| +| Gateway 접근 주소 | `http://localhost:8081` | 컨테이너 내부 `http://gateway:8081` | 배포 환경 Gateway URL | +| SMS Tool 주소 | `http://localhost:8082` | `http://tool-sms:8082` | `PORT` 기본 8082 | +| OTH Tool 주소 | `http://localhost:8084` | `http://tool-oth:8084` | `PORT` 기본 8084 | +| Redis 주소 | 로컬 Redis 또는 `localhost:6379` | `redis:6379` | 운영/개발 Redis 설정 | +| MCI/EAI 대상 | MCI Mock 또는 로컬 설정 | `mci-mock:8080` | 개발망 연동 설정 | +| 브라우저 Gateway 접속 | `http://localhost:8081` | `http://localhost:8281` | 운영·개발 도메인 | + +`local`에서 프로세스를 직접 실행하면 Tool의 `AXHUB_GATEWAY_URL`은 `localhost`를 사용합니다. Docker에서는 각 컨테이너가 서로 다른 네트워크 공간에 있으므로 반드시 Compose 서비스 이름을 사용합니다. +### Gateway 환경 변수 + +| 변수 | 적용 대상 | 설명 | 로컬 기본값/예시 | +|---|---|---|---| +| `SPRING_PROFILES_ACTIVE` | Gateway, 모든 Tool | 활성 Spring 프로필 | `local` | +| `OPENROUTER_API_KEY` | Gateway | Chat/LLM 호출 API 키 | 운영·개발 환경의 비밀 저장소에서 주입 | +| `SPRING_DATA_REDIS_HOST` | Gateway, 모든 Tool | Redis 호스트 | Docker: `redis` | +| `SPRING_DATA_REDIS_PORT` | Gateway, 모든 Tool | Redis 포트 | `6379` | +| `MCP_GATEWAY_FALLBACK_DEFAULT_URL` | Gateway | Registry에 없는 Tool의 기본 fallback URL | Docker: `http://tool-oth:8084` | +| `MCP_GATEWAY_FALLBACK_ROUTES_SMS` | Gateway | SMS 계열 fallback URL | Docker: `http://tool-sms:8082` | +| `ACTIVE_PROFILE` | Docker Compose | Compose가 `SPRING_PROFILES_ACTIVE`에 전달할 프로필 | `local` | + +`OPENROUTER_API_KEY` 같은 인증값은 `application.yml`, README, Git 커밋에 직접 넣지 않습니다. 개발·운영 환경의 Secret, CI/CD 변수 또는 안전한 환경 변수로 주입합니다. + +### Tool 서버 환경 변수 + +| 변수 | 적용 대상 | 설명 | 로컬 기본값/예시 | +|---|---|---|---| +| `PORT` | `dap-tool-sms`, `dap-tool-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` | +| `GLOW_COMMUNICATION_MCI_PORT` | Docker Tool 컨테이너 | MCI 대상 포트 | 로컬 Compose는 `8080` | +| `GLOW_COMMUNICATION_EAI_HOMT` | Docker Tool 컨테이너 | EAI 대상 호스트 | 로컬 Compose는 `mci-mock` | +| `GLOW_COMMUNICATION_EAI_PORT` | Docker Tool 컨테이너 | EAI 대상 포트 | 로컬 Compose는 `8080` | + +`AXHUB_TOOL_URL`은 반드시 Gateway가 실제로 접근 가능한 주소여야 합니다. PC에서 각각 실행할 때는 `localhost`를 사용하고, Docker 컨테이너 안에서는 `tool-sms`, `tool-oth` 같은 Compose 서비스 이름을 사용합니다. + +### 로컬 실행용 권장 설정 + +아래는 키 값 없이 로컬 프로세스를 실행하는 예시입니다. Redis를 Docker로 먼저 기동하거나 전체 Docker Compose를 사용합니다. + +```powershell +# 선택 1: Redis만 기동 +$env:ACTIVE_PROFILE = 'local' +docker compose up -d redis mci-mock + +# 선택 2: 각 프로세스를 로컬에서 기동 +$env:SPRING_PROFILES_ACTIVE = 'local' +$env:OPENROUTER_API_KEY = '<개인 또는 개발용 비밀 저장소의 키>' +.\gradlew.bat :dap-gateway:bootRun +``` + +다른 PowerShell에서 Tool을 실행합니다. + +```powershell +$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 +``` + +### 권한 도메인 설정 + +`mcp.security.tenant-domains`는 테넌트 또는 호출 주체가 접근할 수 있는 `categoryKey`를 정의합니다. 운영 환경에서는 `ALL`을 무분별하게 사용하지 않고, Agent·조직별 허용 도메인을 최소 권한으로 설정합니다. + +```yaml +mcp: + security: + tenant-domains: + claims-agent: claim, customer + notification-agent: notification +``` + +## 공통 기능과 운영 기준 + +### Redis + +Redis는 Tool Registry의 등록 상태와 Heartbeat, Gateway 실행 추적 정보에 사용됩니다. 현재 Redis는 PII 원문 보관소가 아닙니다. + +### 권한과 승인 + +Gateway는 Agent가 전달한 허용 Tool 목록, 서버 정책, 신뢰된 Claim 여부, 쓰기 승인 여부를 확인합니다. Tool마다 권한 로직을 중복 구현하지 말고 Gateway 공통 정책과 `@McpFunction` 메타데이터를 사용합니다. + +### Trace ID와 Request ID + +```text +trace-id : 사용자 요청 전체에서 유지되는 상관관계 ID +request-id : Gateway → Tool, Tool → MCI 등 HTTP 호출마다 새로 생성되는 ID +``` + +Gateway와 Tool에는 관련 헤더 및 MDC 기반 로그 처리가 있습니다. 신규 HTTP Client도 공통 전파 정책을 따르며, Tool별로 임의의 헤더 이름을 추가하지 않습니다. + +### 로그와 개인정보 + +Gateway에는 민감 키와 일부 형식을 마스킹하는 공통 기능이 있습니다. 마스킹은 원문을 Agent에서 분리하는 PII 토큰화와는 다릅니다. + +- 요청·응답 전문을 로그에 남길 때는 반드시 마스킹합니다. +- 운영 로그에 주민번호, 계좌번호, 전화번호, 이메일, 인증값을 남기지 않습니다. +- Tool 서버의 신규 로그도 같은 마스킹 정책을 적용합니다. +- 민감정보 복원 필요 여부는 Tool 개발자가 임의로 결정하지 않고 보안·AA 정책을 따릅니다. + +## AA 협의 기반 향후 전환 과제 + +아래 항목은 현재 구현 완료 기능이 아니라 회의에서 합의한 목표 구조입니다. + +### 부서별 Tool Pod와 저장소 경계 + +현재 `dap-tool-oth`에 함께 있는 업무 Tool을 부서·업무 소유권 단위로 분리합니다. 각 Pod는 독립 이미지, 독립 배포, 독립 장애 범위를 갖도록 구성합니다. 실제 분리는 AA가 확정한 Tool 소유 부서와 운영 책임 매핑을 기준으로 수행합니다. + +### Agent별 Tool 노출 수 제한 + +Agent에게 모든 Tool을 한 번에 제공하지 않고, 업무 도메인과 권한에 따라 약 10~20개 수준의 Tool 그룹을 제공합니다. `categoryKey`는 이를 위한 기초 메타데이터이며, 향후 Agent-Tool Group 정책으로 확장합니다. + +### PII 토큰화 + +```text +원문 개인정보 + → PII Gateway가 Redis에 짧은 TTL로 보관 + → Agent에는 PII 토큰 또는 안전한 식별자만 전달 + → 인가된 Tool이 MCI 호출 직전에 필요한 항목만 복원 +``` + +이 전환 전까지는 현행 마스킹 기능을 PII 분리 구현으로 오해하지 않아야 합니다. + +### MCP SDK와 Glow Framework + +Gateway에는 Spring AI MCP Server 의존성이 포함되어 있습니다. MCP SDK/Glow 표준 적용 시에는 업무 Tool을 재작성하지 않고, 기존 `@McpTool`·`@McpFunction`과 UseCase를 표준 MCP Tool 명세·호출 콜백으로 연결하는 Adapter 계층을 공통 Core에 추가합니다. + +```text +MCP SDK 표준 tools/list, tools/call + ↓ +공통 Adapter + ↓ +기존 UseCase → Converter → MCI Client +``` + +따라서 업무 DTO, Converter, MCI Client의 책임은 유지됩니다. + +## 참고 소스 + +| 주제 | 대표 위치 | +|---|---| +| 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` | +| Docker 환경 | `docker-compose.yml` | + +--- + +문서에 없는 업무·보안·배포 기준은 임의로 추가하지 말고 AA 및 플랫폼 운영 기준과 먼저 합의합니다. \ No newline at end of file