AI-powered tools for Oracle DB & Java/Spring enterprise development Powered by Anthropic Claude API
๐ https://sangmoo.github.io/Claude-Java-Toolkit/#ko
ํ๋ก์ ํธ์ ์ ์ฒด ๊ธฐ๋ฅ, ์ค์น ๋ฐฉ๋ฒ, REST API ๋ชฉ๋ก์ ํ๋์ ํ์ธํ ์ ์๋ GitHub Pages ์๊ฐ ํ์ด์ง์
๋๋ค.
ํ๊ตญ์ด(#ko) / ์์ด(#en) ์ ํ์ ์ง์ํฉ๋๋ค.
claude-java-toolkit์ Java/Spring ์ํฐํ๋ผ์ด์ฆ ํ๊ฒฝ์์ Claude API๋ฅผ ์ค๋ฌด์ ๋ฐ๋ก ํ์ฉํ ์ ์๋ ์คํ์์ค ํดํท์
๋๋ค.
Python์ฉ Claude ํตํฉ ๋๊ตฌ๋ ๋ง์ง๋ง, JDK 1.8+ / Oracle 11g+ / Spring Boot ๋ ๊ฑฐ์ ํ๊ฒฝ์ ํนํ๋ Java ํตํฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ฑฐ์ ์์ต๋๋ค.
๊ตญ๋ด SI / ๊ธ์ต / ์ ํต ํ๊ฒฝ์ ํ์ค์ ๋ฐ์ํ์ฌ ์ค๊ณ๋์์ต๋๋ค.
๋๊ตฌ์ ์ ์ฒด์ฑ ๋ณํ: Consumer โ Platform. Claude Code / Cursor / Cline ๋ฑ AI agent ๊ฐ IDE ์์์ ๋๊ตฌ์ 12+ ๋ถ์ ๊ธฐ๋ฅ์ ์ง์ ํธ์ถ. CI/CD pipeline / curl / ์ฌ๋ด ์๋ํ๋ ๊ฐ์ X-Api-Key ํค๋๋ก ์ฆ์ ํธ์ถ ๊ฐ๋ฅ.
-
๐ API Key ์ธํ๋ผ โ ๋ฉํฐ ํค + RBAC โ
/admin/api-keys- SHA-256 ํด์ ์ ์ฅ + ๋ฐ๊ธ ์งํ plaintext 1ํ ๋ ธ์ถ
- per-key role (READ_ONLY / WRITE / ADMIN) + ๋ถ๋น ํธ์ถ ํ๋ (sliding window)
- ๋ง๋ฃ / ํ์ / ์ฌ์ฉ ํต๊ณ (lastUsedAt + totalCalls)
- timing-safe ๋น๊ต + audit_log ์๋ ๊ธฐ๋ก
-
๐ ๊ณต์ REST API v1 โ
POST /api/v1/analyze๋๊ธฐ ์ง์ ์ - 12๊ฐ feature ํตํฉ ์ง์ ์ + ์บ์ / Live DB / history ์๋
- ์๋ต์ ํ ํฐ + ๋น์ฉ + latency + ๋ชจ๋ธ ์ ๋ณด
GET /api/v1/featuresdiscovery (MCP / SDK ์๋ ์์ฑ์ฉ)
-
๐ค MCP Server โ
@claude-toolkit/mcp-server(Node.js)- Claude Code / Cursor / Cline / Continue.dev ์๋ ํธํ
- 12๊ฐ ๋ถ์ tool ๋ ธ์ถ: SQL ๋ถ์ 6 + Java/Code ๋ถ์ 6
- stdio transport (ํ์ค) + ํ๊ฒฝ๋ณ์ ๊ธฐ๋ฐ ์ค์
- 5๋ถ ํตํฉ: ADMIN ํค ๋ฐ๊ธ โ mcp_settings.json ํ ์ค โ IDE ์ฌ์์
- ๐
docs/mcp-setup.mdํตํฉ ๊ฐ์ด๋
# Claude Code ํตํฉ ์์ โ ~/.config/claude/mcp_settings.json
{
"mcpServers": {
"claude-toolkit": {
"command": "npx",
"args": ["-y", "@claude-toolkit/mcp-server"],
"env": {
"CTK_URL": "http://localhost:8027",
"CTK_API_KEY": "ctk_live_..."
}
}
}
}# CI/CD / curl ์ง์ ํธ์ถ ์์
curl -X POST $CTK_URL/api/v1/analyze \
-H "X-Api-Key: ctk_live_..." \
-H "Content-Type: application/json" \
-d '{"feature":"sql_review","input":"SELECT * FROM T_ORDER ..."}' | jq- ๐ MCP ํฌํธ ์์ผ ๋ฆฌ์ค๋ ํฌ์ค์ฒดํฌ โ
McpServerHealthIndicator์ TCP ์์ผ 500ms ์ฒดํฌ ์ถ๊ฐ. ํ๋ก์ธ์ค ์์กด + ํฌํธ ์ค์ ์คํ ์ฌ๋ถ๋ฅผportListening: true/falsedetail ๋ก ๊ตฌ๋ถ. ๊ธฐ๋ ์ค ์ํ๋ฅผstarting์ผ๋ก ๋ถ๋ฆฌํด Grafana ์๋ ์คํ ๋ฐฉ์ง. - โ๏ธ DB ํ๋กํ ํธ์ง ๋ชจ๋ฌ โ
/db-profiles๋ชฉ๋ก ๊ฐ ํ์ ํธ์ง(โ) ๋ฒํผ ์ถ๊ฐ. ๊ธฐ์กด ์ ์ฅ๊ฐ์GET /{id}/json์ผ๋ก ๋ถ๋ฌ์ ํธ์ง ๋ชจ๋ฌ ํ์.POST /{id}/update-jsonAJAX ์๋ํฌ์ธํธ๋ก ์ ์ฅ. - ๐ ์ด๋ ฅ CSV ์ ํ ๋ด๋ณด๋ด๊ธฐ โ ์ฒดํฌ๋ฐ์ค ์ ํ ๊ฑด๋ง CSV ๋ก ๋ด๋ณด๋ด๋
POST /api/v1/export/csv/history/selected์ถ๊ฐ. ์ ํ ์์ผ๋ฉด ์ ์ฒด 1000๊ฑด fallback. ๋ฒํผ ๋ ์ด๋ธ๋ ์ ํ ๊ฑด์ ์ค์๊ฐ ๋ฐ์. - ๐ Live DB ์ฟผ๋ฆฌ ์คํ ์ด๋ ฅ โ
livedb_query_logJPA ํ ์ด๋ธ์ Live DB ํธ์ถ๋ง๋ค ์๋ ๊ธฐ๋ก (profileId ยท username ยท SQL ยท ์์์๊ฐ ยท ์ํ).GET /api/v1/admin/livedb/query-log๋ก ์กฐํ. - ๐งช
LiveDbBreakerStateRepository@DataJpaTest โ H2 ์ธ๋ฉ๋ชจ๋ฆฌ๋ก ์ค์ JPA ๋์ ๊ฒ์ฆ (save/findAll/deleteById 3 ์ผ์ด์ค). - โ๏ธ DB ํ๋ก์ฐจ๋จ๊ธฐ @PostConstruct ํ๋ฆฌ๋ก๋ โ ์ฑ ์ฌ์์ ์ DB ์ ๋จ์ OPEN ์ํ๋ฅผ in-memory ์บ์๋ก ๋ณต์.
secondsUntilHalfOpen()์ด ์ฌ์์ ์งํ์๋ ์ ํํ ๊ฐ ๋ฐํ. - ๐ฆ API ์๋ต Gzip ์์ถ โ
server.compression.enabled: true,min-response-size: 2048, JSON/CSV/HTML ์ ์ ํ ํฌํจ. ๋์ฉ๋ ์ด๋ ฅ ์๋ต์์ ๋คํธ์ํฌ ์ ์ก๋ ๋ํญ ๊ฐ์. - ๐ฉบ Readiness Probe ๋ช
์์ mcpServer ์ ์ธ โ
/health/readiness๊ทธ๋ฃน์์mcpServer์ ๊ฑฐ ์ด์ ๋ฅผ ์ฃผ์์ผ๋ก ๋ช ์. MCP ํ๋ก์ธ์ค ๋ค์ด์ด K8s Pod ์ฌ์์ ๋ฃจํ๋ก ์ด์ด์ง์ง ์๋๋ก ๋ฐฉ์ง.
- ๐ ์ด๋ ฅ CSV ๋ด๋ณด๋ด๊ธฐ โ
/historyํ์ด์ง์ CSV ๋ฒํผ ์ถ๊ฐ.GET /api/v1/export/csv/history์๋ํฌ์ธํธ๊ฐ UTF-8 BOM ํฌํจ CSV ๋ฅผ ๋ฐํ โ Excel ์์ ํ๊ธ ๊นจ์ง ์์. ๊ธฐ์กด Excel ๋ด๋ณด๋ด๊ธฐ์ ๋ณํ ์ ๊ณต. - ๐ ํ๋ก์ฐจ๋จ๊ธฐ DB ๊ณต์ โ
LiveDbCircuitBreaker๊ฐlivedb_breaker_stateJPA ํ ์ด๋ธ์ source-of-truth ๋ก ์ฌ์ฉ. ๋ฉํฐ ์ธ์คํด์ค ๋ฐฐํฌ์์ ํ ์ธ์คํด์ค์ ์ฐจ๋จ ๊ฒฐ์ ์ด ์ ์ฒด ์ธ์คํด์ค์ ์ฆ์ ์ ํ. ๋จ์ผ ์ธ์คํด์ค์์๋ ๊ธฐ์กด in-memory ๋์ ์ ์ง (DB ์๋ ๊ฐ์ง). - ๐ Grafana MCP ํจ๋ โ
claude-toolkit-overview.json๋์๋ณด๋์ MCP Server ์น์ ์ถ๊ฐ (์ํ stat ํจ๋ 2๊ฐ). - ๐ฉบ Admin ํฌ์ค ํ์ด์ง MCP ์นด๋ โ
/admin/health์ MCP Server ์ํ ์นด๋ ๋ ธ์ถ.GET /api/v1/admin/health/mcp์๋ํฌ์ธํธ์์autoStart / running / pid / port์กฐํ. - ๐ DB ํ๋กํ ์ฐ๊ฒฐ ํ
์คํธ ๋ฒํผ โ
/db-profiles๋ชฉ๋ก์ ๊ฐ ํ์ "์ฐ๊ฒฐ ํ ์คํธ" ๋ฒํผ ์ถ๊ฐ.POST /db-profiles/{id}/test-existing์ด ์ ์ฅ๋ ์๊ฒฉ์ฆ๋ช ์ผ๋ก ์ค์ JDBC ์ฐ๊ฒฐ ์๋. - โ๏ธ
TOOLKIT_MCP_AUTOSTARTํ๊ฒฝ๋ณ์ ๋ ธ์ถ โdocker-compose.yml์์${TOOLKIT_MCP_AUTOSTART:-false}๋ก ๋ณ๊ฒฝ..envํ์ผ ํ ์ค๋ก MCP ์๋ ๊ธฐ๋ ์ ํ ๊ฐ๋ฅ. - ๐งช
McpServerHealthIndicator๋จ์ ํ ์คํธ โ 4 ์ผ์ด์ค: disabled/running+pid/running-no-pid/not-running. - ๐งช
ApiKeyFilter512์ ์ฐจ๋จ ํ ์คํธ โ MockedStatic ์ผ๋ก SecuritySettings ๋ฅผ ๊ฒฉ๋ฆฌ, 513์ ํค โ 401, 512์ ํค โ BCrypt ๋จ๊ณ๊น์ง ํต๊ณผ ๊ฒ์ฆ.
์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ๋ฐ๊ฒฌ๋ ์ด์ ๋ฆฌ์คํฌ๋ฅผ ์ ์ ์ ์ผ๋ก ํด์.
- ๐ ์ํธํ ํค ํ์ผ ๊ถํ ๊ฐ์ (600) โ
CryptoUtils๊ฐ~/.claude-toolkit/.encryption-key์์ฑ ์ OS ์ ์๊ด์์ด owner read/write only ๋ก ์ค์ . Docker/Linux ํ๊ฒฝ์์ ๋ค๋ฅธ ํ๋ก์ธ์ค๊ฐ ํค๋ฅผ ์ฝ์ ์ ์๋๋ก ์ฐจ๋จ. - ๐ก API Key DoS ๋ฐฉ์ด โ
ApiKeyFilter์์ BCrypt ๋น๊ต ์ ํค ๊ธธ์ด 512์ ์ด๊ณผ ์ ์ฌ์ ์ฐจ๋จ. ๋น์ ์์ ์ผ๋ก ๊ธด ์ ๋ ฅ์ผ๋ก CPU ๋ฅผ ์๋ชจ์ํค๋ ์์ฒญ ์๋ ๊ฑฐ๋ถ. - ๐ DataSource ์บ์ ์ ์ ์ข
๋ฃ โ
LiveDbContextService/IndexSimulatorService์@PreDestroy์ถ๊ฐ. JVM graceful shutdown ์ ์บ์ ๋งต ๋ช ์์ ์ ๋ฆฌ. - โ๏ธ Claude ๋ชจ๋ธ ํ๊ฒฝ๋ณ์ํ โ
application.yml์claude.model์ด${CLAUDE_MODEL:claude-sonnet-4-6}์ผ๋ก ๋ณ๊ฒฝ. ์ฌ๋น๋ ์์ด ํ๊ฒฝ๋ณ์ ํ ์ค๋ก ๋ชจ๋ธ ์ ํ ๊ฐ๋ฅ. - ๐ฉบ MCP Server ํฌ์ค ์ธ๋์ผ์ดํฐ โ
McpServerHealthIndicator์ ๊ท ์ถ๊ฐ./actuator/health์mcpServer์ปดํฌ๋ํธ๊ฐ ๋ ธ์ถ๋์ด Prometheus / Grafana ์๋์ ์๋ ์ฐ๋.
๋ณด์ ์ ์ฑ ๊ฐํ: ADMIN ๊ณ์ ์ 2FA(TOTP)๋ฅผ ์ ํ์ด ์๋ ํ์๋ก ์ ํ. ๋์์ ์ธ์ฆยท2FA ํต์ฌ ๋ก์ง ์ ๋ฐ์ ๋จ์ ํ ์คํธ๋ฅผ ์ถ๊ฐํด ํ๊ท ์์ ๋ง์ ๊ตฌ์ถ.
-
๐ ADMIN 2FA ํ์ํ โ
POST /account/disable-2fa์ ADMIN ์ญํ ์ฐจ๋จ ์ถ๊ฐ- ADMIN ๊ณ์ ์ด 2FA ๋นํ์ฑํ ์์ฒญ ์
{ "success": false, "error": "ADMIN ๊ณ์ ์ 2FA๋ฅผ ๋นํ์ฑํํ ์ ์์ต๋๋ค." }๋ฐํ - ๋ก๊ทธ์ธ ํ๋ฆ(
TwoFactorAuthHandler)์ ์ด๋ฏธ ADMIN ์ ๊ฐ์ ๋ก/login/2fa๋ก ๋ฆฌ๋ค์ด๋ ํธํ์ฌ ๋ฏธ์ค์ ์ ์ด๊ธฐ ๋ฑ๋ก ํ์ด์ง ํ์ โ ์ด๋ฒ ํจ์น๋ ์ฌํ ๋นํ์ฑํ ๊ฒฝ๋ก๋ฅผ ์ฐจ๋จํ๋ ๋ง์ง๋ง ํผ์ฆ - REVIEWER / VIEWER ๋ ๊ธฐ์กด๋๋ก 2FA ์ ํ ์ ์ฉ
- ADMIN ๊ณ์ ์ด 2FA ๋นํ์ฑํ ์์ฒญ ์
-
๐งช ๋จ์ ํ ์คํธ 24๊ฐ ์ ๊ท โ
TotpServiceTest/TwoFactorAuthHandlerTest/AccountController2faTest+SecuritySmokeTests4๊ฑด ์ถ๊ฐTotpServiceTest(14 ์ผ์ด์ค) โ RFC 6238 TOTP ์๊ณ ๋ฆฌ์ฆ ๊ฒ์ฆ:generateSecret()Base32 ํฌ๋งทยท์ ์ผ์ฑ, Base32 ์๋ณต ๋ณํ,verifyCode()null/๋น์ ์ ์ฝ๋ ๊ฑฐ๋ถ,buildOtpAuthUri()ํ์TwoFactorAuthHandlerTest(5 ์ผ์ด์ค) โ ๋ก๊ทธ์ธ ์ฑ๊ณต ํธ๋ค๋ฌ: ADMIN โ2fa_pending์ธ์ ์ธํ +/login/2fa๋ฆฌ๋ค์ด๋ ํธ, Non-ADMIN โ/์งํ, ์คํจ ์นด์ดํฐ ๋ฆฌ์ , ์ฌ์ฉ์ ๋ฏธ์กด์ฌ ์์ ์ฒ๋ฆฌAccountController2faTest(5 ์ผ์ด์ค) โ ADMIN ๋นํ์ฑํ ๊ฑฐ๋ถ, REVIEWER/VIEWER ์ ์ ๋นํ์ฑํ, ์ฌ์ฉ์ ๋ฏธ์กด์ฌ no-opSecuritySmokeTests(4 ์ผ์ด์ค ์ถ๊ฐ) โ2fa_pending=true์ธํฐ์ ํฐ ๋ฆฌ๋ค์ด๋ ํธ ๊ฒ์ฆ + ADMIN disable-2fa API ์๋ตsuccess=false๊ฒ์ฆ
v4.7.0 ๋ฉ์ด์ ๋ฆด๋ฆฌ์ค ์งํ ์ด์ํ / ์ธ๋ถ๊ฐ์ฌ์ธ / ์ผ๋ฐ ์ฌ์ฉ์๊ฐ ํ์ฅ์์ ๋ฐ๋ก ์ฒด๊ฐํ ์ ์๋ 5๊ฐ์ง ์์ญ์ ํ ๋ฒ์ ๊ฐํํ ๋์ ํจ์น. ๋ชจ๋ ๊ธฐ์กด ์ปดํฌ๋ํธ ์ฌ์ฌ์ฉ ์์ฃผ๋ก ๋ฐฑ์๋ ๊ตฌ์กฐ ๋ณ๊ฒฝ ์ต์ํ.
-
๐ก Settings ๋ณ๊ฒฝ ๊ฐ์ฌ ๋ก๊ทธ (#15, ADMIN ์ ์ฉ ์ ๊ท ํ์ด์ง) โ
/admin/config-changes- 22๊ฐ ์์คํ
์ค์ (DB / Claude / scanPath / SMTP / rate-limit ๋ฑ) ๋ณ๊ฒฝ ์
์ด์ ๊ฐ/์๊ฐ/๋ณ๊ฒฝ์/IP/์๊ฐ์
config_change_logํ ์ด๋ธ์ ์๋ ๊ธฐ๋ก - ์ธ๋ถ๊ฐ์ฌ์ธ์ด "settings ๊ฐ ์ธ์ ๋๊ตฌ์ ์ํด ์ด๋ป๊ฒ ๋ฐ๋์๋์ง" 1์ฟผ๋ฆฌ๋ก ์ถ์
- ํจ์ค์๋/API ํค ๋ฑ ๋ฏผ๊ฐ ํ๋๋ ์๋ ๋ง์คํน (
***๋ก ์ ์ฅ โ ํ๋ฌธ ๋ณด๊ด X)
- 22๊ฐ ์์คํ
์ค์ (DB / Claude / scanPath / SMTP / rate-limit ๋ฑ) ๋ณ๊ฒฝ ์
์ด์ ๊ฐ/์๊ฐ/๋ณ๊ฒฝ์/IP/์๊ฐ์
-
โก ํ์ผ ํด์ ๊ธฐ๋ฐ ๋ถ์ ์บ์ (#18) โ
AnalysisCacheService4-arg key + ์ ๊ทํ- ์ ๋ ฅ ์ ๊ทํ: BOM ์ ๊ฑฐ / CRLFโLF / trailing whitespace / ์ฐ์ ๋น ์ค ์ถ์ฝ โ ๊ฐ์ ์๋ฏธ์ ์ ๋ ฅ์ ํญ์ ๊ฐ์ ์บ์ ํค๋ก ๋งคํ (์ฒด๊ฐ ์ ์ค๋ฅ ํฅ์)
- ์บ์ ํค:
feature + input + input2 + sourceType4๊ฐ SHA-256 ํด์ฑ โsql_review+ ๊ฐ์ SQL ์ด๋ผ๋reviewvssecurity์ต์ ์ด ๋ค๋ฅด๋ฉด ๋ถ๋ฆฌ (์ถฉ๋ ๋ฒ๊ทธ ํ๊ท ๋ฐฉ์ง) - JVM ๋ผ์ดํํ์ hit/miss ์นด์ดํฐ + ์ ์ค๋ฅ (
/api/v1/admin/cache/stats) - ๋ชจ๋ ๋ถ์ ํ์ด์ง ๊ฒฐ๊ณผ ํค๋์ โก ์บ์ ์ ์ค ์นฉ (์บ์๋ก ์ฆ์ ๋ฐํ๋ ๊ฒฝ์ฐ, Claude API ๋น์ฉ 0)
-
๐ท ๋ฆฌ๋ทฐ ์ด๋ ฅ ํ๊น ์์คํ (#12) โ
/historyํ์ด์ง ํ์ฅ- ๊ฐ ๋ถ์ ๊ฒฐ๊ณผ์ ์ฝค๋ง ๊ตฌ๋ถ ํ๊ทธ (์:
์ฑ๋ฅ, SLA์๋ฐ, DB) โ ๋ณธ์ธ ๋๋ ADMIN ๋ง ์์ - ํค๋ dropdown ์์ ๋น๋์ ํ๊ทธ ๋น ๋ฅธ ์ ํ / ํด๋ฆญ์ผ๋ก ํ ๊ธ ํํฐ
- ์นด๋ ํ์ ํ๊ทธ ์นฉ + ํ์ฅ ์์ญ์ ์๋์์ฑ dropdown (Enter ์ ์ฅ / Esc ์ทจ์)
- ํ๊ทธ ์ ๊ทํ: trim / ๋นยท์ค๋ณต ์ ๊ฑฐ / 30์ ์ํ, ์ฝค๋ง ๋งค์นญ ์ถฉ๋ ๋ฐฉ์ง(
,DB,ํจํด)
- ๊ฐ ๋ถ์ ๊ฒฐ๊ณผ์ ์ฝค๋ง ๊ตฌ๋ถ ํ๊ทธ (์:
-
๐ฌ ๊ฒฐ๊ณผ ํ์ ์ง๋ฌธ ํจ๋ โ Follow-up Q&A (#4) โ 22+ ๋ถ์ ํ์ด์ง์ ํ ๋ฒ์ ์ ์ฉ
- ๋ถ์ ๊ฒฐ๊ณผ ์งํ ๊ฐ์ ํ๋ฉด์์ "์ ์ด ๊ฒฐ๊ณผ๊ฐ ๋์๋์?" / "PostgreSQL ์์๋?" ๋ฑ ์ถ๊ฐ ์ง๋ฌธ ๊ฐ๋ฅ โ ํ์ด์ง ์ด๋ ์์ด ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด์ ์ง๋ฌธ
- ๊ธฐ์กด
/chat/send+/chat/streamSSE ์ธํ๋ผ ๊ทธ๋๋ก ์ฌ์ฌ์ฉ (๋ฐฑ์๋ ๋ณ๊ฒฝ 0) - ๊ฒฐ๊ณผ + ์๋ณธ ์ ๋ ฅ + ํ์ด์ง ๋ผ๋ฒจ์ ์ฒซ ์ง๋ฌธ ์ 1ํ ์ปจํ ์คํธ๋ก ์ ๋ฌ, ์ดํ ๋ฉํฐํด
- ๊ฒฐ๊ณผ๊ฐ ๋ณ๊ฒฝ๋๋ฉด (์ฌ๋ถ์) ๋ฉ์์ง/์ธ์ ์๋ ๋ฆฌ์
-
๐ ๋ถ์ ์ฒด์ด๋ โ ๊ฒฐ๊ณผ ํ ๋ค์ ๋จ๊ณ 1-ํด๋ฆญ (#2) โ 22+ ๋ถ์ ํ์ด์ง์ ํ ๋ฒ์ ์ ์ฉ
- 14๊ฐ feature ร ํ๊ท 3๊ฐ ๋ค์๋จ๊ณ ๋งคํ (์ด ~40๊ฐ ์ฒด์ด๋ ๊ฒฝ๋ก)
sql_review โ /explain โ /sql/index-advisor โ /sql-translate โ /erd๋ฑ - sessionStorage ๊ธฐ๋ฐ 1ํ์ฉ handoff (60์ด TTL), URL ๊ธธ์ด ์ ํยท๋ก๊ทธ ๋ ธ์ถ ์ฐ๋ ค 0
sameInput/extractFirstCodeBlock๋ณํ ์ ์ฑ ์ผ๋ก ๋ค์ ํ์ด์ง ์ ๋ ฅ ์๋ ์ฑ์- ์๋ ์ฑ์์ง ํ์ด์ง์๋ ์ ๋ ฅ ํค๋์ ๐ ์ฒด์ด๋ ์ ๋ ฅ ์ธ๋์ผ์ดํฐ
- 14๊ฐ feature ร ํ๊ท 3๊ฐ ๋ค์๋จ๊ณ ๋งคํ (์ด ~40๊ฐ ์ฒด์ด๋ ๊ฒฝ๋ก)
-
๐ Live DB ์ง์ ์ฐ๊ฒฐ โ EXPLAIN/ํต๊ณ ์๋ ์์ง + ์ธ๋ฑ์ค ์๋ฎฌ๋ ์ดํฐ + ์ด์ ๋์๋ณด๋ (#G3, Phase 0~5 ์๋ฃ) โ Oracle + PostgreSQL
- ์ฌ์ฉ์๊ฐ SQL ๋ง ์ ๋ ฅ โ ๋ฐฑ์๋๊ฐ ์๋์ผ๋ก EXPLAIN PLAN + ํ ์ด๋ธ ํต๊ณ + ์ธ๋ฑ์ค ๋ฉํ ์์ง โ Claude system prompt ์ prepend (์ค๋ฐ์ดํฐ ๊ธฐ๋ฐ ๋ถ์)
- ๊ธฐ์กด 22+ ๋ถ์ ํ์ด์ง ์ค 6๊ฐ SQL ํ์ด์ง (
/advisor,/explain,/sql/index-advisor,/sql-translate,/sql-batch,/erd) ์ ์๋ ์ ์ฉ โ ํ์ด์ง์ ๐ Live DB chip ๋ ธ์ถ - ์ด์ค ๋ณด์ ์ฐจ๋จ: โ
SqlClassifier๊ฐ SELECT/EXPLAIN/DESC/WITH ๋ง ํต๊ณผ (๋ฉํฐ statement / ์ฃผ์ ์ฐํ / ๋ฌธ์์ด ๋ฆฌํฐ๋ด ์ ; ๊น์ง ์ฒ๋ฆฌ), โกReadOnlyJdbcTemplate๊ฐ statement timeout + max rows ๊ฐ์ , โขtoolkit.livedb.enabled=falsekill switch + DbProfile ๋ณ ๋ช ์์ ADMIN ํ ๊ธ - Default OFF โ ์ด์ ํ๊ฒฝ์์ ์๋ ํ์ฑํ ์ฌ๊ณ ๋ฐฉ์ง
- DBMS ๋ณ ๋งคํ:
- Oracle 11g~19c:
DBA_TABLES/ALL_TABLES/USER_TABLES๊ถํ๋ณ fallback,DBA_INDEXES+DBA_IND_COLUMNS,EXPLAIN PLAN SET STATEMENT_ID + DBMS_XPLAN.DISPLAY - PostgreSQL 11+:
pg_class.reltuples+pg_stat_user_tables,pg_index+pg_attribute+LATERAL unnest WITH ORDINALITY๋ก ์ปฌ๋ผ ์์น ๋ณด์กด,EXPLAIN (FORMAT TEXT, COSTS true)(ANALYZE ์์ด โ ๋ถํ 0)
- Oracle 11g~19c:
- (Phase 4) ์ธ๋ฑ์ค ์๋ฎฌ๋ ์ดํฐ โ
/sql/index-advisorํ์ด์ง์ ์ถ์ฒ ์ธ๋ฑ์ค๋ฅผ Oracle INVISIBLE INDEX ํธ๋ฆญ์ผ๋ก ์ ์ ๋ง๋ค์ด EXPLAIN ๋น๊ต. ์ด์ ์ํฅ 0.- ์์ ์ฅ์น: ์ด๋ฆ prefix
CTK_SIM_*๊ฐ์ / INVISIBLE ์ต์ ๊ฐ์ / ์ต๋ 5๊ฐ ์ธ๋ฑ์ค / 60s timeout / try-finally cleanup (์คํจํด๋ ํญ์ DROP) /ALTER SESSION SET optimizer_use_invisible_indexes์ธ์ ๋จ์ ๊ฒฉ๋ฆฌ - UI: ์ถ์ฒ ์ธ๋ฑ์ค ์ ๐ ์๋ฎฌ๋ ์ด์ ๋ฒํผ โ before/after cost ์งํ๋ฐ + ๊ฐ์ ๋ฅ % + ํผ์น๋ฉด EXPLAIN PLAN ์ข์ฐ ๋น๊ต
- ์ ๊ท endpoint:
POST /api/v1/livedb/simulate-index(JSON body)
- ์์ ์ฅ์น: ์ด๋ฆ prefix
- (Phase 5) ์ด์ ๋์๋ณด๋ + Rate Limit + ํ๋ก์ฐจ๋จ๊ธฐ โ ์ด์ํ์ด ์ฆ์ ๋ชจ๋ํฐ๋ง
๊ฐ๋ฅํ ํฌ์ค ์ธํ๋ผ:
- Rate Limiter โ sliding window per (user ร profile), ๋ถ๋น max 10 ํธ์ถ (์ค์ ๊ฐ๋ฅ, 0 ์ด๋ฉด ๋นํ์ฑ). ์ฌ์ฉ์ ๊ฒฉ๋ฆฌ.
- ํ๋ก์ฐจ๋จ๊ธฐ โ 10๋ถ ๋ด timeout 5๊ฑด ๋ฐ์ ์ 5๋ถ ์๋ ๋นํ์ฑ. ๋ง๋ฃ ํ ์๋ ๋ณต๊ตฌ.
ADMIN ์ด ๊ฐ์ ๋ณต๊ตฌ ๊ฐ๋ฅ (
POST /api/v1/admin/livedb/breaker/{id}/close) - ํธ์ถ ํต๊ณ โ ํ๋กํ๋ณ ์ฑ๊ณต/์คํจ/timeout ์นด์ดํธ + ํ๊ท latency, JVM ๋ผ์ดํํ์ ๋์
/admin/health์ ๊ท ์นด๋ โ ํต๊ณ ํ + ํ๋ก ์ํ (๐ข CLOSED / ๐ด OPEN) + ADMIN ๊ฐ์ ๋ณต๊ตฌ ๋ฒํผ- ์ ๊ท endpoint:
GET /api/v1/admin/livedb/stats,POST /api/v1/admin/livedb/stats/reset,POST /api/v1/admin/livedb/breaker/{id}/close
- ์ ๊ท ํจํค์ง
io.github.claudetoolkit.ui.livedbโ 25 ํด๋์ค + 107 ๋จ์/ํตํฉ ํ ์คํธ - ๐ ์ด์ ๊ฐ์ด๋:
docs/livedb-operations.mdโ ํ์ฑํ 3-step- DB ๊ถํ ์๊ตฌ์ฌํญ (Oracle/PG) + ํธ๋ฌ๋ธ์ํ 7๊ฐ Q&A + Kill Switch ์ ์ฐจ
5๊ฐ ํฐ ์์ญ์ ํ ๋ฒ์ ๋์ด์ฌ๋ฆฐ ๋ฉ์ด์ ๋ฆด๋ฆฌ์ค. ๊ฐ ํญ๋ชฉ์ ์ด์ํ / ์ธ๋ถ๊ฐ์ฌ์ธ / ์ฌ์ฉ์ ๋ชจ๋๋ฅผ ๋ณ๋ ์๋๋ฆฌ์ค๋ก ์ง์ํ๋๋ก ์ค๊ณ๋๊ณ , ๋ชจ๋ ๋ณ๊ฒฝ์ ๊ธฐ์กด ํ์ดํ๋ผ์ธ ๋ถ๋ด ์์ด ์ ํ์ ์ผ๋ก ํ์ฑํ/์ฌ์ฉํ ์ ์์ต๋๋ค.
- ๐ก ํ๊ตญ ์ปดํ๋ผ์ด์ธ์ค ๋ฆฌํฌํธ (4์ข
, ADMIN ์ ์ฉ ์ ๊ท ํ์ด์ง) โ
/admin/compliance-report- ์ ์๊ธ์ต๊ฐ๋ ๊ท์ (FSS) / ๊ฐ์ธ์ ๋ณด๋ณดํธ๋ฒ (PIPA) / ์ ๋ณดํต์ ๋ง๋ฒ / ์ธ๋ถ๊ฐ์ฌ ์ข ํฉ 4์ข ๋ชจ๋ ํ์ฑํ. 8~10๊ฐ ์น์ markdown + ์๋ ์ ๊ฒ ํ + ์๋ ๊ฒํ ์ฒดํฌ๋ฆฌ์คํธ.
- ์ธ๋ถ๊ฐ์ฌ ์ข
ํฉ ๋ฆฌํฌํธ๋ 3๊ฐ ๋ฒ๋ น ํก๋จ ๋น๊ต ํ ๋ก โ
/
โ ๏ธ ํ๋์ ํ์ ๊ฐ๋ฅ - ๋ค์ด๋ก๋ 3์ข : .md / .xlsx (Apache POI 4์ํธ) / ์ธ์ยทPDF (๋ธ๋ผ์ฐ์ ๋ค์ดํฐ๋ธ ์ ์ฅ)
- ์ต์ ๐ค AI ๊ฒฝ์์ง ์์ฝ โ Claude ๊ฐ 3-5๋ฌธ์ฅ ํ๊ตญ์ด ์์ฝ์ markdown ์๋จ์ prepend
- ์๊ตฌ ์ ์ฅ ์ด๋ ฅ โ ์์ฑ๋ ๋ฆฌํฌํธ๋ H2 DB(
compliance_reportํ ์ด๋ธ)์ ์๋ ์ ์ฅ, "์ ์ฅ๋ ๋ฆฌํฌํธ ์ด๋ ฅ" ๋ชจ๋ฌ์์ ๊ณผ๊ฑฐ ๋ฆฌํฌํธ ๋ค์ ๋ณด๊ธฐยท๋ค์ด๋ก๋ยท์ญ์ (์ต๋ 500๊ฑด)
- ๐ฌ ์์คํ
ํฌ์ค ๋์๋ณด๋ ํ์ฅ โ
/admin/health๊ฐ ๊ธฐ์กด 4๊ฐ ์นด๋(์๋ฒ/JVM/DB/์์คํ ) + 5๊ฐ ์ ๊ท ์นด๋ (์ธ๋ฑ์ 5์ข / ์บ์ 3์ข / Claude API / ๊ฐ์ฌ ๋ก๊ทธ / ์ค๋ฅ ๋ก๊ทธ) ๋ก ํ์ฅ. silent JPQL ์คํจ ๊ฐ์ ์ฌ๊ณ ๊ฐ ๋ ๋ฐ์ํ๋ฉด ์ด์ํ์ด ์ฆ์ ๋ฐ๊ฒฌ ๊ฐ๋ฅ. 30์ด ์๋ ๊ฐฑ์ . - ๐ ์ฌ์ฉ์/ํ ์ธ์ฌ์ดํธ (
/roi-reportํ์ฅ) โ ๊ธฐ์กด ์์คํ ํต๊ณ + ์ ๋ ์น์ :- ๋ด ํ๋ โ ๋ณธ์ธ ๋ถ์/์ฑํ /์๊ฐ ์ ๊ฐ + Top 5 ๊ธฐ๋ฅ BarChart + 12์ฃผ ๋์ LineChart
- ํ ๋น๊ต โ ํ ํ๊ท /๋์ + ๋ณธ์ธ ์์ยท๋ฐฑ๋ถ์ ์๊ฐ ์งํ ๋ฐ + Top 5 ์ฌ์ฉ์ (ADMIN)
- ๐ ๊ฒ์ ๊ฐํ (ํํฐ + ์ ๋ ฌ + ๋งค์น ๊ฐ์กฐ) โ
/api/v1/search์ type/from/to/sort ํ๋ผ๋ฏธํฐ, ๋งค์น ์์น ยฑ60์ ๋ฐ์ท snippet, ๊ฒฐ๊ณผ์<mark>๋ ธ๋ ๊ฐ์กฐ. SearchPage ์ ํํฐ ํ ๊ธ + URL ๋๊ธฐํ. - โก ๋ถ์ ๋น์ฉ ๋ฏธ๋ฆฌ๋ณด๊ธฐ โ ๋ชจ๋ ๋ถ์ ์์ ๋ฒํผ ์์ ์ธ๋ผ์ธ ํ์:
๐ช ~12K ํ ํฐ ยท ์์ $0.062 (Sonnet 4). ํ๊ตญ์ด 1.8 chars/token + ์์ด 3.5 chars/token ํด๋ฆฌ์คํฑ(ยฑ15% ์ค์ฐจ). 23๊ฐ ๋ถ์ ํ์ด์ง ์๋ ์ ์ฉ. - ๐งช SSE ํ๊ท ํ ์คํธ 4๊ฑด + FSS ๋จ์ ํ ์คํธ 8๊ฑด โ ์คํธ๋ฆฌ๋ฐ ๊ฒฝ๋ก ์ด๋ ฅ ์ ์ฅ ๋๋ฝ / ๋ฆฌํฌํธ markdown ๋น๋ ํ๊ท ๋ฐฉ์ง. mvn test ํ ๋ฒ์ด๋ฉด ๋ฐฑ์๋ ์ ๋ฐ ๊ฒ์ฆ.
์ด๋ฒ ํจ์น๋ ์ฌ์ฉ์ ์ํฌํ๋ก์ ์์ํ ๋ง์ฐฐ์ ํ ๋ฒ์ ์ ๋ฆฌํ๋ ๋ฐ ์ง์คํ์ต๋๋ค. ํฐ ์ ๊ท ๊ธฐ๋ฅ์ ์์ง๋ง, ํ์ฅ์์ ๋ฐ๋ก ์ฒด๊ฐ๋๋ ๊ฐ์ ์ด ๋ชจ์ฌ ์์ต๋๋ค.
- ๐ ์๋จ๋ฐ ๊ธ๋ก๋ฒ ๊ฒ์ โ ๋ชจ๋ ํ์ด์ง์ ์๋จ ๊ฐ์ด๋ฐ ์์ญ์ ๊ฒ์์ฐฝ์ด ์์ ๋
ธ์ถ๋ฉ๋๋ค. Settings ํ๋ ํธ์ accent ์์์ผ๋ก ํ
๋๋ฆฌ๊ฐ ์๋ ๋๊ธฐํ๋๊ณ (ํ
๋ง ๋ณ๊ฒฝ ์ ์ฆ์ ์ถ์ข
), Enter / ๋ฒํผ ํด๋ฆญ์ผ๋ก
/search?q=...๋ก ์ด๋ํฉ๋๋ค. URL ํ๋ผ๋ฏธํฐ๋ก ์ง์ ํ๋ฉด ์๋ ๊ฒ์๋์ด ๊ณต์ ยท์๋ก๊ณ ์นจ์ ์์ . - ๐ ๊ฒ์ ํ์ด์ง โ ๋ฉ๋ด + ์ด๋ ฅ ํตํฉ โ ๋ฉ๋ด ์นดํ๋ก๊ทธ๋ฅผ ํด๋ผ์ด์ธํธ ํํฐ๋ก ์ฆ์ ๋งค์นญ(์: "ํ๋ค์ค" โ ์ฝ๋ ๋ฆฌ๋ทฐ/SPโJava/SQL ์ต์ ํ ํ๋ค์ค 3๊ฑด) + ๋ฐฑ์๋
/api/v1/search๊ฐreview_history์type/title/inputContent/outputContent4๊ฐ ์ปฌ๋ผ์ LIKE ๊ฒ์ํด ๋ฉ๋ด์ ๋ถ์ ์ด๋ ฅ์ ํ ํ๋ฉด์ ๋ณด์ฌ ์ค๋๋ค. - ๐ ๊ฒ์ ๋ฐฑ์๋ ๋ฒ๊ทธ ์์ โ ์ด์ ๊ตฌํ์ ์กด์ฌํ์ง ์๋
h.menuName/h.inputTextํ๋๋ฅผ ์ฐธ์กฐํด JPQL ์ด ๋งค๋ฒ throw ํ๊ณ silent catch ๊ฐ ๋น ๊ฒฐ๊ณผ๋ง ๋ฐํํ๋ ๊ฒฐ์ ์ ๋ฒ๊ทธ๊ฐ ์์์ต๋๋ค. ์ค์ ์ํฐํฐ ํ๋(type/title/inputContent/outputContent) ๋ก ์ ์ ํ๊ณ , ํ๋ก ํธ์๋ ์นํ DTO({id, menuName, title, snippet, createdAt}) ๋ก ํํํ ์๋ต. - ๐พ ๋ถ์ ์ด๋ ฅ ์ ์ฅ โ 4๊ฐ ๊ธฐ๋ฅ ์ถ๊ฐ/์์ โ ๊ธฐ์กด์ ๋๊ธฐ
/analyze๊ฒฝ๋ก๋งreview_history์ ์ ์ฅ๋๊ณ SSE ์คํธ๋ฆฌ๋ฐ ๊ฒฝ๋ก๋ ๋๋ฝ์ด์์ต๋๋ค. ์ด๋ฒ ํจ์น๋ก ํ ์ด๋ธ ์ํฅ ๋ถ์ / SPโJava ๋ง์ด๊ทธ๋ ์ด์ ํ๋ค์ค / SQL ์ต์ ํ ํ๋ค์ค / ์ค๋ฅ ๋ก๊ทธ RCA ํ๋ค์ค ๊ฐ ์คํธ๋ฆฌ๋ฐ ์๋ฃ ์์ ์๋ ์ ์ฅ๋๋๋ก ํต์ผ.ReviewHistoryService.save(type, input, output, username)์ค๋ฒ๋ก๋๋ฅผ ์ถ๊ฐํด ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์๋ username ์ ๋ช ์ ์ ๋ฌ ๊ฐ๋ฅ. - ๐ ํ
์ด๋ธ ์ํฅ ๋ถ์ โ ํ์ผ ๋ชจ๋ฌ ๋ทฐ์ด + ํด๋ฆฝ๋ณด๋ ๋ณต์ฌ โ
/impactํ์ด์ง์์ MiPlatform ๋จ๊ณ๋ ์ ์ธํ๊ณTABLE โ MyBatis โ Java โ Controller4๋จ๊ณ๋ง ๋ ธ์ถ. MyBatis ๊ตฌ๋ฌธ / Java ํ์ผ / Controller ํ ๋ชจ๋ ํด๋ฆญํ๋ฉด ๋ชจ๋ฌ๋ก ์ ์ฒด ์์ค๊ฐ ์ด๋ฆฌ๊ณ , ์ฐ์๋จ "์ ์ฒด ๋ณต์ฌ" ๋ฒํผ์ผ๋ก ํด๋ฆฝ๋ณด๋์ ์ฆ์ ๋ณต์ฌ. ๋ฐฑ์๋GET /api/v1/flow/file?path=...์๋ํฌ์ธํธ(scanPath ์ ํ, 5MB ์ํ, path-traversal ๋ฐฉ์ด) ์ ์ค. - ๐ MyBatisCallerIndex false-positive ์ ๊ฑฐ โ ๊ฐ์ shortId(์:
insertOrder) ๋ฅผ ๊ฐ์ง mapper ๊ฐ ์ฌ๋ฌ ๊ฐ ์กด์ฌํ ๋, ๋จ์.shortId(๋งค์นญ๋ง์ผ๋ก ๋ชจ๋ mapper ์ ํธ์ถ์๋ก ์๋ชป ๊ธฐ๋ก๋๋ ๋ฒ๊ทธ๊ฐ ์์์ต๋๋ค. ์ด์ fullId ๋ณ mapperClass ๋ช ์ด ๊ทธ Java ํ์ผ์ ๋จ์ด ๊ฒฝ๊ณ๋ก ์กด์ฌํ๋์ง ๊น์ง ๊ฒ์ฆํด ๋ฌด๊ดํ ๋งคํผยท์๋น์ค๊ฐ ๋๋ ค๋์ค์ง ์์ต๋๋ค (ํ ์ด๋ธ ์ํฅ ๋ถ์์์ Java/Controller ๊ฒฐ๊ณผ๊ฐ ๋ถํ์ด ๋ณด์ด๋ ๋ฌธ์ ํด๊ฒฐ). - ๐ DB ๊ฐ์ฒด ์บ์ โ TABLE ํฌํจ โ
HarnessCacheService๊ฐALL_OBJECTS์์ PROCEDURE/FUNCTION/PACKAGE/TRIGGER ๋ง ๊ฐ์ ธ์ค๋ ๊ฒ์ TABLE / VIEW ๊น์ง ํ์ฅ. "์์ค์ ํํ๊ธฐ" ๋ฒํผ์ด ์ ๋ฉ๋ด์์ ํ ์ด๋ธ ํฝ์ปค๋ก ๋์. - ๐ฏ ์์ค์ ํํ๊ธฐ ๋ฒํผ โ ๋ฉ๋ด๋ณ ํ์
ํํฐ๋ง โ
SourceSelector์dbTypesํ์ดํธ๋ฆฌ์คํธ +pickName(์ด๋ฆ๋ง ๋ฐํ) ์ต์ ์ถ๊ฐ. ๋ฉ๋ด๋ณ ์ ์ฉ:- ํ
์ด๋ธ ์ํฅ ๋ถ์ โ
TABLE๋ง - SPโJava ํ๋ค์ค / SQL ์ต์ ํ ํ๋ค์ค โ DB ์ค๋ธ์ ํธ (Java ํ์ผ ํญ ์ ๊ฑฐ)
- ํ
์ด๋ธ ์ํฅ ๋ถ์ โ
- ๐ ํ๋ค์ค ์คํธ๋ฆฌ๋ฐ ๋งํฌ๋ค์ด ์ค๋ฐ๊ฟ ๋ฒ๊ทธ ์์ โ
useHarnessStream์ด ๋งค SSE ๋ฉ์์ง๋ง๋ค'\n'์ ์ธ์์ ์ผ๋ก ๋ง๋ถ์ด๊ณ ์์ด Claude ๊ฐ ๋จ์ด ์ค๊ฐ("ํ"/"์ฉ") ์์ ์ฒญํฌ๋ฅผ ๋์ผ๋ฉด"ํ\n์ฉ"์ด ๋งํฌ๋ค์ด soft-break ๋ก ๋จ์ด๊ฐ ๊ฐ๋ผ์ ธ ๋ณด์ด๋ ๋ฒ๊ทธ. flush ๊ธฐ์ค์ ์ค๋ฐ๊ฟ ๋จ์์์ STAGE_MARKER prefix ๋ณดํธ๋ก ๋ณ๊ฒฝํ์ฌ ๋จ์ด ๋ถ๋ฆฌ 0๊ฑด. - ๐ ๋ฉ๋ด ์ ๋ฆฌ โ ํจํค์ง ์์กด์ฑ ๊ทธ๋ํ(
/package-deps) ยท SP ํ๋ฆ ๋ถ์(/sp-impact) ๋ ๋ฉ๋ด๋ฅผ ๋ฐฑ์๋ ์๋ํฌ์ธํธ + ํ๋ก ํธ ๋ผ์ฐํธ + e2e ํ๊ท ํ ์คํธ๊น์ง ์์ ์ ๊ฑฐ. ์์กด ์ฐธ์กฐ 0๊ฑด.
๊ธฐ์กด "์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ค์ค" ์ 4๋จ๊ณ(Analyst โ Builder โ Reviewer โ Verifier) ํจํด์ ๊ณตํต ์ธํ๋ผ๋ก ์ถ์ถํ๊ณ , ์ด์ํยทDBA๊ฐ ์์ฃผ ๋ง์ฃผํ๋ 3๊ฐ์ง ์๋๋ฆฌ์ค์ ๋์ผ ํจํด์ ์ ์ฉํ์ต๋๋ค. 4๊ฐ ํ๋ค์ค๊ฐ ๊ฐ์ OrchestratorยทPromptLoaderยทUI ์ปดํฌ๋ํธ๋ฅผ ๊ณต์ ํฉ๋๋ค.
- ๐ ๊ณตํต ํ๋ค์ค ์ธํ๋ผ (Phase A) โ
HarnessOrchestrator(sync + SSE ์คํธ๋ฆฌ๋ฐ, sentinel ์๋ emit) ยทPromptLoader(prompts/harness/{name}/{stage}.mdclasspath ์ธ๋ถํ + ๊ฒฝ๋ก ์ธ์ ์ ์ฐจ๋จ) ยทHarnessAuditWriter(์ต์ ๋ ๊ฐ์ฌ ๋ก๊ทธ,toolkit.harness.audit=true) ยท ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ReactuseHarnessStreamํ +<HarnessStagePanels />์ปดํฌ๋ํธ - ๐ ์ค๋ฅ ๋ก๊ทธ RCA ํ๋ค์ค (Phase D) โ
/loganalyzerํ์ด์ง์ "RCA ํ๋ค์ค (4๋จ๊ณ)" ๋ชจ๋ ํ ๊ธ. ์ผ๋ฐ RCA / ๋ณด์ RCA(OWASP Top 10) ๋ ๋ชจ๋ ์ง์. ๊ฐ์ค ํ๋ณด โ ๊ฒ์ฆ SQL+ํจ์น+๋กค๋ฐฑ ๊ณํ โ ์ฐ๋ ํ๊ฐ โ ์ฌ๋ด ํ์ค RCA ๋ณด๊ณ ์ + ์ฌ๋ฐ ๋ฐฉ์ง ์ฒดํฌ๋ฆฌ์คํธ ์๋ ์์ฑ - ๐ SPโJava ๋ง์ด๊ทธ๋ ์ด์
ํ๋ค์ค (Phase B) โ
/sp-migration-harness์ ๊ท ํ์ด์ง. DB ์ค๋ธ์ ํธ ์ ํ ๋ฒํผ์ผ๋ก Oracle SP/Function/Package/Trigger๋ฅผ ALL_SOURCE์์ ์๋ ๋ก๋. SP ์๋ฏธ ๋ถ์ โ Service+Mapper+XML+DTO+ํ ์คํธ ์์ฑ โ ํ์ ๋๋ฑ์ฑ ๊ฒ์ฆ โ ์ปดํ์ผยทMyBatis XML ์ ํฉ์ฑ ์ ์ ๊ฒ์ฆ - โก SQL ์ต์ ํ ํ๋ค์ค (Phase C) โ
/sql-optimization-harness์ ๊ท ํ์ด์ง. ์ฟผ๋ฆฌยท์คํ๊ณํยทํต๊ณยท์ธ๋ฑ์คยท๋ณ๊ฒฝ ๋ถ๊ฐ ์ ์ฝ ๋ค์ค ์ ๋ ฅ โ ๋ณ๋ชฉ ๋ถ์ โ N๊ฐ ํ๋ณด (rewrite + DDL + ํํธ, ๋น์ฉ/๋ฆฌ์คํฌ/๋กค๋ฐฑ) โ ๊ฒฐ๊ณผ ๋๋ฑ์ฑยท๋ค๋ฅธ ์ฟผ๋ฆฌ ์ํฅ ํ๊ฐ โ ๋จ๊ณ๋ณ Rollout Plan - ๐ ๊ถํ ๊ฒ์ดํ
ํตํฉ โ 3๊ฐ ์ ๊ท feature key (
loganalyzer-harness,sp-migration-harness,sql-optimization-harness)๊ฐ ์ด๋๋ฏผ ๊ถํ ํ๋ฉด์์ ์ฆ์ ON/OFF. URL/CSRF/์ฌ์ด๋๋ฐ ๋ฉ๋ด๊น์ง ์๋ ์ ์ฉ - ๐งช ํ ์คํธ 27๊ฑด ์ ๊ท โ Orchestrator/PromptLoader 14 + Log RCA 10 + SP Migration 9 + SQL Optimization 6 + ๊ถํ ๋ฑ๋ก ํ๊ท ๋ฐฉ์ง 3. ์ ์ฒด 142๊ฐ ํ ์คํธ ํต๊ณผ, ํ๊ท 0๊ฑด
- ๐ฆ ํจํค์ง ๋ถ์ (์ ๊ท) โ Java ํจํค์ง ๋จ์ 4ํญ ๋ถ์ (
/package-overview):- ๐ ์์ฝ โ ํด๋์ค / Controller / Service / DAO / MyBatis ํต๊ณ
- ๐ ERD โ ์ฐ๊ด ํ ์ด๋ธ Mermaid + heatmap (ํํธ์นด์ดํธ)
- ๐ ํ ํ๋ฆ๋ โ ํจํค์ง๊ฐ ๊ฑด๋๋ฆฌ๋ ๋ชจ๋ ํ ์ด๋ธ โ MyBatis โ Java โ Controller ์๋ ๋ณํฉ
- ๐ ์คํ ๋ฆฌ โ Claude ๊ฐ ์ ์ ์นํ ํ๊ตญ์ด ๋งํฌ๋ค์ด ๋ด๋ฌํฐ๋ธ ์์ฑ
- ๐บ ์ ์ฌ ํจํค์ง ์ง๋ (์ ๊ท) โ
/project-map๋๋ฆด๋ค์ด ์นด๋ ๊ทธ๋ฆฌ๋ + ๊ฒ์ ํํฐ, ๋ฆฌํ ๋ ๋ฒจ ๋๋ฌ ์ ํจํค์ง ๊ฐ์๋ก ์ ํ - ๐ ๋ณด์ ๊ฐํ โ
POST /api/v1/package/refreshADMIN ๊ถํ ๊ฐ์ ,prefix200์ ๊ธธ์ด ์บก - โฑ Claude ํธ์ถ 45s ํ์์์ โ
PackageStoryService๊ฐ ์ธ๋ถ ์ง์ฐ์ผ๋ก ํฐ์บฃ ์ค๋ ๋๋ฅผ hang ์ํค์ง ์์ - ๐
toolkit.indexer.*์ธ๋ถํ โapplication.yml๋กmaxJavaScan/maxFileSizeํ๋ - ๐ค ํจํค์ง ์คํ ๋ฆฌ Markdown export โ
GET /api/v1/package/story/export?name=...์ํค ๋ณต๋ถ์ฉ - ๐ ์ด๋๋ฏผ ์บ์ ํต๊ณ โ
GET /api/v1/admin/caches(ADMIN ์ ์ฉ) - ๐งช ํ
์คํธ 12๊ฐ ์ ๊ท โ
PackageAnalysisControllerTest+PackageStoryServiceTest
- ๐ OpenAPI / Swagger UI โ
/swagger-ui.html(ADMIN ์ ์ฉ) ์ธํฐ๋ํฐ๋ธ API ์นดํ๋ก๊ทธ +/v3/api-docsJSON ์คํ - ๐ ์์ฒด ์๋ฌ ๋ชจ๋ํฐ๋ง (Sentry-style) โ
/admin/error-logdedupe + ์๋ unresolved ๋ณต๊ท + ์คํํธ๋ ์ด์ค ๋ณด์กด - ๐ ๋ฉํธ๋ฆญ 5์ข ์ ๊ท โ ์บ์ / SSE / ํ๋ค์ค 4๋จ๊ณ / ์๋ฌ์จ / ํ์ดํ๋ผ์ธ ๋จ๊ณ. Grafana 18 ํจ๋
- โ Helm Chart Kind ์๋ ๊ฒ์ฆ โ
bash scripts/test-helm.shํ ๋ฒ์ ๊ฒ์ฆ - ๐ค ์๋ ๋ณ๊ฒฝ ๋ก๊ทธ โ release-please-action โ feat/fix commit โ CHANGELOG.md + GitHub Release ์๋ ์์ฑ
- ๐งช ํ ์คํธ 64๊ฐ + JaCoCo โ ์ปค๋ฒ๋ฆฌ์ง ์๋ ๋ฆฌํฌํ
- ๐ ๋ฐ์ดํฐ ํ๋ฆ ๋ถ์ โ ํ
์ด๋ธ/SP/SQL_ID ์์์ ์์ MyBatis ยท Java ยท Controller ยท MiPlatform ๊น์ง ์๋ ์ถ์ โ ReactFlow ๋ค์ด์ด๊ทธ๋จ + LLM narrative + ๋ถ์ ์ด๋ ฅ/๊ณต์ ๋งํฌ.
/flow-analysis(Admin ๊ถํ ๊ด๋ฆฌ์์ ์ฌ์ฉ์๋ณ on/off)
๐ ์ ์ฒด ๋ณ๊ฒฝ ์ด๋ ฅ์ CHANGELOG.md โ release-please ๊ฐ ์๋ ๊ฐฑ์
์บก์ฒ ๊ฐ์ด๋:
docs/screenshots/README.md
| ํ ๋์๋ณด๋ | SQL ๋ฆฌ๋ทฐ |
|---|---|
![]() |
![]() |
| ํ์ดํ๋ผ์ธ ๊ทธ๋ํ | 4๋จ๊ณ ํ๋ค์ค |
![]() |
![]() |
| SQL ์ธ๋ฑ์ค ์๋ฎฌ๋ ์ด์ | ๋น์ฉ ์ตํฐ๋ง์ด์ |
|---|---|
![]() |
![]() |
| ์ธ์ด ์ ํ (5๊ฐ ์ธ์ด) | ๋์๋ณด๋ ํธ์ง ๋ชจ๋ |
![]() |
![]() |
| Swagger UI | ์ค๋ฅ ๋ก๊ทธ (Sentry-style) | Grafana ๋์๋ณด๋ |
|---|---|---|
![]() |
![]() |
![]() |
| ์นดํ ๊ณ ๋ฆฌ | ์ฃผ์ ๊ธฐ๋ฅ |
|---|---|
| ๐ค ๋ฐ์ดํฐ ๋ด๋ณด๋ด๊ธฐ | SARIF 2.1.0 (IDE ์ฐ๋) / Excel ์ํฌ๋ถ (3 ์ํธ + ํฉ๊ณ ์์) |
| ๐ ๋ชจ๋ํฐ๋ง | Prometheus + Grafana ์๋ ํ๋ก๋น์ ๋ (docker-compose --profile monitoring) |
| ๐ฐ ๋น์ฉ ์ต์ ํ | AI ๋ชจ๋ธ ๋น์ฉ ์ตํฐ๋ง์ด์ โ ๋ถ์ ์ ํ๋ณ ์ต์ ๋ชจ๋ธ ์ถ์ฒ (Haiku/Sonnet/Opus) |
| โก DB ๋ถ์ | SQL ์ธ๋ฑ์ค ์ํฉํธ ์๋ฎฌ๋ ์ด์ โ ์ ์ ํ์ฑ + JDBC ๋ฉํ์กฐํ + DDL ์๋ ์์ฑ |
| ๐ ๋ค๊ตญ์ด | ํ / ์ / ์ผ / ์ค / ๋ (5๊ฐ ์ธ์ด) + ์ฌ์ฉ์๋ณ ์๋ ๋๊ธฐํ |
| ๐ ๋์๋ณด๋ | ์์ ฏ ๋๋๊ทธ/๋ฆฌ์ฌ์ด์ฆ/์จ๊น + ์ฌ์ฉ์๋ณ ๋ ์ด์์ ์์ํ |
| ๐ณ ์ํฌํ๋ก | ํ์ดํ๋ผ์ธ ์ธํฐ๋ํฐ๋ธ ๊ทธ๋ํ (reactflow) |
| โ K8s ๋ฐฐํฌ | Helm Chart (Deployment + Service + Ingress + HPA + ServiceMonitor) |
| ๋ชจ๋ | ์ค๋ช | ํํ |
|---|---|---|
claude-spring-boot-starter |
application.yml ์ค์ ๋ง์ผ๋ก ClaudeClient Bean ์๋ ์ฃผ์
. SSE ์คํธ๋ฆฌ๋ฐ + ๋ฐํ์ ๋ชจ๋ธ ์ ํ ์ง์ |
Library |
claude-sql-advisor |
SQL / Oracle SP ๋ฆฌ๋ทฐ, ๋ณด์ ๊ฐ์ฌ, ์ธ๋ฑ์ค ์ต์ ํ ์ ์, ERD ๋ถ์, Mock ๋ฐ์ดํฐ ์์ฑ, DB ๋ง์ด๊ทธ๋ ์ด์ | Library + CLI |
claude-doc-generator |
๊ธฐ์ ๋ฌธ์(Oracle Package ํฌํจ), Javadoc ์์ฑ, ๋ฆฌํฉํฐ๋ง ์ ์, ํ ์คํธ ์ฝ๋, API ๋ช ์ธ, ์ฝ๋ ๋ณํ(iBatis ์ง์), ์ฝ๋ ๋ฆฌ๋ทฐ, ๋ณต์ก๋ ๋ถ์, pom.xml ์์กด์ฑ ๋ถ์, ๋ฐ์ดํฐ ๋ง์คํน, Spring ๋ง์ด๊ทธ๋ ์ด์ , ๋ก๊ทธ ๋ถ์, ์ ๊ท์ ์์ฑ, ์ปค๋ฐ ๋ฉ์์ง ์์ฑ | Library + CLI |
claude-toolkit-ui |
์ ๊ธฐ๋ฅ๋ค์ ์น ๋ธ๋ผ์ฐ์ ์์ ์ฌ์ฉํ๋ React SPA ๋์๋ณด๋ (v4.0 ์ ํ) | Spring Boot + React |
- JDK 1.8+
- Maven 3.6+
- Node.js 18+ (React ๋น๋ โ
frontend-maven-plugin์ด ์๋ ์ค์นํ๋ฏ๋ก ์ ํ) - Anthropic API Key
git clone https://github.com/Sangmoo/Claude-Java-Toolkit.git
cd claude-java-toolkit
# Maven ๋น๋ (React ํ๋ก ํธ์๋ ์๋ ๋น๋ ํฌํจ)
mvn clean package -DskipTestsfrontend-maven-plugin์ด ์๋์ผ๋ก Node.js ์ค์น โ npm install โ npm run build๋ฅผ ์ํํฉ๋๋ค.
๋น๋ ๊ฒฐ๊ณผ๋ฌผ์ claude-toolkit-ui/src/main/resources/static/app/์ ์์ฑ๋ฉ๋๋ค.
ํ๋ก ํธ์๋๋ง ๊ฐ๋ฐํ ๋:
cd claude-toolkit-ui/frontend && npm run dev(Vite ๊ฐ๋ฐ ์๋ฒ 5173 ํฌํธ) IntelliJ ์ฌ์ฉ์: Maven ํจ๋์์ ๋ฃจํธ ํ๋ก์ ํธ โLifecycleโpackage๋๋ธํด๋ฆญ
# Mac / Linux
export CLAUDE_API_KEY=sk-ant-...
# Windows PowerShell
$env:CLAUDE_API_KEY="sk-ant-..."
cd claude-toolkit-ui
mvn spring-boot:run๋ธ๋ผ์ฐ์ ์์ http://localhost:8027 ์ ์ โ ์ค์น ๋ง๋ฒ์ฌ ๋๋ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋
์ด๊ธฐ ๋ก๊ทธ์ธ: admin / admin1234
# 1. ํ๊ฒฝ๋ณ์ ์ค์
cp .env.example .env
# .env ํ์ผ์์ CLAUDE_API_KEY ์ค์
# 2-A. H2 DB (๊ธฐ๋ณธ๊ฐ, ๋ณ๋ DB ๋ถํ์)
docker-compose up -d claude-toolkit
# 2-B. MySQL ์ฌ์ฉ ์
DB_TYPE=mysql docker-compose --profile mysql up -d
# 2-C. PostgreSQL ์ฌ์ฉ ์
DB_TYPE=postgresql docker-compose --profile postgresql up -d
# 2-D. Prometheus + Grafana ๋ชจ๋ํฐ๋ง ์คํ ํจ๊ป ์คํ (v4.3.0)
docker-compose --profile monitoring up -d
# โ Grafana: http://localhost:3000 (admin/admin)
# โ Prometheus: http://localhost:9090
# โ "Claude Toolkit Overview" ๋์๋ณด๋ ์๋ ํ๋ก๋น์ ๋ (10๊ฐ ํจ๋)
# ์ ์ต์
๋ค์ ์กฐํฉ ๊ฐ๋ฅ:
DB_TYPE=postgresql docker-compose --profile postgresql --profile monitoring up -d| ํ๊ฒฝ๋ณ์ | ์ค๋ช | ๊ธฐ๋ณธ๊ฐ |
|---|---|---|
CLAUDE_API_KEY |
Anthropic API ํค | (ํ์) |
CLAUDE_MODEL |
์ฌ์ฉํ Claude ๋ชจ๋ธ ID (์ฌ๋น๋ ์์ด ์ ํ ๊ฐ๋ฅ) | claude-sonnet-4-6 |
DB_TYPE |
๋ด๋ถ DB ์ ํ (h2/mysql/postgresql) |
h2 |
DB_HOST |
DB ํธ์คํธ (MySQL/PG) | db |
DB_PORT |
DB ํฌํธ | 3306 |
DB_NAME |
DB ์ด๋ฆ | claude_toolkit |
DB_USERNAME / DB_PASSWORD |
DB ๊ณ์ | claude / claude1234 |
PORT |
์น ์๋ฒ ํฌํธ | 8027 |
GRAFANA_USER / GRAFANA_PASSWORD |
Grafana ๊ด๋ฆฌ์ ๊ณ์ (monitoring ํ๋กํ) | admin / admin |
TOOLKIT_LIVEDB_ENABLED |
Live DB ์ง์ ์ฐ๊ฒฐ ๊ธฐ๋ฅ ํ์ฑํ | true |
TOOLKIT_LIVEDB_TIMEOUT |
Live DB ์ฟผ๋ฆฌ ํ์์์(์ด) | 30 |
TOOLKIT_LIVEDB_MAX_ROWS |
Live DB ์กฐํ ์ต๋ ํ ์ | 1000 |
TOOLKIT_LIVEDB_RATE_LIMIT |
์ฌ์ฉ์๋ณ ๋ถ๋น Live DB ํธ์ถ ํ๋ | 10 |
TOOLKIT_MCP_AUTOSTART |
์ฌ๋ด๋ง MCP Server ์๋ ๊ธฐ๋ ์ฌ๋ถ (true/false). Docker ์์๋ false ๊ธฐ๋ณธ โ MCP ๋ ๋ณ๋ ์ปจํ
์ด๋๋ก ๋ถ๋ฆฌ. dev ํ๊ฒฝ์์๋ true (application.yml ๊ธฐ๋ณธ) |
false (Docker) |
CTK_DB_PROFILE_ID |
MCP ๋๊ตฌ ๊ธฐ๋ณธ Live DB ํ๋กํ ID | (๋ฏธ์ค์ ) |
Kubernetes / Helm ๋ฐฐํฌ๋ ์๋ ๐ข ๋ฐฐํฌ ๊ฐ์ด๋ ์น์ ์ฐธ๊ณ .
์ต์ด ์คํ ์ /setup ํ์ด์ง๋ก ์๋ ์ด๋ํฉ๋๋ค:
- API ํค ์ ๋ ฅ โ ์ ์ฅ + ์ฐ๊ฒฐ ํ ์คํธ
- Oracle DB ์ค์ โ ์ ์ฅ + ์ฐ๊ฒฐ ํ ์คํธ (์ ํ)
- ์ด๋ฉ์ผ ์ค์ (๋์ค์ Settings์์ ์ค์ ๊ฐ๋ฅ)
- ์๋ฃ โ ๊ด๋ฆฌ์ ๊ณ์ ํ์ธ
"๋์ค์ ์ค์ ํ๊ธฐ" ๋ฒํผ์ผ๋ก ์คํตํ๊ณ Settings์์ ๊ฐ๋ณ ์ค์ ํ ์ ์์ต๋๋ค.
curl http://localhost:8027/actuator/health{
"status": "UP",
"components": {
"claudeApi": { "status": "UP", "details": { "model": "claude-sonnet-4-6", "responseTime": "342ms" } },
"oracleDb": { "status": "UNKNOWN", "details": { "reason": "Oracle DB ๋ฏธ์ค์ " } },
"mcpServer": { "status": "UP", "details": { "status": "running", "pid": 12345, "port": 8028 } }
}
}v4.3.0 ๊ธฐ์ค โ ํ๊ฒฝ์ ๋ฐ๋ผ 3๊ฐ์ง ์ต์ ์ค ์ ํํ์ธ์.
| ํญ๋ชฉ | ๐ข Docker Compose | ๐ก JAR ์ง์ ์คํ | ๐ต Kubernetes (Helm) |
|---|---|---|---|
| ์ค์น ๋์ด๋ | โญ | โญ | โญโญโญ |
| ๋ค์ค ์ธ์คํด์ค | โ | โ | โ |
| ์๋ ์ค์ผ์ผ๋ง (HPA) | โ | โ | โ |
| ๋ฌด์ค๋จ ๋ฐฐํฌ | โ | ์๋ | โ |
| Prometheus ์ฐ๋ | โ (Compose ํ๋กํ) | โ | โ (ServiceMonitor) |
| ์์ ๋ณผ๋ฅจ | โ (volume) | OS FS | โ (PVC) |
| Ingress / TLS | ๋ณ๋ ์ค์ | ๋ณ๋ ์ค์ | โ (annotation 1์ค) |
| ๊ถ์ฅ ํ๊ฒฝ | ๊ฐ๋ฐ / ์๊ท๋ชจ (~10๋ช ) | VM ๋จ์ผ ์๋ฒ | ์ด์, ๋ค์ค ์ฌ์ฉ์ |
# 1. ํ๊ฒฝ๋ณ์ ์ค์
cp .env.example .env
# .env ์์ CLAUDE_API_KEY=sk-ant-... ์ค์
# 2-A. H2 DB (๊ธฐ๋ณธ โ ๋ณ๋ DB ๋ถํ์)
docker-compose up -d
# 2-B. MySQL ์ฌ์ฉ
DB_TYPE=mysql docker-compose --profile mysql up -d
# 2-C. PostgreSQL ์ฌ์ฉ
DB_TYPE=postgresql docker-compose --profile postgresql up -d
# 2-D. Prometheus + Grafana ๋ชจ๋ํฐ๋ง ์คํ ํจ๊ป ์คํ
docker-compose --profile monitoring up -d
# โ Grafana: http://localhost:3000 (admin/admin)
# โ Prometheus: http://localhost:9090
# โ "Claude Toolkit Overview" ๋์๋ณด๋ ์๋ ํ๋ก๋น์ ๋
# ์กฐํฉ ๊ฐ๋ฅ โ ์ธ๋ถ PG + ๋ชจ๋ํฐ๋ง ๋์ ํ์ฑํ
DB_TYPE=postgresql docker-compose --profile postgresql --profile monitoring up -d# 1. ๋น๋
mvn clean package -DskipTests
# 2. ํ๊ฒฝ๋ณ์ ์ค์ + ์คํ
export CLAUDE_API_KEY=sk-ant-...
export DB_TYPE=h2 # ๋๋ mysql / postgresql
java -jar claude-toolkit-ui/target/claude-toolkit-ui-0.1.0-SNAPSHOT.jar
# ๋๋ systemd ์๋น์ค๋ก ๋ฑ๋ก (CentOS/Ubuntu)
sudo cp claude-toolkit.service /etc/systemd/system/
sudo systemctl enable --now claude-toolkit์ ํฉํ ๊ฒฝ์ฐ: Docker ๋ฏธ์ฌ์ฉ ์ฌ๋ด VM, ๊ฐ๋จํ ๋จ์ผ ์๋ฒ ์ด์
โ ๏ธ ํ์ฌ ์ํ:helm/claude-toolkit/v0.1.0 โhelm template์ ์ ๊ฒ์ฆ๋ง ์๋ฃ. ์ค์ K8s ํ๊ฒฝ ์ ์ฉ ์ ํ ์คํธ ํด๋ฌ์คํฐ ๊ฒ์ฆ ๊ถ์ฅ.
GitHub ๋๋ ๋ก์ปฌ ์ ์ฅ์์ ๋ค์ ๊ฒฝ๋ก:
helm/claude-toolkit/
โโโ Chart.yaml โ ์ฐจํธ ๋ฉํ์ ๋ณด (์ด๋ฆ, ๋ฒ์ , appVersion)
โโโ values.yaml โ ๋ชจ๋ ์ค์ ๊ฐ + ํ๊ธ ์ฃผ์ (ํ๋ ์ ์ด ํ์ผ๋ง ๋ณด๋ฉด ๋จ)
โโโ README.md โ 6๊ฐ์ง ์ค์น ์๋๋ฆฌ์ค + ์ต์
ํ
โโโ templates/
โโโ _helpers.tpl โ ๊ณตํต ํฌํผ (์ด๋ฆ, ๋ผ๋ฒจ)
โโโ deployment.yaml โ ์ฑ ์ปจํ
์ด๋ + env + healthcheck
โโโ service.yaml โ ClusterIP
โโโ ingress.yaml โ Ingress (์ต์
)
โโโ secret.yaml โ Claude API Key + DB Password
โโโ pvc.yaml โ H2 ๋ชจ๋ ์์ํ ๋ณผ๋ฅจ
โโโ hpa.yaml โ Horizontal Pod Autoscaler
โโโ serviceaccount.yaml
โโโ servicemonitor.yaml โ prometheus-operator ์ฐ๋
โโโ NOTES.txt โ ์ค์น ํ ์๋ด ๋ฉ์์ง
Windows (winget โ ๊ถ์ฅ):
# ์ฌ์ : Docker Desktop ์ค์น๋์ด ์์ด์ผ ํจ
winget install Kubernetes.kind # Kind (Kubernetes in Docker)
winget install Kubernetes.kubectl # kubectl
winget install Helm.Helm # HelmmacOS:
brew install kind kubectl helmLinux (Ubuntu/Debian):
# Kind
[ $(uname -m) = x86_64 ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.22.0/kind-linux-amd64
chmod +x ./kind && sudo mv ./kind /usr/local/bin/kind
# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
# Helm
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list && \
sudo apt-get update && sudo apt-get install helm# ํ๊ฒฝ๋ณ์ ์ค์ (์์ผ๋ฉด ์ข์, ์์ด๋ ๋์)
export CLAUDE_API_KEY=sk-ant-...
# ๊ฒ์ฆ ์๋ ์คํ
bash scripts/test-helm.sh
# ์ต์
:
bash scripts/test-helm.sh --skip-build # ์ด๋ฏธ์ง ์ฌ๋น๋ ์๋ต (์ฌ์คํ ์)
bash scripts/test-helm.sh --keep # ๊ฒ์ฆ ํ ํด๋ฌ์คํฐ ์ ์ง (์๋ ๋๋ฒ๊น
)์คํฌ๋ฆฝํธ๊ฐ ์๋์ผ๋ก:
- โ ๋๊ตฌ ์ค์น ํ์ธ (docker / kind / kubectl / helm)
- โ
Kind ํด๋ฌ์คํฐ
claude-test์์ฑ - โ Docker ์ด๋ฏธ์ง ๋น๋ โ Kind ํด๋ฌ์คํฐ์ ๋ก๋ (registry ๋ถํ์)
- โ Namespace + Secret ์์ฑ
- โ
helm lint+helm template์ ์ ๊ฒ์ฆ - โ
helm install(์๋๋ฆฌ์ค A: H2 ๋จ์ผ ์ธ์คํด์ค) - โ Pod READY 1/1 ๋๊ธฐ (์ต๋ 3๋ถ)
- โ
ํฌ์ค์ฒดํฌ
/actuator/healthโstatus=UP๊ฒ์ฆ - โ
Prometheus ๋ฉํธ๋ฆญ ๋
ธ์ถ ๊ฒ์ฆ (
claude_*์ค ์นด์ดํธ) - โ Spring Boot ์์ ๋ก๊ทธ ํ์ธ
# ์ฐจํธ๋ง ์ ์ ๊ฒ์ฆ
helm lint ./helm/claude-toolkit # ๋ฌธ๋ฒ ๊ฒ์ฆ
helm template test ./helm/claude-toolkit --debug | less # ๋งค๋ํ์คํธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ5๊ฐ์ง ์๋๋ฆฌ์ค ์์ธ (B: PostgreSQL, C: Ingress+TLS, D: HPA ์๋ ํ์ฅ, E: prometheus-operator) ๋ helm/claude-toolkit/VALIDATION.md ์ฐธ๊ณ .
# 1. ์ปจํ
์ด๋ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ด/๊ณต๊ฐ ๋ ์ง์คํธ๋ฆฌ์ ํธ์
docker build -t ghcr.io/<your-org>/claude-java-toolkit:4.3.0 .
docker push ghcr.io/<your-org>/claude-java-toolkit:4.3.0
# 2. Claude API Key Secret ์์ฑ (๊ถ์ฅ โ values ํ์ผ์ ํ๋ฌธ ์ ์ฅ ๊ธ์ง)
kubectl create namespace claude-toolkit
kubectl create secret generic claude-api-secret \
--from-literal=CLAUDE_API_KEY=sk-ant-... \
-n claude-toolkit
# 3-A. ๋จ์ ์ค์น (H2 + ๋จ์ผ ์ธ์คํด์ค โ ๊ฐ๋ฐ / ๊ฒ์ฆ์ฉ)
helm install claude-toolkit ./helm/claude-toolkit \
-n claude-toolkit \
--set image.repository=ghcr.io/<your-org>/claude-java-toolkit \
--set image.tag=4.3.0 \
--set secret.existingSecret=claude-api-secret
# 3-B. ์ด์ ๊ถ์ฅ ๊ตฌ์ฑ (์ธ๋ถ PostgreSQL + Ingress + HPA + Prometheus ์ฐ๋)
helm upgrade --install claude-toolkit ./helm/claude-toolkit \
-n claude-toolkit \
--set image.repository=ghcr.io/<your-org>/claude-java-toolkit \
--set image.tag=4.3.0 \
--set secret.existingSecret=claude-api-secret \
--set db.type=postgresql --set db.host=postgres.example.com \
--set persistence.enabled=false \
--set ingress.enabled=true --set ingress.host=claude.example.com \
--set ingress.className=nginx \
--set autoscaling.enabled=true \
--set autoscaling.minReplicas=2 --set autoscaling.maxReplicas=5 \
--set monitoring.enabled=true \
--set monitoring.serviceMonitor.enabled=true| ํค | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|
image.repository |
ghcr.io/sangmoo/claude-java-toolkit |
์ด๋ฏธ์ง |
image.tag |
"" (= Chart.appVersion) |
์ด๋ฏธ์ง ํ๊ทธ |
replicaCount |
1 |
Pod ๋ณต์ ์ (HPA ๋นํ์ฑ ์) |
db.type |
h2 |
h2 / mysql / postgresql |
claude.apiKey |
"" |
API ํค (๋๋ secret.existingSecret) |
claude.model |
claude-sonnet-4-6 |
๊ธฐ๋ณธ ๋ชจ๋ธ |
persistence.size |
5Gi |
H2 PVC ํฌ๊ธฐ |
ingress.enabled |
false |
Ingress ํ์ฑํ |
autoscaling.enabled |
false |
HPA ํ์ฑํ |
monitoring.enabled |
false |
Prometheus annotation/ServiceMonitor |
์ ์ฒด ์ต์
์ helm/claude-toolkit/values.yaml, ์๋๋ฆฌ์ค๋ helm/claude-toolkit/README.md ์ฐธ๊ณ .
# ์ ๋ฒ์ ์ผ๋ก ์
๊ทธ๋ ์ด๋ (๋ค๋ฅธ ์ต์
์ ์ง)
helm upgrade claude-toolkit ./helm/claude-toolkit -n claude-toolkit \
--reuse-values --set image.tag=4.3.1
# ์ญ์ (PVC ๋ ๋ณ๋๋ก ์ญ์ ํด์ผ ๋ฐ์ดํฐ ์๊ตฌ ์ญ์ ๋จ)
helm uninstall claude-toolkit -n claude-toolkit
kubectl delete pvc -l app.kubernetes.io/instance=claude-toolkit -n claude-toolkit- ์ด๋ฏธ์ง ๋ ์ง์คํธ๋ฆฌ ํ์ (Helm): ์ฐจํธ๋ ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ์ง ์์ต๋๋ค. GHCR / Docker Hub / ์ฌ๋ด Harbor ๋ฑ์ ๋ฏธ๋ฆฌ ํธ์ ํ์
- API Key ๋ณดํธ:
--set claude.apiKey=...๋ณด๋คkubectl create secret + secret.existingSecret๊ถ์ฅ (--set๊ฐ์ helm history ์ ํ๋ฌธ ๋ ธ์ถ๋จ) - DB ๋ง์ด๊ทธ๋ ์ด์ : H2 โ PostgreSQL ์ ํ ์ ์น UI ์ "Settings โ DB ๋ง์ด๊ทธ๋ ์ด์ ๊ฐ์ด๋" ํ์ด์ง ์ฐธ๊ณ
- ์ฒซ ๋ฐฐํฌ ํ: Helm
NOTES.txt์ ๋ช ๋ น์ด๋ก ํฌ์ค์ฒดํฌ + ๋ก๊ทธ ํ์ธ ๊ถ์ฅ - ๋ชจ๋ํฐ๋ง ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋: Prometheus ~300MB, Grafana ~200MB โ ๋ฆฌ์์ค ์ ํ๋ ํ๊ฒฝ์ ์ตํธ์ธ ๋ฐฉ์์ผ๋ก ๋ถ๋ฆฌ๋จ (๊ธฐ๋ณธ OFF)
- Helm ์ฐจํธ ๋ฏธ๊ฒ์ฆ: ์ค K8s ํ๊ฒฝ ๋ฏธ๋ณด์ ๋ก ์ ์ ๊ฒ์ฆ๋ง ์๋ฃ. ์ด์ ์ ์ฉ ์ ๋ฐ๋์ ํ ์คํธ ํด๋ฌ์คํฐ์์ ๊ฒ์ฆ ํ์
์น ๋์๋ณด๋๋ ์ฌ์ด๋๋ฐ ๋ค๋น๊ฒ์ด์ ๊ตฌ์กฐ๋ก 65+ ํ์ด์ง ์ AI ๋๊ตฌ / ๋ถ์ / ๊ด๋ฆฌ / ์ค์ ํ๋ฉด์ ์ ๊ณตํฉ๋๋ค. ์๋๋ ์นดํ ๊ณ ๋ฆฌ๋ณ ํต์ฌ ๊ธฐ๋ฅ๋ง ์์ฝ. ์์ธํ ์ฌ์ฉ๋ฒ์ ๊ฐ ํ์ด์ง์ ์ธ๋ผ์ธ ๋์๋ง ์ฐธ๊ณ .
| ๊ธฐ๋ฅ | ๊ฒฝ๋ก | ํต์ฌ |
|---|---|---|
| SQL ๋ฆฌ๋ทฐ | /advisor |
์๋ SQL ํ์ ๊ฐ์ง, EXPLAIN PLAN ์ฐ๋, ๋ณด์ / ์ธ๋ฑ์ค / Diff ํญ, HIGH/MED/LOW ํํฐ |
| SQL ์ธ๋ฑ์ค ์๋ฎฌ๋ ์ด์ โจ | /sql/index-advisor |
WHERE/JOIN ์ ์ ํ์ฑ + JDBC ๋ฉํ์กฐํ โ ํ์ฉ ๊ฐ๋ฅ ์ธ๋ฑ์ค + ์ ๊ท DDL ์ถ์ฒ |
| ERD ๋ถ์ / DDL ์์ฑ | /erd |
DB ์๋ ์ค์บ + Mermaid ERD + ์ญ๋ฐฉํฅ DDL (Oracle ๊ท๊ฒฉ) |
| ์คํ๊ณํ ๋ถ์ | /explain |
์ธํฐ๋ํฐ๋ธ ํธ๋ฆฌ, ์คํผ๋ ์ด์ ์์, Cost ๋ฐ, AI ๋ถ์ SSE ์คํธ๋ฆฌ๋ฐ |
| ์คํ๊ณํ Before/After ๋น๊ต | /explain/compare |
๋ SQL ๋์ ์คํ + Cost ๋ณํ์จ (% ๊ฐ์ /์ ํ) |
| SQL ์ฑ๋ฅ ํ์คํ ๋ฆฌ ๋์๋ณด๋ | /explain/dashboard |
Cost ์ถ์ด ์ฐจํธ + ๊ธฐ๊ฐ ํํฐ + ๋ณํ์จ ์์ ๊ตฌ๋ถ |
| ๋ฐฐ์น SQL ๋ถ์ | /sql-batch |
์ต๋ 30๊ฐ SQL ์ผ๊ด + CSV ์ ๋ก๋ + ์์ฝ๋์ธ ๊ฒฐ๊ณผ |
| SQL DB ๋ฒ์ญ | /sql-translate |
Oracle โ MySQL โ PostgreSQL ๋ฌธ๋ฒ ๋ณํ |
| ๊ธฐ๋ฅ | ๊ฒฝ๋ก | ํต์ฌ |
|---|---|---|
| ์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ค์ค | /harness |
Analyst โ Builder โ Reviewer โ Verifier 4๋จ๊ณ + Diff + ํ์ง ์ ์ + ๊ฒ์ฆ ํ์ |
| SPโJava ๋ง์ด๊ทธ๋ ์ด์ ํ๋ค์ค ๐ | /sp-migration-harness |
SP ์๋ฏธ ๋ถ์ โ Service+Mapper+XML+DTO+ํ ์คํธ ์์ฑ โ ํ์ ๋๋ฑ์ฑ โ ์ ์ ๊ฒ์ฆ. DB ์ค๋ธ์ ํธ ์๋ ๋ก๋ |
| SQL ์ต์ ํ ํ๋ค์ค ๐ | /sql-optimization-harness |
๋ณ๋ชฉ ๋ถ์ โ N๊ฐ ํ๋ณด (rewrite + DDL + ํํธ) โ ์ฐ๋ ํ๊ฐ โ ๋จ๊ณ๋ณ Rollout Plan |
| ํ๋ค์ค ๋ฐฐ์น | /harness/batch |
Java/SQL ๋ค์ค ํญ๋ชฉ ์์ฐจ ๋ถ์, ๋ค์ค ์ด๋ฉ์ผ ์๋ฆผ, ์๊ตฌ ์ด๋ ฅ |
| DB ์์กด์ฑ ๋ถ์ | /harness/dependency |
SP/Package/Java ์์กด ํ ์ด๋ธยทํธ์ถ ๊ด๊ณยท์ํ ์ํ |
| ํ์ง ๋์๋ณด๋ | /harness/dashboard |
๋์ ํต๊ณ / ํ์ ๋น์จ / ํ์ง ์ ์ ์ถ์ด + ๋๋ฆด๋ค์ด ๋ชจ๋ฌ |
| Java ์ฝ๋ ๋ฆฌ๋ทฐ | /codereview |
OWASP Top 10, SOLID, ์ปจํ ์คํธ ์ฃผ์ |
| ๋ณต์ก๋ ๋ถ์ | /complexity |
์ํ ๋ณต์ก๋ + ์ฐ์ ์์ ํํฐ (๋จ์ผ/ํ๋ก์ ํธ ๋ชจ๋) |
| ๋ฐ์ดํฐ ํ๋ฆ ๋ถ์ | /flow-analysis |
ํ ์ด๋ธ/SP/SQL_ID โ MyBatis ยท Java ยท Controller ยท MiPlatform ์๋ ์ถ์ |
| ํ ์ด๋ธ ์ํฅ ๋ถ์ โจ | /impact |
ํ ์ด๋ธ โ MyBatis โ Java โ Controller 4๋จ๊ณ ์ญ์ถ์ , ํ์ผ ํ ํด๋ฆญ โ ๋ชจ๋ฌ + ์ ์ฒด ๋ณต์ฌ, DB ํ ์ด๋ธ ํฝ์ปค |
| ํจํค์ง ๋ถ์ ๐ | /package-overview |
Java ํจํค์ง ๋จ์ 4ํญ (์์ฝ/ERD/ํํ๋ฆ๋/์คํ ๋ฆฌ), Markdown export |
| ์ ์ฌ ํจํค์ง ์ง๋ ๐ | /project-map |
๋๋ฆด๋ค์ด ์นด๋ ๊ทธ๋ฆฌ๋ + ๊ฒ์ ํํฐ, ๋ฆฌํ โ ํจํค์ง ๊ฐ์๋ก ์ ํ |
| ๊ธฐ๋ฅ | ๊ฒฝ๋ก | ํต์ฌ |
|---|---|---|
| ๊ธฐ์ ๋ฌธ์ ์์ฑ | /docgen |
Oracle SP/Package, Java/MyBatis โ Markdown/Typst/HTML, ํ/์ |
| Javadoc ์์ฑ | /javadoc |
public ๋ฉค๋ฒ ์๋ ์ฃผ์, SSE ์คํธ๋ฆฌ๋ฐ |
| ๋ฆฌํฉํฐ๋ง ์ ์ | /refactor |
์ฝ๋ ๋ฌธ์ ์ โ ๊ฐ์ ์ฝ๋ (์ /ํ ๋น๊ต) |
| ํ ์คํธ ์ฝ๋ ์์ฑ | /testgen |
Controller/Service/Mapper ๋ณ JUnit 5 ์ ๋ต |
| API ๋ช ์ธ ์์ฑ | /apispec |
OpenAPI 3.0 YAML / Swagger 2.0 ์ด๋ ธํ ์ด์ |
| ์ฝ๋ ๋ณํ | /converter |
SPโJava/MyBatis ์๋ฐฉํฅ + iBatisโMyBatis |
| Mock ๋ฐ์ดํฐ | /mockdata |
DDL โ INSERT/MERGE/CSV (1~1000 ๊ฑด) |
| DB ๋ง์ด๊ทธ๋ ์ด์ | /migration |
DDL ๋น๊ต โ Oracle/Flyway/Liquibase + ์ํ๋ |
| ๋ฐ์ดํฐ ๋ง์คํน | /maskgen |
DDL ๋ถ์ โ ๋ฏผ๊ฐ ์ปฌ๋ผ ์๋ ํ์ง + UPDATE ๋ง์คํน SQL |
| pom.xml ๋ถ์ | /depcheck |
์ทจ์ฝ ์์กด์ฑ, ๋ฒ์ ์ถฉ๋, ์ ๊ทธ๋ ์ด๋ ๊ถ๊ณ |
| Spring Boot 3.x ๋ง์ด๊ทธ๋ ์ด์ | /migrate |
javax โ jakarta ์ฒดํฌ๋ฆฌ์คํธ |
| ๊ธฐ๋ฅ | ๊ฒฝ๋ก | ํต์ฌ |
|---|---|---|
| ๋ก๊ทธ ๋ถ์๊ธฐ | /loganalyzer |
์ผ๋ฐ/๋ณด์ ๋ถ์, .log ์
๋ก๋, ๐ RCA ํ๋ค์ค 4๋จ๊ณ ๋ชจ๋ (๊ฐ์คโ๊ฒ์ฆSQL+ํจ์น+๋กค๋ฐฑโ์ฐ๋โ์ฌ๋ด ํ์ค RCA ๋ณด๊ณ ์) |
| ์ ๊ท์ ์์ฑ๊ธฐ | /regex |
์์ฐ์ด โ regex + 5๊ฐ ์ธ์ด ์์ + ๋น ๋ฅธ ์์ 8์ข |
| ์ปค๋ฐ ๋ฉ์์ง ์์ฑ๊ธฐ | /commitmsg |
Conventional/Gitmoji/Simple/Angular ์คํ์ผ |
| ํตํฉ ์ํฌ์คํ์ด์ค | /workspace |
๋ค์ค ๋ถ์ ๋ณ๋ ฌ ์คํ (Java/SQL ์ธ์ด๋ณ ๊ธฐ๋ฅ ์๋ ๋ ธ์ถ) |
| ๋ถ์ ํ์ดํ๋ผ์ธ | /pipelines |
YAML ๊ธฐ๋ฐ ๋ค๋จ๊ณ, ์ธํฐ๋ํฐ๋ธ ๊ทธ๋ํ ๋ทฐ โจ, ์ค์ผ์ค๋ง |
| AI ์ฑํ | /chat |
Claude ์์ ๋ํ, ๋ฉํฐํด ์ปจํ ์คํธ |
| ๊ธฐ๋ฅ | ๊ฒฝ๋ก | ํต์ฌ |
|---|---|---|
| ๋ฆฌ๋ทฐ ์ด๋ ฅ | /history |
์๋ ์ ์ฅ, ๊ฒ์/ํํฐ, SARIF/Excel ๋ด๋ณด๋ด๊ธฐ โจ, ๊ณต์ ๋งํฌ, ์ฆ๊ฒจ์ฐพ๊ธฐ |
| ์ฆ๊ฒจ์ฐพ๊ธฐ | /favorites |
ํ๊ทธ๋ณ ์ ๋ฆฌ, H2 ์์ํ, (์ฌ์ฉ์, ์ด๋ ฅ) ์ค๋ณต ๋ฐฉ์ง |
| ๊ฒ์ โจ | /search |
๋ฉ๋ด ์นดํ๋ก๊ทธ + ๋ถ์ ์ด๋ ฅ ํตํฉ ๊ฒ์. ์๋จ๋ฐ ๊ธ๋ก๋ฒ ๊ฒ์์ฐฝ(ํ๋ ํธ accent ์์)์์ ๋ชจ๋ ํ์ด์ง ์ด๋์๋ ํธ์ถ ๊ฐ๋ฅ. ํ์ /๋ ์ง ํํฐ + ์ ๋ ฌ + ๋งค์น ๊ฐ์กฐ โจ v4.7 |
| ROI ๋ฆฌํฌํธ + ์ธ์ฌ์ดํธ โจ v4.7 | /roi-report |
์์คํ ํต๊ณ + ๋ด ํ๋ (Top 5 ๊ธฐ๋ฅ / 12์ฃผ ์ถ์ด) + ํ ๋น๊ต (๋ฐฑ๋ถ์ยท์์ยท์งํ ๋ฐ, ADMIN) |
| ์ปดํ๋ผ์ด์ธ์ค ๋ฆฌํฌํธ โจ v4.7 | /admin/compliance-report (ADMIN) |
FSS / PIPA / ์ ๋ณดํต์ ๋ง๋ฒ / ์ธ๋ถ๊ฐ์ฌ 4์ข , Markdown / Excel(POI 4์ํธ) / ์ธ์ยทPDF + AI ๊ฒฝ์์ง ์์ฝ + ์๊ตฌ ์ ์ฅ ์ด๋ ฅ (์ต๋ 500๊ฑด) |
| ์ฌ์ฉ๋ | /usage |
์ผ/์๋ณ ํ ํฐ ์ฌ์ฉ๋ + ์ฌ์ฉ์๋ณ ์ ํ |
| ROI ๋ฆฌํฌํธ | /roi-report |
์ ๊ฐ ์๊ฐ ํ์ฐ + ๋์ ๋ถ์ ํต๊ณ |
| ๋ฆฌ๋ทฐ ์์ฒญ | /review-requests |
VIEWER ์์ฑ โ REVIEWER/ADMIN ์น์ธ/๊ฑฐ์ + ์ฝ๋ฉํธ + @๋ฉ์ |
| ๊ณต์ ๋งํฌ | /share/{token} |
7์ผ ์ ํจ read-only URL (๋ก๊ทธ์ธ ๋ถํ์) |
| ๊ธฐ๋ฅ | ๊ฒฝ๋ก | ํต์ฌ |
|---|---|---|
| ์ฌ์ฉ์ ๊ด๋ฆฌ | /admin/users |
๊ณ์ ์์ฑ/์์ /์ ๊ธ/IP ํ์ดํธ๋ฆฌ์คํธ |
| ์ฌ์ฉ์ ๊ถํ ๊ด๋ฆฌ | /admin/permissions |
featureKey ๋ณ ํ์ฑ/๋นํ์ฑ |
| ํ ๋์๋ณด๋ | /admin/team-dashboard |
์ฌ์ฉ์๋ณ ๋ถ์/์ฑํ ์นด์ดํธ |
| ๋ฆฌ๋ทฐ ๋์๋ณด๋ | /admin/review-dashboard |
์ผ๋ณ ํธ๋ ๋ + ํ์ /๋ฆฌ๋ทฐ์ด๋ณ ํต๊ณ |
| ๊ฐ์ฌ ๋ก๊ทธ | /admin/audit-dashboard |
API ํธ์ถ ๊ธฐ๋ก |
| ์๋ํฌ์ธํธ ํต๊ณ | /admin/endpoint-stats |
Top ์๋ํฌ์ธํธ/์ฌ์ฉ์, ์ํ ์ฝ๋, ์ผ๋ณ ํธ๋ ๋ |
| ๋น์ฉ ์ตํฐ๋ง์ด์ โจ | /admin/cost-optimizer |
๋ถ์ ์ ํ๋ณ ๋ชจ๋ธ ์ถ์ฒ (Haiku/Sonnet/Opus) + ์ ๊ฐ์ก |
| ์์คํ ํฌ์ค | /admin/health |
JVM, DB, ๋์คํฌ, ํฌ์ค์ฒดํฌ |
| ๋ฐฑ์ / ๋ณต์ | /admin/backup |
H2 ํ์ผ ๋ค์ด๋ก๋ + ์ ๋ก๋ ๋ณต์ |
| DB ๋ง์ด๊ทธ๋ ์ด์ | /admin/db-migration |
H2 โ MySQL/PostgreSQL ๊ฐ์ด๋ |
| ๊ธฐ๋ฅ | ์ค๋ช |
|---|---|
| ๋คํฌ / ๋ผ์ดํธ ํ ๋ง | ์๋จ๋ฐ ํ ๊ธ + localStorage |
| 5๊ฐ ์ธ์ด โจ | ko / en / ja / zh / de + ์ฌ์ฉ์๋ณ ์๋ ๋๊ธฐํ |
| ์์ ฏ ์ปค์คํฐ๋ง์ด์ง โจ | ํ ๋์๋ณด๋ ๋๋๊ทธ/๋ฆฌ์ฌ์ด์ฆ/์จ๊น + ์ฌ์ฉ์๋ณ ์ ์ฅ |
| ์๋ ์์์ ์ฅ | ์ ๋ ฅ localStorage 2์ด ๋๋ฐ์ด์ค |
| ํ ํฐ ์ ์ค์๊ฐ ์์ธก | ์์ ๊ฒฝ๊ณ (์์ /์ฃผ์/์ํ) |
| ์ค์๊ฐ ์๋ฆผ (SSE) | ๋ฉ์ , ๋ฆฌ๋ทฐ ๋๊ธฐ, ์์คํ ์ด๋ฒคํธ |
| ์ปค๋งจ๋ ํ๋ ํธ | โK / Ctrl+K โ ํ์ด์ง ์ฆ์ ์ด๋ + ๋ช ๋ น ๊ฒ์ |
โจ = v4.3.0 ์ ๊ท / ๊ฐ์
์ถ๊ฐ ๊ณตํต UX ์ธ๋ถ์ฌํญ (ํผ์น๊ธฐ)
| ๊ธฐ๋ฅ | ์ค๋ช |
|---|---|
| ์ ๋ ฅ ์๋ ์์์ ์ฅ | ์ ๋ ฅ 2์ด ํ localStorage ์ ์ฅ, ์๋ก๊ณ ์นจ/๋ณต๊ท ์ ๋ณต์ |
| ํ ํฐ ์ ์์ธก ํ์ | ์ ๋ ฅ ๊ธ์ ์ ๊ธฐ๋ฐ ์์ ํ ํฐ (40kโ ๋ ธ๋, 80kโ ๋นจ๊ฐ) |
| ์ค๋ณต ์ ์ถ ๋ฐฉ์ง | ๋ถ์ ์คํ ์ ๋ฒํผ ์๋ ๋นํ์ฑํ โ 90์ด ํ ๋ณต์ |
| โก ์ค์๊ฐ ๋ณด๊ธฐ | ๋ชจ๋ ๋๊ตฌ์ SSE ์คํธ๋ฆฌ๋ฐ ๋ฒํผ |
| ํ๋ก์ ํธ ์ปจํ ์คํธ ์๋ ์ฃผ์ | Settings ๋ฉ๋ชจ๊ฐ ์ฝ๋ ๋ฆฌ๋ทฐยทํ ์คํธยทJavadoc ๋ฑ ๋ชจ๋ AI ๋๊ตฌ์ ์๋ ์ ๋ฌ |
| Ctrl+Enter ๋จ์ถํค | ์ฆ์ ๋ถ์ ์คํ (Mac: โ+Enter) |
| ๋ฐ์ํ ๋ ์ด์์ | ๋ชจ๋ฐ์ผ โค768px / ํ๋ธ๋ฆฟ โค992px ์ต์ ํ |
| ์ฌ์ด๋๋ฐ ์น์ ์ ๊ธฐ | ์นดํ ๊ณ ๋ฆฌ๋ณ ํ ๊ธ, ์ ์ฒด ์ ๊ธฐ, localStorage ์ ์ฅ |
| Syntax Highlighting | Prism.js (SQL, Java, XML, YAML, diff) |
| Mermaid.js | ERD mermaid ์ฝ๋๋ธ๋ก ์๋ ๋ ๋๋ง |
| ์ ํญ ์ด๊ธฐ / ๋จ๊ณ ๋ก๋ฉ / ํด๋ฆฝ๋ณด๋ ๋ณต์ฌ / ๋งํฌ๋ค์ด ๋ ๋๋ง ๋ฑ |
Oracle DB ์ฐ๊ฒฐ ์ ๋ณด, Java ํ๋ก์ ํธ ๊ฒฝ๋ก, Claude API, ํ๋ก์ ํธ ์ปจํ
์คํธ, ๋ชจ๋ธ ์ ํ์ ๋ฐํ์์ ์ค์ ํฉ๋๋ค.
์ค์ ์ ~/.claude-toolkit/settings.json ์ ์๋ ์ ์ฅ๋์ด ์๋ฒ ์ฌ์์ ํ์๋ ์ ์ง๋ฉ๋๋ค.
| ๊ทธ๋ฃน | ํญ๋ชฉ | ํ๊ฒฝ๋ณ์ / ํค |
|---|---|---|
| Claude API | API ํค (์ ํจ์ฑ ๊ฒ์ฌ ๋ฒํผ) | CLAUDE_API_KEY |
| ๋ชจ๋ธ ๋ฐํ์ ์ ํ (์ฌ์์ ๋ถํ์) | UI ๋๋กญ๋ค์ด โ opus / sonnet / haiku | |
| Oracle DB | SQL ๋ฉํ์กฐํ + EXPLAIN PLAN + ERD ์ค์บ์ฉ | ORACLE_DB_URL / ORACLE_DB_USERNAME / ORACLE_DB_PASSWORD |
| ํ๋ก์ ํธ ๊ฒฝ๋ก | ์ฝ๋ ๋ฆฌ๋ทฐ / ๋ฌธ์ ์์ฑ ์ ์ฐ๊ด ํ์ผ ์ปจํ ์คํธ ์๋ ํฌํจ | toolkit.project.scan-path |
| ์ปจํ ์คํธ ๋ฉ๋ชจ | ํ๋ก์ ํธ ๊ฐ์ยท์ปจ๋ฒค์ ยท๋๋ฉ์ธ โ ๋ชจ๋ AI ๋๊ตฌ์ ์๋ ์ฃผ์ | toolkit.project-context |
| ์ด๋ฉ์ผ (SMTP) | ๋ฐฐ์น ์๋ฃ ์๋ฆผ / ๋ถ์ ๊ฒฐ๊ณผ ๋ฐ์ก | SMTP_HOST / SMTP_PORT / SMTP_USERNAME / SMTP_PASSWORD |
| Slack / Teams / Jira | ์นํ + ์ด์ ์๋ ์์ฑ | UI Settings ์ ๋ ฅ |
์๋ ๋ถ๋ฅ ํ์ผ ์ ํ (ํ๋ก์ ํธ ์ค์บ ์): Controller (@RestController), Service (@Service), Repository/DAO (@Repository), Mapper (@Mapper), MyBatis XML, DTO/VO (๊ฒฝ๋ก ๊ธฐ๋ฐ), Config (@Configuration). target/, test/, build/, .git/ ์๋ ์ ์ธ.
# ํ๊ฒฝ๋ณ์ ์์
export CLAUDE_API_KEY=sk-ant-...
export ORACLE_DB_URL=jdbc:oracle:thin:@//host:1521/SERVICE_NAMEclaude-spring-boot-starter ๋ฅผ ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด ClaudeClient ๊ฐ ์๋ ์ฃผ์
๋ฉ๋๋ค.
# application.yml
claude:
api-key: ${CLAUDE_API_KEY}
model: claude-sonnet-4-6
max-tokens: 4096@Service
public class MyService {
private final ClaudeClient claude;
public MyService(ClaudeClient claude) { this.claude = claude; }
// ๋๊ธฐ ํธ์ถ
public String review(String sql) {
return claude.chat("Oracle DBA", sql);
}
// SSE ์คํธ๋ฆฌ๋ฐ (JDK 1.8 Consumer)
public void stream(String code) {
claude.chatStream("Java reviewer", code, chunk -> System.out.print(chunk));
}
// ๋ฐํ์ ๋ชจ๋ธ ์ ํ (์ฌ์์ ๋ถํ์)
public void switchModel(String m) { claude.setModelOverride(m); }
public String currentModel() { return claude.getEffectiveModel(); }
}CLI ์ฌ์ฉ๋ฒ (claude-sql-advisor / claude-doc-generator)
# SQL Advisor CLI
java -jar claude-sql-advisor.jar review --file my_query.sql
java -jar claude-sql-advisor.jar review --file SP.sql --type STORED_PROCEDURE --output report.md --api-key sk-ant-...
cat my_query.sql | java -jar claude-sql-advisor.jar review
# Doc Generator CLI
java -jar claude-doc-generator.jar generate --file SP.sql --format md --output docs/SP.md
java -jar claude-doc-generator.jar generate --file OrderService.java --type "Java Service" --format typst1. IntelliJ Maven ํจ๋ (View โ Tool Windows โ Maven)
2. ๋ฃจํธ ํ๋ก์ ํธ(claude-java-toolkit-parent) ์ ํ
3. Lifecycle โ install ๋๋ธํด๋ฆญ (๋๋ install -DskipTests)
4. claude-toolkit-ui โ Plugins โ spring-boot โ spring-boot:run ์คํ
Run โ Edit Configurations โ Environment variables
CLAUDE_API_KEY=sk-ant-...
ORACLE_DB_URL=jdbc:oracle:thin:@//hostname:1521/ORCL
ORACLE_DB_USERNAME=myuser
ORACLE_DB_PASSWORD=mypassword
PROJECT_SCAN_PATH=C:/workspace/my-project/src/main/java
๋ธ๋ผ์ฐ์ (React 18 SPA + Zustand + Recharts + Mermaid + reactflow)
โ EventSource (SSE) โโโโ GET /stream/{id}
โ fetch (JSON) โโโโโโโโ /api/v1/**
โผ
Spring Boot 2.7 + Spring MVC + Spring Security
โ REST Controllers (api/v1) โ 30+ ์ปจํธ๋กค๋ฌ
โ โโ ๋ถ์/์์ฑ: SqlAdvisor, CodeReview, DocGen, Javadoc, Refactor, ApiSpec, ...
โ โโ ์ํฌํ๋ก: HarnessReview (AnalystโBuilderโReviewerโVerifier 4๋จ๊ณ)
โ โโ ๋ฉํ์กฐํ: IndexAdvisor (JDBC ๋ฉํ + DDL ์ถ์ฒ), ErdAnalyzer
โ โโ ์ธํ๋ผ: Auth, Settings, Pipeline, Notification, Share, Export
โ โโ ๊ด๋ฆฌ์: AdminPermission, CostOptimizer, AuditLog, Backup, Health
โผ
Service Layer + JPA (claude-toolkit-ui)
โ ReviewHistoryService / FavoriteService / PipelineExecutor
โ ToolkitMetrics (Prometheus 4์ข
+ ์๋ ๋ฉํธ๋ฆญ) โจ
โ ModelCostService / IndexAdvisorService โจ
โ H2 (๊ฐ๋ฐ) / MySQL / PostgreSQL (์ด์)
โผ
ClaudeClient (claude-spring-boot-starter)
โ chat() / chatStream() / chatStreamWithContinuation() โ SSE
โ setModelOverride() / getEffectiveModel() โ ๋ฐํ์ ๋ชจ๋ธ ์ ํ
โ ํ ํฐ ์ฌ์ฉ๋ ์บก์ฒ (lastInputTokens / lastOutputTokens)
โ OkHttp3 + Forward Proxy + TLSv1.2/1.3
โผ
Anthropic Claude API (Opus 4.x / Sonnet 4.x / Haiku 4.x)
Spring App โ /actuator/prometheus โ Prometheus โ Grafana
โ (15s scrape) (๋์๋ณด๋ 10ํจ๋)
โโ ToolkitMetrics (claude_api_calls_total / claude_api_tokens_total /
analysis_duration_seconds / pipeline_execution_total)
claude-java-toolkit/
โโโ claude-spring-boot-starter/ # Library (ClaudeClient + Auto-config)
โโโ claude-sql-advisor/ # Library + CLI (SQL ๋ฆฌ๋ทฐ, ERD, EXPLAIN PLAN)
โโโ claude-doc-generator/ # Library + CLI (๋ฌธ์/Javadoc/๋ฆฌํฉํฐ๋ง/...)
โโโ claude-toolkit-ui/ # Spring Boot + React SPA (๋ฉ์ธ ์น ๋์๋ณด๋)
โ โโโ src/main/java/io/github/claudetoolkit/ui/
โ โ โโโ api/ # REST ์ปจํธ๋กค๋ฌ (Auth, Data, Sql, Doc, Erd, Health)
โ โ โโโ controller/ # SSE ์คํธ๋ฆผ, Pipeline, Notification, Share ์ปจํธ๋กค๋ฌ
โ โ โโโ config/ # SecurityConfig, ToolkitSettings, GlobalException
โ โ โโโ pipeline/ # PipelineExecutor + Spec/Yaml + StepResult
โ โ โโโ harness/ # 4๋จ๊ณ ์ฝ๋ ๋ฆฌ๋ทฐ (Analyst/Builder/Reviewer/Verifier)
โ โ โโโ workspace/ # AnalysisType + Service Registry
โ โ โโโ history/ # ReviewHistory + Repository
โ โ โโโ user/ # AppUser + Permission + 2FA
โ โ โโโ notification/ # SSE ์๋ฆผ + PendingReviewNotifier
โ โ โโโ metrics/ # ToolkitMetrics (Micrometer) โจ v4.3.0
โ โ โโโ cost/ # ModelCostService + Optimizer Controller โจ v4.3.0
โ โ โโโ sqlindex/ # IndexAdvisor (JDBC ๋ฉํ์กฐํ + DDL ์ถ์ฒ) โจ v4.3.0
โ โ โโโ dashboard/ # UserDashboardLayout + Controller โจ v4.3.0
โ โ โโโ export/ # SARIF + Excel Export Service โจ v4.3.0
โ โโโ src/main/resources/
โ โ โโโ application.yml # ๋ฉ์ธ ์ค์ (DB / Claude / Actuator / Metrics)
โ โ โโโ static/app/ # React ๋ฒ๋ค (Vite ๋น๋ ๊ฒฐ๊ณผ๋ฌผ)
โ โโโ frontend/
โ โโโ src/
โ โ โโโ pages/ # 65+ ํ์ด์ง (analysis/admin/auth)
โ โ โโโ components/ # ๊ณต์ฉ ์ปดํฌ๋ํธ (PipelineGraphView, MentionInput, ...)
โ โ โโโ stores/ # Zustand (auth/theme/sidebar/notification/toast)
โ โ โโโ i18n/ # ko/en/ja/zh/de โจ v4.3.0
โ โ โโโ hooks/utils/types/
โ โโโ e2e/ # Playwright ํ๊ท ํ
์คํธ
โโโ helm/claude-toolkit/ # Kubernetes Helm Chart โจ v4.3.0
โ โโโ Chart.yaml / values.yaml / README.md
โ โโโ templates/ # Deployment / Service / Ingress / Secret /
โ # PVC / HPA / ServiceAccount / ServiceMonitor / NOTES
โโโ monitoring/ # Prometheus + Grafana ํ๋ก๋น์ ๋ โจ v4.3.0
โ โโโ prometheus.yml
โ โโโ grafana/
โ โโโ provisioning/datasources/ # Prometheus ์๋ ๋ฑ๋ก
โ โโโ provisioning/dashboards/ # ๋์๋ณด๋ ์๋ ๋ก๋
โ โโโ dashboards/claude-toolkit-overview.json # 10 ํจ๋
โโโ docs/index.html # GitHub Pages ์๊ฐ ํ์ด์ง
โโโ docker-compose.yml # 4๊ฐ ํ๋กํ (default/mysql/postgresql/monitoring)
โโโ Dockerfile # Multi-stage (Maven + Node + JDK 8 JRE)
โโโ pom.xml # ๋ถ๋ชจ POM (BOM ๋ฒ์ ๊ด๋ฆฌ)
โจ = v4.3.0 ์ ๊ท ๋ชจ๋ / ๋๋ ํ ๋ฆฌ
| ๊ตฌ๋ถ | ๊ธฐ์ |
|---|---|
| Language | Java 1.8 (var / Stream.of / List.of ๋ฏธ์ฌ์ฉ) |
| Framework | Spring Boot 2.7.18, Spring MVC, Thymeleaf 3.0 |
| HTTP Client | OkHttp3 4.12.0 |
| JSON | Jackson 2.15.4 |
| CLI | Picocli 4.7.5 |
| DB Driver | Oracle JDBC ojdbc8 21.5.0 |
| Frontend | Bootstrap 5.3, Font Awesome 6.4, Prism.js 1.29.0, marked.js 9.x, Mermaid.js 10.x |
| Build | Maven 3.6+ (multi-module) |
| Persistence (Settings) | ~/.claude-toolkit/settings.json (OutputStreamWriter + UTF-8) |
| Persistence (History) | Spring Data JPA 2.7 + H2 2.x ํ์ผ DB (~/.claude-toolkit/history-db.mv.db) |
| Persistence (Favorites) | Spring Data JPA 2.7 + H2 2.x ํ์ผ DB (์ฌ์์ ํ ์ฆ๊ฒจ์ฐพ๊ธฐ ๋ณต์) โญ NEW |
9๊ฐ ๊ธฐ๋ฅ์ 5๊ฐ Phase ๋ก ๋๋์ด ๋จ๊ณ๋ณ๋ก ๋์ ์๋ฃ. ์ด์ ๊ฐ์์ฑ, ๋น์ฉ ์ต์ ํ, ๋ค๊ตญ์ด ํ์ฅ, K8s ๋ฐฐํฌ ์ค๋น๋ฅผ ํ ์ฌ์ดํด์ ์ ๋ฆฌ.
- SARIF 2.1.0 ๋ด๋ณด๋ด๊ธฐ โ
/api/v1/export/sarif/{id}. ์ด๋ ฅ ํ์ด์ง ํ๋ง๋ค ๋ค์ด๋ก๋ ๋ฒํผ + โน๏ธ ๋์๋ง ํ ๊ธ (VS Code SARIF Viewer / JetBrains Qodana / GitHub Code Scanning ์ฌ์ฉ๋ฒ ์๋ด) - Excel ์ํฌ๋ถ ๋ด๋ณด๋ด๊ธฐ โ Apache POI 5.2.5 ๊ธฐ๋ฐ. 3 ์ํธ (์์ฝ/์ด๋ ฅ ์์ธ/์ ํ๋ณ ํต๊ณ) + ํฉ๊ณ ์์ + ํค๋ ์คํ์ผ.
/api/v1/export/excel/history?limit=N
- Prometheus ๋ฉํธ๋ฆญ ์ต์คํฌํฐ โ
micrometer-registry-prometheus+/actuator/prometheus. ์ปค์คํ ๋ฉํธ๋ฆญ 4์ข :claude_api_calls_total{model,feature,status},claude_api_tokens_total{model,direction},analysis_duration_seconds{type},pipeline_execution_total{status}+ Spring ์๋ ๋ฉํธ๋ฆญ (HTTP, JVM) - Grafana ์คํ โ
docker-compose.yml์monitoringํ๋กํ (Prometheus + Grafana). ์์:docker-compose --profile monitoring up -dโ http://localhost:3000 (admin/admin) - ๋ฉํธ๋ฆญ ํธ์ถ์ฒ ํตํฉ โ SseStreamController/ReviewHistoryService/PipelineExecutor + 3 REST ์ปจํธ๋กค๋ฌ Timer ๋ํ. ์ฝ 95% ๋ถ์ ํ๋ฆ ์ปค๋ฒ
- AI ๋ชจ๋ธ ๋น์ฉ ์ตํฐ๋ง์ด์ โ
/admin/cost-optimizer(ADMIN). Anthropic ๊ณต์ ๋จ๊ฐํ + ๋ถ์ ์ ํ๋ณ ํ๊ท ์ ๋ ฅ/์น์ธ๋ฅ ๊ธฐ๋ฐ ์ถ์ฒ (Haiku/Sonnet/Opus). ์ ๊ฐ์ก + ๋น์ฉ ๋น๊ต ์ฐจํธ - SQL ์ธ๋ฑ์ค ์ํฉํธ ์๋ฎฌ๋ ์ด์
โ
/sql/index-advisor. ์ ์ SQL ํ์ฑ + JDBC ๋ฉํ๋ฐ์ดํฐ (MySQL/PostgreSQL/Oracle/H2 ์๋ ํธํ) โ ๊ธฐ์กด ์ธ๋ฑ์ค ํ์ฉ ๊ฐ๋ฅ ์ฌ๋ถ + ์ ๊ท ์ธ๋ฑ์ค DDL ์ถ์ฒ (Oracle 30์ ์ ํ ์ค์). ํตํฉ ์ํฌ์คํ์ด์ค SQL ์ธ์ด ์ ํ์nonStreaming๊ธฐ๋ฅ์ผ๋ก๋ ๋ ธ์ถ
- ๋ค๊ตญ์ด ํ์ฅ (์ผ/์ค/๋
) โ ํ/์ โ 5๊ฐ ์ธ์ด (
ja.ts/zh.ts/de.ts). TopBar ์ LanguageSwitcher (๐ + ๊ตญ๊ธฐ). ๋ฐฑ์๋app_user.locale์ปฌ๋ผ +PUT /api/v1/auth/locale./me์๋ต์ locale ํฌํจ โ ๋ค๋ฅธ ๊ธฐ๊ธฐ ์๋ ๋๊ธฐํ - ๋์๋ณด๋ ์์ ฏ ์ปค์คํฐ๋ง์ด์ง โ
react-grid-layout. HomePage ํธ์ง ๋ชจ๋: ๋๋๊ทธ/๋ฆฌ์ฌ์ด์ฆ/ํ์-์จ๊น ํ ๊ธ + ์ ์ฅ/๊ธฐ๋ณธ๊ฐ ๋ณต์.UserDashboardLayout์ํฐํฐ๋ก ์ฌ์ฉ์๋ณ ๋ ์ด์์ DB ์์ํ. ๋๋ฝ ์์ ฏ ์๋ ๋ณด๊ฐ (๋ง์ด๊ทธ๋ ์ด์ ์์ )
- ์ธํฐ๋ํฐ๋ธ ๊ทธ๋ํ ๋ทฐ (PipelineGraphView) โ
reactflow11. ํ์ดํ๋ผ์ธ YAML ์ ๋ ธ๋ ๊ทธ๋ํ๋ก ์๊ฐํ. ๋ณ๋ ฌ ๋จ๊ณ๋ ๊ฐ์ ์ปฌ๋ผ ๋ค๋ฅธ row, ์์ฐจ๋ ๊ฐ๋ก ์งํ. context ์์กด์ฑ ์๋ ์ถ๋ก + dependsOn ๋ช ์ ์ง์. ์กฐ๊ฑด๋ถ step ์ ์ฃผํฉ์ ์ฃ์ง. ๋ฏธ๋๋งต + ์ค. PipelineEditor ์ฐ์ธก ํจ๋์ "๐ ๊ทธ๋ํ / ๐ Mermaid" ํญ์ผ๋ก ์ถ๊ฐ (๊ธฐ์กด PipelineBuilder/Mermaid ๋ ๊ทธ๋๋ก ์ ์ง) - Kubernetes Helm Chart (์ค๋น์ฉ) โ
helm/claude-toolkit/v0.1.0. Deployment + Service + Ingress + Secret + PVC + HPA + ServiceMonitor. DB ์ต์ ๋ณ (h2/mysql/postgresql) values. 6๊ฐ์ง ์ค์น ์๋๋ฆฌ์ค README ๋๋ด. ํ ์์ ์ค์ K8s ํ๊ฒฝ ๋ฏธ๋ณด์ โhelm template์ ์ ๊ฒ์ฆ๋ง ์๋ฃ. ์ฌ์ฉ ์์ ์ ์ฌ์ฉ์ ํ๊ฒฝ์์ ์ถ๊ฐ ๊ฒ์ฆ ํ์
-
DataRestController/AuthRestControllersilent catch ๋ธ๋ก โ SLF4J ๋ก๊น ์ผ๋ก ์ ํ (~22 ๊ณณ) - SQL ์ธ๋ฑ์ค ์๋ฎฌ๋ ์ด์
๊ถํ ๋ถ๋ฆฌ โ
featureKey: 'index-advisor'์ ๊ท ๋ฑ๋ก. ๊ด๋ฆฌ์๊ฐ SQL ๋ฆฌ๋ทฐ์ ๋ ๋ฆฝ์ ์ผ๋ก ๊ถํ ์ ์ด ๊ฐ๋ฅ - ํตํฉ ์ํฌ์คํ์ด์ค nonStreaming ํจํด โ ๋น-AI ๊ธฐ๋ฅ(JDBC ๋ฉํ์กฐํ ๋ฑ)๋ ์ํฌ์คํ์ด์ค ๋ค์ค ์ ํ + ๋ณ๋ ฌ ์คํ ํ๋ฆ์ ํตํฉ ๊ฐ๋ฅ
๐ ์ด์ ๋ฒ์ ํ์คํ ๋ฆฌ (v0.3.0 ~ v4.2.x) โ ํด๋ฆญํ์ฌ ํผ์น๊ธฐ
์ ๊ท ๋๊ตฌ (3์ข )
- ๋ก๊ทธ ๋ถ์๊ธฐ
/loganalyzerโ ์ผ๋ฐ ๋ถ์ + ๋ณด์ ์ํ ํ์ง, ํ์ผ ์ ๋ก๋ - ์ ๊ท์ ์์ฑ๊ธฐ
/regexโ 5๊ฐ ์ธ์ด ์ง์, ๋น ๋ฅธ ์์ 8์ข - ์ปค๋ฐ ๋ฉ์์ง ์์ฑ๊ธฐ
/commitmsgโ 4๊ฐ์ง ์คํ์ผ, diff ํ์ผ ์ ๋ก๋
๊ธฐ์กด ๊ธฐ๋ฅ ๊ฐ์
- ๋คํฌ/๋ผ์ดํธ ํ ๋ง ํ ๊ธ
- ์ ๋ ฅ ์๋ ์์์ ์ฅ (localStorage, 2์ด ๋๋ฐ์ด์ค)
- ํ ํฐ ์ ์ค์๊ฐ ์์ธก ํ์ (์์ ๊ฒฝ๊ณ ํฌํจ)
- ํผ ์ค๋ณต ์ ์ถ ๋ฐฉ์ง (๋ฒํผ ์๋ ๋นํ์ฑํ)
- ์ด๋ ฅ ํ์ด์ง ์ค์๊ฐ ํค์๋ ๊ฒ์ + ์ ํ๋ณ ํญ ํํฐ
์ธํ๋ผ ๊ฐ์
- SSE ์คํธ๋ฆฌ๋ฐ ์ํคํ
์ฒ (
POST /stream/initโGET /stream/{id}) -
ClaudeClient.chatStream()โConsumer<String>์ฝ๋ฐฑ ์คํธ๋ฆฌ๋ฐ - ํ๋ก์ ํธ ์ปจํ ์คํธ ๋ฉ๋ชจ (๋ชจ๋ AI ์์ฒญ์ ์๋ ์ถ๊ฐ)
์ ๊ท UX ๊ธฐ๋ฅ
- โก ์ค์๊ฐ ๋ณด๊ธฐ ๋ฒํผ โ ๋ชจ๋ ๋๊ตฌ ํ์ด์ง์ SSE ์คํธ๋ฆฌ๋ฐ ๋ฒํผ ๋ ธ์ถ
- ์ค์๊ฐ ๊ฒฐ๊ณผ ํจ๋ + ๋ณต์ฌ ๋ฒํผ + ์ปค์ ์ ๋๋ฉ์ด์
ํ๋ก์ ํธ ์ปจํ ์คํธ ์๋ ์ฃผ์
- Settings ์ปจํ ์คํธ ๋ฉ๋ชจ๋ฅผ ์ฝ๋ ๋ฆฌ๋ทฐยทํ ์คํธ ์์ฑ ๋ฑ 8๊ฐ ๋๊ตฌ์ ์๋ ์ฃผ์
์ด๋ ฅ ์๊ตฌ ์ ์ฅ
- H2 ํ์ผ DB ์์ํ โ
~/.claude-toolkit/history-db.mv.db(์๋ฒ ์ฌ์์ ํ ๋ณต์) -
ReviewHistoryโ JPA@Entity๋ณํ (javax.persistence.*, Spring Boot 2.7 ํธํ) -
ReviewHistoryRepository(Spring Data JPA) ์ ๊ท ์ถ๊ฐ
์ ๊ท ๊ธฐ๋ฅ
- iBatis โ MyBatis ๋งคํผ ์๋ ๋ณํ โ
/converterํญ ์ถ๊ฐ, iBatis XML โ MyBatis 3.x XML ๋ณํ - Claude ๋ชจ๋ธ ์ ํ โ Settings ํ์ด์ง์์ ๋ฐํ์ ๋ชจ๋ธ ์ ํ,
volatile์ค๋ฒ๋ผ์ด๋ - Ctrl+Enter ๋จ์ถํค โ ๋ชจ๋ ํ์ด์ง ๊ณตํต ํผ ์ ์ถ ๋จ์ถํค (toolkit.js)
- ์ฆ๊ฒจ์ฐพ๊ธฐ H2 DB ์์ํ โ
FavoriteJPA @Entity ๋ณํ,FavoriteRepository์ ๊ท ์ถ๊ฐ - ๋ฆฌ๋ทฐ diff ๋น๊ต โ
/history์์ ๋ ํญ๋ชฉ ๋๋ํ ์ ๋ ฅ/๊ฒฐ๊ณผ ๋น๊ต ๋ชจ๋ฌ
์ ๊ท ๋๊ตฌ (3์ข )
- Javadoc ์๋ ์์ฑ
/javadocโ Java ์์ค์ ํ๊ตญ์ด Javadoc ์ฃผ์ ์๋ ์ฝ์ , SSE ์คํธ๋ฆฌ๋ฐ ์ง์ - ๋ฆฌํฉํฐ๋ง ์ ์
/refactorโ ์ฝ๋ ๋ฌธ์ ์ ๋ถ์ + ๊ฐ์ ์ฝ๋ ์์ฑ, SSE ์คํธ๋ฆฌ๋ฐ ์ง์ - pom.xml ์์กด์ฑ ๋ถ์
/depcheckโ ์ทจ์ฝ์ ยท์ถฉ๋ยท์ ๊ทธ๋ ์ด๋ ๊ถ๊ณ , HIGH/MEDIUM/LOW ํํฐ
๋ฐ์ํ ๋ ์ด์์
- ๋ชจ๋ฐ์ผ(โค768px) / ํ๋ธ๋ฆฟ(โค992px) ๋ฏธ๋์ด์ฟผ๋ฆฌ โ toolkit.css ๋๊ท๋ชจ ์ถ๊ฐ
- 2์ปฌ๋ผ ๋ ์ด์์ โ ๋ชจ๋ฐ์ผ ๋จ์ผ ์ปฌ๋ผ ์๋ ์ ํ
์ ๊ท ๋๊ตฌ (2์ข )
- ๋ฐ์ดํฐ ๋ง์คํน ์คํฌ๋ฆฝํธ
/maskgenโ DDL โ ๊ฐ์ธ์ ๋ณด ๋ณดํธ Oracle UPDATE ์คํฌ๋ฆฝํธ ์์ฑ - Spring Boot 3.x ๋ง์ด๊ทธ๋ ์ด์
/migrateโ Spring Boot 2.xโ3.x ๋ง์ด๊ทธ๋ ์ด์ ์ฒดํฌ๋ฆฌ์คํธ ์๋ ์์ฑ
๊ธฐ์กด ๊ธฐ๋ฅ ํ์ฅ
- Oracle Package ๋ฌธ์ํ โ
/docgen์์ค ์ ํ์ "Oracle Package (SPEC+BODY)" ์ถ๊ฐ - ์ธ๋ฑ์ค ์ต์ ํ ์ ์ โ
/advisor์ธ ๋ฒ์งธ ํญ ์ถ๊ฐ, CREATE INDEX ๊ตฌ๋ฌธ ํฌํจ ์ ์ ์์ฑ - ๋ณด๊ณ ์ ๋ฌถ์ ๋ด๋ณด๋ด๊ธฐ โ
/history๋ค์ค ์ ํ โ Markdown ๋ฒ๋ค ํ์ผ ๋ค์ด๋ก๋
์ฌ์ด๋๋ฐ ๊ฐํ
- ์ฌ์ด๋๋ฐ ์น์ ๋ณ(๋ถ์/์์ฑ/๊ธฐ๋ก/๋๊ตฌ) ์ ๊ธฐ/ํผ์น๊ธฐ ํ ๊ธ
- ์ ์ฒด ์ฌ์ด๋๋ฐ ์ ๊ธฐ/ํผ์น๊ธฐ + ํ๋กํ ์ด๊ธฐ ํญ (position:fixed ์ดํ ํด๊ฒฐ)
- localStorage ์ํ ์ ์ฅ (ํ์ฌ ํ์ด์ง ์น์ ์๋ ์คํ)
์ ๊ท ๋๊ตฌ (1์ข )
- Oracle ์คํ๊ณํ ์๊ฐํ
/explainโ EXPLAIN PLAN ๊ฒฐ๊ณผ๋ฅผ ์ธํฐ๋ํฐ๋ธ ํธ๋ฆฌ๋ก ๋ ๋๋ง- PLAN_TABLE ์ฟผ๋ฆฌ โ ๋ถ๋ชจ-์์ ํธ๋ฆฌ ๊ตฌ์กฐ ํ์ฑ
- ์คํผ๋ ์ด์ ์ข ๋ฅ๋ณ ์์ ๊ตฌ๋ถ (FULL=๐ด, INDEX=๐ข, JOIN=๐ฃ, SORT=๐ก, NESTED=๐ )
- ๋ ธ๋๋ณ Cost ๋น์จ ๋ฐ ์๊ฐํ
- Claude AI ์ฑ๋ฅ ์ด์ / ์ต์ ํ ์ ์ / ํต์ฌ ๋จ๊ณ ํด์ค
์ ๊ท ๋๊ตฌ (3์ข )
-
ERD โ Oracle DDL ์ญ๋ณํ
/erdDDL ์์ฑ ํญ- Mermaid erDiagram, ํ ์ด๋ธ ๊ตฌ์กฐ ์ค๋ช , ์์ฐ์ด ์ ๋ ฅ ์ง์
- Oracle 11g/12c ํธํ โ VARCHAR2, NUMBER, DATE
- PRIMARY KEY / FOREIGN KEY ์ ์ฝ์กฐ๊ฑด + FK ์ธ๋ฑ์ค + COMMENT ON COLUMN ์๋ ์์ฑ
- ์คํ ์์ ์์กด์ฑ ์ฃผ์ ํฌํจ
-
์คํ๊ณํ Before/After ๋น๊ต
/explain/compare- ์ต์ ํ ์ /ํ SQL ๋์ ์คํ๊ณํ ๋ถ์
- ๋๋ํ ํธ๋ฆฌ ๋น๊ต + Cost ๋ณํ์จ (%) ๋ฐฐ์ง (๊ฐ์ /์ ํ/๋์ผ)
- DBMS_XPLAN ์๋ฌธ ๊ฐ๊ฐ ์ ์ด์ ํ์
-
์ด๋ ฅ ์ฌ์คํ
/history์ฌ์คํ ๋ฒํผ- ์ด๋ ฅ ์์ธ ๋ชจ๋ฌ โ "์ฌ์คํ" ๋ฒํผ ํด๋ฆญ ์ ํด๋น ๊ธฐ๋ฅ ํ์ด์ง๋ก ์ ๋ ฅ ๋ด์ฉ ์๋ ์ ๋ฌ
- 22๊ฐ์ง ๊ธฐ๋ฅ ์ ํ ๋ชจ๋ ์ง์ (SQL ๋ฆฌ๋ทฐ, ERD, DDL, ์ฝ๋ ๋ฆฌ๋ทฐ, ํ ์คํธ, ์คํ๊ณํ ๋ฑ)
- ํ๋กฌํํธ ํ
ํ๋ฆฟ ๊ด๋ฆฌ โ
/prompts์์ ๊ธฐ๋ฅ๋ณ ์์คํ ํ๋กฌํํธ ํธ์งยท์ ์ฅยท์ ์ฉ - ๋ถ์ ๊ฒฐ๊ณผ ๋ด๋ณด๋ด๊ธฐ โ ์คํ๊ณํยทERD ๊ฒฐ๊ณผ Markdown ๋ค์ด๋ก๋, ํ์คํ ๋ฆฌ ๋จ๊ฑด
/history/{id}/export
- GitHub Actions CI/CD ํ์ดํ๋ผ์ธ (์๋ ๋น๋ ยท master push / PR ํธ๋ฆฌ๊ฑฐ)
- REST API ๋ชจ๋ (์ธ๋ถ CI/CD ํ์ดํ๋ผ์ธ ์ฐ๋์ฉ JSON API)
| Method | Endpoint | ์ค๋ช |
|---|---|---|
| GET | /api/v1/health |
์๋ฒ ์ํ ๋ฐ ์ค์ ํ์ธ |
| POST | /api/v1/sql/review |
SQL ์ฑ๋ฅยทํ์ง ๋ฆฌ๋ทฐ |
| POST | /api/v1/sql/security |
SQL ๋ณด์ ์ทจ์ฝ์ ๊ฒ์ฌ |
| POST | /api/v1/sql/explain |
Oracle ์คํ๊ณํ ๋ถ์ |
| POST | /api/v1/doc/generate |
์์ค์ฝ๋ ๊ธฐ์ ๋ฌธ์ ์์ฑ |
| POST | /api/v1/code/review |
Java/Spring ์ฝ๋ ๋ฆฌ๋ทฐ |
| POST | /api/v1/code/security |
Java/Spring ๋ณด์ ๊ฐ์ฌ |
| POST | /api/v1/erd/analyze |
ERD ๋ถ์ |
์์ฒญ/์๋ต ์์:
# SQL ๋ฆฌ๋ทฐ
curl -X POST http://localhost:8027/api/v1/sql/review \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT * FROM ORDERS WHERE STATUS = 1"}'
# ์๋ต
{
"success": true,
"data": {
"sqlType": "SELECT",
"review": "## Summary\n...",
"reviewedAt": "2026-04-01 12:00:00"
},
"error": null,
"timestamp": "2026-04-01 12:00:00"
}- SQL ์ฑ๋ฅ ํ์คํ ๋ฆฌ ๋์๋ณด๋
/explain/dashboardโ ์คํ๊ณํ Cost ์ถ์ด Chart.js ๋ผ์ธ ์ฐจํธ, ๊ธฐ๊ฐ ํํฐ(์ ์ฒด/30์ผ/7์ผ), ํต๊ณ ์นด๋ - ๋ฐฐ์น SQL ๋ถ์
/sql-batchโ ํ ์คํธ ์ง์ ์ ๋ ฅ(---๊ตฌ๋ถ์), CSV/ํ ์คํธ ํ์ผ ์ ๋ก๋, ์ต๋ 30๊ฐ SQL ์ผ๊ด ๋ฆฌ๋ทฐ, MD ๋ณด๊ณ ์ ๋ค์ด๋ก๋
UI / UX ๊ฐ์
- ๋คํฌ/๋ผ์ดํธ ํ
๋ง ์ปค์คํ
์์ โ ์ก์ผํธ ์ปฌ๋ฌ ์ ํ (
/settings) ยท 5๊ฐ ํ๋ฆฌ์ + ์ปฌ๋ฌํผ์ปค, localStorage ์ฆ์ ์ ์ฉ - ๊ฒฐ๊ณผ ํ์ด์ง ์ธ์/PDF ๋ด๋ณด๋ด๊ธฐ โ ๋ชจ๋ ๋ถ์ ๊ฒฐ๊ณผ ํ์ด์ง ์ฐ์ธก ํ๋จ ์ธ์ ๋ฒํผ, ๋ธ๋ผ์ฐ์ ์ธ์ ์ต์ ํ CSS
- ๋ถ์ ๊ฒฐ๊ณผ ๊ณต์ ๋งํฌ โ
/history๋จ๊ฑด โPOST /history/{id}/shareโ 7์ผ ์ ํจ UUID ๊ณต์ URL ์์ฑ,/share/{token}๋ ๋ฆฝ ๋ทฐ
์ด์ ํธ์
- Claude ๋ชจ๋ธ ์ฌ์ฉ๋ ๋ชจ๋ํฐ๋ง
/usageโ ์ค์ API ํ ํฐ ์(input/output) ์๋ ๊ธฐ๋ก, ์ผ๋ณยท๊ธฐ๋ฅ๋ณ Chart.js ์ฐจํธ, ๋ชจ๋ธ๋ณ ๋น์ฉ ์ถ์ ($) - ๋ถ์ ์ค์ผ์ค๋ง
/scheduleโ cron ํํ์์ผ๋ก ์ ๊ธฐ SQL ๋ฆฌ๋ทฐ ์๋ ์คํ, SpringTaskScheduler๋์ ๋ฑ๋ก/ํด์ , ์ฆ์ ์คํ ์ง์, ๊ฒฐ๊ณผ ์ด๋ ฅ ์๋ ์ ์ฅ
SQL ๋ถ์ ๊ณ ๋ํ
- ์คํ๊ณํ SQL ์ฆ๊ฒจ์ฐพ๊ธฐ โ ๋ถ์ ๊ฒฐ๊ณผ โ
์ฆ๊ฒจ์ฐพ๊ธฐ ๋ฑ๋ก,
/explain/dashboard์์ ์ฌ์คํ - SQL ์ต์ ํ ์ ์ ์ ์ฉ โ AI ๋ฆฌ๋ทฐ ๊ฒฐ๊ณผ์์ SQL ์ฝ๋ ๋ธ๋ก ์ถ์ถ โ ์ธ๋ผ์ธ ํธ์ง โ ์ฌ๋ถ์
์ด์ ํธ์
- ์ค์ผ์ค ๊ฒฐ๊ณผ ์ด๋ฉ์ผ ๋ฐ์ก โ JavaMail(
spring-boot-starter-mail) ์ฐ๋, SMTP ์ค์ (/settings), ์์ ์ฃผ์ ๋ฑ๋ก ์ ์ค์ผ์ค ์๋ฃ ํ ์๋ ๋ฐ์ก - ๋ค์ค SQL ํ๋กํ
/db-profilesโ Oracle DB ์ฐ๊ฒฐ ํ๋กํ ์ ์ฅยทํธ์งยท์ญ์ , ์ํด๋ฆญ ์ ํ(ํ์ฌ Settings์ ์ฆ์ ๋ฐ์)
UI / UX ๊ฐ์
- ๋์๋ณด๋ ํ ์ปค์คํฐ๋ง์ด์ง โ ํ(
/) ์ ์ต๊ทผ ์ฆ๊ฒจ์ฐพ๊ธฐยท๋ถ์ ์ด๋ ฅ ์์ ฏ ์ถ๊ฐ - ๊ธ๋ก๋ฒ ๊ฒ์
/searchโ ์ด๋ ฅยท์ฆ๊ฒจ์ฐพ๊ธฐยท๊ธฐ๋ฅ ๋ชฉ๋ก ํตํฉ ํค์๋ ๊ฒ์,/๋จ์ถํค ํฌ์ปค์ค
SQL ๋ถ์ ๊ณ ๋ํ
- ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ ์คํ๊ณํ ๋ถ์ โ EXPLAIN PLAN ํธ๋ฆฌ๋ฅผ DB ์ฟผ๋ฆฌ ์ฆ์ ๋ ๋๋ง ํ, Claude AI ๋ถ์์ SSE ์คํธ๋ฆฌ๋ฐ์ผ๋ก ์ค์๊ฐ ์ถ๋ ฅ
- 2๋จ๊ณ ๋ถ๋ฆฌ:
POST /explain/stream-init(DB ์ ์ฉ) โGET /stream/{id}(AI ์คํธ๋ฆฌ๋ฐ) - ๊ฒฐ๊ณผ ์ ์ฅ
POST /explain/stream-saveโ ์คํธ๋ฆฌ๋ฐ ์๋ฃ ํ ์ด๋ ฅ ๊ธฐ๋ก ExplainPlanService.analyzePlanOnly()์ ๊ท ์ถ๊ฐ (DB ๋จ๊ณ๋ง ๋ถ๋ฆฌ)SseStreamController.registerStream()๋ด๋ถ ๋ฑ๋ก API ์ถ๊ฐ (HTTP ๋ผ์ด๋ํธ๋ฆฝ ์์ด ์ง์ ๋ฑ๋ก)
- 2๋จ๊ณ ๋ถ๋ฆฌ:
- SQL ์๋ ๋ฆฌํฉํฐ๋ง diff ๋ทฐ โ ์๋ณธ SQL vs AI ์ ์ ์ต์ ํ SQL์ LCS ๊ธฐ๋ฐ ๋ผ์ธ diff๋ก ์๊ฐํ
/advisor๋ฆฌ๋ทฐ ๊ฒฐ๊ณผ์์ ์ต์ ํ SQL ์ฝ๋ ๋ธ๋ก ์๋ ์ถ์ถ- ์ญ์ ๋ผ์ธ(๐ด ๋นจ๊ฐยท์ทจ์์ ) / ์ถ๊ฐ ๋ผ์ธ(๐ข ์ด๋ก) / ๋์ผ ๋ผ์ธ ์์ ๊ตฌ๋ถ
- ์๋ณธ ๋ณต์ฌ / AI ์ ์ ๋ณต์ฌ ๋ฒํผ ยท
.md์ ์ฅ
์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ค์ค (Harness)
- ์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ค์ค
/harnessโ Analyst โ Builder โ Reviewer 3๋จ๊ณ AI ํ์ดํ๋ผ์ธ- ๋ถ์๊ฐ(Analyst): ์ฑ๋ฅ ๋ฌธ์ ยท์ํฐํจํดยท๋ณด์ ์ทจ์ฝ์ ํ์
- ๊ฐ์ ๊ฐ(Builder): ๋ชจ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๊ฐ์ ์ฝ๋ ์์ฑ
- ๊ฒํ ์(Reviewer): ๋ณ๊ฒฝ ๋ด์ญ ๊ฒ์ฆ + APPROVED/NEEDS_REVISION ํ์
- LCS ๊ธฐ๋ฐ ์๋ณธ vs ๊ฐ์ ์ฝ๋ side-by-side Diff ๋ทฐ (์ญ์ ๐ด/์ถ๊ฐ๐ข/๋์ผ ์์)
- SSE ์คํธ๋ฆฌ๋ฐ ๋ชจ๋: ํ์ดํ๋ผ์ธ ์งํ ๋จ๊ณ๋ณ ์ค์๊ฐ ์ถ๋ ฅ
- Java / SQL ์ธ์ด ์ ํ, ์ด๋ ฅ ์๋ ์ ์ฅ (
HARNESS_REVIEW) HarnessReviewService+HarnessController์ ๊ท ์ถ๊ฐ
๐ ๋ถ์ ํ์ง ๊ฐํ
- ์ฝ๋ ํ์ง ์ ์ โ ํ๋ค์ค Reviewer๊ฐ ๊ฐ๋ ์ฑยท์ฑ๋ฅยท์ ์ง๋ณด์์ฑยท๋ณด์ ํญ๋ชฉ๋ณ X/10 ์ ์ + ์ข ํฉ ํ์ ์ฐ์ถ. ์ ์ฉ "ํ์ง ์ ์" ํญ ์ ๊ณต
- ๋ฐฐ์น ๋ถ์
/harness/batchโ ์ฌ๋ฌ Java ํ์ผ / SQL ์ค๋ธ์ ํธ๋ฅผ ๋์ ๋ชฉ๋ก์ ์ถ๊ฐ ํ ์์ฐจ ๋น๋๊ธฐ ๋ถ์. ์งํ๋ฅ ๋ฐยท๊ฒฐ๊ณผ ํ ์ด๋ธยท์์ธ ๋ชจ๋ฌ ์ ๊ณต. ์๋ฃ ํ ๋ค์ค ์ด๋ฉ์ผ ์๋ฆผ ์๋ ๋ฐ์ก. ๋ฐฐ์น ์ด๋ ฅ H2 DB ์๊ตฌ ์ ์ฅ (batch_history) + ์ด๋ ฅ ์์ธ์์ ํญ๋ชฉ๋ณ ๋ถ์ ๊ฒฐ๊ณผ ์ง์ ํ์ธ - ํ๋ค์ค ๋ถ์ ํ ํ๋ฆฟ โ ์ผ๋ฐยท์ฑ๋ฅ ์ต์ ํยท๋ณด์ ์ทจ์ฝ์ ยท๋ฆฌํฉํฐ๋งยทOracle SQL ์ฑ๋ฅยท๊ฐ๋ ์ฑ 6๊ฐ์ง ํ๋ฆฌ์ ์ ํ. Analyst ์์คํ ํ๋กฌํํธ์ ๋ชฉ์ ๋ณ ์ง์ค ์ง์ ์ถ๊ฐ
๐๏ธ Oracle / DB ํนํ
- DB ์ค๋ธ์ ํธ ์์กด์ฑ ๋ถ์
/harness/dependencyโ Oracle SP/Package/Function ๋๋ Java ํด๋์ค์ ํธ์ถ ๊ด๊ณยทํ ์ด๋ธ ์์กด์ฑ 5๊ฐ ์น์ ๋ณด๊ณ ์ ์ถ๋ ฅ. Java ํ์ผ ๋ธ๋ผ์ฐ์ + DB ์ค๋ธ์ ํธ ๊ฒ์ ์์ค ์ ํ ํจ๋ ๋ด์ฅ - DB ์บ์ ์๋ ๊ฐฑ์ ์ค์ผ์ค โ Settings์์ cron ํํ์ ์
๋ ฅ(ํ๋ฆฌ์
5์ข
).
@Scheduled(fixedRate=60s)+CronExpression.parse()๊ธฐ๋ฐ ์๋ ๊ฐฑ์ . WAS ์ฌ๊ธฐ๋ ์์ด ์ต์ ์ค๋ธ์ ํธ ๋ชฉ๋ก ์ ์ง
๐ค ๋ด๋ณด๋ด๊ธฐ / ๋ฆฌํฌํ
- ๋ถ์ ๊ฒฐ๊ณผ HTML ๋ด๋ณด๋ด๊ธฐ โ ํ๋ค์ค ๊ฒฐ๊ณผ(์๋ณธ ์ฝ๋ยท๊ฐ์ ์ฝ๋ยทDiffยท๋ถ์ ์์ฝยท๋ณ๊ฒฝ ๋ด์ญยท๊ฒํ ๊ฒฐ๊ณผ)๋ฅผ ๋
๋ฆฝ ์คํํ HTML ํ์ผ๋ก ์ ์ฅ (
POST /harness/export-html) - ํ์ง ๋์๋ณด๋
/harness/dashboardโ ๋์ HARNESS_REVIEW์ด๋ ฅ ๊ธฐ๋ฐ ํต๊ณ ์นด๋(์ด ๋ถ์ ์, APPROVED %, ์ธ์ด ๋น์จ) + Chart.js ํ์ ๋๋ยท์ธ์ด ๋ง๋ยท์ ์ ์ถ์ด ๋ผ์ธ ์ฐจํธ + ์ต๊ทผ ๋ถ์ ํ์๋ผ์ธ
โก UX / ์ด์ ํธ์
- ํ๋ค์ค ํ์คํ ๋ฆฌ ์ฌ๋ก๋ โ ํ์คํ ๋ฆฌ ์์ธ ๋ชจ๋ฌ์์ "ํ๋ค์ค ์ฌ๋ถ์" ๋ฒํผ ํด๋ฆญ ์ ์๋ณธ ์ฝ๋ยท์ธ์ด๋ฅผ localStorage์ ์ ์ฅ ํ
/harnessํ์ด์ง๋ก ์๋ ์ด๋ยท๋ก๋ - ๋ถ์ ์๋ฆผ (์ด๋ฉ์ผ) โ ๋ฐฐ์น ๋ถ์ ์๋ฃ ์ ์ง์ ์ด๋ฉ์ผ๋ก
emailService.sendJobResult()์๋ ๋ฐ์ก (SMTP ์ค์ ํ์)
๐ ๋ณด์ ๊ฐํ (์ฐจ๊ธฐ ๋ฒ์ ์ผ๋ก ์ด์)
- REST API ํค ์ธ์ฆ โ REST API ํธ์ถ ์
X-API-Keyํค๋ ์ธ์ฆ (/settings์์ ํค ๋ฐ๊ธยท๊ด๋ฆฌ) - Settings ๋น๋ฐ๋ฒํธ ์ ๊ธ โ ์ค์ ํ์ด์ง ์ ๊ทผ ์ PIN ๋๋ ๋น๋ฐ๋ฒํธ ์ ๋ ฅ ์๊ตฌ
๐ก๏ธ ํ๋ค์ค 4๋จ๊ณ ํ์ดํ๋ผ์ธ โ Verifier ์ถ๊ฐ
- ๊ฒ์ฆ์(Verifier) ๋จ๊ณ โ Analyst โ Builder โ Reviewer โ Verifier 4๋จ๊ณ ์์ฑ
- Java ๊ฒ์ฆ:
## ๐ ์ปดํ์ผ ๊ฐ๋ฅ์ฑ(import ๋๋ฝยทํ์ ๋ถ์ผ์นยท์ ๊ทผ ์ ์ด์ ์ค๋ฅ),## ๐จ ์ํ ๋ณ๊ฒฝ ๊ฐ์ง(๋ฉ์๋ ์๊ทธ๋์ฒ ๋ณ๊ฒฝยทNullPointerException ์ถ๊ฐยท๋ฆฌ์์ค ๋์, ์ฌ๊ฐ๋ HIGH/MEDIUM/LOW),## ๐ Spring/JPA ํธํ์ฑ(์ํ ์์กดยทN+1ยทLazyInitializationExceptionยท@Transactional ๋๋ฝ),## ๐ ์ต์ข ๊ฒ์ฆ ํ์ - SQL ๊ฒ์ฆ:
## ๐ SQL ๋ฌธ๋ฒ ๊ฒ์ฆ(Oracle ํค์๋ยท๊ดํธ ๋ถ์ผ์น),## ๐จ ์ํ ๋ณ๊ฒฝ ๊ฐ์ง(DROP/TRUNCATE/WHERE ์๋ DELETEยทUPDATE),## ๐ Oracle ์์กด์ฑ ๊ฒ์ฆ(DBMS_*ยทํํฐ์ ยท์ํ์คยทํํธ ๊ตฌ๋ฌธ),## ๐ ์ต์ข ๊ฒ์ฆ ํ์ - ํ์ ๋ฐฐ์ง: ๐ข VERIFIED (๋ฌธ์ ์์) / ๐ก WARNINGS (์ฃผ์ ํ์) / ๐ด FAILED (์ฌ๊ฐํ ๋ฌธ์ ) โ ๊ฒ์ฆ ๊ฒฐ๊ณผ ํญ ์๋จ์ ์ปฌ๋ฌ ๋ฐฐ์ง ํ์
- SSE ์คํธ๋ฆฌ๋ฐ: ํ์ดํ๋ผ์ธ ์คํธ๋ฆฌ๋ฐ ์ Verifier ์งํ ๋จ๊ณ ์ค์๊ฐ ํ์ (
streamStepVerifier) - ์ฌ๊ฐ๋ ํ์ด๋ผ์ดํ : HIGH/MEDIUM/LOW ํค์๋ ์๋ ์์ ๋ ๋๋ง
- Java ๊ฒ์ฆ:
- ๋ฐฐ์น ๋ถ์ + Verifier ํตํฉ โ ๋ฐฐ์น ์คํ ์ ๊ฐ ํญ๋ชฉ์ Verifier ๋จ๊ณ ํฌํจ. ์ด๋ ฅ ์๋ ์ ์ฅ
๐ SQL DB ๋ฒ์ญ โ /sql-translate
- ์ด์ข DB SQL ๋ฒ์ญ โ Oracle / MySQL / PostgreSQL / MSSQL ๊ฐ SQL ๋ฌธ๋ฒ ์๋ ๋ณํ. SSE ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ
- ์์ค/ํ๊ฒ DB ์ ํ โ 4์ข DB ์กฐํฉ ์ ํ UI + ๋น ๋ฅธ ์ค์ ๋ฒํผ
- ๋ฒ์ญ ์ด๋ ฅ ์ ์ฅ โ ๋ฒ์ญ ๊ฒฐ๊ณผ ReviewHistory
SQL_TRANSLATE์ ํ์ผ๋ก ์๋ ์ ์ฅ
๐ ROI ๋ฆฌํฌํธ โ /roi-report
- ์๋ณ ROI ์๊ฐํ โ ๊ธฐ๊ฐ ์ ํ(1~24๊ฐ์) + Chart.js ๋ผ์ธ/๋ฐ ์ฐจํธ (์ ๊ฐ์๊ฐยท๋น์ฉ์ ๊ฐยทROI%)
- ๊ธฐ๋ฅ๋ณ ๋ถ์ โ ๊ธฐ๋ฅ ์ ํ๋ณ ์ด๋ ฅ ์ง๊ณ โ ์ ๊ฐ ํจ๊ณผ ์์ ํ
- ๋จ๊ฐ ์ค์ โ ์๊ฐ๋น ์ธ๊ฑด๋นยทAI API ๋จ๊ฐยทํ์จ ์ปค์คํฐ๋ง์ด์ง (
~/.claude-toolkit/roi-settings.json) - ROI ์ค์ผ์ค๋ฌ โ
@Scheduled์๋ณ ์์ฝ ์๋ ๊ณ์ฐ
๐ ๋ณด์ ์ค์ โ /security
- REST API ํค ์ธ์ฆ โ
X-API-Keyํค๋ ์ธ์ฆ./security์์ ํค ๋ฐ๊ธยทํ๊ธฐยท์ฌ๋ฐ๊ธ.ApiKeyFilter์๋ธ๋ฆฟ ํํฐ - Settings ๋น๋ฐ๋ฒํธ ์ ๊ธ โ ์ค์ ํ์ด์ง ์ ๊ทผ ์ BCrypt ๋น๋ฐ๋ฒํธ ์ ๋ ฅ ์๊ตฌ. ์ธ์ ๊ธฐ๋ฐ ์ ๊ธ ํด์
- ๊ฐ์ฌ ๋ก๊ทธ(Audit Log) โ API ํธ์ถยท์ค์ ๋ณ๊ฒฝ ์ด๋ฒคํธ๋ฅผ ํ์์คํฌํยทIP์ ํจ๊ป H2 DB ๊ธฐ๋ก. ํ์ด์ง๋ค์ด์ ์กฐํ
-
spring-security-crypto์์กด์ฑ ์ถ๊ฐ (BCryptPasswordEncoder,spring-boot-starter-security๋ฏธ์ฌ์ฉ)
๐ต๏ธ ๋ฏผ๊ฐ์ ๋ณด ๋ง์คํน โ /input-masking
- 8๊ฐ์ง ํจํด โ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธยท์ ์ฉ์นด๋ยท์ด๋ฉ์ผยท์ ํ๋ฒํธยทIP์ฃผ์ยท๋น๋ฐ๋ฒํธยทAPIํคยท๊ณ์ข๋ฒํธ regex ํ์ง
- ์๋ฐฉํฅ ์ฒ๋ฆฌ โ ๋ง์คํน(ํ ํฐ ์นํ) โ Claude ์ ์ก โ ๋ณต์(ํ ํฐ โ ์๋ณธ) ์์ ์ํฌํ๋ก์ฐ
- ํ ํฐ ๋งต ์๊ฐํ โ ํ ํฐโ์๋ณธ ๋งคํ ํ ์ด๋ธ + ๋ณต์ฌ ๋ฒํผ + ์ ํ๋ณ ์นด์ดํธ ๋ฐฐ์ง
- LocalStorage ์์์ ์ฅ โ ์๋ก๊ณ ์นจ ํ์๋ ์ ๋ ฅ ํ ์คํธยทํ ํฐ ๋งต ๋ณต์
๐ก๏ธ ํ๋ค์ค Verifier ์์ฑ
- 4๋จ๊ณ ํ์ดํ๋ผ์ธ ์์ฑ โ Analyst โ Builder โ Reviewer โ Verifier ๋๊ธฐยท์คํธ๋ฆฌ๋ฐ ๋ชจ๋ ์ง์
- Java ๊ฒ์ฆ: ์ปดํ์ผ ๊ฐ๋ฅ์ฑยทSpring/JPA ํธํ์ฑยท์ํ ๋ณ๊ฒฝ ๊ฐ์ง (์ฌ๊ฐ๋ HIGH/MEDIUM/LOW)
- SQL ๊ฒ์ฆ: SQL ๋ฌธ๋ฒยทOracle ์์กด์ฑยท์ํ ๋ณ๊ฒฝ ๊ฐ์ง (DROP/TRUNCATE/WHERE ์๋ DELETE)
- ํ์ ๋ฐฐ์ง: ๐ข VERIFIED / ๐ก WARNINGS / ๐ด FAILED โ ๊ฒ์ฆ ๊ฒฐ๊ณผ ํญ ์๋จ ์ปฌ๋ฌ ๋ฐฐ์ง
๐งฉ ํตํฉ ์ํฌ์คํ์ด์ค + ํ๋ฌ๊ทธ์ธ ์ํคํ ์ฒ (Group 4)
- AnalysisService ํ๋ฌ๊ทธ์ธ ์ธํฐํ์ด์ค โ 9๊ฐ์ง ๋ถ์ ์ ํ(CODE_REVIEWยทSECURITY_AUDITยทTEST_GENERATIONยทJAVADOCยทREFACTORยทSQL_REVIEWยทSQL_SECURITYยทSQL_TRANSLATEยทHARNESS)์ ์ธํฐํ์ด์ค๋ก ์ถ์ํ.
AnalysisServiceRegistryBean ๋ฑ๋ก - ์ปค์คํ
์์คํ
ํ๋กฌํํธ ์ ์ฅ โ
custom_prompt์ํฐํฐ. ๋ถ์ ์ ํ๋ณ ํ๋กฌํํธ ํธ์งยท์ ์ฅยท์ด๊ธฐํ UI (/settings/prompts).PromptServiceDBโ๋ด์ฅ ํ๋กฌํํธ ์ฐ์ ์์ ํด์ - ํตํฉ ์ํฌ์คํ์ด์ค
/workspaceโ ๋ณต์ ๋ถ์ ์ ํ ๋์ ์ ํ โ Thread ๋ณ๋ ฌ ์คํ โ ํญ๋ณ SSE ์คํธ๋ฆฌ๋ฐ ๊ฒฐ๊ณผ. ์ธ์ด ์๋ ๊ฐ์ง. ๋ฒ๋ค ์ ์ฅ/๋ถ๋ฌ์ค๊ธฐ. ์์ค ์ ํ๊ธฐ(Java ํ์ผยทDB ์ค๋ธ์ ํธ) ์ ์ฒด ๋ชฉ๋ก ์ง์ - AI ๋ชจ๋ธ ๋น๊ต ๋ถ์ โ
/workspace/compare: ๋์ผ ์ฝ๋๋ฅผ ๋ณต์ ๋ชจ๋ธ๋ก ๋ณ๋ ฌ ๋ถ์, ์ข์ฐ 2์ปฌ๋ผ ๋น๊ต. ๋ฐํ์ ๋ชจ๋ธ ์ ํ(setModelOverride) + ์๋ณต - ๋ฉํฐ ์ธ์ด ์ง์ ํ์ฅ โ PythonยทJavaScriptยทTypeScriptยทKotlin ์ธ์ด๋ณ AnalysisType ์ง์. ์ธ์ด ์๋ ๊ฐ์ง ๋ถ๊ธฐ
- ์ ์ฒด ๊ฒฐ๊ณผ ์ด๋ฉ์ผ ์ผ๊ด ๋ฐ์ก โ ๋ชจ๋ ๋ถ์ ํญ ๊ฒฐ๊ณผ๋ฅผ ํ๋๋ก ํฉ์ณ ์ด๋ฉ์ผ ๋ฐ์ก. ํฌํจ ๋ถ์ ์ ํ ๋ฒ์ ํ์
- ์ด๋ ฅ ์์ธ ํ์ โ ์ด๋ ฅ ํด๋ฆญ ์ ๋ชจ๋ฌ ํ์ ์ผ๋ก ์ ์ฒด ์์ธ(์ผ์ยท์ธ์ดยท๋ถ์์ ํยท์ ๋ ฅ์ฝ๋) ํ์. MD/PDF ๋ค์ด๋ก๋, ํด๋ฆฝ๋ณด๋ ๋ณต์ฌ
- ๊ฒฐ๊ณผ PDF ๋ค์ด๋ก๋ โ ๋ถ์ ๊ฒฐ๊ณผ ์ ์ฒด๋ฅผ MarkdownโHTML ๋ณํ ํ ์ธ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ก PDF ์ ์ฅ
๐ง SSE ์คํธ๋ฆฌ๋ฐ ์์ ํ
- SSE ๋ฉํฐ๋ผ์ธ ๋ฐ์ดํฐ ์์ ์ ์ก โ
sendSseData()์ ํธ๋ฆฌํฐ:chunk.split("\n")ํ ๊ฐ ์ค์ ๋ณ๋data:๋ผ์ธ์ผ๋ก ์ ์กํ์ฌ ์ค๋ฐ๊ฟ ์์ค ๋ฐฉ์ง (Spring SseEmitter ํ๊ณ ๊ทน๋ณต) - AuditLogFilter SSE ๊ฒฝ๋ก ์ ์ธ โ
ContentCachingResponseWrapper๊ฐ/workspace/stream/๊ฒฝ๋ก๋ฅผ ๋ฒํผ๋งํ์ง ์๋๋ก ์์ (startsWith โ contains) - SQL DB ๋ฒ์ญ ๋
๋ฆฝ ํ์ด์ง โ
/sql-translate์ฌ์ด๋๋ฐ "๋ถ์" ์นดํ ๊ณ ๋ฆฌ ๋ฑ๋ก. ์์ค/ํ๊ฒ DB ์ ํ(4์ข ). ๋์ผ DB ๋ฐฉ์ด. Progress bar UI. DB ์ค๋ธ์ ํธ ์์ค ๋ก๋. ์ ์ฐํ ์๋ต ํ์ฑ - ์์ค ์ ํ๊ธฐ ๊ฐ์ โ ํ์ผ ์๋ ์ ํ(Java 200๊ฐยทDB 300๊ฐ) ํด์ โ ์ ์ฒด ๋ชฉ๋ก ํ์. ์ํฌ์คํ์ด์คยท๊ธฐ์ ๋ฌธ์ยท์ฝ๋๋ณํ ๋์ผ ์ ์ฉ
- ๊ธฐ์ ๋ฌธ์ยท์ฝ๋๋ณํ ํผ ์์ ์ฑ โ ์์ค ์ ํ ํญ
type="button"๋๋ฝ ์์ . ๋น ์ ๋ ฅ ๋ฐฉ์ด(ํ๋ก ํธ์๋ onsubmit + ๋ฐฑ์๋ ๊ฒ์ฆ) - ๊ธ๋ก๋ฒ ๊ฒ์ ํ์ฅ โ ๊ฒ์ ๋์ 20๊ฐโ37๊ฐ ๋ฉ๋ด๋ก ํ๋. ๋ชจ๋ ์ฌ์ด๋๋ฐ ๋ฉ๋ด ๊ฒ์ ๊ฐ๋ฅ
๐ฅ ๋ฉํฐ์ ์ / ํ ๊ธฐ๋ฅ (Group 5)
- ๊ณ์ ๊ด๋ฆฌ + RBAC โ
app_user์ํฐํฐ. ADMIN / REVIEWER / VIEWER ์ญํ . Spring Security Form ๋ก๊ทธ์ธ(/login). ์ต์ด ์คํ ์ admin/admin1234 ์๋ ์์ฑ.sec:authorize๋ก ์ฌ์ด๋๋ฐ ์ญํ ๋ณ ๋ฉ๋ด ํ์ - ํ ์ค์ ๊ณต์ โ
shared_config์ํฐํฐ. ํ๋ก์ ํธ ์ปจํ ์คํธยท๋ถ์ ํ ํ๋ฆฟยท์ปค์คํ ํ๋กฌํํธ ํ ๋จ์ ๊ณต์ ./settings/shared - ๋ถ์ ๊ฒฐ๊ณผ ๊ณต์ ๋งํฌ โ
share_token์ํฐํฐ. 7์ผ ๋ง๋ฃ ๋จ์ถ URL./share/{token}๋ก๊ทธ์ธ ๋ถํ์ ๋ ๋ฆฝ ๋ทฐ
๐ ์ด์ / ๋ฐฐํฌ (Group 6)
- Docker ๋ฉํฐ์คํ
์ด์ง ๋น๋ โ
maven:3.8-openjdk-11-slimโeclipse-temurin:11-jre-alpine. docker-compose.yml ํฌํจ. HEALTHCHECK/actuator/health - ์ธ๋ถ DB ์ง์ โ
application-h2.yml/application-mysql.yml/application-postgresql.yml.DB_TYPEํ๊ฒฝ๋ณ์๋ก ํ๋กํ์ผ ์๋ ์ ํ.@Lobโ@Column(columnDefinition="TEXT")ํธํ์ฑ ์ฒ๋ฆฌ - ์ค์น ๋ง๋ฒ์ฌ
/setupโ 4๋จ๊ณ ์์ ๋ (APIํค ์ ๋ ฅ+์ ์ฅ+ํ ์คํธ โ DB ์ค์ +์ ์ฅ+ํ ์คํธ โ ์ด๋ฉ์ผ(์ ํ) โ ์๋ฃ). "๋์ค์ ์ค์ ํ๊ธฐ" ์คํต ๋ฒํผ.SetupInterceptor๋ก ๋ฏธ์๋ฃ ์ ์๋ ๋ฆฌ๋ค์ด๋ ํธ - ํฌ์ค์ฒดํฌ ๊ฐํ โ
ClaudeApiHealthIndicator(API ping + ์๋ต์๊ฐ) +OracleDbHealthIndicator(SELECT 1 FROM DUAL + ์๋ต์๊ฐ)./actuator/health
๐ ๊ฐ์ฌ ๋ก๊ทธ ๊ฐํ
- ์ฌ์ฉ์ ์ถ์ โ ์ธ์ฆ๋ ์ฌ์ฉ์๋ช ์๋ ๊ธฐ๋ก (Spring Security ์ฐ๋)
- ์ก์ ์ ํ ๋ถ๋ฅ โ endpoint/method ๊ธฐ๋ฐ ์๋ ๋ถ๋ฅ (์กฐํยท๋ถ์์คํยท๋ค์ด๋ก๋ยท์ด๋ฉ์ผยท๋ก๊ทธ์ธ ๋ฑ)
- ์๋ต ์๊ฐ ๊ธฐ๋ก โ ์์ฒญ ์ฒ๋ฆฌ ์์์๊ฐ(ms) ๊ธฐ๋ก, 1์ดโ ๋ ธ๋ / 3์ดโ ๋นจ๊ฐ ํ์
- ํํฐ๋ง โ ์ฌ์ฉ์ยท์ก์ ์ ํยท์๋ํฌ์ธํธยท๋ ์ง๋ฒ์(1h/์ค๋/7d/30d) ์ค์๊ฐ ํํฐ
- CSV ๋ด๋ณด๋ด๊ธฐ โ
/security/audit-log/export(BOM ํฌํจ Excel ํ๊ตญ์ด ํธํ)
๐ ์ธ๋ถ ์ฐ๋ (Group 7)
- Slack / Teams ์นํ
์๋ฆผ โ
NotificationService. Settings์์ URL ์ ๋ ฅ + ํ ์คํธ ๋ฐ์ก.~/.claude-toolkit/settings.json์์ํ - GitHub PR ์๋ ์ฝ๋ฉํธ โ
/github-pr: PR URL โ diff ๊ฐ์ ธ์ค๊ธฐ โ AI ๋ฆฌ๋ทฐ โ PR ์ฝ๋ฉํธ ์๋ ๋ฑ๋ก (GitHub REST API) - Jira ์ฐ๋ โ
JiraServiceREST API v3. Settings์์ URL/์ด๋ฉ์ผ/APIํ ํฐ/ํ๋ก์ ํธํค ์ค์ + ์ฐ๊ฒฐ ํ ์คํธ - Git ๋ก์ปฌ ์ฐ๋ โ
/git-diff: ๋ก์ปฌ ์ ์ฅ์ ์ปค๋ฐ ๋ชฉ๋ก โ diff โ AI ๋ฆฌ๋ทฐ (ProcessBuilder git ์คํ)
๐ค ๋ฉํฐ์ ์ ๊ณ ๋ํ
- ์ฌ์ฉ์๋ณ ํ๋ก๊ทธ๋จ ๊ถํ ๊ด๋ฆฌ โ
/admin/permissions: RBAC(์ญํ ) ์ธ ๊ฐ๋ณ ์ฌ์ฉ์ ๋จ์ 35๊ฐ ๊ธฐ๋ฅ ์ ๊ทผ ํ์ฉ/์ฐจ๋จ. ์ฌ์ด๋๋ฐ + URL ์ ๊ทผ ์ ์ด ๋๊ธฐํ - Docker Windows ๊ฒฝ๋ก ์๋ ๋ณํ โ Settings์
D:\path์ ๋ ฅ โ Docker์์/host/d/path๋ก ์๋ ๋ณํ. ์ฌ์ฉ์ ๊ฒฝํ ์ผ๊ด์ฑ
๐ ๊ณ์ ๋ฐ ์ธ์ ๊ด๋ฆฌ
- ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ UI โ
/account/password: ํ์ฌ ๋น๋ฐ๋ฒํธ ํ์ธ + ์ ๋น๋ฐ๋ฒํธ ์ ๋ ฅ. ์ฌ์ด๋๋ฐ ํ๋จ ๐ ์์ด์ฝ - ๋ด ์ค์ ํ์ด์ง โ
/account/settings: ๋ด ์ ๋ณด(์ด๋ฆ/์ด๋ฉ์ผ/ํธ๋ํฐ) ์์ + ๊ฐ์ธ API ํค ์ ๋ ฅ/์ญ์ . ์ฌ์ด๋๋ฐ ํ๋จ โ ์์ด์ฝ - ์ธ์
ํ์์์ 1์๊ฐ โ ๋ฏธํ๋ 1์๊ฐ ํ ์๋ ๋ง๋ฃ. ์ฌ์ฉ์ ํ๋(ํด๋ฆญ/ํค ์
๋ ฅ) ์ ์๋ ๊ฐฑ์ . ์ฐ์ธก ์๋จ์
๋ถ:์ด์นด์ดํธ๋ค์ด + ๐ ๊ฐฑ์ ๋ฒํผ. 5๋ถโ๋ ธ๋, 1๋ถโ๋นจ๊ฐ ๊ฒฝ๊ณ - ์ฌ์ฉ์๋ณ API ํค โ ๊ฐ์ธ Claude API ํค ์ค์ . ๋ถ์ ์คํ ์ ๊ฐ์ธ ํค ์๋ ์ ํ โ ์์ฒญ ์๋ฃ ํ ์๋ฒ ํค ๋ณต์. ๋น์๋๋ฉด ์๋ฒ ๊ณต์ฉ ํค ์ฌ์ฉ
โ๏ธ Settings ๋ฐ ๋ณด์ ๊ฐํ
- ์
๋ ฅ๊ฐ ์ ํจ์ฑ ๊ฒ์ฆ โ DB URL
jdbc:ํ์ ์ฒดํฌ ๋ฑ ๋ฐฑ์๋ ๊ฒ์ฆ - API Rate Limiting โ ์ฌ์ฉ์ ๊ด๋ฆฌ์์ ์ฌ์ฉ์๋ณ ๋ถ๋น/์๊ฐ๋น ํธ์ถ ์ ํ ์ค์ (์์ ๋ชจ๋ฌ).
RateLimitService๋ฉ๋ชจ๋ฆฌ ๊ธฐ๋ฐ ์ถ์ . ์ด๊ณผ ์ HTTP 429 ์๋ต - ๊ฐ์ฌ ๋ก๊ทธ ํ์ด์ง๋ค์ด์ โ 300๊ฑด ์ ํ โ ์๋ฒ ์ฌ์ด๋ 50๊ฑด/ํ์ด์ง + ํ์ด์ง ์ด๋. ์ฌ์ฉ์/๊ธฐ๊ฐ ์๋ฒ ํํฐ + ์ก์ /์๋ํฌ์ธํธ ํด๋ผ์ด์ธํธ ํํฐ
๐พ ๋ฐ์ดํฐ ๊ด๋ฆฌ
- ๋ฐ์ดํฐ ๋ฐฑ์
/๋ณต์ โ
/admin/backup: H2 DB + settings.json + security-settings.json์ ZIP ๋ค์ด๋ก๋/์ ๋ก๋. ์ฌ์ด๋๋ฐ ๊ด๋ฆฌ ์น์ - ์๋ ์คํค๋ง ๋ง์ด๊ทธ๋ ์ด์
โ
SchemaMigration: ์ฑ ์์ ์ ๋๋ฝ ์ปฌ๋ผ ์๋ ์ถ๊ฐ (DEFAULT ๊ฐ ํฌํจ). ๊ธฐ์กด Docker volume DB ํธํ
๐ ๋์๋ณด๋ ๊ฐํ
- ์์คํ ์ํ ์์ ฏ โ ์ด๋ฒ ๋ฌ ๋ถ์ ์์ฒญ/ํ ํฐ ์ฌ์ฉ๋, ์ค๋ API ํธ์ถ, ๋ฑ๋ก ์ฌ์ฉ์ ์ ์์ฝ ์นด๋
- ์ฐ๊ฒฐ ์ํ ํ์ โ Claude API / Oracle DB / ์ด๋ฉ์ผ ์ฐ๊ฒฐ ์ํ ์ค์๊ฐ ํ์
- ๊ด๋ฆฌ ๋๊ตฌ ์น์ โ ์ฌ์ฉ์ ๊ด๋ฆฌ(ADMIN), ๋ณด์ ์ค์ (ADMIN), ๋ด ์ค์ (์ ์ฒด) ์นด๋
๐จ ํ ๋ง ํ๋ฆฌ์
- 5๊ฐ์ง ์์ ํ๋ฆฌ์ โ Settings์์ ์ค๋ ์ง/๋ธ๋ฃจ/๊ทธ๋ฆฐ/ํผํ/ํํฌ ์ํด๋ฆญ ์ ํ
๐ 2FA ์ด์ค ์ธ์ฆ
- Google Authenticator ์ฐ๋ โ
/account/settings์์ QR์ฝ๋ ์ค์บ โ 6์๋ฆฌ ์ฝ๋ ์ธ์ฆ โ 2FA ํ์ฑํ - TOTP (RFC 6238) โ
TotpService: Base32 ์ํฌ๋ฆฟ ์์ฑ, HMAC-SHA1 ์ฝ๋ ์์ฑ, ยฑ30์ด ํ์ฉ - ์ฌ์ฉ์๋ณ ๋ ๋ฆฝ ์ค์ โ ๊ฐ ์ฌ์ฉ์๊ฐ ์์ ์ 2FA๋ฅผ ๊ฐ๋ณ ๊ด๋ฆฌ (ํ์ฑํ/๋นํ์ฑํ)
- ADMIN 2FA ๊ฐ์ โ
TwoFactorAuthHandler+TwoFactorInterceptor. ADMIN ๋ก๊ทธ์ธ ์ 2FA ๊ฒ์ฆ ํ์. QR์ฝ๋ qrcodejs ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๐ ๋ณด์ ๊ฐํ
- ๋ฏผ๊ฐ์ ๋ณด AES ์ํธํ โ
settings.json์ APIํค/DB๋น๋ฐ๋ฒํธ/์ด๋ฉ์ผ๋น๋ฐ๋ฒํธ/Jiraํ ํฐ์ AES-128-CBC๋ก ์ํธํ ์ ์ฅ.~/.claude-toolkit/.encryption-key์๋ ์์ฑ. ๊ธฐ์กด ํ๋ฌธ ๋ฐ์ดํฐ ์๋ ๋ง์ด๊ทธ๋ ์ด์ - DB ๋ฏผ๊ฐํ๋ ์ํธํ โ
AppUser.totpSecret,personalApiKeygetter/setter์์ ์๋ AES ์ํธํ/๋ณตํธํ. ์ปฌ๋ผ 200/64โ500์ ํ์ฅ - ์ด๊ธฐ admin ๋น๋ฐ๋ฒํธ ๊ฐ์ ๋ณ๊ฒฝ โ
mustChangePasswordํ๋๊ทธ +PasswordChangeInterceptor. ์ฒซ ๋ก๊ทธ์ธ ์/account/password๋ก ๊ฐ์ ๋ฆฌ๋ค์ด๋ ํธ - ๋น๋ฐ๋ฒํธ ์ ์ฑ ๊ฐํ โ ์ต์ 8์, ๋๋ฌธ์/์๋ฌธ์/์ซ์/ํน์๋ฌธ์ ๊ฐ 1๊ฐ ์ด์ ํ์. ์ด์ ๋น๋ฐ๋ฒํธ ์ฌ์ฌ์ฉ ๋ฐฉ์ง
- ์ธ์
๋ณด์ โ ์ธ์
๊ณ ์ ๊ณต๊ฒฉ ๋ฐฉ์ง(
sessionFixation().newSession()), ์ค๋ณต ๋ก๊ทธ์ธ ์ ๊ธฐ์กด ์ธ์ ํด์ (maximumSessions=1)
๐ก๏ธ ์์ ์ฑ ๊ฐ์
- Global Exception Handler โ
@ControllerAdviceํตํฉ ์๋ฌ ์ฒ๋ฆฌ. HTML ์์ฒญโerror.html์๋ฌ ํ์ด์ง(403/404/500), AJAXโJSON ์๋ฌ ์๋ต. SLF4J ๋ก๊น - Logger ํต์ผ โ 12๊ฐ ํ์ผ
System.err.println/System.out.printlnโ SLF4Jlog.error()/log.info()๊ต์ฒด - GitService ํ์์์ โ
process.waitFor(30, SECONDS)+ ์ด๊ณผ ์destroyForcibly()๊ฐ์ ์ข ๋ฃ - ๋ฐฐ์น ์๋ฌ ํธ๋ค๋ง โ ๊ฐ๋ณ ํ์ผ ์คํจ ์ ์คํต + ์ฑ๊ณต/์คํจ ๊ฑด์ ์์ฝ (
successCount,failCount) - AuditLog ๊ฐ์ โ UserAgent 300โ500์ ํ์ฅ. ๋ณด์ ์ค์ ํ์ด์ง์ "90์ผ ์ด์ ๋ก๊ทธ ์๋ ์ญ์ " ์๋ด ๋ฌธ๊ตฌ ์ถ๊ฐ
๐จ UI/UX ๋๊ท๋ชจ ๊ฐ์
- ๊ธ๋ก๋ฒ ํ ์คํธ ์๋ฆผ โ
alert()141๊ฐ ์ ๊ฑฐ โ ์ฐํ๋จ ์คํํ ํ ์คํธ (success/error/warning/info, ์งํ ๋ฐ, ์๋ ์๋ฉธ 3์ด, ์ต๋ 5๊ฐ) - Breadcrumb ๋ค๋น๊ฒ์ด์ โ URL ๊ฒฝ๋ก ๊ธฐ๋ฐ ์๋ ์์ฑ (ํ > ๋ถ์ > ์คํ๊ณํ ๋น๊ต). 40๊ฐ+ ํ๊ตญ์ด ๊ฒฝ๋ก ๋งคํ
- ํผ ์ ํจ์ฑ ์๊ฐํ โ
validateForm(formId, rules)์ ํธ๋ฆฌํฐ +.is-invalid๋นจ๊ฐ ํ ๋๋ฆฌ +.invalid-feedback์๋ฌ ๋ฉ์์ง - ๋ฒํผ ์คํ์ผ ํต์ผ โ
.btn-danger-tk/.btn-success-tk/.btn-info-tkCSS ํด๋์ค ์ถ๊ฐ - ๋ชจ๋ฐ์ผ ๊ฒฐ๊ณผ ๋ฐ์ํ โ ๊ฒฐ๊ณผ ์์ญ
max-height: 60vh, ์คํธ๋ฆฌ๋ฐ50vh(๊ธฐ์กด ๊ณ ์ 400px/300px) - ๋คํฌ/๋ผ์ดํธ ๋ชจ๋ ์ ๋ฆฌ โ
.text-success-tk/.text-danger-tk/.text-warning-tkํ ๋ง ์ธ์ ์ ํธ ํด๋์ค - ๊ฒฐ๊ณผ ์ ์ฒด ๋ณต์ฌ โ
copyAllResults()ํจ์ + ํ ์คํธ ํผ๋๋ฐฑ - ์ค์ผ๋ ํค ๋ก๋ฉ UI โ
showSkeleton()/hideSkeleton()+ CSS ํ์ค ์ ๋๋ฉ์ด์ (.skeleton,.skeleton-line) - ์ฌ์ด๋๋ฐ ๊ธฐ๋ฅ ๊ฒ์ โ ๋ฉ๋ด 30๊ฐ+ ์ค์๊ฐ ํํฐ๋ง ๊ฒ์ ์ ๋ ฅ๋. ๋งค์นญ ์น์ ์๋ ํผ์นจ
โก ๊ธฐ๋ฅ ๊ฐ์
- CodeMirror ์๋ํฐ โ
initCodeEditor(textareaId, language)CDN ๋์ ๋ก๋. Java/SQL/Python/JS/XML 5๊ฐ ์ธ์ด ๋ชจ๋. ๋ผ์ธ ๋ฒํธ, ๊ตฌ๋ฌธ ๊ฐ์กฐ, ๊ดํธ ๋งค์นญ - PDF ๋ฆฌํฌํธ ๋ด๋ณด๋ด๊ธฐ โ
exportPdf(elementId, title)์ ์ฐฝ ์ธ์ ๊ธฐ๋ฐ. ๋ถ์ ๊ฒฐ๊ณผ ํค๋/ํธํฐ ํฌํจ ์ธ์ ๋ ์ด์์ - ๋ถ์ ๊ฒฐ๊ณผ ๋๊ธ โ
ReviewComment์ํฐํฐ. ์ด๋ ฅ ์์ธ์์ ๋๊ธ ์์ฑ/์ญ์ (๋ณธ์ธ ๋๋ ADMIN). AJAX ์ค์๊ฐ ๋ฐ์ - ์๋ฆผ ์ผํฐ โ
Notification์ํฐํฐ. ๐ ๋ฒจ ์์ด์ฝ + ๋ฏธ์ฝ์ ๋ฐฐ์ง + ๋๋กญ๋ค์ด ๋ชฉ๋ก. 60์ด ํด๋ง. ๋๊ธ ์์ฑ ์ ๋ถ์ ์์ฑ์์๊ฒ ์๋ ์๋ฆผ - ๋ถ์ ๊ฒฐ๊ณผ ์บ์ฑ โ
AnalysisCacheServiceSHA-256 ํด์ ๊ธฐ๋ฐ LRU ์ธ๋ฉ๋ชจ๋ฆฌ ์บ์ (์ต๋ 200๊ฐ, TTL 1์๊ฐ). ์บ์ ํํธ ์ "[์บ์ ๊ฒฐ๊ณผ]" ๋ฐฐ์ง ํ์
๐ ์ ๊ธฐ๋ฅ
- AI ์ฑํ
์ธํฐํ์ด์ค โ
/chat: ๋ํํ AI ์ง์์๋ต. EventSource ๊ธฐ๋ฐ SSE ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ (2๋จ๊ณ: POST ๋ฉ์์ง ์ ์ฅ โ GET ์คํธ๋ฆฌ๋ฐ). Thinking ์ธ๋์ผ์ดํฐ(ํ์ค ์ + ๊ฒฝ๊ณผ ์๊ฐ ํ์). ์คํธ๋ฆฌ๋ฐ ์ค ์ปค์ ๊น๋นก์ ํจ๊ณผ. ํํธ๋นํธ keep-alive (์ ํด ์ฐ๊ฒฐ ๋๊น ๋ฐฉ์ง). ๋งํ์ ๋ณ ๋ณต์ฌ ๋ฒํผ (HTTP fallback ์ง์). ๋ถ์ ๊ฒฐ๊ณผ ์ปจํ ์คํธ ์ฒจ๋ถ. ์ธ์ ๊ธฐ๋ฐ 20ํด ํ์คํ ๋ฆฌ.PermissionInterceptor๋ก ์ฌ์ฉ์ ๊ถํ ์ ์ด - ํ ๋์๋ณด๋ โ
/admin/team-dashboard(ADMIN ์ ์ฉ): ์ผ๋ณ ๋ถ์ ํธ๋ ๋ ๋ผ์ธ ์ฐจํธ, ์ฌ์ฉ์๋ณ ๋ถ์ ๊ฑด์/ํ ํฐ ๋ฐ ์ฐจํธ, ๊ธฐ๋ฅ๋ณ ์ฌ์ฉ ๋น๋ ๋๋ ์ฐจํธ (Chart.js 4.4.1). ๊ธฐ๊ฐ ์ ํ๊ธฐ (7/30/90์ผ) - ๊ฐ์ฌ ๋ก๊ทธ ์๊ฐํ โ
/admin/audit-dashboard(ADMIN ์ ์ฉ): ์๊ฐ๋๋ณ ์์ฒญ ๋ถํฌ ๋ฐ ์ฐจํธ, ์ฌ์ฉ์๋ณ ํ๋ Top 10, ์ก์ ์ ํ ํ์ด ์ฐจํธ, ์ผ๋ณ ์๋ฌ์จ ์ถ์ด. ๊ธฐ๊ฐ ์ ํ๊ธฐ (์ค๋/7์ผ/30์ผ) - ๋ถ์ ๋น์ฉ ์ถ์ โ ๋ชจ๋ธ๋ณ ํ ํฐ ๋จ๊ฐ ์ ์ฉ (์
๋ ฅ $3/1M, ์ถ๋ ฅ $15/1M). ์ผ๋ณ ๋น์ฉ ์ถ์ด (
/usage/daily-cost). ์ฌ์ฉ์๋ณ ๋น์ฉ ๋ถ๋ฐฐ (/usage/cost-by-user, ADMIN ์ ์ฉ). KRW ํ์ฐ
๐ง ๋ฒ๊ทธ ์์
- ๋ก๊ทธ์ธ CSRF ์ค๋ฅ ์์ โ
POST /login์ด CSRF ํํฐ์์ ์ฐจ๋จ๋์ด 500 ์๋ฌ ๋ฐ์./login์ CSRF ์์ธ ๋ชฉ๋ก์ ์ถ๊ฐ - Jackson ๋ฒ์ ๋ถ์ผ์น ํด๊ฒฐ โ
jackson-databind2.15.4๋ง ์ ์ธํ๊ณjackson-core/jackson-annotations๋ Spring Boot BOM์ 2.13.x ์ฌ์ฉ โNoClassDefFoundError: StreamConstraintsException. 3๊ฐ ๋ชจ๋ ๋ชจ๋ 2.15.4๋ก ํต์ผ - AuditLogFilter SSE ๋ฒํผ๋ง ์์ โ
ContentCachingResponseWrapper๊ฐ/chat/stream์ ๋ฒํผ๋งํ์ฌ ์คํธ๋ฆฌ๋ฐ ๋ถ๊ฐ.shouldSkip()ํจํด์"/stream/"โ"/stream"์ผ๋ก ๋ณ๊ฒฝ - ChatController ์๋ฌ ํธ๋ค๋ง โ
catch(Exception)โcatch(Throwable)๋ก ๋ณ๊ฒฝํ์ฌNoClassDefFoundError๋ฑError๋ ํฌ์ฐฉ. ์๋ฌ ์ ๋น ๋งํ์ ๋์ ์ค๋ฅ ๋ฉ์์ง ํ์
๐ก๏ธ ์ฌ์ฉ์๋ณ ๊ถํ ๊ด๋ฆฌ ๋ณด์
- ์ฌ์ด๋๋ฐ ๊ถํ ์ฒดํฌ ๋๋ฝ 11๊ฐ ๋ฉ๋ด ๋ณด์ โ
depcheck,migrate,history,favorites,usage,roi-report,schedule,maskgen,github-pr,git-diff,prompts์th:if๊ถํ ์ฒดํฌ ์ถ๊ฐ. ๊ถํ ์๋ ๋ฉ๋ด ํด๋ฆญ ์ ๋ก๊ทธ์์๋๋ ๋ฌธ์ ํด์ - AI ์ฑํ
๊ถํ ๊ด๋ฆฌ ์ถ๊ฐ โ
AdminPermissionControllerFEATURES ๋ชฉ๋ก์chatํญ๋ชฉ ์ถ๊ฐ. "์ฌ์ฉ์ ๊ถํ ๊ด๋ฆฌ" ํ์ด์ง์์ AI ์ฑํ on/off ๊ฐ๋ฅ -
/admin๋ฆฌ๋ค์ด๋ ํธ โ/admin์ ๊ทผ ์ 404 ๋์/admin/users๋ก ์๋ ๋ฆฌ๋ค์ด๋ ํธ
๐ฌ AI ์ฑํ UX ๋ํญ ๊ฐ์
- EventSource ๊ธฐ๋ฐ ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ โ
fetch+ReadableStreamโEventSource(๋ค์ดํฐ๋ธ SSE)๋ก ์ ํ. ๊ธ์ ๋จ์ ์ค์๊ฐ ํ์ดํ ํจ๊ณผ - 2๋จ๊ณ SSE ์ํคํ
์ฒ โ
POST /chat/send(๋ฉ์์ง ์ ์ฅ) โGET /chat/stream(EventSource ์คํธ๋ฆฌ๋ฐ) ๋ถ๋ฆฌ - ํํธ๋นํธ keep-alive โ 3์ด ๊ฐ๊ฒฉ SSE ์ฝ๋ฉํธ ์ ์ก์ผ๋ก Docker/ํ๋ก์ ์ ํด ์ฐ๊ฒฐ ๋๊น ๋ฐฉ์ง
- Thinking ์ธ๋์ผ์ดํฐ โ ํ์ค ์ ๋๋ฉ์ด์ 3์ + ๊ฒฝ๊ณผ ์๊ฐ ์ค์๊ฐ ์นด์ดํธ ("์๋ต ์์ฑ ์ค ยท 5์ด")
- ์คํธ๋ฆฌ๋ฐ ์ปค์ โ ์๋ต ์์ฑ ์ค ๋งํ์ ๋์ ์ฃผํฉ์ ์ปค์ ๊น๋นก์, ์๋ฃ ์ ์๋ ์ ๊ฑฐ
- ๋งํ์ ๋ณต์ฌ ๋ฒํผ โ ํธ๋ฒ ์ ํ์, ์ฌ์ฉ์ ๋ฉ์์ง๋ ์ข์ธก ํ๋จยทAI ์๋ต์ ์ฐ์ธก ํ๋จ. HTTP ํ๊ฒฝ
execCommandfallback ํฌํจ. ๋ณต์ฌ ์ โ ํผ๋๋ฐฑ
๐ง ๋์์ฑ / ์์ ์ฑ
- API ํค ๋์์ฑ ์์ โ
ClaudeClient์InheritableThreadLocal๊ธฐ๋ฐapiKeyOverride์ถ๊ฐ.PermissionInterceptor๋ ๊ณต์ClaudeProperties.setApiKey()๋์claudeClient.setApiKeyOverride()ํธ์ถ. SSE ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋๋ ๋ถ๋ชจ ์ค๋ ๋์ ํค ์์. ๋ฉํฐ์ ์ ๋์ ์์ฒญ ์์ ๊ฒฉ๋ฆฌ - ์ฑํ
์ธ์
๋์์ฑ ์์ โ ์ ๊ท
ChatHistoryStore์ปดํฌ๋ํธ (ConcurrentHashMap๊ธฐ๋ฐ).HttpSession์ง์ ์ฐ๊ธฐ ์ ๊ฑฐ. ์ธ์ ID๋ฅผ ์กฐํ ํค๋ก๋ง ์ฌ์ฉ. 24์๊ฐ TTL ์๋ ์ ๋ฆฌ (KST ์๋ฒฝ 4์) - ์ฌ์ด๋๋ฐ ๋น ์น์
์๋ ์จ๊น โ
fragments/sidebar.html์ ๋ถ์/์์ฑ/๊ธฐ๋ก/๋๊ตฌ 4๊ฐ ์น์ ์ Thymeleaf<div th:if="...">๋ํผ๋ก ๊ฐ์ธ ํด๋น ์น์ ๋ด ํ์ฉ๋ ๋ฉ๋ด๊ฐ ํ๋๋ ์์ผ๋ฉด ์น์ ํค๋ ์์ฒด๋ฅผ ์จ๊น - CSRF ํ ํฐ ๋ง๋ฃ UX โ
login.html์?denied=true/?expired=true๊ฐ์ง โ "์ธ์ ์ด ๋ง๋ฃ๋์์ต๋๋ค" ์๋ด ํ์. 15๋ถ ํ ์ ๋ ฅ ํ๋ ๋น์ด์์ผ๋ฉด ์๋ ์๋ก๊ณ ์นจ์ผ๋ก ํ ํฐ ์ฌ๋ฐ๊ธ
โก ์ฑ๋ฅ
- AI ์ฑํ
๋ฉํฐํด ์ต์ ํ โ
ClaudeClient์chatStream(systemPrompt, List<ClaudeMessage>, ...)์ค๋ฒ๋ก๋ ์ถ๊ฐ.ChatController๊ฐ ํ ์คํธ concatenation([์ด์ ๋ํ]\n[user]: ...) ์ ๊ฑฐํ๊ณmessages๋ฐฐ์ด์ Claude Messages API์ ์ง์ ์ ๋ฌ. ํ ํฐ ์ ๊ฐ + prompt caching ํ์ฉ ๊ฐ๋ฅ
๐ ์ธ์ฆ / ์ ๊ทผ ์ ์ด
- ๋ก๊ทธ์ธ ์คํจ ์ ๊ธ โ
AppUser์failedLoginAttempts,lockedUntilํ๋ ์ถ๊ฐ.LoginAttemptHandler(AuthenticationFailureHandler) ์ ๊ท ์ถ๊ฐ โ 5ํ ์ฐ์ ์คํจ ์ 10๋ถ ์ ๊ธ + ๊ฐ์ฌ ๋ก๊ทธ ๊ธฐ๋ก.AppUserDetailsService์์LockedException๋ฐ์. ๋ก๊ทธ์ธ ์ฑ๊ณต ์TwoFactorAuthHandler์์ ์นด์ดํฐ ์๋ ๋ฆฌ์ . ์ฌ์ฉ์ ๊ด๋ฆฌ ํ์ด์ง์์ ADMIN ์๋ ํด์ ๋ฒํผ ์ ๊ณต - ๋น๋ฐ๋ฒํธ ๋ง๋ฃ ์๋ฆผ โ
lastPasswordChangeAt,passwordSnoozeAtํ๋ ์ถ๊ฐ.PasswordExpiryInterceptor์ ๊ท ์ถ๊ฐ โ 90์ผ ๊ฒฝ๊ณผ ์ ๋ณ๊ฒฝ ๊ถ๊ณ ํ์ด์ง ํ์. "๋ค์์ ๋ณ๊ฒฝํ๊ธฐ" ๋ฒํผ(POST /account/snooze-password) ํด๋ฆญ ์passwordSnoozeAt = now()โ 1์ผ๋ถํฐ ์ฌ์นด์ดํ . ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ ์lastPasswordChangeAt๊ฐฑ์ + ์ค๋์ฆ ์ด๊ธฐํ - IP ํ์ดํธ๋ฆฌ์คํธ (์ฌ์ฉ์๋ณ) โ
AppUser.ipWhitelistํ๋ ์ถ๊ฐ.IpWhitelistChecker์ ํธ๋ฆฌํฐ (IPv4 + CIDR, ์์ Java ๋นํธ ์ฐ์ฐ). "์ฌ์ฉ์ ๊ด๋ฆฌ" ํ์ด์ง์ ๋ณ๋ ํ์ ๋ชจ๋ฌ โ ๋ฑ๋ก/์ถ๊ฐ/์ญ์ /์ ์ฅ.AppUserDetailsService.loadUserByUsername()์์RequestContextHolder๋ก ํด๋ผ์ด์ธํธ IP ์ถ์ถ ํ ๊ฒ์ฆ. ์ด๊ธฐ๊ฐ ๋ฌด์ ํ, ์ค์ ์ ํด๋น ์ฌ์ฉ์๋ ํ์ฉ๋ IP์์๋ง ๋ก๊ทธ์ธ ๊ฐ๋ฅ - API ํค ์ฌ์ฉ๋ ์ ํ (์ผ์ผ/์๊ฐ) โ
AppUser.dailyApiLimit,monthlyApiLimitํ๋ ์ถ๊ฐ.RateLimitServiceํ์ฅ โ ์ผ์๋ณ ์นด์ดํธ ๋งต(Map<LocalDate, AtomicInteger>) ๊ธฐ๋ฐ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ถ์ . 32์ผ ์ด์ ์นด์ดํธ ์๋ ์ ๋ฆฌ(@Scheduled).getUsageStats()API๋ก ์ค๋/์ด๋ฒ ๋ฌ ์ฌ์ฉ๋ ์กฐํ. ์ฌ์ฉ์ ๊ด๋ฆฌ ๋ชจ๋ฌ์ ํ๋ ์ ๋ ฅ + ํ์ฌ ์ฌ์ฉ๋ ํ์
๐ฌ AI ์ฑํ
- AI ์ฑํ
Markdown ๋ ๋๋ง โ
marked.js+DOMPurify+Prism.js๋ก ์ฝ๋ ๋ธ๋ก/๋ณผ๋/๋ฆฌ์คํธ/ํ ์ด๋ธ/์ธ์ฉ/ํ ๋ ๋๋ง. ์คํธ๋ฆฌ๋ฐ ์ค์๋ ํ๋ฌธ + ์ปค์, ์๋ฃ ์ Markdown ์ฌ๋ ๋๋ง. XSS ๋ฐฉ์ง (DOMPurify) - ๋ํ ๋ด๋ณด๋ด๊ธฐ โ ํค๋์ "๐ฅ ๋ด๋ณด๋ด๊ธฐ" ๋ฒํผ. ํ์ฌ ์ธ์
์ ์ ์ฒด ๋ํ๋ฅผ
.mdํ์ผ๋ก ๋ค์ด๋ก๋. ํ์ผ๋ช :chat_YYYYMMDD_HHmmss.md - ๋ํ ๋ชฉ๋ก/์ธ์
๊ด๋ฆฌ โ
ChatSession/ChatMessageH2 ์ํฐํฐ ์์ํ. ์ข์ธก 240px ํจ๋์ ๋ํ ๋ชฉ๋ก ํ์,+ ์ ๋ํ, ํธ๋ฒ ์ ์ด๋ฆ ๋ณ๊ฒฝ/์ญ์ ๋ฒํผ. ์ฌ์ฉ์๋ณ ๋ค์ค ์ธ์ ์ง์. ์ฒซ ๋ฉ์์ง๋ก ์ ๋ชฉ ์๋ ์์ฑ. ์ธ์ ์์ ๊ถ ๊ฒ์ฆ
โก ์ฑ๋ฅ
- ์ ์ ๋ฆฌ์์ค ๋ก์ปฌ ๋ฒ๋ค๋ง โ WebJars ๊ธฐ๋ฐ Maven ์์กด์ฑ ์ถ๊ฐ:
bootstrap5.3.2,font-awesome6.5.1,prism1.29.0,marked9.1.6,chart.js4.4.1,dompurify3.0.8. 60๊ฐ ํ ํ๋ฆฟ์์ 206๊ฑด CDN URL์/webjars/**๋ก ์ผ๊ด ๊ต์ฒด.WebJarsConfig๋ก 1๋ ์บ์ ํค๋ ์ค์ . ์คํ๋ผ์ธ/ํ์๋ง ์์ ์ง์ (Mermaid, QRCode๋ง CDN ์ ์ง) - ๋ถ์ ์บ์ DB ์์ํ โ
AnalysisCache์ํฐํฐ +analysis_cacheH2 ํ ์ด๋ธ.AnalysisCacheService์ฌ์์ฑ:findByCacheKey์กฐํ โ TTL ์ฒดํฌ โ hitCount ์ฆ๊ฐ.put()์ upsert.@Scheduled๋งค ์๊ฐ ๋ง๋ฃ ์บ์ ์๋ ์ญ์ .SseStreamControllerํธ์ถ ์ธํฐํ์ด์ค(get/put) ์ ์ง
๐ง ๊ธฐ๋ฐ ๊ตฌ์กฐ ๊ฐ์
-
ChatHistoryStore์ ๊ฑฐ (v2.5.0์์ ์ถ๊ฐํ๋ ์์ ConcurrentHashMap ๊ธฐ๋ฐ โ v2.7.0 DB ์์ํ๋ก ๋์ฒด) -
ChatController์ ๋ฉด ๋ฆฌํฉํฐ๋ง: ์ธ์ CRUD ์๋ํฌ์ธํธ 6๊ฐ ์ ๊ท (/chat/sessions,/chat/sessions/new,/chat/sessions/{id}/rename|delete|clear|messages)./chat/send๋sessionIdํ๋ผ๋ฏธํฐ ๋ฐ๊ณ , ์์ผ๋ฉด ์๋ ์์ฑ
๐ ๋ชจ๋ํฐ๋ง
- SSE ์ค์๊ฐ ์๋ฆผ โ 60์ด ํด๋ง โ Server-Sent Events ์ฆ์ ์๋ฆผ.
NotificationPublisher์ปดํฌ๋ํธ(ConcurrentHashMap ๊ธฐ๋ฐ ๊ตฌ๋ ์ ๊ด๋ฆฌ) +/notifications/streamSSE ์๋ํฌ์ธํธ. ์๋ฆผ ์์ฑ ์publisher.publish()ํธ์ถ โ DB ์ ์ฅ + ๊ตฌ๋ ์์๊ฒ ์ฆ์ push. ์ฐ๊ฒฐ ์คํจ ์ 60์ด ํด๋ง ์๋ fallback + 30์ด ํ ์ฌ์ฐ๊ฒฐ ์๋. ํ ์คํธ ์๋ฆผ์ผ๋ก ์ฆ๊ฐ ํผ๋๋ฐฑ - ๊ฐ์ฌ ๋ก๊ทธ ๊ธฐ๋ฅ ๋ฉ๋ด ํํฐ โ
menuName๊ธฐ๋ฐ ๋๋กญ๋ค์ด ํํฐ ์ถ๊ฐ (38๊ฐ ๊ธฐ๋ฅ ๋ฉ๋ด). ํน์ ๊ธฐ๋ฅ๋ณ ๊ฐ์ฌ ๋ก๊ทธ ์กฐํ ๊ฐ๋ฅ. ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ์ค์๊ฐ ํํฐ๋ง - ์์คํ
ํฌ์ค ๋์๋ณด๋ โ
/admin/healthํ์ด์ง +SystemHealthController. JVM ํ ๋ฉ๋ชจ๋ฆฌ/์ฌ์ฉ๋ฅ ๊ฒ์ด์ง, ์ ํ์, ์ค๋ ๋ ์, Java/OS ๋ฒ์ , H2 DB ํ์ผ ํฌ๊ธฐ, ๋์คํฌ ์ฌ์ ๊ณต๊ฐ, Claude API ์ํ, ๋ฑ๋ก ์ฌ์ฉ์ ์. Bootstrap 2์ด ์นด๋ ๋ ์ด์์, 30์ด ์๋ ๊ฐฑ์ ,/admin/health/dataREST API
๐จ UX
- ํค๋ณด๋ ๋จ์ถํค ๊ฐ์ด๋ โ
?ํค๋ก ๋ชจ๋ฌ ํ์/ํ ๊ธ (input/textarea ํฌ์ปค์ค ์ ์ ์ธ). ๋จ์ถํค ๋ชฉ๋ก(?, Esc, Ctrl+Enter, โ+Enter, /, Shift+Enter, Enter) +<kbd>์คํ์ผ๋ง. Esc ๋๋ ๋ฐ๊นฅ ํด๋ฆญ์ผ๋ก ๋ซ๊ธฐ - ๋ชจ๋ฐ์ผ ํ๋จ ๋ค๋น๊ฒ์ด์ โ 768px ์ดํ์์ ์๋ ํ์ฑํ๋๋ ํ๋จ ๊ณ ์ ํญ ๋ฐ. 5๊ฐ ํญ๋ชฉ(ํ/์ฑํ /๊ฒ์/์ด๋ ฅ/๋ฉ๋ด). ํ์ฑ ์ํ ํ์ด๋ผ์ดํธ, ์ฌ์ด๋๋ฐ ์ด๊ธฐ ๋ฒํผ ํตํฉ. ๋ฐ์ํ CSS ๋ฏธ๋์ด ์ฟผ๋ฆฌ
โ v3.0.0 โ ํ์ดํ๋ผ์ธ ๊ณ ๋ํ (์ค์ผ์ค๋ง, Monaco Editor, ํ๋ก์ฐ์ฐจํธ, ๋ณ๋ ฌ ์คํ)
โฐ ํ์ดํ๋ผ์ธ ์ค์ผ์ค๋ง (cron)
-
PipelineDefinition์scheduleCron,scheduleInput,scheduleEnabledํ๋ ์ถ๊ฐ -
PipelineSchedulerโ@Scheduled(fixedRate=60s)๋งค๋ถ cron ๋งค์นญ ํ์ดํ๋ผ์ธ ์๋ ์คํ -
POST /pipelines/{id}/scheduleโ ์ค์ผ์ค ์ค์ API (cron ์ฌ์ ๊ฒ์ฆ) - Spring 6-ํ๋
CronExpression๊ธฐ๋ฐ ํํ์ ํ์ฑ (์ด ๋ถ ์ ์ผ ์ ์์ผ)
๐ป Monaco Editor ํตํฉ
- ํ์ดํ๋ผ์ธ YAML ํธ์ง๊ธฐ์ Monaco Editor (jsDelivr CDN, v0.45.0) ์ ์ฉ
- YAML ๋ฌธ๋ฒ ๊ฐ์กฐ, ๋ผ์ธ ๋ฒํธ, ์๋ ๋ฉ, ํญ ์ฌ์ด์ฆ 2, ์๋ ๋ ์ด์์
- ๋คํฌ/๋ผ์ดํธ ํ ๋ง ์๋ ๊ฐ์ง (vs-dark / vs)
- ๋ด์ฅ ํ์ดํ๋ผ์ธ์
readOnly๋ชจ๋ - Monaco โ textarea ์ค์๊ฐ ๋๊ธฐํ (Monaco ๋ก๋ ์คํจ ์ textarea fallback)
๐ ์๊ฐ์ ํ๋ก์ฐ์ฐจํธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ
- ํธ์ง๊ธฐ ์ฐ์ธก์ Mermaid.js ๊ธฐ๋ฐ ์ค์๊ฐ ํ๋ก์ฐ์ฐจํธ ํ์
- YAML โ Mermaid
graph LR์๋ ๋ณํ (๋จ๊ณ ID, ๋ถ์ ์ ํ, ์กฐ๊ฑด ๋ถ๊ธฐ ์๊ฐํ) - "๊ฐฑ์ " ๋ฒํผ + 3์ด ๊ฐ๊ฒฉ ์๋ ๊ฐฑ์
- ์กฐ๊ฑด๋ถ ๋จ๊ณ๋ ๋ง๋ฆ๋ชจ ๋
ธ๋
{}, ์ผ๋ฐ ๋จ๊ณ๋ ์ฌ๊ฐํ[]
โก ๋ณ๋ ฌ ๋จ๊ณ ์คํ
- YAML์
parallel: true/dependsOn: "stepId1,stepId2"ํ๋ ์ง์ -
PipelineExecutor์์parallel: true๋จ๊ณ๋ ๋ณ๋ daemon ์ค๋ ๋์์ ๋์ ์คํ -
dependsOn๋๊ธฐ โ ์ง์ ๋ ๋จ๊ณ๋ค์ด ์๋ฃ๋ ๋๊น์ง 500ms ๊ฐ๊ฒฉ ํด๋ง (์ต๋ 10๋ถ) -
completedStepIds(ConcurrentSet) ๊ธฐ๋ฐ ๋๊ธฐํ -
executeSingleStep()ํฌํผ ๋ฉ์๋ ์ถ์ถ โ ์์ฐจ/๋ณ๋ ฌ ๋ชจ๋ ๋์ผ ๋ก์ง ์ฌ์ฌ์ฉ
๐ ๋ถ์ ํ์ดํ๋ผ์ธ ์ค์ผ์คํธ๋ ์ดํฐ
- YAML ๊ธฐ๋ฐ ํ์ดํ๋ผ์ธ ์ ์ โ
PipelineDefinition์ํฐํฐ, SnakeYAML ํ์, SpEL ์กฐ๊ฑด์ ํ๊ฐ๊ธฐ - 4๊ฐ ๋ด์ฅ ํ์ดํ๋ผ์ธ โ SQL ์ต์ ํ ํ ์คํ / Java ํ์ง ํ ์ฒดํฌ / ๋ฌธ์ํ ํจํค์ง / ๋ณด์ ๊ฐ์ฌ ํ ํ๋ก์ฐ (์ฑ ์์ ์ ์๋ ๋ฑ๋ก)
- ์์ฐจ ์คํ ์์ง โ
PipelineExecutor๊ฐ ๋จ๊ณ๋ณ ์กฐ๊ฑด๋ถ ์คํ, ๋ณ์ ์นํ(${stepId.output}), ๊ฐ ๋จ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ๋จ๊ณ ์ปจํ ์คํธ๋ก ์๋ ์ ๋ฌ - ์ค์๊ฐ SSE ์งํ ์ํฉ โ
PipelineStreamBroker๋ก ๊ฐ ๋จ๊ณ ์ฒญํฌ/์ํ/์๋ฃ ์ด๋ฒคํธ ์ค์๊ฐ push, ์ฐ๊ฒฐ ์คํจ ์ 3์ด ํด๋ง fallback - ์คํ ์ด๋ ฅ ๊ด๋ฆฌ โ
PipelineExecution+PipelineStepResult๋ก ๊ฐ ์คํ์ ๋ชจ๋ ๋จ๊ณ ๊ฒฐ๊ณผ ์์ํ, ์ฌ์กฐํ ๊ฐ๋ฅ - ํ์ดํ๋ผ์ธ CRUD UI โ ๋ชฉ๋ก/ํธ์ง๊ธฐ/์คํ ์์ธ ํ์ด์ง 3๊ฐ. YAML textarea ํธ์ง๊ธฐ + ๋ถ์ ์ ํ/๋ณ์ ๋์๋ง ํจ๋ + ๊ฒ์ฆ ๋ฒํผ
- ์ฌ์ฉ์ ๊ถํ ๊ด๋ฆฌ ํตํฉ โ
pipelinesfeature key ์ถ๊ฐ (PermissionInterceptor, AdminPermissionController, AuditLog, ๊ฐ์ฌ ๋ก๊ทธ ํํฐ)
๐ DB ์๋ ๋ง์ด๊ทธ๋ ์ด์
- ์ํด๋ฆญ H2 โ PostgreSQL/MySQL/Oracle ์ด๊ด โ
/admin/db-migration5๋ฒ์งธ ํญ "๐ ์๋ ์ด๊ด" -
DbMigrationExecutorโ JDBC Batch (500๊ฑด/๋ฐฐ์น) ๊ธฐ๋ฐ ํ ์ด๋ธ ๋ณต์ฌ, ์ธ๋ํค ์์กด์ฑ ์์ ์ค์ (17๊ฐ ํ ์ด๋ธ) - ์์ ์ฅ์น โ ํ๊ฒ DB ์คํค๋ง ์กด์ฌ ์ฒดํฌ, ๋น์ด์์ ํ์ธ(๋๋ overwrite ์ต์ ), ์ด๊ด ํ ๋ ์ฝ๋ ์ ๊ฒ์ฆ
- ์์ ํ๊ฒ DataSource โ
DriverManagerDataSource๋ก ์ฑ ์ฌ๊ธฐ๋ ์์ด ์ฆ์ ์ฐ๊ฒฐ - ์ค์๊ฐ SSE ์งํ๋ฅ โ
DbMigrationStreamBroker๋ก ํ ์ด๋ธ๋ณ ์งํ๋ฅ , ํ ์, ์ฑ๊ณต/์คํจ ์ด๋ฒคํธ ์ค์๊ฐ ํ์ - ์ฐ๊ฒฐ ํ ์คํธ โ ์์ ์ ํ๊ฒ DB ์ฐ๊ฒฐ ๊ฒ์ฆ (์ ํ/๋ฒ์ ์กฐํ)
- ์ด๊ด ์ด๋ ฅ โ
DbMigrationJob์ํฐํฐ๋ก ๋ชจ๋ ์ด๊ด ์์ ์ด๋ ฅ ์์ํ
๐ฌ ์ฑํ ํ์ฅ
- ๋ถ์ ๊ฒฐ๊ณผ โ ์ฑํ
์ฐ๊ณ โ
toolkit.js์askAboutResult()์ ํธ ํจ์ + DOMContentLoaded ์๋ ์ฃผ์ . ๋ชจ๋ ๋ถ์ ๊ฒฐ๊ณผ ํ์ด์ง(#resultMd,#streamResult๋ฑ)์ "๐ฌ ์ด ๊ฒฐ๊ณผ์ ๋ํด AI์๊ฒ ์ง๋ฌธํ๊ธฐ" ํ๋กํ ๋ฒํผ ์๋ ํ์. ๋ฒํผ ํด๋ฆญ ์ ๊ฒฐ๊ณผ ํ ์คํธ(์ต๋ 3000์)๋ฅผ ์ปจํ ์คํธ๋ก/chat?context=...๋ฆฌ๋ค์ด๋ ํธ. ํ ํ๋ฆฟ ์์ 0๊ฑด (๋จ์ผ JS ์ฃผ์ ๋ฐฉ์)
๐ ์ ๊ท ๊ธฐ๋ฅ
- ํ ์ฝ๋ ๋ฆฌ๋ทฐ ์ํฌํ๋ก์ฐ โ
ReviewRequest์ํฐํฐ +review_requestH2 ํ ์ด๋ธ. ์์ฑ์ โ REVIEWER ์น์ธ ์์ฒญ โ ์น์ธ/๋ฐ๋ ค โ ์ค์๊ฐ SSE ์๋ฆผ(NotificationPublisher ์ฌ์ฌ์ฉ)- ์๋ํฌ์ธํธ:
GET /review-requests(๋ชฉ๋ก),GET /review-requests/{id}(์์ธ),POST /review-requests(์์ฑ),POST /review-requests/{id}/approve(์น์ธ),POST /review-requests/{id}/reject(๋ฐ๋ ค),POST /review-requests/{id}/delete(์ทจ์),GET /review-requests/reviewers(๋ฆฌ๋ทฐ์ด ๋ชฉ๋ก) - ๋ชฉ๋ก ํ์ด์ง: "๋ด๊ฒ ์จ ๋ฆฌ๋ทฐ" / "๋ด๊ฐ ์์ฒญํ ๋ฆฌ๋ทฐ" ํญ, ์ํ ๋ฐฐ์ง(PENDING/APPROVED/REJECTED), ํธ๋ฒ ์นด๋
- ์์ธ ํ์ด์ง: ์๋ณธ ๋ถ์ ๊ฒฐ๊ณผ + ์์ฒญ์ ๋ฉ๋ชจ + ๋ฆฌ๋ทฐ ์๋ต ์์ญ (์น์ธ/๋ฐ๋ ค ๋ฒํผ, ๋ฐ๋ ค ์ ์ฝ๋ฉํธ ํ์)
- ์ด๋ ฅ ํตํฉ:
history/index.html์์ธ ๋ชจ๋ฌ์ "๐ฅ ๋ฆฌ๋ทฐ ์์ฒญ" ๋ฒํผ โ ๋ฆฌ๋ทฐ์ด ์ ํ ๋ชจ๋ฌ (REVIEWER/ADMIN ์ญํ ๋ง ๋ ธ์ถ) - ์๋ฆผ: ์์ฒญ/์๋ต ์ ์์ชฝ์ SSE ์ค์๊ฐ ์๋ฆผ + ์์ธ ๋งํฌ
- ์๋ํฌ์ธํธ:
๐๏ธ ์ํคํ ์ฒ
- DB ๋ง์ด๊ทธ๋ ์ด์
๊ฐ์ด๋ โ
/admin/db-migrationADMIN ํ์ด์ง +DbMigrationController. ํ์ฌ DB ์๋ ๊ฐ์ง(DatabaseMetaData), URL ๋น๋ฐ๋ฒํธ ๋ง์คํน. 4๊ฐ ํญ ๊ฐ์ด๋- PostgreSQL: Docker Compose ์คํ, ํ๊ฒฝ๋ณ์, ์๋ ์คํค๋ง ์์ฑ
- MySQL: 8.0+ ํ์ ์ฃผ์, utf8mb4 ๋ฌธ์์ , DB ์์ฑ SQL
- Oracle 11g: BOOLEAN โ NUMBER(1), TEXT โ CLOB, ์ํ์ค ID,
Oracle10gDialect, ojdbc8 ์์กด์ฑ ์ถ๊ฐ ๊ฐ์ด๋, ์ ๊ทapplication-oracle.yml์์ - ๋ฐฑ์
/๋ณต์: H2
SCRIPT TO๋คํ, ZIP ๋ฐฑ์ ๋ค์ด๋ก๋, CSV ์ด๋ ฅ ๋ด๋ณด๋ด๊ธฐ - ๊ฐ ๋จ๊ณ ๋ณต์ฌ ๊ฐ๋ฅ ์ฝ๋ ๋ธ๋ก, ์ฃผ์์ฌํญ ๋ฐ์ค
๐ก๏ธ ์ฌ์ฉ์ ๊ถํ ๊ด๋ฆฌ ํตํฉ
-
review-requests๊ธฐ๋ฅ ๊ถํ ์ถ๊ฐ โPermissionInterceptor.FEATURE_PATHS,AdminPermissionController.FEATURES,AuditLog.MENU_NAME_MAP, ๊ฐ์ฌ ๋ก๊ทธ ํํฐ ๋๋กญ๋ค์ด ๋ชจ๋ ๋๊ธฐํ. VIEWER/REVIEWER ์ฌ์ฉ์๋ณ on/off ๊ฐ๋ฅ - v2.5.0 ~ v2.9.0 ์ฌ์ฉ์ ๊ถํ ๊ด๋ฆฌ ๋ฒ์ ์ ๊ฒ ์๋ฃ โ ์์ ์ฑ/๋ณด์ ๊ฐํ(v2.5-2.6)๋ ADMIN ๊ธฐ๋ฅ์ด๋ผ ์ ์ธ, ์ฑํ
์ธ์
/Markdown/WebJars(v2.7)๋ ๊ธฐ์กด
chat๊ถํ ์ฌ์ฌ์ฉ, ๋ชจ๋ํฐ๋ง/UX(v2.8)๋ ์ ์ญ ๋๋ ADMIN ์ ์ฉ์ด๋ผ ์ ์ธ, v2.9 ์ ๊ทreview-requests๋ง ์ฌ์ฉ์ ์ ์ด ๋์์ผ๋ก ์ถ๊ฐ
Thymeleaf ๊ธฐ๋ฐ ํ๋ก ํธ์๋(69๊ฐ ํ
ํ๋ฆฟ, ~24,000์ค HTML, 15,000์ค JS)๋ฅผ
React 18 + TypeScript + Vite ๊ธฐ๋ฐ SPA๋ก ์ ๋ฉด ์ ํํ์ต๋๋ค. Phase 15
๋ฅผ ๊ฑฐ์ณ v4.1.0 ์์ ์ Thymeleaf ๊ฐ ์์ ํ ์ ๊ฑฐ๋์๊ณ (ํ
ํ๋ฆฟ 0๊ฐ, pom.xml
์์กด์ฑ ์ ๊ฑฐ), ์ดํ v4.2.x ์ด์ ์์ ํ ๋ผ์ธ์ด ์ด์ด์ง๊ณ ์์ต๋๋ค. ํ์ฌ 64๊ฐ
React ํ์ด์ง + ๊ณต์ฉ ์ปดํฌ๋ํธ/ํ
/์คํ ์ด 35+ ๊ฐ๊ฐ ์ ์ฒด UI ๋ฅผ ๋ด๋นํ๋ฉฐ
Spring Boot ๋ฐฑ์๋์ REST API + SSE ์คํธ๋ฆฌ๋ฐ ์๋ํฌ์ธํธ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํฉ๋๋ค.
- Vite + React 18 + TypeScript ํ๋ก์ ํธ ์ด๊ธฐํ (
claude-toolkit-ui/frontend/) - Vite ๊ฐ๋ฐ ์๋ฒ ํ๋ก์ ์ค์ (API/SSE โ Spring Boot 8027 ํฌํธ)
- ๋น๋ ์ถ๋ ฅ โ
static/react/(Spring Boot ์ ์ ๋ฆฌ์์ค ์๋น) -
SpaForwardControllerโ/react/**SPA ๋ผ์ฐํ ํฌ์๋ฉ -
SecurityConfigโ/react/**์ ์ ๋ฆฌ์์ค ๊ณต๊ฐ ํ์ฉ -
frontend-maven-pluginโmvn package์ React ์๋ ๋น๋ ํตํฉ - React Router ๊ธฐ๋ณธ ๋ผ์ฐํ
+
/api/v1/health์ฐ๋ ํ์ธ ํ์ด์ง - ๊ธฐ์กด Thymeleaf UI ์ ์ฒด ์ ์ ์๋ ์ ์ง
- Layout ์ปดํฌ๋ํธ (์ฌ์ด๋๋ฐ 7์น์ ์ ์ฒด ๋ฉ๋ด, ํ๋ฐ, ๋ชจ๋ฐ์ผ ํ๋จ๋ฐ)
- ์ธ์ฆ ์ฐ๋ (
AuthRestController+useAuth+ ๋ก๊ทธ์ธ ํ์ด์ง) - ๊ณต์ฉ ํ
:
useApi(),useSSE(),useToast() - ๋คํฌ/๋ผ์ดํธ ํ ๋ง ์์คํ (CSS ๋ณ์, localStorage ์ ์ฅ)
- ๊ธ๋ก๋ฒ ์ํ ๊ด๋ฆฌ (Zustand โ theme, auth, sidebar, toast ์คํ ์ด)
- SPA ๋ผ์ฐํ
ํฌ์๋ฉ (
SpaForwardController) - Vite ํ๋ก์ ์ค์ (API/SSE/ํ์ด์ง ๋ผ์ฐํธ โ Spring Boot 8027)
- AI ์ฑํ (๋ค์ค ์ธ์ ๊ด๋ฆฌ, SSE 2-step ์คํธ๋ฆฌ๋ฐ, Markdown ๋ ๋๋ง, ๋ํ ๋ด๋ณด๋ด๊ธฐ)
- ๋ถ์ ํ์ดํ๋ผ์ธ (๋ชฉ๋ก/์คํ/์์ธ ํ์ด์ง, ์ค์๊ฐ SSE ์งํ๋ฅ , ์คํ ๋ชจ๋ฌ)
- ๋์๋ณด๋ ํ (Health API ์ฐ๋, ๋๊ตฌ ์นด๋ ๊ทธ๋ฆฌ๋, ์ธ์ฌ๋ง)
-
/notifications/unread-count๊ฐ์ฌ ๋ก๊ทธ ์ ์ธ ์ฒ๋ฆฌ - Markdown ์คํ์ผ๋ง (react-markdown + remark-gfm, ์ฝ๋ ๋ธ๋ก/ํ ์ด๋ธ/์ธ์ฉ)
-
AnalysisPageTemplate๊ณต์ฉ ์ปดํฌ๋ํธ (์ ๋ ฅ/SSE ์คํธ๋ฆฌ๋ฐ/๊ฒฐ๊ณผ ํจํด ํตํฉ) - SQL ๋ถ์ 5์ข (SQL ๋ฆฌ๋ทฐ, DB ๋ฒ์ญ, ERD ๋ถ์, ๋ณต์ก๋, ์คํ๊ณํ)
- ์ฝ๋ ๋ถ์/์์ฑ 8์ข (์ฝ๋ ๋ฆฌ๋ทฐ, ๊ธฐ์ ๋ฌธ์, API ๋ช ์ธ, ์ฝ๋ ๋ณํ, Mock ๋ฐ์ดํฐ, DB ๋ง์ด๊ทธ๋ ์ด์ , ์์กด์ฑ, Spring ๋ง์ด๊ทธ๋ ์ด์ )
- ๋๊ตฌ 5์ข (๋ก๊ทธ ๋ถ์๊ธฐ, ์ ๊ท์, ์ปค๋ฐ ๋ฉ์์ง, ๋ง์คํน ์คํฌ๋ฆฝํธ, ๋ฏผ๊ฐ์ ๋ณด ๋ง์คํน)
- ๊ธฐ๋ก ํ์ด์ง (๋ฆฌ๋ทฐ ์ด๋ ฅ โ ๊ฒ์/ํํฐ/์์ฝ๋์ธ/๋ด๋ณด๋ด๊ธฐ, ์ฆ๊ฒจ์ฐพ๊ธฐ)
- ์ค์ ํ์ด์ง (๋ชจ๋ธ ์ ํ, ํ ํฐ ์, ํ๋ก์ ํธ ์ปจํ ์คํธ)
- Lazy loading + code splitting (๊ฐ ํ์ด์ง ๋ณ๋ ์ฒญํฌ ์๋ ๋ถ๋ฆฌ)
- ์ฌ์ด๋๋ฐ React ๋ผ์ฐํธ ์ ๋ฉด ์ ํ (Thymeleaf ์ ์ฉ ๊ฒฝ๋ก๋ง ์ธ๋ถ ๋งํฌ)
- React base path
/react/โ/๋ฃจํธ๋ก ์ ํ - ๋จ์ Thymeleaf ์ ์ฉ ํ์ด์ง React ์ ํ (workspace, sql-batch, harness ํ์, admin ๋ฑ)
- ๊ด๋ฆฌ์ ํ์ด์ง (์ฌ์ฉ์ ๊ด๋ฆฌ, ์์คํ ํฌ์ค, ๊ฐ์ฌ ๋ก๊ทธ) React ์ ํ
- ๊ธฐ๋ฅ ํ์ด์ง (๊ฒ์, ์ฌ์ฉ๋, ์ค์ผ์ค, ๋ฆฌ๋ทฐ ์์ฒญ, ROI, ํ๋กฌํํธ) React ์ ํ
- SPA ๋ผ์ฐํ
โ
SpaForwardController๋ฃจํธ ํฌ์๋ฉ - React ๋น๋ ์ถ๋ ฅ โ
static/app/(49๊ฐ ์ฒญํฌ, lazy loading) - Thymeleaf ๋ ๊ฑฐ์ ํธํ ์ ์ง (
/react/**โ ๊ธฐ์กด ๋ถ๋งํฌ ์ง์) - ์ ์ฒด 40+ React ํ์ด์ง ์์ฑ
- Thymeleaf ์์ ์ ๊ฑฐ (templates 69๊ฐ ์ญ์ , toolkit.js/css ์ญ์ , WebJars ์์กด์ฑ ์ ๊ฑฐ)
- 56๊ฐ @Controller ํ์ด์ง GET ๋งคํ ์ ๊ฑฐ (POST/API ์ ์ง, SpaViewResolver ๋ทฐ ํฌ์๋ฉ)
- REST API ์ ํ (
/api/v1/auth/login,/api/v1/pipelines,/history,/favorites) - 2FA ์ ์ฉ ํ์ด์ง, Setup ๋ง๋ฒ์ฌ, Share ๋ทฐ์ด, 404 ํ์ด์ง
- AI ํ๋กฌํํธ ๊ด๋ฆฌ, ํ ์ค์ ๊ณต์ , ์ฌ์ฉ์ ๊ถํ ๊ด๋ฆฌ, ํ ๋์๋ณด๋, DB ๋ง์ด๊ทธ๋ ์ด์ ๊ฐ์ด๋, ๋ฐฑ์ /๋ณต์
- Monaco Editor + Mermaid ํ๋ก์ฐ์ฐจํธ ์ค์๊ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ์ดํ๋ผ์ธ ํธ์ง๊ธฐ
- recharts ๋์๋ณด๋ (์ฑ๋ฅ ํ์คํ ๋ฆฌ, ํ์ง ๋์๋ณด๋)
- ์๋ฆผ SSE ์ค์๊ฐ ๊ตฌ๋
+ ์ธ์
ํ์ด๋จธ 60๋ถ + ํค๋ณด๋ ๋จ์ถํค (
?๋์๋ง) - i18n (ํ๊ตญ์ด/์์ด), PWA manifest, ErrorBoundary, ํ์ผ ์ ๋ก๋
- Playwright E2E 5/5 ํต๊ณผ
-
mvn clean packageJDK 1.8 ๋น๋ ๊ฒ์ฆ (JAR ๋ด React 119๊ฐ ํ์ผ ํฌํจ ํ์ธ) -
.dockerignore์ถ๊ฐ (Docker ๋น๋ ์ต์ ํ) - ์ ์ฒด 52๊ฐ React ํ์ด์ง, 35๊ฐ ์ปดํฌ๋ํธ/ํ /์คํ ์ด ์์ฑ
ํต์ฌ ๊ธฐ๋ฅ
- ์ฌ์ฉ์ ๊ถํ ๊ด๋ฆฌ ์ฐ๋ โ React ์ฌ์ด๋๋ฐ๊ฐ per-user ๋นํ์ฑ ๊ธฐ๋ฅ ์๋ ์จ๊น (
/api/v1/auth/my-permissions) - ํ์ดํ๋ผ์ธ ๋น์ฃผ์ผ ๋น๋ โ 12์ข ๋ถ์์ ๋ฒํผ ํด๋ฆญ์ผ๋ก ์กฐ๋ฆฝ โ YAML ์๋ ์์ฑ
- ํ์ดํ๋ผ์ธ ์คํ ์ด๋ ฅ ํ์ด์ง (
/pipelines/history) - DB ํ๋กํ ์ถ๊ฐ ๋ชจ๋ฌ โ ์ฐ๊ฒฐ ํ ์คํธ + ์ ์ฅ ํตํฉ ์ํฌํ๋ก์ฐ
- DB ์๋ ์ด๊ด (H2 โ Oracle 11g/PostgreSQL/MySQL) โ ์ํด๋ฆญ ์คํ + SSE ์งํ๋ฅ
UX ๊ฐ์
- OS ๋คํฌ/๋ผ์ดํธ ๋ชจ๋ ์๋ ๊ฐ์ง (
prefers-color-scheme) - SSE ์๋ ์ฌ์ฐ๊ฒฐ (exponential backoff, ์ต๋ 3ํ)
- ์ฑํ ์ธ์ ๊ฒ์ (์ค์๊ฐ ํํฐ)
- ํ์ผ ๋๋๊ทธ์ค๋๋กญ ์ ๋ก๋ (๋ถ์ ํ์ด์ง + ์๊ฐ ํผ๋๋ฐฑ)
- ๋ชจ๋ฐ์ผ ๋ฐ์ํ ๊ทธ๋ฆฌ๋ (
repeat(auto-fit, minmax(400px, 1fr))) - ์ธ๋ จ๋ ์ต์ UI โ select โ pill/chip ๋ฒํผ ์ผ๊ด ์ ํ
- ์ ๊ท์ ์์ฑ๊ธฐ 8์ข ์์ ๋ฒํผ (ํธ๋ํฐ/์ฃผ๋ฏผ๋ฒํธ/์ด๋ฉ์ผ/์ฃผ์ ๋ฑ)
๋ฐฑ์๋ REST API ๋ณด๊ฐ
-
/api/v1/auth/my-permissionsโ ํ์ฌ ์ฌ์ฉ์ ๋นํ์ฑ ๊ธฐ๋ฅ ๋ชฉ๋ก -
/api/v1/db-profilesโ DB ํ๋กํ ๋ชฉ๋ก -
/api/v1/pipelines/executionsโ ํ์ดํ๋ผ์ธ ์คํ ์ด๋ ฅ -
/api/v1/explain/dashboard,/api/v1/harness/dashboardโ ๋์๋ณด๋ ๋ฐ์ดํฐ -
/api/v1/admin/team-dashboard,/api/v1/admin/health/dataโ ๊ด๋ฆฌ์ ํต๊ณ -
/api/v1/admin/users,/audit-logs,/permissionsโ ๊ด๋ฆฌ์ ๋ชฉ๋ก API -
/api/v1/review-requests,/schedule,/roi-report,/prompts,/settings/prompts,/settings/shared,/search
์ธํฐ์
ํฐ ์ ๋น (5๊ฐ ์ธํฐ์
ํฐ์ /assets/**, /api/**, /chat/**, /stream/** ์ ์ธ)
-
PermissionInterceptor,SetupInterceptor,PasswordChangeInterceptor -
PasswordExpiryInterceptor,TwoFactorInterceptor
ํต์ฌ ๋ฒ๊ทธ ์์
-
useApiํ ์์ ์ ์ฐธ์กฐ (useEffect ๋ฌดํ ๋ฃจํ ๋ฐฉ์ง) - 15๊ฐ ํ์ด์ง
[api]์์กด์ฑ โ[](Failed to fetch ๋ฌดํ ๋ฐ๋ณต ํด๊ฒฐ) -
AuthRestController: ADMIN ๋ก๊ทธ์ธ ์require2fa=true๋ฐํ โ 2FA ํ์ด์ง ์ด๋ -
TwoFactorController:ResponseEntity<String>๋ฐํ (redirect ๋ฌธ์์ด ๋ฒ๊ทธ ์์ ) -
GlobalExceptionHandler: 405 Method Not Allowed โ React SPA ํฌ์๋ฉ - Vite
outDir์static/๋ฃจํธ๋ก ๋ณ๊ฒฝ (์์ MIME ํ์ ์ค๋ฅ ํด๊ฒฐ) - React ๋น๋ JS ๊ฒฝ๋ก ์ผ๊ด์ฑ ํ๋ณด (
/assets/**)
์ธํ๋ผ/TLS/ํ์์กด
- Runtime ์ด๋ฏธ์ง
eclipse-temurin:8-jre-alpineโ8-jre-jammy(Ubuntu glibc โ TLS handshake ์์ ) -
docker-compose.yml์JAVA_OPTSoverride ์ ๊ฑฐ โ Dockerfile ๊ธฐ๋ณธ๊ฐ ๋ณด์กด - TLS ์ค์ :
-Dhttps.protocols=TLSv1.2,TLSv1.3 -Djdk.tls.client.protocols=TLSv1.2,TLSv1.3 - DNS ์บ์ ํ๋:
-Dnetworkaddress.cache.ttl=60,-Djava.net.preferIPv4Stack=true - ORA-01882 ๋์:
-Doracle.jdbc.timezoneAsRegion=false+TZ=Asia/Seoul+tzdata -
ClaudeToolkitUiApplication.main()์์System.setProperty๋ก defense-in-depth -
ClaudeClient: forward proxy (CLAUDE_PROXY_HOST/PORT๋๋HTTPS_PROXYenv) -
ClaudeClient.formatNetworkError(): handshake_failure/PKIX/timeout/unknown host ํ๊ตญ์ด ๋ณํ -
/admin/health/claude-api-diagnose์ง๋จ ์๋ํฌ์ธํธ +AdminHealthPageUI
DB ์๋ ์ด๊ด / ๋ฐํ์ ์ ํ / Oracle ํธํ
-
DbOverrideConfig:data/db-override.properties๊ธฐ๋ฐ ๋ฐํ์ DataSource ์ค๋ฒ๋ผ์ด๋ -
POST /admin/db-migration/auto/switch-target//switch-h2โ ์ฌ์์์ผ๋ก ์ด์ DB ์ ํ -
GET /admin/db-migration/auto/tablesโ 19๊ฐ ๋์ ํ ์ด๋ธ ๋ชฉ๋ก ๋ ธ์ถ -
POST /admin/db-migration/auto/validateโ ๋์ DB ์ถฉ๋/๊ธฐ์กด ํ์ ์ฌ์ ๊ฒ์ฆ - Oracle URL ์๋ ํฌ๋งท ๊ฐ์ง: SID(
@host:port:SID) โ Service Name(@//host:port/SVC) fallback -
SystemHealthController+DataRestController: ํ์ฑ DB ์๋ ๊ฐ์ง โ ์๋ ์ด๊ด ํ ์์คํ ํฌ์ค๊ฐ ์ค์ ์ด์ DB ์ ๋ณด ํ์ -
SourceSelectorํ๋ก์ ํธ ์ค์บ ์ค ์ํ ํด๋ง (๋ถํ ์งํ "Java ํ์ผ ์์" ์คํด ์ ๊ฑฐ)
๊ณ์ / ๋น๋ฐ๋ฒํธ / ๊ฐ์ฌ๋ก๊ทธ / ํ์ดํ๋ผ์ธ
-
AccountController.meGET โ ํ๋กํ prefill -
SecurityPage: ํ์ฌ ๋น๋ฐ๋ฒํธ ํ๋ฆด ๋ ๋ช ํํ ์๋ฌ (์ด์ ์ ํญ์ "์ฑ๊ณต") -
PasswordChangePage์ ๊ท (/account/password) โ 90์ผ ๋ง๋ฃ ๋ฐฐ๋ + "๋ค์์ ๋ณ๊ฒฝํ๊ธฐ" ๋ฒํผ -
AuditLogPage์ฌ์์ฑ: ํ์ด์ง๋น 20๊ฑด ํ์ด์ง๋ค์ด์ + ๊ธฐ๊ฐ/์ฌ์ฉ์ ํํฐ + CSV ๋ค์ด๋ก๋ -
DbMigrationGuidePage: ๋์ ํ ์ด๋ธ details + ์ฌ์ ๊ฒ์ฆ ๊ฒฐ๊ณผ + ๋ฐํ์ ์ ํ ๋ฒํผ -
PipelineExecutionPage์ ๋ฉด ๊ฐํธ: ์คํธ๋ฆฌ๋ฐ ์ฒญํฌ + ํญ ๋ค๋น + ์งํ์ outputContentDB ์ ์ฅ + 1์ด ํด๋ง ๋ฐฑ์ -
PipelineExecutor:setOutputContentsetter ์ถ๊ฐ + 300ms/1000์ throttled ์ ์ฅ -
WorkspacePage/PipelineExecutionPage/CodeReviewPage: ๋ณต์ฌ ๋ฒํผexecCommandfallback (HTTP IP ํ๊ฒฝ ๋์)
Claude API / CSRF / ์ด๋ฉ์ผ
-
SseStreamController: ์คํธ๋ฆฌ๋ฐ ์๋ฃ ํhistoryService.save()ํธ์ถ (React ๊ฒฝ๋ก์์ ์ด๋ ฅ ์ ์ฅ ๋๋ฝ ๋ฒ๊ทธ) -
ReviewHistoryService.save():SecurityContextHolder์์ username ์ถ์ถํดsetUsernameํธ์ถ -
SecurityConfigCSRF ignore ์/pipelines/**,/email/**์ถ๊ฐ (๊ธฐ์กด ๋๋ฝ) -
EmailController์ ๊ท โPOST /email/send(์ผํ/์ธ๋ฏธ์ฝ๋ก ๊ตฌ๋ถ ์ต๋ 10๋ช ),POST /email/test -
EmailModal๊ณต์ฉ ์ปดํฌ๋ํธ โ Workspace/Pipeline/CodeReview ๋ชจ๋ฌ ๋ฒํผ ์ฐ๊ฒฐ -
SettingsPage: SMTP ์นด๋ (Gmail ์ฑ ๋น๋ฐ๋ฒํธ ์๋ด + ํ ์คํธ ๋ฐ์ก)
์คํค๋ง ํ์ฅ
-
ReviewHistory:reviewStatus(PENDING/ACCEPTED/REJECTED),reviewedBy,reviewedAt,reviewNote์ปฌ๋ผ ์ถ๊ฐ -
ReviewComment:parentId์ปฌ๋ผ ์ถ๊ฐ (๋๋๊ธ ์ง์)
๋ฐฑ์๋ API
-
POST /history/{id}/review-statusโ REVIEWER/ADMIN ์ด ๋ฆฌ๋ทฐ ์ด๋ ฅ ์น์ธ/๊ฑฐ์ + ์์ฑ์์๊ฒ ์๋ฆผ -
POST /history/{id}/commentsโparentIdํ๋ผ๋ฏธํฐ ์ง์ (๋๋๊ธ โ ๋ถ๋ชจ ๋๊ธ ์์ฑ์ ์๋ฆผ) -
GET /api/v1/review-queue?tab=received|sentโ ์ญํ ๊ธฐ๋ฐ ๋ฆฌ๋ทฐ ํreceived: REVIEWER/ADMIN โ ํ์ธ์ PENDING ์ด๋ ฅ, VIEWER โ ๋ณธ์ธ ์ด๋ ฅ ์ค ๊ฒํ ์๋ฃ๋ ๊ฒsent: ๋ณธ์ธ์ด ์์ฑํ ๋ชจ๋ ์ด๋ ฅ
UI/UX
-
HistoryPage์ฌ์์ฑ:- ํ๋ ๋งคํ ์์ (
inputContent/outputContentโ ์ด์ ์ ์๋ชป๋inputText/resultText์ฌ์ฉ) - ๋ฆฌ๋ทฐ ์ํ ๋ฐฐ์ง (์น์ธ๋จ/๊ฑฐ์ ๋จ/๊ฒํ ๋๊ธฐ)
- REVIEWER/ADMIN ์น์ธยท๊ฑฐ์ ๋ฒํผ (VIEWER ๋ ์ฝ๊ธฐ ์ ์ฉ)
- ๋๊ธ ์น์ + ๋๋๊ธ ํธ๋ฆฌ (๋ค์ฌ์ฐ๊ธฐ ๊ตฌ์กฐ)
copyToClipboard์ ์ฉ (HTTP IP ๋ณต์ฌ ์ง์)
- ํ๋ ๋งคํ ์์ (
-
ReviewRequestsPage์ฌ์์ฑ:- ๋ด๊ฒ ์จ / ๋ด๊ฐ ์์ฒญํ ํญ
- ์ญํ ๋ณ ๋ฌธ๊ตฌ (REVIEWER/ADMIN vs VIEWER)
- ์น์ธ/๊ฑฐ์ ํ์ธ ๋ค์ด์ผ๋ก๊ทธ ("์น์ธํ์๊ฒ ์ต๋๊น?" / "๊ฑฐ์ ํ์๊ฒ ์ต๋๊น?")
- ๋ น์ ์น์ธ / ๋นจ๊ฐ ๊ฑฐ์ ๋ฒํผ (REVIEWER/ADMIN ๋ง, VIEWER ๋ ๐ ์๋ด)
-
AdminUsersPage๋ผ๋ฒจ ๋ณ๊ฒฝ:์ฌ์ฉ์๋ชโ์์ด๋,ํ์ ์ด๋ฆโ์ฌ์ฉ์๋ช
์ฌ์ด๋๋ฐ UX
-
Sidebarsection max-height ๊ณ์ฐ:items.length * 36โitems.length * 40 + 8(๋ง์ง๋ง ํญ๋ชฉ ์๋ฆผ ํด๊ฒฐ)
๋ฌธ์
-
README.md/docs/index.html๋ฒ์ ํ์คํ ๋ฆฌ ๊ฐฑ์
ํ์ดํ๋ผ์ธ ๋จ๊ณ ๊ฐ ์ปจํ ์คํธ ์ ํ ๋ฒ๊ทธ
-
PipelineExecutor๊ฐstep.getContext()๋ฅผWorkspaceRequest.projectContext์ ์ ์ฅํ์ง๋งAnalysisService.buildUserMessage()๊ตฌํ์ฒด๋ค์ดprojectContext๋ฅผ ๋ฌด์ โ step 2 ๊ฐ step 1 ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ง ๋ชปํด ๋์ผ ํ๋กฌํํธ ๋ฐ๋ณตํ๋ ๋ฒ๊ทธ -
PipelineExecutor๋ ๊ฒฝ๋ก(์์ฐจ + ๋ณ๋ ฌ) ๋ชจ๋์์stepContext๋ฅผuserMsg์์ ์ง์ prepend ํ๋๋ก ์์ โ 'SQL ์ต์ ํ ํ ์คํ' ๋ฑ ์ฐ์ ํ์ดํ ์ ์ ๋์ -
[Pipeline] ๋จ๊ณ ์คํ: exec=N, step=X, inputLen=A, contextLen=B๋ก๊น ์ถ๊ฐ - ๋น ์๋ต ๋ฐฉ์ด (Claude ๊ฐ 0 ์ฒญํฌ๋ก ๋๋๋ ๊ฒฝ์ฐ ์ฌ์ฉ์ ์นํ ๋ฉ์์ง + WARN ๋ก๊ทธ)
ํ์ดํ๋ผ์ธ ๊ฒฐ๊ณผ โ ๋ฆฌ๋ทฐ ์ด๋ ฅ ์ ์ฅ ๋๋ฝ
- React ๋ง์ด๊ทธ๋ ์ด์
ํ
PipelineExecutor๊ฐclaudeClient.chatStream()์ ์ง์ ํธ์ถํ์ฌ/stream/init๊ฒฝ๋ก์saveHistory()๊ฐ ํธ์ถ๋์ง ์์ -
PipelineExecutor.saveHistoryForExecution()์ถ๊ฐ โ ๋ชจ๋ ๋จ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋งํฌ๋ค์ด 1๊ฑด์ผ๋ก ์กฐ๋ฆฝํด type=PIPELINE๋ก ReviewHistory ์ ์ฅ -
ReviewHistoryService.save()/saveHarness()๊ฐSecurityContextHolder์์ username ์ถ์ถํ์ฌ setUsername ํธ์ถ (์ด์ ์ null ์ ์ฅ โ /history ํํฐ๋ง์์ ํญ์ ์ ์ธ) -
SseStreamController.saveHistory()๋ ๋์ผ ๋ฐฉ์ โ 3๊ฐ ๋ถ๊ธฐ(harness/sql_translate/generic) ๋ชจ๋
ํ์ดํ๋ผ์ธ Broken pipe ๋ก๊ทธ ํญ์ฃผ + max_tokens ์๋ฆผ
-
GlobalExceptionHandler.isClientDisconnect()โClientAbortException/Broken pipe/Connection reset์ cause chain 10๋จ๊ณ๊น์ง ๊ฒ์ฌํ์ฌ ERROR โ DEBUG ๋ก ๊ฐ๋ฑ + null ๋ฐํ (์๋ต ๋ณธ๋ฌธ ์ฐ๊ธฐ ์๋ ์์ฒด ํํผ) -
PipelineStreamBroker.push()catch (Exception)โcatch (Throwable)๋ก ํ์ฅ -
PipelineExecutor๊ฐchatStream()โchatStreamWithContinuation(..., 3, ...)์ฌ์ฉ โ max_tokens ๋๋ฌ ์ ์๋์ผ๋ก ์ต๋ 3ํ ์ด์ด๋ฐ๊ธฐ (๋จ๊ณ๋น ์ต๋ 32K ํ ํฐ)
๊ธฐํ
-
ReviewHistory์ํฐํฐ์reviewStatus/reviewedBy/reviewedAt/reviewNote์ปฌ๋ผ ์ถ๊ฐ (PENDING/ACCEPTED/REJECTED) -
ReviewComment์ํฐํฐ์parentId์ปฌ๋ผ ์ถ๊ฐ (๋๋๊ธ)
๋ฆฌ๋ทฐ ์ํฌํ๋ก์ฐ ์์ฑ
-
ReviewStatusController์ ๊ท:POST /history/{id}/review-statusโ REVIEWER/ADMIN ๋ง ์น์ธ/๊ฑฐ์ ๊ฐ๋ฅ, ์์ฑ์์๊ฒ ์๋ฆผ ๋ฐ์ก -
ReviewCommentController์parentIdํ๋ผ๋ฏธํฐ ์ง์ โ ๋๋๊ธ ์์ฑ ์ ๋ถ๋ชจ ๋๊ธ ์์ฑ์์๊ฒCOMMENT_REPLY์๋ฆผ -
DataRestController.reviewQueue()์ ๊ท:GET /api/v1/review-queue?tab=received|sent์ญํ ๊ธฐ๋ฐ ํํฐ๋ง (REVIEWER/ADMIN โ VIEWER) -
HistoryPage์ฌ์์ฑ: ํ๋๋ช ๋งคํ ์์ (inputContent/outputContent), ๋ฆฌ๋ทฐ ์ํ ๋ฐฐ์ง, ์น์ธ/๊ฑฐ์ ๋ฒํผ, ๋๊ธ/๋๋๊ธ ํธ๋ฆฌ,copyToClipboard์ ์ฉ -
ReviewRequestsPage์ฌ์์ฑ: ๋ด๊ฒ ์จ / ๋ด๊ฐ ์์ฒญํ ํญ, ์ญํ ๋ณ ์๋ด, ์น์ธ/๊ฑฐ์ confirm ๋ค์ด์ผ๋ก๊ทธ, ๋ น์/๋นจ๊ฐ ๋ฒํผ ๋ถ๊ธฐ
๋ณด์ / ๊ถํ / CSRF
-
SecurityConfigCSRF ignore ์/history/**,/email/**,/pipelines/**์ถ๊ฐ (์ด์ ์/history/*/share,/history/*/comments/**๋ง ์์ด์ review-status, pipelines/{id}/run, history/{id}/delete ๋ฑ์ด 405 ๋ฐ์) -
AdminUsersPage๋ผ๋ฒจ ๋ณ๊ฒฝ:์ฌ์ฉ์๋ชโ์์ด๋,ํ์ ์ด๋ฆโ์ฌ์ฉ์๋ช -
Sidebar์น์ max-height ๊ณ์ฐ:items.length * 36โitems.length * 40 + 8(๋ง์ง๋ง ๋ฉ๋ด ์๋ฆผ ํด๊ฒฐ)
์ด๋ฉ์ผ ๋ฐ์ก + Settings SMTP
-
EmailController์ ๊ท:POST /email/send(๋ค์ ์์ ์, ์ต๋ 10๋ช ),POST /email/test(SMTP ๊ฒ์ฆ) -
EmailModal๊ณต์ฉ ์ปดํฌ๋ํธ: ์์ ์ ์ถ๊ฐ (์ต๋ 10), ๋ณธ๋ฌธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ, ๋ฐ์ก ์ ๋ถ๋ถ ์คํจ ์๋ด -
Workspace/Pipeline/CodeReview๊ฒฐ๊ณผ ํ์ด์ง์ ์ด๋ฉ์ผ ๋ฐ์ก ๋ฒํผ ์ฐ๊ฒฐ -
SettingsPageSMTP ์นด๋ ์ ๊ท (Gmail ์ฑ ๋น๋ฐ๋ฒํธ ์๋ด + ํ ์คํธ ๋ฐ์ก ๋ฒํผ)
๋ฆฌ๋ทฐ ์ด๋ ฅ ๋์๋ณด๋ + ์ฌ์ฉ์ ๊ถํ ๋์ ๋ก๋ฉ
-
DataRestController.reviewDashboard()์ ๊ท: 1/3/7/30์ผ ๋๋ ์ฌ์ฉ์ ์ง์ ๊ธฐ๊ฐ ํ๋ผ๋ฏธํฐ, ์ํ๋ณ ์นด์ดํธ/% + dailyTrend + byType + byReviewer ์ง๊ณ -
ReviewDashboardPage์ ๊ท (/admin/review-dashboard): ๋๋/๋ง๋/๋ผ์ธ ์ฐจํธ + 4์ข ์คํฏ์นด๋ + ๋ถ์ ์ ํ๋ณ / ๋ฆฌ๋ทฐ์ด๋ณ ํ (recharts) -
AdminPermissionController.listFeatures()์ ๊ท:GET /admin/permissions/features์นดํ ๊ณ ๋ฆฌ๋ณ ๊ทธ๋ฃนํ ์๋ต -
AdminPermissionsPage๊ฐ features ๋ชฉ๋ก์ ๋์ ๋ก๋ โ ํ๋์ฝ๋ฉ ์ด์ค ๊ด๋ฆฌ ์ ๊ฑฐ -
authStore.login()์disabledFeaturesfetch ์ถ๊ฐ โ ๋ก๊ทธ์ธ ์งํ ์ฌ์ด๋๋ฐ ๊ถํ ํ ๊ธ ์ฆ์ ๋ฐ์ (์ด์ ์ ์๋ก๊ณ ์นจ ํ์๋ง ์ ์ฉ๋จ)
๋์๋ณด๋ / DB ์ํ / ์ค์
-
SystemHealthController+DataRestController์/admin/health/data๊ฐ DataSource.getMetaData() ๋ก ์ค์ ์ด์ DB ์๋ ๊ฐ์ง โ ์๋ ์ด๊ด ํ ์์คํ ํฌ์ค๊ฐ H2/Oracle/MySQL/PostgreSQL ์ ํํ ํ์ -
data/db-override.propertiesํ์ฑ ์ฌ๋ถ๋กdbOverrideActiveํ๋๊ทธ ๋ ธ์ถ -
AdminHealthPage: ์ ํ ๋ฐฐ์ง + ์ ํ/URL/๊ณ์ + ๋ณด๋ผ์ "๐ ์๋ ์ด๊ด์ผ๋ก ์ ํ๋ ์ด์ DB ์ฌ์ฉ ์ค" ๋ฐฐ๋
Oracle DB ํฌ์ค์ฒดํฌ ์์ปค ์ ์ ํด์ (์์ค ์ ํ๊ธฐ ๋ฉ์ถค ์์ธ)
-
OracleDbHealthIndicator๊ฐ ๋งค ํธ์ถ๋ง๋ค timeout ์๋DriverManager.getConnection()๋ก Tomcat ์์ปค ์ค๋ ๋๋ฅผ 80์ด์ฉ ์ ์ ํ๋ ๋ฒ๊ทธ -
oracle.net.CONNECT_TIMEOUT=3s+oracle.jdbc.ReadTimeout=3s+DriverManager.setLoginTimeout(3)์ผ๋ก ๋น ๋ฅธ ์คํจ ๋ณด์ฅ - ๊ฒฐ๊ณผ ์บ์ฑ (TTL 60์ด) โ ๋งค ํธ์ถ๋ง๋ค connection ์ฌ์์ฑ X
-
HarnessCacheService์ ๋ Oracle connection (DB ๊ฐ์ฒด ๋ชฉ๋ก/์์ค ์กฐํ) ํธ์ถ๋ ๋์ผ timeout ์ ์ฉ โopenOracleConnection()ํฌํผ๋ก ์ถ์ถ -
application.yml์management.endpoint.health.cache.time-to-live: 30sโ Spring Actuator ์์ฒด ์บ์ฑ (์ด์ค ๋ฐฉ์ด)
์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ค์ค 4๋จ๊ณ ํญ ๋ถ๋ฆฌ
-
HarnessReviewService.analyzeStream()์ด ๋จ๊ณ ์์ ์ง์ sentinel ๋ง์ปค[[HARNESS_STAGE:N]]emit (Reviewer/Verifier ๊ฐ ๊ฐ๊ฐ 4 sub-section ์ ๊ฐ์ ธ ๋จ์ ํค๋ ๋งค์นญ์ผ๋ก ๋ถ์์ ) -
SseStreamController๊ฐ ์ด๋ ฅ ์ ์ฅ ์ ๋ง์ปค๋ ์ ๊ท์์ผ๋ก ์ ๊ฑฐ โ DB ์๋ ๊น๋ํ ๋งํฌ๋ค์ด๋ง ์ ์ฅ -
CodeReviewPage์ ๋ฉด ์ฌ์์ฑ: 5๊ฐ ํญ (1.๋ถ์/2.๊ฐ์ ์ฝ๋/3.๊ฒํ /4.๊ฒ์ฆ/์ ์ฒด๊ฒฐ๊ณผ) - ์ฒญํฌ ํ์
processChunk()โ ๋ง์ปค๊ฐ chunk ๊ฒฝ๊ณ์ ๊ฑธ์น๋ ์ผ์ด์ค ๋ฐฉ์ด (accumRef ๋์ + ๋ง์ง๋ง 24์ ๋ณด๋ฅ) - ํ์ฑ ์คํธ๋ฆฌ๋ฐ ๋จ๊ณ ์๋ ํญ ์ ํ + spinner ์ธ๋์ผ์ดํฐ
- ํญ๋ณ [๋ณต์ฌ / MD / PDF / ์ด๋ฉ์ผ] ๋ฏธ๋๋ฒํผ 4์ข + '์ ์ฒด ๊ฒฐ๊ณผ' ํญ์ 4๋จ๊ณ ํฉ๋ณธ
๋ฆฌ๋ทฐ ์ด๋ ฅ ๋์๋ณด๋ ๊ถํ ํตํฉ (3๊ณณ ๋ฑ๋ก ๋๋ฝ)
-
AdminPermissionController.FEATURES์review-dashboard์ถ๊ฐ -
PermissionInterceptor.FEATURE_PATHS์ ๋งคํ ์ถ๊ฐ -
sidebarMenus.ts๋ฉ๋ด ํญ๋ชฉ์featureKey: 'review-dashboard'๋ถ์ฌ โ ํ ๊ธ OFF ์ ์ฌ์ด๋๋ฐ ์๋ ์จ๊น
๊ธฐํ UX ๋ฒ๊ทธ ์์
-
RegexPage์์ ์นฉ์ดnavigator.clipboard.writeText๋ง ํธ์ถํด์ HTTP IP ํ๊ฒฝ์์ silent ์คํจ โAnalysisPageTemplate.inputExamplesprop ์ ๊ท, ํด๋ฆญ ์setInput()์ผ๋ก textarea ์ฆ์ ์ฑ์ - ์ ๊ท์ ์์ฑ๊ธฐ์ 8๊ฐ ์์ (ํธ๋ํฐ/์ฃผ๋ฏผ/์ด๋ฉ์ผ/์ฃผ์/์ด๋ฆ/IP/URL/๋ ์ง) ๊ฐ ์ด์ ํ ๋ฒ ํด๋ฆญ์ ์ ๋ ฅ์นธ์ ๋ค์ด๊ฐ
์ด๋ฒ ๋ผ์ด๋๋ ์ฌ์ฉ ์ค ๋์ ๋ ํ์ค ๋ฒ๊ทธ๋ฅผ ํ์ด ๋ด๊ณ , ๋ฏธ๋ฌํ๊ฒ ๊ตฌ๋ฉ์ด ๋ ์๋ ๊ถํ ์ฒดํฌ๋ฅผ ์ ๋ฐ์ ์ผ๋ก ์กฐ์ฌ๋ธ ์ด์ ํ์ง ๋ผ์ด๋์ ๋๋ค. 5๊ฐ ๋จ๊ณ(Phase 1~4) ๋ก ๋๋ ์งํํ์ต๋๋ค.
Phase 1 โ ๋ฒ๊ทธ & ๋ณด์
-
/api/v1/admin/**์ญํ ๊ฒ์ดํ ๋๋ฝ ์์ โ/admin/**๊ท์น์ด/api/v1/admin/**๋ฅผ ๋งค์นญํ์ง ๋ชปํด VIEWER/REVIEWER ๋ ๊ด๋ฆฌ์ API ๋ฅผ ํธ์ถํ ์ ์๋ ๊ตฌ๋ฉ์SecurityConfig์.hasRole("ADMIN")์ผ๋ก ์ฐจ๋จ -
FavoriteController.delete์์ ์ ์ฒดํฌ ์ถ๊ฐ โ ๊ธฐ์กด์ ๋๊ตฌ๋ ๋จ์ ์ฆ๊ฒจ์ฐพ๊ธฐ๋ฅผ ์ง์ธ ์ ์์์. ๋ณธ์ธ ์์ ์๋๋ฉด HTTP 403 + JSON ์๋ต์ผ๋ก ๋ณ๊ฒฝ -
/favorites/star์ค๋ณต ์ ์ฅ ๋ฐฉ์ง โFavoriteRepository.findByUsernameAndHistoryId+FavoriteService.save()์์ ๊ธฐ์กด ๋ ์ฝ๋ ์ฌ์ฌ์ฉ ๋ก์ง -
ReviewCommentController.deleteComment์ฃผ์๊ณผ ๊ตฌํ ๋ถ์ผ์น ์์ โ ์ฃผ์์ "๋ณธ์ธ ๋๋ ADMIN" ์ด๋ผ ์ ํ์์์ง๋ง ์ค์ ๋ก ๋ณธ์ธ๋ง ๊ฐ๋ฅํ๋ ๋ฒ๊ทธ.HttpServletRequest.isUserInRole("ADMIN")๋ก ADMIN ํ์ฉ - ์๋ฆผ SSE
onerrorfallback ํด๋ง ์ง์ํ โ ๊ธฐ์กด์setTimeout1ํ๋ง ๋๊ณ ์์ํ ๋ฉ์ถฐ์ ์ข ์์ด์ฝ ์นด์ดํธ๊ฐ ๋ณต๊ตฌ๋์ง ์๋ ๋ฒ๊ทธ.setInterval๊ธฐ๋ฐ ์ง์ ํด๋ง + ์ฃผ๊ธฐ์ SSE ์ฌ์ฐ๊ฒฐ ์๋๋ก ์ ํ -
toggleFavorite์ฐ์ ํด๋ฆญ ๊ฒฝํฉ ๊ฐ๋ โtogglingIdsRef: Set<number>๋ก ๋์ผ historyId ์ ๋ํ ์ค๋ณต ์์ฒญ ์ฐจ๋จ - CSRF ์ ์ ์ ๊ฒ ์๋ฃ โ
SecurityConfig.ignoringAntMatchers์ ์ด๋ฒ ์ธ์ ์ ๋ชจ๋ state-changing ๊ฒฝ๋ก๊ฐ ์ด๋ฏธ ํฌํจ๋ผ ์์์ ํ์ธ
Phase 2 โ UX ๊ณต๋ฐฑ
- ๋๊ธ
@username๋ฉ์ ์๊ฐ ๊ฐ์กฐ โrenderCommentContent()๊ฐ ๋ณธ๋ฌธ์ ์ํํ๋ฉฐ ๋ฉ์ ํ ํฐ์ accent ๋ฐฐ์ง๋ก ๋ ๋ -
HistoryPage๋ค์ค ์ ํ ์ญ์ โ "์ ํ N๊ฑด ์ญ์ " ๋ฒํผ (canDelete ์กฐ๊ฑด๋ถ),Promise.all๋ก ๋ณ๋ ฌ DELETE, ์ฑ๊ณต/์คํจ ๊ฐ์ ํ ์คํธ ๋ถ๋ฆฌ -
FavoritesPage์ ๋ฉด ์ ๊ทธ๋ ์ด๋ โ ๊ฒ์, ์ ์ฒด์ ํ, ๋ค์ค ๋ด๋ณด๋ด๊ธฐ, ๋ค์ค ์ญ์ , ์ ํ ํ์ด๋ผ์ดํธ, ๊ฒ์ ๊ฒฐ๊ณผ ์์ ๋ถ๊ธฐ. HistoryPage ์ ๋์ผํ ํจํด ์ด์ - ์๋ฆผ "์ ์ฒด ์ญ์ " โ
NotificationRepository.deleteAllByRecipientUsername+POST /notifications/delete-all+ ๋๋กญ๋ค์ด ํค๋ ๋นจ๊ฐ ๋ฒํผ - ๋ชจ๋ฌ fade ์ ๋๋ฉ์ด์
โ
theme.css์modal-overlay-in/modal-body-inkeyframe ์ถ๊ฐ,ReviewActionDialog+ ๋ฆฌ๋ทฐ ์์ธ ๋ชจ๋ฌ์ ์ ์ฉ
Phase 3 โ ์ฝ๋ ํ์ง & ์ฑ๋ฅ
-
utils/date.ts๊ณตํต ์ถ์ถ โformatDate/formatRelative. HistoryPage, ReviewRequestsPage ์ ์ค๋ณต ํจ์ ์ ๊ฑฐ -
utils/harnessMarkdown.tsx์ถ์ถ โnormalizeBuilderSection/harnessMdComponents/flattenChildren์CodeReviewPage.tsx์์ ๋ถ๋ฆฌ โ ํ์ด์ง ์ปดํฌ๋ํธ๋ ์ํ/์คํธ๋ฆฌ๋ฐ orchestration ์ ์ง์ค -
toolkit.history.max/toolkit.favorites.max์ค์ ์ธ๋ถํ โapplication.yml+@Value์ฃผ์ . ๊ธฐ๋ณธ๊ฐ ์ํฅ (history 100โ200, favorites 200โ500). ํ๊ฒฝ๋ณ์TOOLKIT_HISTORY_MAX/TOOLKIT_FAVORITES_MAX๋ก ์ค๋ฒ๋ผ์ด๋ -
/api/v1/historyยท/api/v1/favoritesํ์ด์ง๋ค์ด์ โ?page=&size=ํ๋ผ๋ฏธํฐ,X-Total-Count/X-Has-More/X-Page/X-Page-Size์๋ต ํค๋. ๊ธฐ์กด ํ๋ก ํธ๋ ํ๋ผ๋ฏธํฐ ์๋ตํ๋ฉด backward-compatible - ํ๋ก ํธ "๋ ๋ณด๊ธฐ" ๋ฒํผ โ HistoryPage / FavoritesPage ์
PAGE_SIZE=50๊ธฐ๋ฐ load-more UX
Phase 4 โ ๋ฌธ์/๋ฆด๋ฆฌ์ฆ
- README v4.2.6 โ v4.2.7 ๋ฒ์ ๋ฐฐ์ง + v4.2.7 ์น์ ์ถ๊ฐ
-
CHANGELOG.md์ ์ค โ Keep a Changelog ํฌ๋งท์ผ๋ก v4.0 ~ v4.2 ๋ฆด๋ฆฌ์ฆ ์ ๋ฆฌ
Phase 5.2 โ ์๋ ์๊ฐ ํ์
-
utils/date.ts์formatRelative()๋ฅผ 3๊ฐ ๊ฒฝ๋ก์ ์ ์ฉ: ๋๊ธ(CommentBox), ์๋ฆผ ๋๋กญ๋ค์ด(TopBar), ์ด๋ ฅ ์นด๋(HistoryPage) - ํ๊ธฐ ์์: "๋ฐฉ๊ธ", "5๋ถ ์ ", "2์๊ฐ ์ ", "์ด์ ", "3์ผ ์ ", 7์ผ ์ด๊ณผ์ ์ ๋ ์๊ฐ
- hover ์
title์์ฑ์ผ๋ก ์๋ณธ ์ ๋ ์๊ฐ ๋ ธ์ถ (์ ๋ฐ ํ์ธ ๊ฐ๋ฅ) - ๋ฐฑ์๋
ReviewCommentController/NotificationController์๋ต์createdAtIsoํ๋ ์ถ๊ฐ โ ๊ธฐ์กด pre-formattedcreatedAt์ ๊ทธ๋๋ก ์ ์งํ๋ฉฐ ํ๋ก ํธ๊ฐ ์๋ ์๊ฐ ๊ณ์ฐ์ฉ ์๋ณธ ISO ๋ฅผ ํจ๊ป ๋ฐ๋๋ก ํ์ฅ
Phase 6 โ ํ ์คํธ ์ธํ๋ผ (ํ๊ท ๋ฐฉ์ง ์์ ๋ง)
-
spring-security-test์์กด์ฑ ์ถ๊ฐ (pom.xml) -
src/test/resources/application-test.ymlโ H2 in-memory ๊ฒฉ๋ฆฌ ํ๋กํ์ผ - JUnit ์ค๋ชจํฌ ํ
์คํธ 16๊ฐ ์ ๋ถ green โ
src/test/java/.../smoke/-MentionPatternTest(8) โ ๋ฉ์ ์ ๊ท์ ํ๊ท ๋ฐฉ์ง (์ด๋ฉ์ผ ์ ์ธ, ๋ค์ค ๋ฉ์ , ํ ํฐ ๋ฌธ์_/./-, ํ๊ธ ๊ฒฝ๊ณ ๋ฑ) -SecuritySmokeTests(7) โ@SpringBootTest + MockMvc + @WithMockUser๋ก Phase 1 ์ ๋ชจ๋ ๊ถํ ๊ฒฝ๋ก ๊ฒ์ฆ (VIEWER/REVIEWER/ADMIN ร admin API ร history ์ญ์ ) -FavoriteDedupSmokeTest(1) โFavoriteService.save์ค๋ณต ๋ฐฉ์ง ํตํฉ ๊ฒ์ฆ - Playwright E2E ํ์ฅ โ
frontend/e2e/app.spec.ts์ 4๊ฐ ์ถ๊ฐ ํ ์คํธ green -utils/date์์ ๋ก์ง ๋ธ๋ผ์ฐ์ ๊ฒ์ฆ (7 ์ผ์ด์ค) -@๋ฉ์ ์ ๊ท์ ๋ธ๋ผ์ฐ์ ๊ฒ์ฆ (4 ์ผ์ด์ค) - ๋ผ์ฐํธ ๋ก๋ ์ฒดํฌ์ v4.2.7 ์ ๊ท ๊ฒฝ๋ก ํฌํจ (/favorites,/review-requests,/harness,?historyId=42๋ฅ๋งํฌ) - ํฅํ ๋ฆฌํฉํฐ๋ง์ ์ด ํ ์คํธ๋ค์ด ์๋์ผ๋ก Phase 1 ๋ณด์ ํ๊ท๋ฅผ ์ก์๋
๊ธฐํ (์ธ์ ์ค ํจ๊ป ์ฒ๋ฆฌ๋ ํญ๋ชฉ)
- VIEWER ๊ถํ์ด ์์ฑํ ๋ฆฌ๋ทฐ ์ด๋ ฅ โ REVIEWER/ADMIN ์ ์ "๋๊ธฐ ์๋ฆผ" โ
PendingReviewNotifier์ ์ค,ReviewHistoryService/SseStreamController/PipelineExecutor4๊ฐ ์ ์ฅ ๊ฒฝ๋ก ๋ชจ๋ ํ ํน - ๋๊ธ
@๋ฉ์ โ ์๋ฆผ ๋ฐํ โMENTION_PATTERN+AppUserRepository์กด์ฌ ๊ฒ์ฆ +Notification(type="MENTION") -
/api/v1/users/mentions์๋ํฌ์ธํธ โ ๋ฉ์ ์๋์์ฑ ํ๋ณด (๋ชจ๋ ๋ก๊ทธ์ธ ์ฌ์ฉ์ ์ ๊ทผ ๊ฐ๋ฅ, ๋ฏผ๊ฐํ๋ ์ ์ธ) -
MentionInput.tsx๊ณต์ฉ ์ปดํฌ๋ํธ โ@ํธ๋ฆฌ๊ฑฐ ๋๋กญ๋ค์ด, ArrowUp/Down, Enter/Tab ์ฝ์ , Escape, ์ด๋ฉ์ผa@b.com์ ๋ฉ์ ์๋ ๊ฒ์ผ๋ก ํ์ - ์ด๋ ฅ ์์ธ ๋ชจ๋ฌ โ ๋ฆฌ๋ทฐ ์์ฒญ ํ์ด์ง์์ VIEWER ์์ฑ ๋ณธ๋ฌธ์ ํ์ ์ผ๋ก ํ์ธ + ๋ชจ๋ฌ ๋ด์์ ์น์ธ/๊ฑฐ์ ๊ฐ๋ฅ
-
ReviewActionDialog๊ณต์ฉ ๋ค์ด์ผ๋ก๊ทธ โ ์น์ธ/๊ฑฐ์ ํ์ ์ ์ ํ ์ฝ๋ฉํธ ์ ๋ ฅ, Ctrl+Enter / Esc ๋จ์ถํค,ReviewNoteCard๋ก ๋ชจ๋ ๊ถํ์ ํผ๋๋ฐฑ ํ์ -
Favorite์ํฐํฐusername/historyId์ปฌ๋ผ ์ถ๊ฐ โ ๊ธฐ์กด ํํฐ ์ฟผ๋ฆฌ๊ฐ NULL ์กฐ๊ฑด์ผ๋ก ๋น ๋ฐฐ์ด๋ง ๋๋ ค์ฃผ๋ ์น๋ช ์ ๋ฒ๊ทธ ์์ - ์๋ฆผ ๊ฐ๋ณ ์ญ์ โ ๋๋กญ๋ค์ด ๊ฐ ํญ๋ชฉ ์ฐ์ธก ์๋จ X ๋ฒํผ +
POST /notifications/{id}/delete -
OracleDbHealthIndicator์ ๋ฉด ์ฌ์์ฑ โ@PostConstruct+@Scheduled(1h)๋ก ํ๋ก๋ธ๋ฅผhealth()ํธ์ถ๊ณผ ์์ ๋ถ๋ฆฌ. Tomcat ์์ปค ์ค๋ ๋ ์ ์ ์ ๊ฑฐ -
HarnessCacheService.initOnStartup()@PostConstruct์ ํ โ Spring ์ปจํ ์คํธ ์ด๊ธฐํ ๋จ๊ณ์์ ์ธ๋ผ์ธ ์คํํ์ฌ WAS ๊ฐ ์์ฒญ์ ๋ฐ๊ธฐ ์์ํ ๋ ์ด๋ฏธ ํ๋ก์ ํธ ํ์ผ / DB ์ค๋ธ์ ํธ ์บ์๊ฐ ๋ก๋๋ ์ํ ๋ณด์ฅ -
Dockerfile๋น๋ ๋ก๊ทธ ์ ๋ฆฌ โdependency:go-offline์คํจ ๋ก๊ทธ๋ฅผ>/dev/null 2>&1๋ก ์์ ๋ฌต์ ์ฒ๋ฆฌ + ์ด์ ์ฃผ์ - ํ๋ค์ค "๊ฐ์ ์ฝ๋" ํญ "sql" ๋ผ๋ฒจ ๋ผ์ธ ์ ๊ฑฐ โ Claude Builder ๊ฐ ํ๋กฌํํธ
์์๋ฅผ ๋ฐ๋ผ ์ฒซ ์ค์
sql์ ๋ฑ๋ ์ฆ์์ ํ๋ก ํธ ์ ๊ท์์ผ๋ก clean up - VIEWER ๊ถํ์ ์ด๋ ฅ ์ญ์ ์์ด์ฝ ๋
ธ์ถ ์์ฒด๋ฅผ ์ ์ธ + ๋ฐฑ์๋
ReviewHistoryController.delete์์๋isUserInRole์ฒดํฌ๋ก 403 ๋ฐํ
PR๊ณผ Issue๋ฅผ ํ์ํฉ๋๋ค! ๊ธฐ์ฌ ๊ฐ์ด๋: CONTRIBUTING.md










