133 Commits

Author SHA1 Message Date
jade
8b4d4da0b9 Merge branch 'main' of https://git.devjun.net/kimhyungsik/ax_hub_mcp_tool into DEV-SRTEST 2026-07-29 17:02:21 +09:00
jade
c551b15378 테스트 푸쉬 2026-07-29 17:01:05 +09:00
juheelee
b448e68f45 Merge branch 'main' of https://git.devjun.net/kimhyungsik/ax_hub_mcp_tool
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 2m49s
2026-07-29 16:50:56 +09:00
juheelee
b68fbb9726 feat: 메타 통합 코드 조회 2026-07-29 16:48:28 +09:00
c6a2f82455 push TEST
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 2m16s
2026-07-29 16:08:45 +09:00
juheelee
2f5dad05df feat: 메타 통합 코드 조회 2026-07-29 15:34:01 +09:00
jade
b0bf4a9c68 fix: use rsync to clean stale files during host volume sync
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 2m1s
2026-07-29 13:25:10 +09:00
jade
752aae4dde fix: run gradle natively in runner to avoid isolated mount issue
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1m50s
2026-07-29 13:22:03 +09:00
jade
5cb37cfda6 fix: mount correct app path for gradle build
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 4s
2026-07-29 13:16:31 +09:00
jade
2a4b3e68e9 fix: allow gradle build libs to be copied into docker container
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 2m3s
2026-07-29 13:03:11 +09:00
jade
ca1082f1c2 fix: rollback register to false as per user request
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 31s
2026-07-29 12:58:22 +09:00
jade
7b1d3131e6 fix: set register true for tools to fix catalog display
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 33s
2026-07-29 12:55:06 +09:00
jade
139ddbc6ca fix: optimize dockerfile to use external gradle build
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 37s
2026-07-29 12:54:02 +09:00
jade
870ebb723f fix: hide markdown emphasis markers in chat responses
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m22s
2026-07-29 11:35:22 +09:00
jade
a08a7d25df refactor: move team member tool to smp category
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-29 11:30:52 +09:00
jade
60acc5df81 refactor: rename solgit tool to sol
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m23s
2026-07-29 11:23:49 +09:00
jade
f5be5b2514 refactor: remove unused team member legacy types
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m32s
2026-07-29 11:14:44 +09:00
jade
aae4898d18 feat: invoke Solgit MCI before returning dummy response
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m26s
2026-07-29 11:02:10 +09:00
jade
06b724459e feat: add solgitReqList and get_mcp_tool_team_members tools
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m31s
2026-07-29 10:26:32 +09:00
jade
4133b63595 불필요 한 파일 삭제
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m19s
2026-07-28 14:39:50 +09:00
jade
1e18d2bc47 feat: support nested MCP input schema validation
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-28 14:39:31 +09:00
jade
0c89a09ccf docs: Update README.md with Web UI Scaffolder enhancements
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 8m5s
2026-07-27 15:41:32 +09:00
jade
3ce9201cd5 fix: Add throws Exception to MciClient template 2026-07-27 15:37:27 +09:00
jade
be81c095c9 fix: Correct text block alignment and formatting arguments 2026-07-27 15:36:40 +09:00
jade
d4554ce34f fix: Remove excessive indentation in string formatting for UseCaseImpl text block 2026-07-27 15:30:58 +09:00
jade
b6d4ab1bec fix: Remove duplicated package path in MCI DTOs and Converter 2026-07-27 15:26:38 +09:00
jade
2c8497ebee fix: Correct ToolScaffolder template issues 2026-07-27 15:10:15 +09:00
jade
8a93d5d6dc feat: Support 4-letter MCI system code in Scaffolder and refactor Onnba classes 2026-07-27 15:03:13 +09:00
jade
c0de1f4edb fix: Include local fallback tools in generateToolsMarkdown API
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m15s
2026-07-27 14:11:25 +09:00
jade
1ee9f9e802 style: Add chat.html link to all top navigation bars 2026-07-27 14:07:33 +09:00
jade
25f0d2bab5 feat: move Onnba MCI request conversion to use case
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m1s
2026-07-27 14:01:04 +09:00
jade
9b9159a605 feat: Add UI/UX improvements to catalog.html including search, config json copy, and guide modal
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 34s
2026-07-27 14:00:27 +09:00
jade
c8dd43221e ci: Fix Node.js installation by dropping sudo/curl and supporting alpine
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 6m41s
2026-07-27 13:09:01 +09:00
jade
7b8c38489e ci: Add Node.js installation step to Gitea Actions deploy workflow
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1s
2026-07-27 13:07:29 +09:00
jade
63187429b4 feat: validate MCP tool input constraints
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1s
2026-07-27 13:01:40 +09:00
jade
d4953d60ec test: fix gateway test package references
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 18s
2026-07-27 12:51:28 +09:00
jade
72891dc2db 불필요한 소스 삭제
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-27 12:38:31 +09:00
jade
82c27bd468 test: prevent duplicate MCP tool names
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-27 12:37:35 +09:00
jade
ff5ca4fc7a Fix: Add missing formatted arguments for MCI and EAI ToolScaffolder templates 2026-07-27 11:43:37 +09:00
jade
ba39a23b09 Fix: Add missing @Service annotations to MCP Tool use case implementations
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-27 10:45:09 +09:00
jade
579979b5fe Refactor: Move MCP tool annotations to interface level 2026-07-27 10:21:24 +09:00
jade
db46f30e72 refactor: DTO 레코드를 별도 파일(dto 패키지)로 분리
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-27 10:06:43 +09:00
jade
2c64d86a56 refactor: UseCase 인터페이스 내 DTO 분리 및 강결합 제거
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-27 10:04:56 +09:00
jade
ded5ef9e8f 신한 라이프 경로에 맞게 수정
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-27 08:57:03 +09:00
jade
a6ca2b82a7 scaffold 수정
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-26 16:00:45 +09:00
jade
94cdaaa8c9 불필요 없는 소스 삭제
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-26 15:46:48 +09:00
jade
bea517c4c1 fix: resolve bean injection error and gateway routing configs
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-26 15:34:43 +09:00
jade
ad950071b2 refactor: fix annotation package - remove duplicate annotation.annotation directory
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 02:00:47 +09:00
jade
e6407b07a2 refactor: apply Glow standard to dap-tool-core (usecase, javadoc, etc)
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 01:56:41 +09:00
jade
e58c004d9d refactor: rename biz/other to biz/oth
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 01:43:47 +09:00
jade
92fe2399be refactor: rename dap-tool-other to dap-tool-oth
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 01:36:12 +09:00
jade
7ba18f7809 refactor: rename domain categories (common->cmm, sample->smp) and restrict category key to 3 chars
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 01:29:48 +09:00
jade
4dcc5a2978 refactor: restructure MCP tools without Controller layer, using UseCase pattern per Glow standards
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 01:13:25 +09:00
jade
994f5b4348 fix: remove docker compose down from deploy pipeline to prevent killing the CI runner
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 00:13:57 +09:00
jade
0a50b20960 feat: add chat.html link to index.html dashboard
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 00:13:00 +09:00
jade
97ba0c3267 fix: add --remove-orphans to docker compose down to prevent port conflicts with legacy Blue/Green containers
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-25 00:09:36 +09:00
jade
388fa33c67 fix: revert Nginx and Blue/Green deployment to restore stable Apache connection
Some checks are pending
Deploy to OCIWP / deploy (push) Waiting to run
2026-07-25 00:05:54 +09:00
jade
4015207b86 fix: map Nginx 8281 to host 8080 to restore Apache proxy connection
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 7m53s
2026-07-24 23:52:55 +09:00
jade
4dcc85eff0 fix: override gateway fallback URLs via env vars to support blue/green
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-24 23:42:30 +09:00
jade
72f04aef62 fix: remove curl command that fails in act_runner
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 11m35s
2026-07-24 23:26:48 +09:00
jade
b5565a4da0 fix: repair blue-green deployment architecture
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 54s
2026-07-24 23:24:55 +09:00
jade
c38d59887c fix: remove 8080 from deploy script
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 10m59s
2026-07-24 23:04:38 +09:00
jade
f3639043fc chore: fetch gateway logs in deploy action 2026-07-24 17:52:15 +09:00
jade
e278602d32 fix: map gateway to 8281 to match Apache 2026-07-24 17:51:38 +09:00
jade
f6f86e5afb chore: test curl to gateway inside deploy action 2026-07-24 17:51:03 +09:00
jade
a89843518b fix: restore gateway 8081 port mapping 2026-07-24 17:48:34 +09:00
jade
d7b80a644c fix: bypass nginx and map gateway directly to 8281 2026-07-24 17:47:41 +09:00
jade
05a242e5de chore: test nginx static HTML response 2026-07-24 17:46:46 +09:00
jade
a409652bdf fix: Nginx port 8281 mapping restored 2026-07-24 17:46:09 +09:00
jade
fa268edf7d fix: Nginx SSL support for Apache proxy 2026-07-24 17:44:23 +09:00
jade
c69f11e1c9 fix: add http2 support to Nginx to match Apache h2c proxying
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 13m41s
2026-07-24 17:42:25 +09:00
jade
56483d804a chore: test nginx proxy to google
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-24 17:41:33 +09:00
jade
1c353ce3a7 chore: test nginx plain response
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-24 17:39:36 +09:00
jade
35f361a976 fix: restore Nginx port 8281 mapping - Apache VirtualHost proxies to 8281
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 13m41s
2026-07-24 16:08:59 +09:00
jade
9066c621a0 fix: resolve Nginx 503 error on rollback and apply package refactoring
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 11m44s
2026-07-24 15:00:01 +09:00
jade
4ed534b1d6 fix: restore Nginx 8080 port mapping for Apache and refactor MciCfpaClient
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 13m42s
2026-07-24 14:27:00 +09:00
jade
0892597c31 fix: start containers sequentially and fix nginx startup config
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 3m20s
2026-07-24 13:29:43 +09:00
jade
6416d3f844 fix: revert Nginx port mapping to 8281 to match external Apache proxy
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-24 13:21:10 +09:00
jade
dbe479a64b fix: build sequentially to avoid OOM on dev server
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 13m8s
2026-07-24 13:04:42 +09:00
jade
53f5047c8e fix: map Nginx 8281 to host 8080 to restore Apache proxy connection
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 12m3s
2026-07-24 11:44:01 +09:00
jade
f56e503a29 fix: make Blue-Green deployment safe by checking Nginx reload status and removing redundant host Gradle build to prevent OOM server crashes
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 10m6s
2026-07-24 10:55:32 +09:00
jade
aae95591a0 fix: add Nginx SSE proxy configs to prevent buffering of AI streams, and fix Gateway internal routing to use Nginx
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-24 10:45:27 +09:00
jade
227edf9744 fix: remove stale src directories in host volume before copy to resolve compilation conflict
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 12m14s
2026-07-24 10:28:24 +09:00
jade
12ac05c805 fix: add clean to gradle build in Gitea deploy pipeline to clear persistent host volume cache
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 38s
2026-07-24 10:26:09 +09:00
jade
74ea89bf1c refactor: apply naming convention for MCI interface IO objects (CLCNNB00001_I, CUST_INQ_001_I) and separate layers
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 24s
2026-07-24 10:24:00 +09:00
jade
d562e35ad8 fix: resolve stale class conflict in Docker build by adding clean and .dockerignore
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 23s
2026-07-24 10:20:00 +09:00
jade
fc8c3da666 refactor: apply MSA naming standard for DTOs and Scaffold template (Req->Request, Res->Response)
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 30s
2026-07-24 10:07:50 +09:00
jade
438c190447 refactor: rename packages dapms to mcg and dapmt to mcc
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 42s
2026-07-24 10:00:45 +09:00
jade
51f37cfcbc chore: add restart always policy to nginx
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 14m33s
2026-07-24 09:45:23 +09:00
jade
44c2fcebba fix: invalid docker filter syntax in deploy.yml
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 13m9s
2026-07-24 09:15:02 +09:00
jade
2758f6da8e fix: Nginx port conflict with legacy containers
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 14m45s
2026-07-24 08:55:30 +09:00
jade
8b11c1aae8 feat: 적용 무중단 배포 시스템 (Blue-Green Deployment)
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 51s
2026-07-24 08:51:01 +09:00
jade
60a7226199 chore: remove unnecessary Node.js installation from deploy.yml
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 13m32s
2026-07-24 00:29:02 +09:00
jade
b3bdaa2b28 fix: revert ports and add zombie container cleanup to CI
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-24 00:22:20 +09:00
jade
260f59b143 fix: change dozzle port to 18288 to resolve conflict
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 13m26s
2026-07-24 00:02:54 +09:00
jade
ad2ab985d8 feat: allow Chat AI to use unregistered (register=false) tools
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 13m58s
2026-07-23 23:46:04 +09:00
jade
18959b5128 fix: install nodejs for checkout action
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-23 23:32:44 +09:00
jade
7f85b10938 chore: remove accidentally committed SearchHr generated files
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1s
2026-07-23 23:30:53 +09:00
jade
b5dbcc54f0 fix: add checkout step and sync code to host volume in deployment pipeline
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 8s
2026-07-23 23:28:29 +09:00
jade
abf7a435a4 feat/fix: update ToolScaffolder interface, update scaffold.html, fix mci-mock port
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 14m20s
2026-07-23 23:11:30 +09:00
jade
f0a295632f ci: add docker system prune before deployment
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 16m15s
2026-07-23 22:52:48 +09:00
jade
cec56e015b fix: change Redis host port mapping to avoid conflicts
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 11m48s
2026-07-23 22:33:09 +09:00
kimhyungsik
1e54370fb8 fix: restore missing closing brace in DynamicMcpController
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 15m36s
2026-07-23 13:08:16 +09:00
kimhyungsik
408a3f6b28 fix: add DELETE mapping and .well-known endpoints for Cursor compatibility
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1m23s
2026-07-23 13:05:20 +09:00
jade
541150d3f4 Fix: gateway local profile fallback URL을 컨테이너 이름으로 변경
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-23 13:01:53 +09:00
jade
165a6340ea Refactor: ToolService 인터페이스 분리 및 GlowEaiComponent 로드 에러 수정
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-23 12:59:34 +09:00
kimhyungsik
5f8228bfce fix: add EnableScheduling to enable dynamic tool synchronization
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 17m7s
2026-07-23 11:16:54 +09:00
kimhyungsik
6893bb6b35 fix: comment out emitter.onCompletion/onTimeout session removal to keep sessions persistent
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 14m21s
2026-07-23 10:33:56 +09:00
jade
3c183e901b chore: Remove hardcoded axhub.tool.url from application-dev.yml
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 13m57s
2026-07-23 10:17:38 +09:00
kimhyungsik
2948465b8a fix: comment out emitter.complete() to allow keep-alive sessions for standard SSE MCP client
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-23 10:16:15 +09:00
jade
2b4adb45d2 fix: Correct tool-payment internal port mapping to 8085
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-23 10:10:45 +09:00
jade
323d227eee feat: Add AxhubEaiComponent and MCI/EAI mock frameworks
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 3m26s
2026-07-23 09:19:21 +09:00
kimhyungsik
74b50482bf chore: optimize dev fallback routes to use internal docker service names
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 13m24s
2026-07-22 17:29:00 +09:00
kimhyungsik
dfa5a73b20 chore: force docker compose project name to ax_hub_mcp_tool
All checks were successful
Deploy to OCIWP / deploy (push) Successful in 7m48s
2026-07-22 16:44:29 +09:00
kimhyungsik
86302d0947 chore: optimize deploy pipeline to ultra-fast relay
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-22 16:31:13 +09:00
kimhyungsik
ad37714c5b chore_fix_docker_compose_typo
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 13m31s
2026-07-22 16:01:03 +09:00
jade
b57d96f83f refactor: rename project codes mcg to dapms and mcc to dapmt
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 37s
2026-07-22 15:48:45 +09:00
kimhyungsik
fabbd368a2 chore_resolve_path_and_arg_issues
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 57s
2026-07-22 15:37:33 +09:00
kimhyungsik
f3cdfaccc3 chore_apply_gradle_jdk21_build_and_compose_direct_mount
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 34s
2026-07-22 15:33:57 +09:00
kimhyungsik
e42ca92811 chore_resolve_git_conflict
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 15s
2026-07-22 15:31:13 +09:00
jade
c18efe251e refactor: migrate all properties files to yaml
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 0s
2026-07-22 14:57:44 +09:00
jade
8d4e8fd976 chore: replace author name with employee ID 0986406
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 1s
2026-07-22 14:36:15 +09:00
kimhyungsik
edec314a83 test: final verify auto deploy
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 0s
2026-07-22 14:08:45 +09:00
kimhyungsik
da09997452 chore: enable docker run relay deploy
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 0s
2026-07-22 14:05:46 +09:00
kimhyungsik
5c15ae3505 test: verify auto deploy triggering
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 0s
2026-07-22 14:03:22 +09:00
kimhyungsik
3eebf07b0e chore: restore standard runs-on label
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 0s
2026-07-22 14:00:36 +09:00
kimhyungsik
a161dcf319 chore: quote runs-on host label
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-22 13:59:46 +09:00
kimhyungsik
ae3352ccb4 chore: fix runs-on syntax and add gradle caching
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 0s
2026-07-22 13:57:00 +09:00
kimhyungsik
2deffd3f10 chore: change runs-on to host mode
Some checks failed
Deploy to OCIWP / deploy (push) Has been cancelled
2026-07-22 13:52:22 +09:00
kimhyungsik
f334b8ecc5 feat: add deploy workflow
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 0s
2026-07-22 13:47:01 +09:00
jade
1db19344bc fix: restore broken Korean comments in application-dev.properties and add UTF-8 encoding rule 2026-07-22 13:36:22 +09:00
jade
8e6682d089 chore: migrate environment endpoints from render to OCI 2026-07-22 13:35:01 +09:00
jade
6968c7dfb7 refactor: use Spring DI for MapStruct and update AGENTS.md rule 2026-07-22 12:37:16 +09:00
339 changed files with 4928 additions and 3952 deletions

View File

@@ -5,9 +5,10 @@
## 개발 가이드라인
* 패키지명은 `controller` 대신 `presentation`을 사용합니다.
* MapStruct 사용 시, 테스트 환경 에러를 방지하기 위해 생성자(`new ...Impl()`) 대신 `Mappers.getMapper(인터페이스명.class)` 방식으로 인스턴스를 가져옵니다.
* MapStruct 사용 시, Spring DI를 활용하여 의존성 주입(`private final Converter converter;`)을 받는 방식을 권장합니다. (단위 테스트 시에는 `@MockBean` 또는 직접 구현체를 주입하여 테스트)
## 명심해야 할 규칙 추가란
* `application.yml` 등 설정 파일 수정 시 한글이 깨지지 않도록 항상 UTF-8 인코딩을 유지하고, 깨진 문자열(`?\uFFFD` 등)이 발생하지 않도록 각별히 주의한다.
* 이모지는 무조건 넣지 않는다
* import 할것 무조건 한다
* Git commit과 push는 사용자의 명시적인 허락(지시) 없이는 절대 수행하지 않는다.
@@ -16,13 +17,13 @@
* @package io.shinhanlife.axhub.biz.mcp.tool.sms
* @className AxHubToolSmsApplication
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

10
.dockerignore Normal file
View File

@@ -0,0 +1,10 @@
.git
.gradle
.idea
/build/
target/
*/target/
bin/
*/bin/
out/
*/out/

View File

@@ -0,0 +1,67 @@
name: Deploy to OCIWP
on:
push:
branches:
- main
- master
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Prepare Environment (Install Node.js & Git & Java 21)
run: |
if command -v apt-get &> /dev/null; then
apt-get update
apt-get install -y nodejs git openjdk-21-jdk rsync
elif command -v apk &> /dev/null; then
apk add --no-cache nodejs git openjdk21 rsync
fi
- name: Checkout Code
uses: actions/checkout@v3
- name: Sync Code to Host Volume
run: |
echo "Copying latest code to /app (Host Volume) and removing stale files..."
if command -v rsync &> /dev/null; then
rsync -a --delete --exclude='.git' . /app/
else
rm -rf /app/dap-* /app/src /app/build.gradle /app/settings.gradle
cp -a . /app/
fi
- name: Deploy Task on Host
run: |
echo "Starting Standard CI/CD Deploy pipeline..."
# 1. runner에 설치된 java로 직접 빌드! (격리된 환경 문제 해결)
cd /app
chmod +x gradlew
./gradlew bootJar -x test
# 2. 빌드 결과 plain jar 아카이브 정리
find /app -name "*-plain.jar" -delete
# 3. 좀비 컨테이너 청소 (현재 우리가 쓸 포트를 점유 중인 옛날 컨테이너들만 정확히 색출해서 사살)
echo "Finding and killing zombie containers holding our ports..."
for port in 6379 8080 8089 8281 8282 8283 8284 8285 8288; do
cid=$(docker ps -q --filter "publish=$port")
if [ ! -z "$cid" ]; then
echo "Force killing container $cid using port $port"
docker rm -f $cid
fi
done
# 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
# 5. 배포 후 대롱대롱 매달려 있는 가비지 이미지 자동 소거 청소!
docker image prune -f
echo "CI/CD Deploy Success!"

62
.gitignore vendored
View File

@@ -56,11 +56,69 @@ build/
# ===== VS Code =====
.vscode/
# ===== 鍮뚮뱶 寃곌낵臾?=====
target/
!**/src/main/**/target/
!**/src/test/**/target/
*.class
*.jar
!gradle/wrapper/gradle-wrapper.jar
*.war
*.ear
*.nar
# ===== 濡쒓렇 =====
*.log
logs/
spring-shell.log
# ===== Maven =====
.mvn/wrapper/maven-wrapper.jar
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# ===== IntelliJ IDEA =====
.idea/
*.iws
*.iml
*.ipr
out/
# ===== Eclipse / STS =====
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
# ===== NetBeans =====
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
.gradle/
!**/src/main/**/build/
!**/src/test/**/build/
# ===== VS Code =====
.vscode/
# ===== OS =====
.DS_Store
Thumbs.db
ehthumbs.db
# ===== ?섍꼍?ㅼ젙 (誘쇨컧?뺣낫 遺꾨━ ?? =====
# application-local.properties
# application-secret.properties
# application-local.yml
# application-secret.yml

View File

@@ -1,30 +1,31 @@
# 1. ??š®ë±???<3F>ê¼<EFBFBD> (JDK 21)
# 1. ??<EFBFBD><EFBFBD><EFBFBD>???<3F> (JDK 21)
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
# Gradle ??ë<EFBFBD><EFBFBD>?? ???’ª 蹂듭ê¶?
# Gradle ??<3F>?? ???<EFBFBD><EFBFBD> 蹂듭<E8B982>?
COPY gradlew .
COPY gradle gradle
COPY build.gradle settings.gradle ./
COPY src src
# æ²…ëš°ë¸??ºÂ€??è«???š®ë±?(???’ª????–쇅)
# 沅뚰<EFBFBD>??<3F>€??<3F>???<3F><><EFBFBD>?(???<EFBFBD><EFBFBD>????<EFBFBD>)
RUN chmod +x gradlew
RUN ./gradlew clean build -x test
# 2. ??½ë»¾ ??<3F>ê¼<EFBFBD> (JRE 21)
# 2. ??<EFBFBD> ??<3F> (JRE 21)
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# ???꾩ã€???¼ì ™ (??“뎅 ??“ì»™)
# ???<EFBFBD>???<3F>젙 (??<3F>뎅 ??<3F>)
RUN apk add --no-cache tzdata
ENV TZ=Asia/Seoul
# ??š®ë±??JAR ???”ª 蹂듭ê¶?
# ??<EFBFBD><EFBFBD><EFBFBD>??JAR ???<EFBFBD><EFBFBD> 蹂듭<E8B982>?
COPY --from=builder /app/build/libs/*.jar app.jar
# 湲곕???????몄텧
EXPOSE 8081
# ?Œâ‘¦???€ê¼???½ë»¾ ??JAR ??½ë»¾
# ?<EFBFBD>⑦???<3F><>???<3F> ??JAR ??<EFBFBD>
ENTRYPOINT ["java", "-jar", "app.jar"]

View File

@@ -2,13 +2,13 @@
* @package io.shinhanlife
* @className McpBridge
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

319
README.md
View File

@@ -1,165 +1,154 @@
# 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-other: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.properties`의 `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 (기능) 생성 탭**: 생성된 모듈에 새로운 툴(서비스/DTO) 코드를 자동으로 주입합니다.
### 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.common.util.PodScaffolder [모듈명] [포트번호]
# 실행 예시 (dap-tool-hr 모듈을 8086 포트로 생성)
java -cp dap-common/src/main/java io.shinhanlife.dap.common.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.common.util.ToolScaffolder [Tool이름] [인터페이스ID] "[기능설명]" "[그룹명]" "[통신방식]" "[모듈명]"
# 실행 예시 (payment 모듈에 결제 승인 기능 추가)
java -cp dap-common/src/main/java io.shinhanlife.dap.common.util.ToolScaffolder PaymentApproval PAY_001 "결제 승인 처리 기능" "COMMON" "HTTP" "dap-tool-payment"
```
---
## 패키지 구조 (Package Structure)
```text
dap-backend-main (Root)
├── dap-gateway # MCP 라우팅 허브 서버 (외부 LLM과 통신 및 Tool 분배)
├── dap-common # 공통 모듈 (Security, Session, Config 등)
├── dap-tool-core # Tool 공통 기능 (AbstractMcpToolService, Annotation, Scaffolder)
├── dap-tool-email # [Tool] 이메일 발송 특화 어댑터 모듈
├── dap-tool-sms # [Tool] SMS 발송 특화 어댑터 모듈
├── dap-tool-payment # [Tool] 결제 비즈니스 어댑터 모듈 (Scaffolded)
└── dap-tool-other # [Tool] 기타 비즈니스(청구, 계약, 고객, HR 등) 어댑터 모듈
```
*(참고: 기존 단일 모듈 프로젝트에서 마이크로서비스 확장을 위해 모듈별로 분리되었으며, 각 Tool 서버는 독립적으로 확장 및 배포할 수 있습니다.)*
# 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"
```
---

View File

@@ -1,18 +1,8 @@
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY gradlew .
COPY gradle gradle
COPY build.gradle settings.gradle ./
COPY dap-tool-core dap-tool-core
COPY dap-gateway dap-gateway
RUN chmod +x gradlew
RUN ./gradlew :dap-gateway:build -x test
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN apk add --no-cache tzdata
ENV TZ=Asia/Seoul
COPY --from=builder /app/dap-gateway/build/libs/*-SNAPSHOT.jar app.jar
COPY dap-gateway/build/libs/*-SNAPSHOT.jar app.jar
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "app.jar"]

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg;
* @package io.shinhanlife.dap.mcg
* @className DapGatewayApplication
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
@@ -19,10 +19,12 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication(scanBasePackages = {"io.shinhanlife.dap.mcg", "io.shinhanlife.dap.common.mcp", "io.shinhanlife.dap.common.config"})
@ConfigurationPropertiesScan(basePackages = {"io.shinhanlife.dap.mcg", "io.shinhanlife.dap.common.mcp", "io.shinhanlife.dap.common.config"})
@SpringBootApplication(scanBasePackages = {"io.shinhanlife.dap.mcg", "io.shinhanlife.dap.lib.mcp", "io.shinhanlife.dap.lib.config"})
@ConfigurationPropertiesScan(basePackages = {"io.shinhanlife.dap.mcg", "io.shinhanlife.dap.lib.mcp", "io.shinhanlife.dap.lib.config"})
@EnableCaching
@EnableScheduling
public class DapGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(DapGatewayApplication.class, args);

View File

@@ -11,13 +11,13 @@ import org.springframework.util.StopWatch;
* @package io.shinhanlife.dap.mcg.aop
* @className GatewayLoggingAspect
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.audit;
* @package io.shinhanlife.dap.mcg.audit
* @className AuditLogService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.config;
* @package io.shinhanlife.dap.mcg.config
* @className AgentResponseBudgetProperties
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -11,13 +11,13 @@ import java.util.Map;
* @package io.shinhanlife.dap.mcg.config
* @className GatewayFallbackProperties
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.config;
* @package io.shinhanlife.dap.mcg.config
* @className McpGatewayProperties
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
@@ -25,7 +25,7 @@ import java.util.stream.Collectors;
@Component
@ConfigurationProperties(prefix = "mcp")
/**
* application.properties의 mcp.* 설정을 Java 코드에서 사용하기 위한 설정 클래스입니다.
* application.yml의 mcp.* 설정을 Java 코드에서 사용하기 위한 설정 클래스입니다.
*
* API Key, 허용 Tool, 감사 로그, Redis Trace, Retry, Circuit Breaker,
* EIMS/Tool 서버 주소 같은 MCP Gateway 운영 설정을 한 곳에서 관리합니다.

View File

@@ -13,13 +13,13 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
* @package io.shinhanlife.dap.mcg.config
* @className RedisConfig
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -4,13 +4,13 @@ package io.shinhanlife.dap.mcg.dto;
* @package io.shinhanlife.dap.mcg.dto
* @className OperationType
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -20,13 +20,13 @@ import java.util.HashSet;
* @package io.shinhanlife.dap.mcg.dto
* @className ToolMetadata
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.guardrail;
* @package io.shinhanlife.dap.mcg.guardrail
* @className GuardrailService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.guardrail;
* @package io.shinhanlife.dap.mcg.guardrail
* @className SensitiveDataMasker
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.guardrail;
* @package io.shinhanlife.dap.mcg.guardrail
* @className ToolResponseGuardrailService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -13,13 +13,13 @@ import java.util.UUID;
* @package io.shinhanlife.dap.mcg.messaging
* @className KafkaProducerService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.presentation;
* @package io.shinhanlife.dap.mcg.presentation
* @className ChatController
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
@@ -41,6 +41,7 @@ public class ChatController {
private final ObjectMapper objectMapper;
private final RedisRegistryService registryService;
private final ChatClient.Builder chatClientBuilder;
private final McpRouterController mcpRouterController;
@PostMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter chatStream(@RequestBody Map<String, String> request,
@@ -54,9 +55,16 @@ public class ChatController {
try {
List<ToolCallback> callbacks = new ArrayList<>();
for (ToolMetadata meta : registryService.getAllTools()) {
if (Boolean.TRUE.equals(meta.getVisible())) {
callbacks.add(new DynamicMcpToolCallback(meta, executeService, objectMapper, effectiveTenantId));
io.shinhanlife.dap.lib.adapter.dto.JsonRpcResponse toolsResponse = mcpRouterController.listTools(null).getBody();
if (toolsResponse != null && toolsResponse.getResult() instanceof Map) {
Map<String, Object> resultMap = (Map<String, Object>) toolsResponse.getResult();
if (resultMap.containsKey("tools")) {
List<ToolMetadata> allTools = (List<ToolMetadata>) resultMap.get("tools");
for (ToolMetadata meta : allTools) {
if (Boolean.TRUE.equals(meta.getVisible())) {
callbacks.add(new DynamicMcpToolCallback(meta, executeService, objectMapper, effectiveTenantId));
}
}
}
}

View File

@@ -11,13 +11,13 @@ import java.util.Map;
* @package io.shinhanlife.dap.mcg.presentation
* @className KillSwitchController
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,25 +5,25 @@ package io.shinhanlife.dap.mcg.presentation;
* @package io.shinhanlife.dap.mcg.presentation
* @className McpRouterController
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
import com.fasterxml.jackson.databind.ObjectMapper;
import io.shinhanlife.dap.common.adapter.dto.JsonRpcRequest;
import io.shinhanlife.dap.common.adapter.dto.JsonRpcResponse;
import io.shinhanlife.dap.common.adapter.dto.Params;
import io.shinhanlife.dap.lib.adapter.dto.JsonRpcRequest;
import io.shinhanlife.dap.lib.adapter.dto.JsonRpcResponse;
import io.shinhanlife.dap.lib.adapter.dto.Params;
import io.shinhanlife.dap.mcg.config.GatewayFallbackProperties;
import io.shinhanlife.dap.mcg.dto.ToolMetadata;
import io.shinhanlife.dap.mcg.registry.RedisRegistryService;
import io.shinhanlife.dap.mcg.service.ExecuteService;
import io.shinhanlife.dap.common.mcp.security.SecurityProperties;
import io.shinhanlife.dap.lib.mcp.security.SecurityProperties;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.time.LocalDateTime;
@@ -91,7 +91,7 @@ public class McpRouterController {
try {
Object result = executeService.execute(payload, effectiveTenantId);
io.shinhanlife.dap.common.adapter.dto.JsonRpcResponse response = new io.shinhanlife.dap.common.adapter.dto.JsonRpcResponse();
io.shinhanlife.dap.lib.adapter.dto.JsonRpcResponse response = new io.shinhanlife.dap.lib.adapter.dto.JsonRpcResponse();
response.setJsonrpc("2.0");
response.setId(payload.containsKey("id") ? String.valueOf(payload.get("id")) : UUID.randomUUID().toString());
response.setResult(result);
@@ -112,9 +112,7 @@ public class McpRouterController {
}
}
@GetMapping("/tools/list")
public ResponseEntity<JsonRpcResponse> listTools(
@RequestParam(value = "categoryKey", required = false) String categoryKey) {
private List<ToolMetadata> fetchAllActiveTools() {
List<ToolMetadata> activeTools = redisRegistryService.getAllTools()
.stream()
.filter(ToolMetadata::getVisible)
@@ -148,6 +146,14 @@ public class McpRouterController {
log.warn("Failed to fetch local tools from fallback URL: {}", url);
}
}
return activeTools;
}
@GetMapping("/tools/list")
public ResponseEntity<JsonRpcResponse> listTools(
@RequestParam(value = "categoryKey", required = false) String categoryKey) {
List<ToolMetadata> activeTools = fetchAllActiveTools();
if (categoryKey != null && !categoryKey.trim().isEmpty()) {
activeTools = activeTools.stream()
@@ -161,12 +167,10 @@ public class McpRouterController {
return ResponseEntity.ok(response);
}
@GetMapping(value = "/tools/docs/markdown", produces = "text/markdown;charset=UTF-8")
public ResponseEntity<String> generateToolsMarkdown() {
List<ToolMetadata> tools = redisRegistryService.getAllTools()
.stream()
.filter(ToolMetadata::getVisible)
.collect(Collectors.toList());
List<ToolMetadata> tools = fetchAllActiveTools();
StringBuilder md = new StringBuilder();
md.append("# \uD83E\uDD16 Shinhan AI Tool Catalog\n\n");

View File

@@ -5,19 +5,19 @@ package io.shinhanlife.dap.mcg.presentation;
* @package io.shinhanlife.dap.mcg.presentation
* @className ScaffoldingController
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
import io.shinhanlife.dap.common.util.PodScaffolder;
import io.shinhanlife.dap.common.util.ToolScaffolder;
import io.shinhanlife.dap.common.util.ToolSourceUpdater;
import io.shinhanlife.dap.lib.util.PodScaffolder;
import io.shinhanlife.dap.lib.util.ToolScaffolder;
import io.shinhanlife.dap.lib.util.ToolSourceUpdater;
import java.io.File;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@@ -34,7 +34,7 @@ public class ScaffoldingController {
@PostMapping("/pod")
public String scaffoldPod(@RequestBody Map<String, String> req) {
try {
String moduleName = req.getOrDefault("moduleName", "dap-tool-other");
String moduleName = req.getOrDefault("moduleName", "dap-tool-oth");
if (!moduleName.startsWith("dap-tool-")) moduleName = "dap-tool-" + moduleName;
String port = req.getOrDefault("port", "8085");
String shortName = moduleName.replace("dap-tool-", "").replace("-", "");
@@ -57,14 +57,15 @@ public class ScaffoldingController {
String description = req.get("description");
String group = req.getOrDefault("categoryKey", req.getOrDefault("group", "COMMON"));
String routingType = req.getOrDefault("routingType", "HTTP");
String moduleName = req.getOrDefault("moduleName", "dap-tool-other");
String moduleName = req.getOrDefault("moduleName", "dap-tool-oth");
String author = req.get("author");
if (author == null || author.trim().isEmpty()) author = System.getProperty("user.name");
String date = req.get("date");
if (date == null || date.trim().isEmpty()) date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd"));
boolean register = Boolean.parseBoolean(req.getOrDefault("register", "true"));
String clientSystemCode = req.get("clientSystemCode");
return ToolScaffolder.scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, date, register);
return ToolScaffolder.scaffold(baseName, interfaceId, description, group, routingType, moduleName, author, date, register, clientSystemCode);
} catch (Exception e) {
return "오류 발생: " + e.getMessage();
}
@@ -96,12 +97,12 @@ public class ScaffoldingController {
File[] files = dir.listFiles(f -> f.isDirectory() && f.getName().startsWith("dap-tool-") && !f.getName().equals("dap-tool-core"));
if (files == null || files.length == 0) {
return List.of("dap-tool-other", "dap-tool-email", "dap-tool-hr", "dap-tool-payment", "dap-tool-sms");
return List.of("dap-tool-oth", "dap-tool-hr", "dap-tool-sms");
}
return Arrays.stream(files).map(File::getName).sorted().collect(Collectors.toList());
} catch (Exception e) {
return List.of("dap-tool-other", "dap-tool-email", "dap-tool-hr", "dap-tool-payment", "dap-tool-sms");
return List.of("dap-tool-oth", "dap-tool-hr", "dap-tool-sms");
}
}
}

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.redis;
* @package io.shinhanlife.dap.mcg.redis
* @className McpMonitorEventService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.redis;
* @package io.shinhanlife.dap.mcg.redis
* @className RedisToolTraceService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -16,13 +16,13 @@ import java.util.Objects;
* @package io.shinhanlife.dap.mcg.registry
* @className RedisRegistryService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.resilience;
* @package io.shinhanlife.dap.mcg.resilience
* @className CircuitBreaker
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.resilience;
* @package io.shinhanlife.dap.mcg.resilience
* @className CircuitBreakerMonitorController
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.resilience;
* @package io.shinhanlife.dap.mcg.resilience
* @className CircuitBreakerService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.resilience;
* @package io.shinhanlife.dap.mcg.resilience
* @className CircuitBreakerSnapshot
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.resilience;
* @package io.shinhanlife.dap.mcg.resilience
* @className CircuitBreakerState
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.resilience;
* @package io.shinhanlife.dap.mcg.resilience
* @className FailureType
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.resilience;
* @package io.shinhanlife.dap.mcg.resilience
* @className RetryPolicy
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.resilience;
* @package io.shinhanlife.dap.mcg.resilience
* @className ToolExecutionException
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.security;
* @package io.shinhanlife.dap.mcg.security
* @className McpRequestContext
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.security;
* @package io.shinhanlife.dap.mcg.security
* @className McpRequestContextResolver
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.security;
* @package io.shinhanlife.dap.mcg.security
* @className ToolAuthorizationService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.service;
* @package io.shinhanlife.dap.mcg.service
* @className ExecuteService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -9,13 +9,13 @@ import org.springframework.stereotype.Service;
* @package io.shinhanlife.dap.mcg.service
* @className KillSwitchService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,6 +1,6 @@
package io.shinhanlife.dap.mcg.service;
import io.shinhanlife.dap.common.mcp.security.SecurityProperties;
import io.shinhanlife.dap.lib.mcp.security.SecurityProperties;
import io.shinhanlife.dap.mcg.registry.RedisRegistryService;
import io.shinhanlife.dap.mcg.dto.ToolMetadata;
import io.shinhanlife.dap.mcg.config.GatewayFallbackProperties;
@@ -15,13 +15,13 @@ import java.util.Map;
* @package io.shinhanlife.dap.mcg.service
* @className ToolPlanner
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -4,13 +4,13 @@ package io.shinhanlife.dap.mcg.sync;
* @package io.shinhanlife.dap.mcg.sync
* @className CustomWebMvcSseServerTransportProvider
* @description AX HUB MCP Gateway SSE 전송 제공자 - SSE 기반의 MCP 서버 트랜스포트를 구현하는 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
@@ -95,8 +95,8 @@ public class CustomWebMvcSseServerTransportProvider implements McpServerTranspor
McpServerSession session = sessionFactory.create(sessionTransport);
sessions.put(sessionId, session);
emitter.onCompletion(() -> sessions.remove(sessionId));
emitter.onTimeout(() -> sessions.remove(sessionId));
// emitter.onCompletion(() -> sessions.remove(sessionId));
// emitter.onTimeout(() -> sessions.remove(sessionId));
new Thread(() -> {
try {
@@ -179,8 +179,8 @@ public class CustomWebMvcSseServerTransportProvider implements McpServerTranspor
log.info("Message sent to session handler");
if (emitter != null && !map.containsKey("id")) {
emitter.complete();
log.info("Completed emitter for notification");
// emitter.complete();
log.info("Completed emitter for notification (disabled for keep-alive)");
}
return ResponseEntity.ok().build();
@@ -220,7 +220,7 @@ public class CustomWebMvcSseServerTransportProvider implements McpServerTranspor
// Custom 프로토콜: 1회 요청당 1응답 후 종료 (스트림을 닫아버림)
// 클라이언트가 한 번의 POST 후 응답을 받고 연결을 끊기 때문
this.emitter.complete();
// this.emitter.complete();
}
} catch (Exception e) {
log.error("Error sending message to SSE emitter", e);

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.sync;
* @package io.shinhanlife.dap.mcg.sync
* @className DynamicMcpController
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
@@ -84,4 +84,19 @@ public class DynamicMcpController {
.header("Mcp-Session-Id", activeSessionId)
.body(emitter);
}
@org.springframework.web.bind.annotation.DeleteMapping("/mcp/sse/{category}")
public ResponseEntity<Void> handleDeleteSse(@PathVariable("category") String category) {
return ResponseEntity.ok().build();
}
@GetMapping("/.well-known/oauth-protected-resource")
public ResponseEntity<java.util.Map<String, Object>> handleOauthProtectedResource() {
return ResponseEntity.ok().body(java.util.Map.of());
}
@GetMapping("/.well-known/oauth-protected-resource/**")
public ResponseEntity<java.util.Map<String, Object>> handleOauthProtectedResourceWildcard() {
return ResponseEntity.ok().body(java.util.Map.of());
}
}

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.sync;
* @package io.shinhanlife.dap.mcg.sync
* @className DynamicMcpServerManager
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
@@ -53,7 +53,7 @@ public class DynamicMcpServerManager {
getOrCreateServer("payment");
getOrCreateServer("sms");
getOrCreateServer("email");
getOrCreateServer("other");
getOrCreateServer("oth");
getOrCreateServer("sample");
}

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.sync;
* @package io.shinhanlife.dap.mcg.sync
* @className RegistryMcpToolSpecificationFactory
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -22,13 +22,13 @@ import java.util.stream.Collectors;
* @package io.shinhanlife.dap.mcg.sync
* @className RegistryMcpToolSynchronizer
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.tool.large;
* @package io.shinhanlife.dap.mcg.tool.large
* @className AgentResponseBudgetService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.tool.large;
* @package io.shinhanlife.dap.mcg.tool.large
* @className LargeToolResponseService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.tool.large;
* @package io.shinhanlife.dap.mcg.tool.large
* @className PaginationRequestValidator
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.tool.result;
* @package io.shinhanlife.dap.mcg.tool.result
* @className ToolExecutionResult
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.tool.result;
* @package io.shinhanlife.dap.mcg.tool.result
* @className ToolExecutionResultFormatter
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.transport;
* @package io.shinhanlife.dap.mcg.transport
* @className HttpToolInvoker
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.transport;
* @package io.shinhanlife.dap.mcg.transport
* @className ToolInvoker
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,37 +0,0 @@
package io.shinhanlife.dap.sample.converter;
import io.shinhanlife.dap.sample.domain.model.AppliSystNtfyPatiModel;
import io.shinhanlife.dap.sample.dto.AppliSystNtfyRgiInDTO;
import io.shinhanlife.dap.sample.dto.StrnTermListInDTO;
import io.shinhanlife.dap.sample.dto.StrnTermListOutDTO;
import io.shinhanlife.dap.sample.presentation.io.AppliSystNtfyPatiRequest;
import io.shinhanlife.dap.sample.presentation.io.StrnTermRequest;
import io.shinhanlife.dap.sample.presentation.io.StrnTermResponse;
import org.mapstruct.Mapper;
/**
* @package io.shinhanlife.dap.sample.converter
* @className SampleConverter
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Mapper(componentModel = "spring")
public abstract class SampleConverter {
public abstract StrnTermListInDTO convertRequestToDto(StrnTermRequest req);
public abstract StrnTermResponse convertDtoToResponse(StrnTermListOutDTO outDto);
public abstract AppliSystNtfyPatiModel convertDtoToModel(AppliSystNtfyRgiInDTO inDto);
public abstract AppliSystNtfyRgiInDTO convertAppliRequestToDto(AppliSystNtfyPatiRequest request);
}

View File

@@ -1,35 +0,0 @@
package io.shinhanlife.dap.sample.domain.model;
import io.shinhanlife.glow.db.dto.AuditInfo;
import lombok.*;
/**
* @package io.shinhanlife.dap.sample.domain.model
* @className AppliSystNtfyPatiModel
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AppliSystNtfyPatiModel extends AuditInfo {
private String appliSystNtfyPatiId;
private String appliSystNtfyKdCd;
private String appliDutjCd;
private String appliDutjPrjcCd;
private String ntfyMsgCt;
private String ntfyOccDt;
private String ntfyCfmDt;
private String ntfyTrgtPrafNo;
private String shmsPmlYn;
private String ntfyCfmYn;
}

View File

@@ -1,34 +0,0 @@
package io.shinhanlife.dap.sample.domain.repository;
import io.shinhanlife.dap.sample.domain.model.AppliSystNtfyPatiModel;
import io.shinhanlife.dap.sample.dto.StrnTermListInDTO;
import io.shinhanlife.dap.sample.dto.StrnTermListOutDTO;
import io.shinhanlife.glow.GlowIndexPaging;
import io.shinhanlife.glow.GlowMybatisMapper;
import io.shinhanlife.glow.PageInfo;
import java.util.List;
/**
* @package io.shinhanlife.dap.sample.domain.repository
* @className GlowSampleRepository
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@GlowMybatisMapper
public interface GlowSampleRepository {
@GlowIndexPaging
List<StrnTermListOutDTO.StrnTerm> selectStrnTerm(StrnTermListInDTO inDto, PageInfo pageInfo);
int insertAppliSystNtfyPati(AppliSystNtfyPatiModel appliSystNtfyPatiModel);
}

View File

@@ -1,25 +0,0 @@
package io.shinhanlife.dap.sample.domain.service;
import io.shinhanlife.dap.sample.dto.AppliSystNtfyRgiInDTO;
import io.shinhanlife.dap.sample.dto.StrnTermListInDTO;
import io.shinhanlife.dap.sample.dto.StrnTermListOutDTO;
import io.shinhanlife.glow.PageInfo;
/**
* @package io.shinhanlife.dap.sample.domain.service
* @className GlowSampleService
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
public interface GlowSampleService {
StrnTermListOutDTO getStrnTerms(StrnTermListInDTO inDto, PageInfo pageInfo);
int insertAppliSystNtfyPati(AppliSystNtfyRgiInDTO inDto);
}

View File

@@ -1,55 +0,0 @@
package io.shinhanlife.dap.sample.domain.service.impl;
import io.shinhanlife.dap.sample.converter.SampleConverter;
import io.shinhanlife.dap.sample.domain.model.AppliSystNtfyPatiModel;
import io.shinhanlife.dap.sample.domain.repository.GlowSampleRepository;
import io.shinhanlife.dap.sample.domain.service.GlowSampleService;
import io.shinhanlife.dap.sample.dto.AppliSystNtfyRgiInDTO;
import io.shinhanlife.dap.sample.dto.StrnTermListInDTO;
import io.shinhanlife.dap.sample.dto.StrnTermListOutDTO;
import io.shinhanlife.glow.GlowLogger;
import io.shinhanlife.glow.GlowLogTarget;
import io.shinhanlife.glow.PageInfo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @package io.shinhanlife.dap.sample.domain.service.impl
* @className GlowSampleServiceImpl
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Service
@RequiredArgsConstructor
public class GlowSampleServiceImpl implements GlowSampleService {
@GlowLogTarget(GlowLogTarget.Target.FILE)
private final GlowLogger log;
private final GlowSampleRepository glowSampleRepository;
private final SampleConverter sampleConverter;
@Override
public StrnTermListOutDTO getStrnTerms(StrnTermListInDTO inDto, PageInfo pageInfo) {
List<StrnTermListOutDTO.StrnTerm> out = glowSampleRepository.selectStrnTerm(inDto, pageInfo);
return StrnTermListOutDTO.builder()
.strnTerms(out)
.pageInfo(pageInfo)
.build();
}
@Override
public int insertAppliSystNtfyPati(AppliSystNtfyRgiInDTO inDto) {
AppliSystNtfyPatiModel appliSystNtfyPatiModel = sampleConverter.convertDtoToModel(inDto);
return glowSampleRepository.insertAppliSystNtfyPati(appliSystNtfyPatiModel);
}
}

View File

@@ -1,41 +0,0 @@
package io.shinhanlife.dap.sample.dto;
import lombok.Getter;
import lombok.Setter;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.*;
/**
* @package io.shinhanlife.dap.sample.dto
* @className AppliSystNtfyRgiInDTO
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AppliSystNtfyRgiInDTO {
private String appliSystNtfyPatiId;
private String appliSystNtfyKdCd;
private String appliDutjCd;
private String appliDutjPrjcCd;
private String ntfyMsgCt;
private String ntfyOccDt;
private String ntfyCfmDt;
private String ntfyTrgtPrafNo;
private String shmsPmlYn;
private String ntfyCfmYn;
}

View File

@@ -1,31 +0,0 @@
package io.shinhanlife.dap.sample.dto;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* @package io.shinhanlife.dap.sample.dto
* @className StrnTermListInDTO
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class StrnTermListInDTO {
private String strnTermHanNm;
}

View File

@@ -1,46 +0,0 @@
package io.shinhanlife.dap.sample.dto;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import io.shinhanlife.glow.PageInfo;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* @package io.shinhanlife.dap.sample.dto
* @className StrnTermListOutDTO
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class StrnTermListOutDTO {
private List<StrnTerm> strnTerms;
private PageInfo pageInfo;
@Builder
@Getter
@Setter
public static class StrnTerm{
private String strnTermHanNm;
private String strnTermEngcAbrNm;
private String strnTermEngNm;
private String strnTermScrnTermNm;
}
}

View File

@@ -1,52 +0,0 @@
package io.shinhanlife.dap.sample.presentation;
import io.shinhanlife.dap.sample.presentation.io.AppliSystNtfyPatiRequest;
import io.shinhanlife.dap.sample.presentation.io.AppliSystNtfyPatiResponse;
import io.shinhanlife.dap.sample.presentation.io.StrnTermRequest;
import io.shinhanlife.dap.sample.presentation.io.StrnTermResponse;
import io.shinhanlife.dap.sample.usecase.GlowSampleUseCase;
import io.shinhanlife.glow.*;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @package io.shinhanlife.dap.sample.presentation
* @className GlowSampleController
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@RestController
@RequiredArgsConstructor
public class GlowSampleController {
@GlowLogTarget({GlowLogTarget.Target.CONSOLE, GlowLogTarget.Target.FILE})
private final GlowLogger log;
private final GlowSampleUseCase glowSampleUseCase;
@GlowControllerId(value = "selectStrnTerm")
@PostMapping("/selectStrnTerm")
public ResponseEntity<BaseResponse<StrnTermResponse>> getStrnTerms(@RequestBody StrnTermRequest request) {
return ResponseUtil.ok(glowSampleUseCase.getStrnTerms(request));
}
@GlowControllerId(value = "insertAp")
@PostMapping("/insertAppliSystNtfyPati")
public ResponseEntity<BaseResponse<AppliSystNtfyPatiResponse>> insertAppliSystNtfyPati(@RequestBody AppliSystNtfyPatiRequest request) {
return ResponseUtil.ok(glowSampleUseCase.insertAppliSystNtfyPati(request));
}
}

View File

@@ -1,34 +0,0 @@
package io.shinhanlife.dap.sample.presentation.io;
import lombok.*;
/**
* @package io.shinhanlife.dap.sample.presentation.io
* @className AppliSystNtfyPatiRequest
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class AppliSystNtfyPatiRequest {
private String appliSystNtfyPatiId;
private String appliSystNtfyKdCd;
private String appliDutjCd;
private String appliDutjPrjcCd;
private String ntfyMsgCt;
private String ntfyOccDt;
private String ntfyCfmDt;
private String ntfyTrgtPrafNo;
private String shmsPmlYn;
private String ntfyCfmYn;
}

View File

@@ -1,26 +0,0 @@
package io.shinhanlife.dap.sample.presentation.io;
import lombok.*;
/**
* @package io.shinhanlife.dap.sample.presentation.io
* @className AppliSystNtfyPatiResponse
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class AppliSystNtfyPatiResponse {
private String successYn;
private String msg;
}

View File

@@ -1,28 +0,0 @@
package io.shinhanlife.dap.sample.presentation.io;
import io.shinhanlife.glow.PageInfo;
import lombok.*;
/**
* @package io.shinhanlife.dap.sample.presentation.io
* @className StrnTermRequest
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class StrnTermRequest {
private PageInfo pageInfo;
private String strnTermHanNm;
}

View File

@@ -1,41 +0,0 @@
package io.shinhanlife.dap.sample.presentation.io;
import io.shinhanlife.glow.PageInfo;
import lombok.*;
import java.util.List;
/**
* @package io.shinhanlife.dap.sample.presentation.io
* @className StrnTermResponse
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class StrnTermResponse {
private List<StrnTerm> strnTerms;
private PageInfo pageInfo;
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class StrnTerm{
private String strnTermHanNm;
private String strnTermEngcAbrNm;
private String strnTermEngNm;
private String strnTermScrnTermNm;
}
}

View File

@@ -1,25 +0,0 @@
package io.shinhanlife.dap.sample.usecase;
import io.shinhanlife.dap.sample.presentation.io.AppliSystNtfyPatiRequest;
import io.shinhanlife.dap.sample.presentation.io.AppliSystNtfyPatiResponse;
import io.shinhanlife.dap.sample.presentation.io.StrnTermRequest;
import io.shinhanlife.dap.sample.presentation.io.StrnTermResponse;
/**
* @package io.shinhanlife.dap.sample.usecase
* @className GlowSampleUseCase
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
public interface GlowSampleUseCase {
StrnTermResponse getStrnTerms(StrnTermRequest req);
AppliSystNtfyPatiResponse insertAppliSystNtfyPati(AppliSystNtfyPatiRequest req);
}

View File

@@ -1,57 +0,0 @@
package io.shinhanlife.dap.sample.usecase.impl;
import io.shinhanlife.dap.sample.converter.SampleConverter;
import io.shinhanlife.dap.sample.domain.service.GlowSampleService;
import io.shinhanlife.dap.sample.dto.StrnTermListOutDTO;
import io.shinhanlife.dap.sample.presentation.io.AppliSystNtfyPatiRequest;
import io.shinhanlife.dap.sample.presentation.io.AppliSystNtfyPatiResponse;
import io.shinhanlife.dap.sample.presentation.io.StrnTermRequest;
import io.shinhanlife.dap.sample.presentation.io.StrnTermResponse;
import io.shinhanlife.dap.sample.usecase.GlowSampleUseCase;
import io.shinhanlife.glow.GlowAppServiceId;
import io.shinhanlife.glow.GlowLogTarget;
import io.shinhanlife.glow.GlowLogger;
import io.shinhanlife.glow.GlowServiceGroupId;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* @package io.shinhanlife.dap.sample.usecase.impl
* @className GlowSampleUseCaseImpl
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
@Service
@RequiredArgsConstructor
@GlowServiceGroupId(value = "GlowSample", description="GlowSampleUseCase")
public class GlowSampleUseCaseImpl implements GlowSampleUseCase {
@GlowLogTarget(GlowLogTarget.Target.FILE)
private final GlowLogger log;
private final GlowSampleService glowSampleService;
private final SampleConverter converter;
@GlowAppServiceId(value = "SAMPLE-DB-0001", description = "표준용어조회(DB)")
public StrnTermResponse getStrnTerms(StrnTermRequest req) {
StrnTermListOutDTO outDto = glowSampleService.getStrnTerms(converter.convertRequestToDto(req), req.getPageInfo());
return converter.convertDtoToResponse(outDto);
}
@GlowAppServiceId(value = "SAMPLE-DB-0002", description = "어플리케이션시스템알림내역등록")
public AppliSystNtfyPatiResponse insertAppliSystNtfyPati(AppliSystNtfyPatiRequest req) {
int rtn = glowSampleService.insertAppliSystNtfyPati(converter.convertAppliRequestToDto(req));
return AppliSystNtfyPatiResponse.builder()
.successYn(rtn > 0 ? "Y" : "N")
.msg(rtn > 0 ? "성공적으로 처리 되었습니다." : "처리중 오류발생!!")
.build();
}
}

View File

@@ -1,160 +0,0 @@
-- ============================================================
-- AX HUB 역할-Tool/지식 권한 관리 DDL
-- 대상 스키마 : 프로젝트 AP 스키마 (S_EIAM_TIS 는 읽기전용 참조)
-- 작성일 : 2026-06-25
-- ============================================================
-- ① Tool 마스터
CREATE TABLE AX_TOOL_MST (
TOOL_ID VARCHAR(20) NOT NULL, -- Tool ID (PK)
TOOL_NM VARCHAR(100) NOT NULL, -- Tool명
TOOL_DS VARCHAR(500), -- Tool설명
TOOL_TYPE_CD VARCHAR(20), -- Tool유형코드 (MCP / FUNC / EXT)
PUSE_YN VARCHAR(1) DEFAULT 'Y',-- 사용여부
SYST_RGI_DT DATE,
SYST_RGI_PRAF_NO VARCHAR(8),
SYST_RGI_OGNZ_NO VARCHAR(7),
SYST_RGI_SYST_CD VARCHAR(3),
SYST_RGI_PRGR_ID VARCHAR(100),
SYST_CHG_DT DATE,
SYST_CHG_PRAF_NO VARCHAR(8),
SYST_CHG_OGNZ_NO VARCHAR(7),
SYST_CHG_SYST_CD VARCHAR(3),
SYST_CHG_PRGR_ID VARCHAR(100),
CONSTRAINT PK_AX_TOOL_MST PRIMARY KEY (TOOL_ID)
);
COMMENT ON TABLE AX_TOOL_MST IS 'AX HUB Tool 마스터';
COMMENT ON COLUMN AX_TOOL_MST.TOOL_ID IS 'Tool ID (PK)';
COMMENT ON COLUMN AX_TOOL_MST.TOOL_NM IS 'Tool명';
COMMENT ON COLUMN AX_TOOL_MST.TOOL_DS IS 'Tool설명';
COMMENT ON COLUMN AX_TOOL_MST.TOOL_TYPE_CD IS 'Tool유형코드 (MCP/FUNC/EXT)';
COMMENT ON COLUMN AX_TOOL_MST.PUSE_YN IS '사용여부 (Y/N)';
-- ② 지식(Knowledge) 마스터
CREATE TABLE AX_KNWL_MST (
KNWL_ID VARCHAR(20) NOT NULL, -- 지식 ID (PK)
KNWL_NM VARCHAR(100) NOT NULL, -- 지식명
KNWL_DS VARCHAR(500), -- 지식설명
KNWL_TYPE_CD VARCHAR(20), -- 지식유형코드 (PRODUCT/MANUAL/LEGAL 등)
PUSE_YN VARCHAR(1) DEFAULT 'Y',
SYST_RGI_DT DATE,
SYST_RGI_PRAF_NO VARCHAR(8),
SYST_RGI_OGNZ_NO VARCHAR(7),
SYST_RGI_SYST_CD VARCHAR(3),
SYST_RGI_PRGR_ID VARCHAR(100),
SYST_CHG_DT DATE,
SYST_CHG_PRAF_NO VARCHAR(8),
SYST_CHG_OGNZ_NO VARCHAR(7),
SYST_CHG_SYST_CD VARCHAR(3),
SYST_CHG_PRGR_ID VARCHAR(100),
CONSTRAINT PK_AX_KNWL_MST PRIMARY KEY (KNWL_ID)
);
COMMENT ON TABLE AX_KNWL_MST IS 'AX HUB 지식 마스터';
COMMENT ON COLUMN AX_KNWL_MST.KNWL_ID IS '지식 ID (PK)';
COMMENT ON COLUMN AX_KNWL_MST.KNWL_NM IS '지식명';
COMMENT ON COLUMN AX_KNWL_MST.KNWL_DS IS '지식설명';
COMMENT ON COLUMN AX_KNWL_MST.KNWL_TYPE_CD IS '지식유형코드 (PRODUCT/MANUAL/LEGAL/REPORT)';
COMMENT ON COLUMN AX_KNWL_MST.PUSE_YN IS '사용여부 (Y/N)';
-- ③ 역할-Tool 권한 매핑
-- SYST_ID + ROLE_NO → S_EIAM_TIS.AA_ROLE 참조 (FK 미설정: 스키마 분리 환경)
CREATE TABLE AX_ROLE_TOOL_ATHR (
ROLE_TOOL_ATHR_ID VARCHAR(20) NOT NULL, -- 역할Tool권한ID (PK)
SYST_ID VARCHAR(10) NOT NULL, -- 시스템ID (EIAM 연동 키)
ROLE_NO VARCHAR(50) NOT NULL, -- 역할번호 (EIAM AA_ROLE.ROLE_NO)
TOOL_ID VARCHAR(20) NOT NULL, -- Tool ID
PUSE_YN VARCHAR(1) DEFAULT 'Y',
SYST_RGI_DT DATE,
SYST_RGI_PRAF_NO VARCHAR(8),
SYST_RGI_OGNZ_NO VARCHAR(7),
SYST_RGI_SYST_CD VARCHAR(3),
SYST_RGI_PRGR_ID VARCHAR(100),
SYST_CHG_DT DATE,
SYST_CHG_PRAF_NO VARCHAR(8),
SYST_CHG_OGNZ_NO VARCHAR(7),
SYST_CHG_SYST_CD VARCHAR(3),
SYST_CHG_PRGR_ID VARCHAR(100),
CONSTRAINT PK_AX_ROLE_TOOL_ATHR PRIMARY KEY (ROLE_TOOL_ATHR_ID)
);
-- (SYST_ID, ROLE_NO, TOOL_ID) 중복 방지
CREATE UNIQUE INDEX UK_AX_ROLE_TOOL_ATHR
ON AX_ROLE_TOOL_ATHR (SYST_ID, ROLE_NO, TOOL_ID);
COMMENT ON TABLE AX_ROLE_TOOL_ATHR IS '역할-Tool 권한 매핑';
COMMENT ON COLUMN AX_ROLE_TOOL_ATHR.ROLE_TOOL_ATHR_ID IS '역할Tool권한ID (PK)';
COMMENT ON COLUMN AX_ROLE_TOOL_ATHR.SYST_ID IS '시스템ID';
COMMENT ON COLUMN AX_ROLE_TOOL_ATHR.ROLE_NO IS '역할번호 (EIAM)';
COMMENT ON COLUMN AX_ROLE_TOOL_ATHR.TOOL_ID IS 'Tool ID';
COMMENT ON COLUMN AX_ROLE_TOOL_ATHR.PUSE_YN IS '사용여부';
-- ④ 역할-지식 권한 매핑
CREATE TABLE AX_ROLE_KNWL_ATHR (
ROLE_KNWL_ATHR_ID VARCHAR(20) NOT NULL, -- 역할지식권한ID (PK)
SYST_ID VARCHAR(10) NOT NULL,
ROLE_NO VARCHAR(50) NOT NULL,
KNWL_ID VARCHAR(20) NOT NULL,
PUSE_YN VARCHAR(1) DEFAULT 'Y',
SYST_RGI_DT DATE,
SYST_RGI_PRAF_NO VARCHAR(8),
SYST_RGI_OGNZ_NO VARCHAR(7),
SYST_RGI_SYST_CD VARCHAR(3),
SYST_RGI_PRGR_ID VARCHAR(100),
SYST_CHG_DT DATE,
SYST_CHG_PRAF_NO VARCHAR(8),
SYST_CHG_OGNZ_NO VARCHAR(7),
SYST_CHG_SYST_CD VARCHAR(3),
SYST_CHG_PRGR_ID VARCHAR(100),
CONSTRAINT PK_AX_ROLE_KNWL_ATHR PRIMARY KEY (ROLE_KNWL_ATHR_ID)
);
CREATE UNIQUE INDEX UK_AX_ROLE_KNWL_ATHR
ON AX_ROLE_KNWL_ATHR (SYST_ID, ROLE_NO, KNWL_ID);
COMMENT ON TABLE AX_ROLE_KNWL_ATHR IS '역할-지식 권한 매핑';
COMMENT ON COLUMN AX_ROLE_KNWL_ATHR.ROLE_KNWL_ATHR_ID IS '역할지식권한ID (PK)';
COMMENT ON COLUMN AX_ROLE_KNWL_ATHR.SYST_ID IS '시스템ID';
COMMENT ON COLUMN AX_ROLE_KNWL_ATHR.ROLE_NO IS '역할번호 (EIAM)';
COMMENT ON COLUMN AX_ROLE_KNWL_ATHR.KNWL_ID IS '지식ID';
COMMENT ON COLUMN AX_ROLE_KNWL_ATHR.PUSE_YN IS '사용여부';
-- ============================================================
-- 샘플 데이터 (개발용)
-- ============================================================
INSERT INTO AX_TOOL_MST
(TOOL_ID, TOOL_NM, TOOL_DS, TOOL_TYPE_CD, PUSE_YN,
SYST_RGI_DT, SYST_RGI_PRAF_NO, SYST_RGI_OGNZ_NO, SYST_RGI_SYST_CD, SYST_RGI_PRGR_ID,
SYST_CHG_DT, SYST_CHG_PRAF_NO, SYST_CHG_OGNZ_NO, SYST_CHG_SYST_CD, SYST_CHG_PRGR_ID)
VALUES
('TOOL_A', '지식 검색', '지식베이스 내 문서 의미 기반 검색', 'MCP', 'Y',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100'),
('TOOL_B', 'DB 조회', '사내 데이터베이스 직접 조회 (GLOW)', 'MCP', 'Y',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100'),
('TOOL_C', '문서 생성', 'AI 기반 문서 초안 자동 생성', 'FUNC', 'Y',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100');
INSERT INTO AX_KNWL_MST
(KNWL_ID, KNWL_NM, KNWL_DS, KNWL_TYPE_CD, PUSE_YN,
SYST_RGI_DT, SYST_RGI_PRAF_NO, SYST_RGI_OGNZ_NO, SYST_RGI_SYST_CD, SYST_RGI_PRGR_ID,
SYST_CHG_DT, SYST_CHG_PRAF_NO, SYST_CHG_OGNZ_NO, SYST_CHG_SYST_CD, SYST_CHG_PRGR_ID)
VALUES
('KNWL_Q', '상품기초서류', '보험 상품 기초서류 약 8만건', 'PRODUCT', 'Y',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100'),
('KNWL_W', '업무매뉴얼', '내부 업무 매뉴얼 및 프로세스 가이드', 'MANUAL', 'Y',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100'),
('KNWL_E', '법률/규정', '보험업법 및 금융당국 규정', 'LEGAL', 'Y',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100',
CURRENT_DATE, 'SYSTEM', 'SYSTEM', 'AXH', 'SOATM0100');
COMMIT;

View File

@@ -1,19 +0,0 @@
# Dev 환경 전용 설정 (개발 서버 DB 연결 정보 등)
# spring.datasource.url=jdbc:oracle:thin:@//dev-db-host:1521/XEPDB1
# spring.datasource.driverClassName=oracle.jdbc.OracleDriver
# spring.datasource.username=dev_user
# spring.datasource.password=dev_password
# Render 클라우드 환경 전용 Fallback 라우팅
mcp.gateway.fallback.routes.sms=https://axhub-tool-sms.onrender.com
mcp.gateway.fallback.routes.email=https://axhub-tool-email.onrender.com
mcp.gateway.fallback.default-url=https://axhub-tool-other.onrender.com
mcp.gateway.fallback.routes.payment=https://axhub-tool-payment.onrender.com
mcp.gateway.fallback.routes.hr=https://axhub-tool-hr.onrender.com
# --- 신한라이프 EAI/MCI 연계 IP 정보 (개발 환경) ---
shinhan.integration.envrTypeCd=D
shinhan.integration.eai.url=http://10.176.32.181
shinhan.integration.internalMci.url=http://10.176.32.173
shinhan.integration.bancaMci.url=http://10.176.32.117
shinhan.integration.externalMci.url=http://10.176.32.176

View File

@@ -0,0 +1,29 @@
# Dev 환경 전용 설정 (개발 서버 DB 연결 정보 등)
# spring:
# datasource:
# url: jdbc:oracle:thin:@//dev-db-host:1521/XEPDB1
# driverClassName: oracle.jdbc.OracleDriver
# username: dev_user
# password: dev_password
# OCI (Oracle Cloud) 개발 환경 라우팅
mcp:
gateway:
fallback:
default-url: http://tool-oth:8084
routes:
sms: http://tool-sms:8082
hr: http://tool-oth:8084
# --- 신한라이프 EAI/MCI 연계 IP 정보 (개발 환경) ---
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

View File

@@ -1,27 +0,0 @@
# Local ?섍꼍 ?꾩슜 ?ㅼ젙 (H2 硫붾え由?DB ??
spring.datasource.url=jdbc:p6spy:h2:mem:testdb;DB_CLOSE_DELAY=-1;
spring.datasource.driverClassName=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.username=sa
spring.datasource.password=password
spring.h2.console.enabled=true
# EIMS 동적 라우팅 접속 정보
eims.http.url=http://localhost:${server.port}/api/gateway
eims.tcp.host=127.0.0.1
eims.tcp.port=8090
eims.tcp.timeout=5000
eims.jsp.form.url=http://localhost:${server.port}/mock/jsp-form
eims.jsp.json.url=http://localhost:${server.port}/mock/jsp-json
eims.mci.url=http://localhost:${server.port}/api/mock/esb/api
eims.mcistring.url=http://localhost:${server.port}/api/mock/esb/string
# Gateway/Tool URLs
mcp.adapter.url=http://localhost:${server.port}/rpc/v1/execute
mcp.gateway.url=http://localhost:${server.port}/mcp/api/v1
mcp.tool.host=localhost
# Disable Kafka
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration
# Suppress Kafka Connection Logs
logging.level.org.apache.kafka=ERROR

View File

@@ -0,0 +1,48 @@
# 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
autoconfigure:
exclude: org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration
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:
adapter:
url: http://localhost:${server.port}/rpc/v1/execute
gateway:
url: http://localhost:${server.port}/mcp/api/v1
fallback:
default-url: http://localhost:8084
routes:
sms: http://localhost:8082
email: http://localhost:8083
payment: http://localhost:8085
hr: http://localhost:8084
tool:
host: localhost
logging:
level:
org.apache.kafka: ERROR

View File

@@ -1,12 +0,0 @@
# Prod 환경 전용 설정 (운영 서버 DB 연결 정보 등)
# spring.datasource.url=jdbc:oracle:thin:@//prod-db-host:1521/PROD
# spring.datasource.driverClassName=oracle.jdbc.OracleDriver
# spring.datasource.username=prod_user
# spring.datasource.password=prod_password
# --- 신한라이프 EAI/MCI 연계 IP 정보 (운영 환경) ---
shinhan.integration.envrTypeCd=R
shinhan.integration.eai.url=http://10.172.32.181
shinhan.integration.internalMci.url=http://10.172.32.173
shinhan.integration.bancaMci.url=http://10.172.32.117
shinhan.integration.externalMci.url=http://10.172.32.177

View File

@@ -0,0 +1,20 @@
# Prod 환경 전용 설정 (운영 서버 DB 연결 정보 등)
# spring:
# datasource:
# url: jdbc:oracle:thin:@//prod-db-host:1521/PROD
# driverClassName: oracle.jdbc.OracleDriver
# username: prod_user
# password: prod_password
# --- 신한라이프 EAI/MCI 연계 IP 정보 (운영 환경) ---
shinhan:
integration:
envrTypeCd: R
eai:
url: http://10.172.32.181
internalMci:
url: http://10.172.32.173
bancaMci:
url: http://10.172.32.117
externalMci:
url: http://10.172.32.177

View File

@@ -1,52 +0,0 @@
spring.application.name=dap-admin
# Gateway Fallback Routing (For unregistered tools)
mcp.gateway.fallback.routes.sms=http://tool-sms:8082
mcp.gateway.fallback.routes.email=http://tool-email:8083
mcp.gateway.fallback.default-url=http://tool-other:8084
mcp.gateway.fallback.routes.payment=http://tool-payment:8085
mcp.gateway.fallback.routes.hr=http://tool-hr:8086
server.port=8081
server.http2.enabled=true
# 기본적으로 local 환경을 실행
spring.profiles.active=local
# MyBatis 공통 설정
mybatis.mapper-locations=classpath:mapper/**/*.xml
mybatis.type-aliases-package=
mybatis.configuration.map-underscore-to-camel-case=true
spring.sql.init.mode=always
logging.level.com.corundumstudio.socketio=DEBUG
# 우아한 종료 (Graceful Shutdown) 설정
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=20s
# Suppress Kafka Connection Logs
logging.level.org.apache.kafka=ERROR
# Spring AI MCP Server Settings
# spring.ai.mcp.server.protocol=STATELESS
mcp.agent-claims-required=false
mcp.trusted-claims-required=false
mcp.write-approval-required=false
mcp.default-page-size=100
mcp.max-page-size=500
mcp.max-pages-per-call=3
mcp.max-stream-bytes=1048576
mcp.max-response-bytes-from-tool=5242880
mcp.tool-registry-sync-interval-millis=5000
# API 보안 키 설정
mcp.security.tenant-domains.TESTER-DEV=ALL
mcp.security.tenant-domains.system=ALL
# Spring AI OpenAI / Gemini API Config
spring.ai.openai.api-key=AQ.Ab8RN6KFZggsQf8iooY1v_3h3vp2TIjiYB54dV4Yay3vVKEMtg
spring.ai.openai.base-url=https://generativelanguage.googleapis.com/v1beta/openai/
spring.ai.openai.chat.options.model=gemini-flash-latest
spring.ai.openai.chat.options.temperature=0.3

View File

@@ -0,0 +1,56 @@
spring:
application:
name: dap-admin
profiles:
active: local
sql:
init:
mode: always
lifecycle:
timeout-per-shutdown-phase: 20s
ai:
openai:
api-key: AQ.Ab8RN6KFZggsQf8iooY1v_3h3vp2TIjiYB54dV4Yay3vVKEMtg
base-url: https://generativelanguage.googleapis.com/v1beta/openai/
chat:
options:
model: gemini-flash-latest
temperature: 0.3
server:
port: 8081
http2:
enabled: true
shutdown: graceful
mcp:
gateway:
fallback:
default-url: http://tool-oth:8084
routes:
sms: http://tool-sms:8082
hr: http://tool-oth:8084
agent-claims-required: false
trusted-claims-required: false
write-approval-required: false
default-page-size: 100
max-page-size: 500
max-pages-per-call: 3
max-stream-bytes: 1048576
max-response-bytes-from-tool: 5242880
tool-registry-sync-interval-millis: 5000
security:
tenant-domains:
TESTER-DEV: ALL
system: ALL
mybatis:
mapper-locations: classpath:mapper/**/*.xml
type-aliases-package: ""
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.corundumstudio.socketio: DEBUG
org.apache.kafka: ERROR

View File

@@ -1,8 +1,8 @@
# SLF4J ??? ?? (logback?? ???)
# SLF4J ??? ?? (logback?? ???)
appender=com.p6spy.engine.spy.appender.Slf4JLogger
# ??? ??? ??? ?? (???? ?? ??)
logMessageFormat=io.shinhanlife.dap.common.config.P6SpySqlFormatter
logMessageFormat=io.shinhanlife.dap.lib.config.P6SpySqlFormatter
# ?? ?? 0ms?? (connection ?? ??) ???
filter.executionThreshold=1
filter.executionThreshold=1

View File

@@ -380,6 +380,7 @@
<a href="/admin/scaffold.html" style="color:#ffffff;" class="font-semibold">Scaffold</a>
<a href="/catalog.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Catalog</a>
<a href="/playground.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Playground</a>
<a href="/chat.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Chat</a>
</nav>
</div>
<div class="flex items-center">
@@ -462,13 +463,13 @@
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">Domain Category</label>
<input type="text" class="form-control" name="categoryKey" pattern="^[a-z0-9][a-z0-9_-]*$" list="groupList" placeholder="e.g. common, hr, payment" oninput="this.value = this.value.toLowerCase()" required>
<input type="text" class="form-control" name="categoryKey" pattern="^[a-z0-9]{3}$" maxlength="3" minlength="3" list="groupList" placeholder="e.g. cmm, smp, pay" oninput="this.value = this.value.toLowerCase()" required>
<datalist id="groupList">
<option value="sample">sample</option>
<option value="common">common</option>
<option value="customer">customer</option>
<option value="contract">contract</option>
<option value="claim">claim</option>
<option value="smp">smp</option>
<option value="cmm">cmm</option>
<option value="cus">cus</option>
<option value="cnt">cnt</option>
<option value="clm">clm</option>
</datalist>
</div>
<div class="col-md-6 mt-3 mt-md-0">
@@ -477,7 +478,6 @@
<option value="HTTP">HTTP (REST)</option>
<option value="TCP">TCP (Socket)</option>
<option value="MCI">MCI (Legacy)</option>
<option value="EAI">EAI (Internal)</option>
</select>
</div>
</div>
@@ -486,10 +486,8 @@
<div class="col-md-6">
<label class="form-label">Target Module</label>
<select class="form-select" name="moduleName" id="targetModuleSelect" required>
<option value="dap-tool-other">dap-tool-other</option>
<option value="dap-tool-email">dap-tool-email</option>
<option value="dap-tool-oth">dap-tool-oth</option>
<option value="dap-tool-hr">dap-tool-hr</option>
<option value="dap-tool-payment">dap-tool-payment</option>
<option value="dap-tool-sms">dap-tool-sms</option>
</select>
<div class="input-hint">Destination Pod directory.</div>
@@ -497,12 +495,19 @@
<div class="col-md-6 mt-3 mt-md-0">
<label class="form-label">Gateway Registration</label>
<select class="form-select" name="register">
<option value="true">True (Public)</option>
<option value="false">False (Private)</option>
<option value="false" selected>False (Private)</option>
</select>
<div class="input-hint">Controls dynamic routing exposure.</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6">
<label class="form-label">Target System Code (MCI Only)</label>
<input type="text" class="form-control" name="clientSystemCode" pattern="^[a-zA-Z]{4}$" maxlength="4" minlength="4" placeholder="e.g. cfpa" oninput="this.value = this.value.toLowerCase()">
<div class="input-hint">Required for MCI Protocol (4 letters).</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6">
@@ -579,7 +584,7 @@
</div>
<div class="mb-3">
<label class="form-label">Category</label>
<input type="text" class="form-control" id="editCategoryKey" name="categoryKey" pattern="^[a-z0-9][a-z0-9_-]*$" oninput="this.value = this.value.toLowerCase()">
<input type="text" class="form-control" id="editCategoryKey" name="categoryKey" pattern="^[a-z0-9]{3}$" maxlength="3" minlength="3" oninput="this.value = this.value.toLowerCase()">
</div>
<div class="mb-3">
<label class="form-label">Description</label>
@@ -658,7 +663,7 @@
.then(modules => {
const select = document.getElementById('targetModuleSelect');
if (modules && modules.length > 0) {
select.innerHTML = modules.map(m => `<option value="${m}" ${m === 'dap-tool-other' ? 'selected' : ''}>${m}</option>`).join('');
select.innerHTML = modules.map(m => `<option value="${m}" ${m === 'dap-tool-oth' ? 'selected' : ''}>${m}</option>`).join('');
}
})
.catch(err => console.error('Failed to load modules:', err));

View File

@@ -69,6 +69,7 @@
<a href="/admin/scaffold.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Scaffold</a>
<a href="/catalog.html" style="color:#ffffff;" class="font-semibold">Catalog</a>
<a href="/playground.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Playground</a>
<a href="/chat.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Chat</a>
</nav>
</div>
<div class="flex items-center">
@@ -79,12 +80,24 @@
<main class="max-w-6xl mx-auto px-6 py-10">
<div class="flex flex-col md:flex-row justify-between items-start md:items-end mb-8">
<div class="max-w-2xl">
<h1 class="text-3xl font-bold tracking-tight mb-2" style="color:#f4f4f5;">Registry Catalog</h1>
<p class="text-sm" style="color:#a1a1aa;">Currently integrated AI function tools across all modules. View parameters, descriptions, and integration types.</p>
<div class="max-w-2xl w-full">
<div class="flex items-center space-x-3 mb-2">
<h1 class="text-3xl font-bold tracking-tight" style="color:#f4f4f5;">Registry Catalog</h1>
<button onclick="openGuideModal()" class="w-6 h-6 rounded-full flex items-center justify-center text-sm font-bold bg-zinc-800 text-zinc-400 hover:bg-zinc-700 hover:text-white transition-colors" title="Connection Guide">?</button>
</div>
<p class="text-sm mb-6" style="color:#a1a1aa;">Currently integrated AI function tools across all modules. View parameters, descriptions, and integration types.</p>
<div class="relative max-w-md w-full">
<input type="text" id="searchInput" placeholder="Search tools by name, description, or category..." class="w-full bg-zinc-900 border border-zinc-700 rounded-lg px-4 py-2.5 text-sm text-zinc-200 placeholder-zinc-500 focus:outline-none focus:border-blue-500 transition-colors">
<svg class="w-4 h-4 absolute right-3 top-3 text-zinc-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
</div>
</div>
<div class="mt-4 md:mt-0">
<button id="copyMdBtn" class="btn-primary flex items-center">
<div class="mt-4 md:mt-0 flex space-x-3 w-full md:w-auto">
<button id="copyJsonBtn" class="btn-primary flex items-center justify-center flex-1 md:flex-none" style="background: linear-gradient(135deg, #10b981, #059669); box-shadow: 0 0 20px rgba(16, 185, 129, 0.2);">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg>
<span id="copyJsonText">Copy Client Config</span>
</button>
<button id="copyMdBtn" class="btn-primary flex items-center justify-center flex-1 md:flex-none">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"></path></svg>
<span id="copyBtnText">Copy Markdown Docs</span>
</button>
@@ -99,13 +112,52 @@
</div>
</main>
<!-- Guide Modal -->
<div id="guideModal" class="fixed inset-0 z-[100] hidden flex items-center justify-center">
<div class="absolute inset-0 bg-black/60 backdrop-blur-sm" onclick="closeGuideModal()"></div>
<div class="relative bg-zinc-900 border border-zinc-700 rounded-xl shadow-2xl w-full max-w-2xl mx-4 overflow-hidden">
<div class="p-5 border-b border-zinc-800 flex justify-between items-center bg-zinc-900/50">
<h3 class="text-lg font-semibold text-zinc-100 flex items-center">
<svg class="w-5 h-5 mr-2 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
AI Agent Connection Guide
</h3>
<button onclick="closeGuideModal()" class="text-zinc-400 hover:text-white transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<div class="p-6 space-y-6 max-h-[70vh] overflow-y-auto text-sm text-zinc-300">
<div>
<h4 class="font-semibold text-blue-400 mb-3 flex items-center"><span class="w-5 h-5 rounded bg-blue-500/20 flex items-center justify-center mr-2 text-xs">1</span> Cursor IDE 연동</h4>
<p class="mb-2">1. Cursor 설정 창 열기 (<kbd class="px-1.5 py-0.5 bg-zinc-800 border border-zinc-700 rounded text-xs text-zinc-300">Cmd/Ctrl + ,</kbd>)</p>
<p class="mb-2">2. 사이드바에서 <strong>Features</strong> &gt; <strong>MCP</strong> 메뉴로 이동</p>
<p>3. <strong>+ Add New MCP Server</strong>를 클릭하고, 툴 카테고리의 [Copy SSE URL] 버튼으로 복사한 주소를 `SSE` 타입으로 붙여넣기 하세요.</p>
</div>
<div class="border-t border-zinc-800 pt-6">
<h4 class="font-semibold text-orange-400 mb-3 flex items-center"><span class="w-5 h-5 rounded bg-orange-500/20 flex items-center justify-center mr-2 text-xs">2</span> Claude Desktop 연동</h4>
<p class="mb-2">1. 아래 설정 파일을 텍스트 에디터로 엽니다:</p>
<div class="bg-black/40 p-3 rounded-lg border border-zinc-800/80 font-mono text-xs mb-3 text-zinc-400">
<div class="mb-1"><strong>Mac:</strong> ~/Library/Application Support/Claude/claude_desktop_config.json</div>
<div><strong>Win:</strong> %APPDATA%\Claude\claude_desktop_config.json</div>
</div>
<p>2. 화면 우측 상단의 <strong>[Copy Client Config]</strong> 버튼을 누른 후, 복사된 JSON 코드를 이 파일 안에 그대로 붙여넣습니다.</p>
</div>
<div class="border-t border-zinc-800 pt-6">
<h4 class="font-semibold text-emerald-400 mb-3 flex items-center"><span class="w-5 h-5 rounded bg-emerald-500/20 flex items-center justify-center mr-2 text-xs">3</span> 커스텀 Python/Node Agent 연동</h4>
<p class="mb-2">1. MCP 공식 SDK를 설치합니다. <code class="text-xs bg-zinc-800 px-1.5 py-0.5 rounded text-zinc-300 border border-zinc-700">npm i @modelcontextprotocol/sdk</code></p>
<p>2. SSE Client Transport를 사용하여 복사한 카테고리별 URL로 연결합니다.</p>
</div>
</div>
</div>
</div>
<script>
let allTools = []; // 글로벌 변수로 툴 데이터 보관
async function fetchTools() {
try {
const response = await fetch('/mcp/api/v1/tools/list');
const data = await response.json();
const tools = data.result?.tools || [];
renderCatalog(tools);
allTools = data.result?.tools || [];
renderCatalog(allTools);
} catch (err) {
document.getElementById('catalogContainer').innerHTML = `
<div style="padding:16px; border:1px solid #7f1d1d; background:rgba(127,29,29,0.15); color:#fca5a5; font-size:14px; border-radius:8px;">
@@ -134,7 +186,12 @@
header.style.cssText = 'margin-bottom:16px; padding-bottom:8px; border-bottom:1px solid #27272a; display:flex; align-items:baseline; justify-content:space-between;';
header.innerHTML = `
<h2 style="font-size:20px; font-weight:600; color:#f4f4f5; text-transform:capitalize;">${group}</h2>
<span class="geist-mono" style="font-size:12px; color:#71717a;">${grouped[group].length} items</span>
<div style="display:flex; align-items:center; gap:8px;">
<span class="geist-mono" style="font-size:12px; color:#71717a;">${grouped[group].length} items</span>
<button onclick="copySseUrl('${group.toLowerCase()}', this)" class="hover:text-blue-400 text-zinc-500 transition-colors p-1.5 rounded hover:bg-zinc-800/50" title="Copy SSE URL">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path></svg>
</button>
</div>
`;
section.appendChild(header);
const grid = document.createElement('div');
@@ -227,6 +284,60 @@
}
});
document.getElementById('searchInput').addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
const filtered = allTools.filter(t =>
(t.name && t.name.toLowerCase().includes(query)) ||
(t.displayName && t.displayName.toLowerCase().includes(query)) ||
(t.description && t.description.toLowerCase().includes(query)) ||
(t.categoryKey && t.categoryKey.toLowerCase().includes(query))
);
renderCatalog(filtered);
});
function copySseUrl(category, btnElement) {
const url = `${window.location.origin}/mcp/sse/${category}`;
navigator.clipboard.writeText(url).then(() => {
const originalSvg = btnElement.innerHTML;
btnElement.innerHTML = `<svg class="w-4 h-4 text-emerald-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>`;
setTimeout(() => { btnElement.innerHTML = originalSvg; }, 2000);
}).catch(() => alert('Failed to copy.'));
}
function openGuideModal() {
document.getElementById('guideModal').classList.remove('hidden');
}
function closeGuideModal() {
document.getElementById('guideModal').classList.add('hidden');
}
document.getElementById('copyJsonBtn').addEventListener('click', () => {
const groups = [...new Set(allTools.map(t => t.categoryKey || 'Others'))];
const config = { mcpServers: {} };
groups.forEach(g => {
const safeKey = g.toLowerCase().replace(/[^a-z0-9]/g, '-');
config.mcpServers[`axhub-${safeKey}`] = {
"command": "node",
"args": [],
"env": {},
"type": "sse",
"url": `${window.location.origin}/mcp/sse/${g.toLowerCase()}`
};
// Note: Claude desktop expects command/args for stdio, but for SSE it usually accepts just url/type or requires specialized settings.
// Adjusted to basic config.
delete config.mcpServers[`axhub-${safeKey}`].command;
delete config.mcpServers[`axhub-${safeKey}`].args;
delete config.mcpServers[`axhub-${safeKey}`].env;
});
const textSpan = document.getElementById('copyJsonText');
const originalText = textSpan.textContent;
navigator.clipboard.writeText(JSON.stringify(config, null, 2)).then(() => {
textSpan.textContent = 'Copied JSON!';
setTimeout(() => { textSpan.textContent = originalText; }, 2000);
}).catch(() => alert('Failed to copy JSON.'));
});
document.addEventListener('DOMContentLoaded', fetchTools);
</script>
</body>

View File

@@ -173,6 +173,10 @@
.replace(/'/g, "&#039;");
}
function removeMarkdownEmphasis(text) {
return text.replace(/\*\*/g, '');
}
async function sendMessage() {
const text = chatInput.value.trim();
if (!text || isLoading) return;
@@ -205,6 +209,7 @@
// 5. 스트림 읽기
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let responseText = '';
while (true) {
const { done, value } = await reader.read();
@@ -215,7 +220,8 @@
for (const line of lines) {
if (line.startsWith('data:')) {
const data = line.substring(5);
textContainer.innerHTML += escapeHtml(data);
responseText += data;
textContainer.textContent = removeMarkdownEmphasis(responseText);
scrollToBottom();
}
}

View File

@@ -100,6 +100,7 @@
<a href="/admin/scaffold.html" class="text-zinc-400 hover:text-white transition-colors">Scaffold</a>
<a href="/catalog.html" class="text-zinc-400 hover:text-white transition-colors">Catalog</a>
<a href="/playground.html" class="text-zinc-400 hover:text-white transition-colors">Playground</a>
<a href="/chat.html" class="text-zinc-400 hover:text-white transition-colors">Chat</a>
</nav>
</div>
<div class="flex items-center">
@@ -121,7 +122,7 @@
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 w-full max-w-5xl">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 w-full max-w-6xl">
<!-- Scaffold Card -->
<a href="/admin/scaffold.html" class="card-panel p-8 group block">
<div class="w-12 h-12 icon-box rounded-xl flex items-center justify-center mb-6 text-zinc-300">
@@ -163,6 +164,20 @@
Enter Playground <svg class="w-3 h-3 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path></svg>
</div>
</a>
<!-- Chat Card -->
<a href="/chat.html" class="card-panel p-8 group block">
<div class="w-12 h-12 icon-box rounded-xl flex items-center justify-center mb-6 text-zinc-300">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"></path></svg>
</div>
<h2 class="text-lg font-semibold text-zinc-100 mb-3">AI Assistant</h2>
<p class="text-[13px] text-zinc-400 leading-relaxed">
Interact with the MCP tool ecosystem through a natural language interface. Test your LLM routing logic in a premium chat UI.
</p>
<div class="mt-6 flex items-center text-[12px] font-semibold text-zinc-300 group-hover:text-blue-400 group-hover:translate-x-1 transition-all">
Start Chatting <svg class="w-3 h-3 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path></svg>
</div>
</a>
</div>
</main>
</body>

View File

@@ -111,6 +111,7 @@
<a href="/admin/scaffold.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Scaffold</a>
<a href="/catalog.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Catalog</a>
<a href="/playground.html" style="color:#ffffff;" class="font-semibold">Playground</a>
<a href="/chat.html" style="color:#a1a1aa;" class="hover:text-white transition-colors">Chat</a>
</nav>
</div>
<div class="flex items-center space-x-3">

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg;
* @package io.shinhanlife.dap.mcg
* @className DapGatewayApplicationTests
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -7,6 +7,7 @@ import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import io.shinhanlife.dap.mcg.sync.DynamicMcpController;
import org.junit.jupiter.api.Test;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

View File

@@ -5,7 +5,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.spec.McpSchema;
import io.shinhanlife.dap.biz.mcp.gateway.dto.ToolMetadata;
import io.shinhanlife.dap.mcg.dto.ToolMetadata;
import io.shinhanlife.dap.mcg.sync.RegistryMcpToolSpecificationFactory;
import org.junit.jupiter.api.Test;
class RegistryMcpToolSpecificationFactoryTest {

View File

@@ -5,13 +5,13 @@ package io.shinhanlife.dap.mcg.tool.large;
* @package io.shinhanlife.dap.mcg.tool.large
* @className PaginationRequestValidatorTest
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,39 +0,0 @@
package io.shinhanlife.dap.common.integration.mci.component;
import io.shinhanlife.glow.communication.dto.Transfer;
import io.shinhanlife.glow.communication.module.mci.component.GlowMciComponent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 신한라이프 내부 Glow 표준 컴포넌트 어댑터
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class AxhubMciComponent {
@SuppressWarnings("rawtypes")
private final GlowMciComponent mci;
@SuppressWarnings("unchecked")
public <O, I> Transfer<O> callTo(String itrfName, String rcvSvcId, I inputDto, Class<O> resBodyClass) {
log.info("[AxhubMciComponent] MCI 호출 준비 - 인터페이스: {}, 수신서비스: {}", itrfName, rcvSvcId);
// Header 세팅 로직 생략 (Mock)
Transfer<Object> request = Transfer.builder()
.body(inputDto)
.resBodyClass((Class<Object>) (Class<?>) resBodyClass)
.build();
return syncMci(request);
}
@SuppressWarnings("unchecked")
private <O> Transfer<O> syncMci(Transfer<Object> request) {
log.info("[AxhubMciComponent] GlowMciComponent.sync() 호출");
return (Transfer<O>) mci.sync(request);
}
}

View File

@@ -1,19 +0,0 @@
package io.shinhanlife.dap.common.integration.mci.config;
import io.shinhanlife.glow.communication.module.mci.component.GlowMciComponent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* TODO: 실제 Glow Framework 의존성이 추가되어 io.shinhanlife.glow 패키지가
* ComponentScan에 잡히게 되면 이 설정 클래스는 삭제하세요.
*/
@Configuration
public class GlowMockConfig {
@Bean
@SuppressWarnings("rawtypes")
public GlowMciComponent glowMciComponent() {
return new GlowMciComponent();
}
}

View File

@@ -1,481 +0,0 @@
package io.shinhanlife.dap.common.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.Scanner;
/**
* MCP Tool 코드를 자동 생성(Scaffolding)하는 유틸리티 클래스
*
* [실행 방법]
* 방법 1. IDE(IntelliJ 등)에서 직접 실행 (대화형 모드 추천 ⭐)
* - 이 클래스(ToolScaffolder.java)를 열고 main 메서드를 직접 실행(Run)합니다.
* - 콘솔 창에 뜨는 질문에 차례대로 값을 입력하기만 하면 파일이 생성됩니다.
*
* 방법 2. 커맨드라인(터미널)에서 실행 (명령어 기반)
* - 컴파일: javac -encoding UTF-8 dap-tool-core/src/main/java/io/shinhanlife/dap/mcc/util/ToolScaffolder.java
* - 실행: java -cp dap-tool-core/src/main/java io.shinhanlife.dap.mcc.util.ToolScaffolder [이름] [ID] "[설명]" "[그룹]" "[통신방식]" "[모듈명]"
*/
/**
* @package io.shinhanlife.dap.mcc.util
* @className ToolScaffolder
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
*
* </pre>
*/
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-tool-other): ");
if (moduleName.trim().isEmpty()) {
moduleName = "dap-tool-other";
}
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);
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) throws IOException {
baseName = toPascalCase(baseName);
String envSourceDir = System.getenv("AXHUB_SOURCE_DIR");
Path rootDir = envSourceDir != null ? Paths.get(envSourceDir) : Paths.get(".");
Path serviceDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "service"));
Path dtoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "dto"));
Path legacyDtoDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "legacy"));
Path converterDir = rootDir.resolve(Paths.get(moduleName, BASE_PACKAGE_PATH, "converter"));
Files.createDirectories(serviceDir);
Files.createDirectories(dtoDir);
Files.createDirectories(legacyDtoDir);
Files.createDirectories(converterDir);
StringBuilder log = new StringBuilder();
// Generate Req DTO
String reqContent = """
package %s.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import %s.annotation.McpParameter;
import lombok.Data;
/**
* @package %s.dto
* @className %sReq
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 최초생성
*
* </pre>
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class %sReq {
@McpParameter(description = "수신자 전화번호", required = true)
private String phoneNumber;
@McpParameter(description = "전송할 메시지 내용", required = true)
private String message;
}
""".formatted(BASE_PACKAGE, BASE_PACKAGE, BASE_PACKAGE, baseName, author, createDate, createDate, author, baseName);
Files.writeString(dtoDir.resolve(baseName + "Req.java"), reqContent);
// Generate Res DTO
String resContent = """
package %s.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* @package %s.dto
* @className %sRes
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 최초생성
*
* </pre>
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class %sRes {
private String status;
private String message;
// TODO: Add response fields here
}
""".formatted(BASE_PACKAGE, BASE_PACKAGE, baseName, author, createDate, createDate, author, baseName);
Files.writeString(dtoDir.resolve(baseName + "Res.java"), resContent);
String toolName = baseName.isEmpty() ? baseName : Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1);
boolean isMci = "MCI".equalsIgnoreCase(routingType);
String serviceContent;
if (isMci) {
serviceContent = """
package %s.service;
import %s.annotation.McpFunction;
import %s.annotation.McpTool;
import %s.dto.%sReq;
import %s.dto.%sRes;
import io.shinhanlife.dap.common.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 org.mapstruct.factory.Mappers;
import %s.converter.%sLegacyConverter;
import %s.legacy.%sLegacyReq;
/**
* @package %s.service
* @className %sService
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 최초생성
*
* </pre>
*/
@Slf4j
@Service
@RequiredArgsConstructor
@McpTool(
routingType = "%s",
categoryKey = "%s"
)
public class %sService {
private final AxhubMciComponent mci;
private final %sLegacyConverter converter = Mappers.getMapper(%sLegacyConverter.class);
@McpFunction(
displayName = "%s 툴",
name = "%s",
description = "%s",
prompt = "%s",
mappingId = "%s",
register = %s,
requiresApproval = false,
openWorldHint = true
)
public Object execute(%sReq req) {
log.info("[MCI Tool] {} 요청 수신.", "%s");
try {
// MapStruct를 이용한 자동 매핑 (AI DTO -> MCI DTO)
%sLegacyReq legacyReq = converter.toLegacyReq(req);
Transfer<Object> resTransfer = mci.callTo(
"%s",
null,
legacyReq,
Object.class
);
return resTransfer.getBody() != null ? resTransfer.getBody() : Map.of("status", "SUCCESS");
} catch (Exception e) {
log.error("[MCI Tool] 연동 중 오류 발생: {}", e.getMessage(), e);
return Map.of("status", "ERROR", "message", e.getMessage() != null ? e.getMessage() : "Unknown error");
}
}
}
""".formatted(
BASE_PACKAGE,
BASE_PACKAGE,
BASE_PACKAGE,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
author,
createDate,
createDate, author,
routingType, group.toLowerCase(),
baseName,
baseName, baseName,
baseName, toolName, description, description + " 해줘.", interfaceId, register,
baseName,
baseName,
baseName,
interfaceId
);
} else {
serviceContent = """
package %s.service;
import %s.annotation.McpFunction;
import %s.annotation.McpTool;
import %s.dto.%sReq;
import %s.dto.%sRes;
import %s.converter.%sLegacyConverter;
import org.springframework.stereotype.Service;
import org.mapstruct.factory.Mappers;
/**
* @package %s.service
* @className %sService
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 최초생성
*
* </pre>
*/
@Service
@McpTool(
routingType = "%s",
categoryKey = "%s"
)
public class %sService extends AbstractMcpToolService {
private final %sLegacyConverter converter = Mappers.getMapper(%sLegacyConverter.class);
@McpFunction(
displayName = "%s 툴",
name = "%s",
description = "%s",
prompt = "%s",
mappingId = "%s",
register = %s,
requiresApproval = false
)
public Object execute(%sReq req) {
// %sLegacyReq legacyReq = converter.toLegacyReq(req);
return executeLegacy("%s", "%s", req); // Or pass legacyReq
}
}
""".formatted(
BASE_PACKAGE,
BASE_PACKAGE,
BASE_PACKAGE,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
author,
createDate,
createDate, author,
routingType, group.toLowerCase(),
baseName,
baseName, baseName,
baseName, toolName, description, description + " 해줘.", interfaceId, register,
baseName,
baseName,
routingType, interfaceId
);
}
Files.writeString(serviceDir.resolve(baseName + "Service.java"), serviceContent);
String legacyReqContent = """
package %s.legacy;
import lombok.Data;
/**
* @package %s.legacy
* @className %sLegacyReq
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 최초생성
*
* </pre>
*/
@Data
public class %sLegacyReq {
/**
* EAI 시스템이 요구하는 수신자 번호 파라미터명
*/
private String phone;
/**
* EAI 시스템이 요구하는 메시지 내용 파라미터명
*/
private String content;
}
""".formatted(BASE_PACKAGE, BASE_PACKAGE, baseName, author, createDate, createDate, author, baseName);
Files.writeString(legacyDtoDir.resolve(baseName + "LegacyReq.java"), legacyReqContent);
String legacyResContent = """
package %s.legacy;
import lombok.Data;
/**
* @package %s.legacy
* @className %sLegacyRes
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 최초생성
*
* </pre>
*/
@Data
public class %sLegacyRes {
// TODO: Add legacy response fields here
}
""".formatted(BASE_PACKAGE, BASE_PACKAGE, baseName, author, createDate, createDate, author, baseName);
Files.writeString(legacyDtoDir.resolve(baseName + "LegacyRes.java"), legacyResContent);
String converterContent = """
package %s.converter;
import %s.dto.%sReq;
import %s.dto.%sRes;
import %s.legacy.%sLegacyReq;
import %s.legacy.%sLegacyRes;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
/**
* @package %s.converter
* @className %sLegacyConverter
* @description AX HUB 시스템 처리 클래스
* @author %s
* @create %s
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* %s %s 최초생성
*
* </pre>
*/
@Mapper(componentModel = "spring")
public interface %sLegacyConverter {
@Mapping(source = "phoneNumber", target = "phone")
@Mapping(source = "message", target = "content")
%sLegacyReq toLegacyReq(%sReq req);
@Mapping(source = "phone", target = "phoneNumber")
@Mapping(source = "content", target = "message")
%sReq toReq(%sLegacyReq legacyReq);
// %sRes toRes(%sLegacyRes legacyRes);
}
""".formatted(
BASE_PACKAGE,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName,
BASE_PACKAGE, baseName, author, createDate, createDate, author,
baseName, baseName, baseName, baseName, baseName, baseName, baseName
);
Files.writeString(converterDir.resolve(baseName + "LegacyConverter.java"), converterContent);
log.append("\n=========================================\n");
log.append(" Scaffolding Complete! (Routing: " + routingType + ")\n");
log.append("=========================================\n");
log.append("[Service] ").append(serviceDir.resolve(baseName + "Service.java")).append("\n");
log.append("[Req DTO] ").append(dtoDir.resolve(baseName + "Req.java")).append("\n");
log.append("[Res DTO] ").append(dtoDir.resolve(baseName + "Res.java")).append("\n");
log.append("[Legacy Req DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyReq.java")).append("\n");
log.append("[Legacy Res DTO] ").append(legacyDtoDir.resolve(baseName + "LegacyRes.java")).append("\n");
log.append("[Legacy Converter] ").append(converterDir.resolve(baseName + "LegacyConverter.java")).append("\n");
log.append("\n Tip: ").append(interfaceId).append(" 목업 데이터를 mock-responses.json에 추가하세요.\n");
return log.toString();
}
private static String toPascalCase(String str) {
if (str == null || str.isEmpty()) {
return str;
}
StringBuilder result = new StringBuilder();
boolean capitalizeNext = true;
for (char c : str.toCharArray()) {
if (c == '_' || c == '-' || c == ' ') {
capitalizeNext = true;
} else if (capitalizeNext) {
result.append(Character.toUpperCase(c));
capitalizeNext = false;
} else {
result.append(c);
}
}
if (result.length() > 0) {
result.setCharAt(0, Character.toUpperCase(result.charAt(0)));
}
return result.toString();
}
}

View File

@@ -1,4 +1,4 @@
package io.shinhanlife.dap.common.adapter.aop;
package io.shinhanlife.dap.lib.adapter.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
@@ -9,16 +9,16 @@ import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @package io.shinhanlife.dap.common.adapter.aop
* @package io.shinhanlife.dap.lib.adapter.aop
* @className EimsMonitoringAspect
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/
@@ -27,8 +27,8 @@ import java.util.Arrays;
@Component
public class EimsMonitoringAspect {
// 포인트컷: io.shinhanlife.dap.common.adapter.sender 패키지 내의 EimsSender를 구현한 모든 클래스의 메서드를 타겟으로 지정합니다.
@Around("execution(* io.shinhanlife.dap.common.adapter.sender.*EimsSender.*(..))")
// 포인트컷: io.shinhanlife.dap.lib.adapter.sender 패키지 내의 EimsSender를 구현한 모든 클래스의 메서드를 타겟으로 지정합니다.
@Around("execution(* io.shinhanlife.dap.lib.adapter.sender.*EimsSender.*(..))")
public Object monitorEimsCommunication(ProceedingJoinPoint joinPoint) throws Throwable {
// 1. 호출되는 클래스와 메서드 이름 추출

View File

@@ -1,7 +1,7 @@
package io.shinhanlife.dap.common.adapter.connector;
package io.shinhanlife.dap.lib.adapter.connector;
import io.shinhanlife.dap.common.adapter.support.DynamicPayloadBuilder;
import io.shinhanlife.dap.common.adapter.support.DynamicSchemaValidator;
import io.shinhanlife.dap.lib.adapter.support.DynamicPayloadBuilder;
import io.shinhanlife.dap.lib.adapter.support.DynamicSchemaValidator;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import lombok.extern.slf4j.Slf4j;
@@ -12,16 +12,16 @@ import java.util.Map;
import java.util.List;
/**
* @package io.shinhanlife.dap.common.adapter.connector
* @package io.shinhanlife.dap.lib.adapter.connector
* @className ExternalApiConnector
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,7 +1,7 @@
package io.shinhanlife.dap.common.adapter.connector;
package io.shinhanlife.dap.lib.adapter.connector;
import io.shinhanlife.dap.common.adapter.support.DynamicPayloadBuilder;
import io.shinhanlife.dap.common.adapter.support.DynamicSchemaValidator;
import io.shinhanlife.dap.lib.adapter.support.DynamicPayloadBuilder;
import io.shinhanlife.dap.lib.adapter.support.DynamicSchemaValidator;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import lombok.extern.slf4j.Slf4j;
@@ -12,16 +12,16 @@ import java.util.Map;
import java.util.List;
/**
* @package io.shinhanlife.dap.common.adapter.connector
* @package io.shinhanlife.dap.lib.adapter.connector
* @className InternalSystemConnector
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,6 +1,6 @@
package io.shinhanlife.dap.common.adapter.connector;
package io.shinhanlife.dap.lib.adapter.connector;
import io.shinhanlife.dap.common.adapter.support.ResultStandardizer;
import io.shinhanlife.dap.lib.adapter.support.ResultStandardizer;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import lombok.RequiredArgsConstructor;
@@ -13,16 +13,16 @@ import java.util.Map;
import java.util.stream.Collectors;
/**
* @package io.shinhanlife.dap.common.adapter.connector
* @package io.shinhanlife.dap.lib.adapter.connector
* @className LegacyDbConnector
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,7 +1,7 @@
package io.shinhanlife.dap.common.adapter.connector;
package io.shinhanlife.dap.lib.adapter.connector;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.shinhanlife.dap.common.adapter.sender.EimsSender;
import io.shinhanlife.dap.lib.adapter.sender.EimsSender;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
@@ -12,19 +12,19 @@ import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import io.shinhanlife.dap.common.adapter.util.LegacyDataTransformer;
import io.shinhanlife.dap.lib.adapter.util.LegacyDataTransformer;
/**
* @package io.shinhanlife.dap.common.adapter.connector
* @package io.shinhanlife.dap.lib.adapter.connector
* @className LegacyEimsConnector
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,4 +1,4 @@
package io.shinhanlife.dap.common.adapter.connector;
package io.shinhanlife.dap.lib.adapter.connector;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
@@ -8,16 +8,16 @@ import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @package io.shinhanlife.dap.common.adapter.connector
* @package io.shinhanlife.dap.lib.adapter.connector
* @className ThirdPartySecurityConnector
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,4 +1,4 @@
package io.shinhanlife.dap.common.adapter.dto;
package io.shinhanlife.dap.lib.adapter.dto;
import lombok.Builder;
import lombok.NoArgsConstructor;
@@ -8,16 +8,16 @@ import lombok.Getter;
import lombok.Setter;
/**
* @package io.shinhanlife.dap.common.adapter.dto
* @package io.shinhanlife.dap.lib.adapter.dto
* @className ErrorDetail
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,19 +1,19 @@
package io.shinhanlife.dap.common.adapter.dto;
package io.shinhanlife.dap.lib.adapter.dto;
import lombok.Getter;
import lombok.Setter;
/**
* @package io.shinhanlife.dap.common.adapter.dto
* @package io.shinhanlife.dap.lib.adapter.dto
* @className JsonRpcRequest
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

View File

@@ -1,4 +1,4 @@
package io.shinhanlife.dap.common.adapter.dto;
package io.shinhanlife.dap.lib.adapter.dto;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Getter;
@@ -6,16 +6,16 @@ import lombok.Setter;
// 2. 응답 DTO
/**
* @package io.shinhanlife.dap.common.adapter.dto
* @package io.shinhanlife.dap.lib.adapter.dto
* @className JsonRpcResponse
* @description AX HUB 시스템 처리 클래스
* @author 김형식
* @author 0986406
* @create 2026.09.01
* <pre>
* ---------- 개정이력 ----------
* 수정일 수정자 수정내용
* ---------- -------- ---------------------------
* 2026.09.01 김형식 최초생성
* 2026.09.01 0986406 최초생성
*
* </pre>
*/

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