[STK6-05] Chat REST API 엔드포인트 (claude -p 애그리게이터)
작업 내용 (설계 의도)
변경 사항
presentation/ai/ChatApiController.kt를 신규 생성한다. POST /api/chat/message 엔드포인트를 제공하며 ProcessBuilder로 claude -p subprocess를 실행한다. --mcp-config에 stock-mcp.json 경로를 전달하고, --output-format stream-json으로 스트리밍 JSON을 파싱해 SSE로 클라이언트에 전달한다. 대화 히스토리는 ConcurrentHashMap<sessionId, List<String>> 인메모리로 유지하고 프롬프트에 prepend한다.
선행 티켓: STK6-01, STK6-02, STK6-03
핵심 구현 포인트
- subprocess 스트림은 별도 Thread에서 blocking read → SSE emitter send
SseEmittertimeout: 120초 (claude -p 최대 응답 시간 고려)claude -p비정상 종료(exit code ≠ 0) 시 SSE error 이벤트 전송--allowedTools "mcp__stock__*,mcp__brave-search__*"로 허용 Tool 제한
다이어그램
처리 흐름
sequenceDiagram participant FE as Chat UI participant API as ChatApiController participant PB as ProcessBuilder participant CLI as claude -p FE->>API: POST /api/chat/message {sessionId, message} API->>API: history 조회 + 프롬프트 조립 API->>PB: ProcessBuilder(claude -p, --mcp-config, --output-format stream-json) PB->>CLI: subprocess 실행 CLI-->>PB: stream-json 청크 (라인별) PB-->>API: InputStream 스트리밍 API-->>FE: SSE send(chunk) CLI-->>PB: EOF (종료) PB-->>API: process.waitFor() API-->>FE: SSE complete
클래스 의존
flowchart LR ChatApiController -->|subprocess| ClaudeCLI[claude -p] ChatApiController -->|read/write| SessionMap[ConcurrentHashMap] ChatApiController -->|config inject| McpConfigPath[MCP_CONFIG_PATH]
테스트 케이스
POST /api/chat/message요청 시ProcessBuilder가claude -p인자로 실행된다.--mcp-config인자가MCP_CONFIG_PATH환경 변수 값으로 설정된다.- 동일
sessionId로 2회 요청 시 첫 번째 대화가 두 번째 프롬프트에 포함된다. sessionId가 빈 문자열이면 400 Bad Request를 반환한다.- subprocess exit code가 1이면 SSE error 이벤트를 전송하고 emitter를 종료한다.
- subprocess가 120초 내에 응답하지 않으면 SSE timeout 이벤트를 전송한다.