Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughGemini 기반 AI 통합을 제거하고 Claude 기반 통합으로 교체합니다. 새로운 ClaudeClient와 ClaudeProperties를 추가하고 GeminiClient/GeminiProperties를 삭제했으며, SlackAIService 및 테스트 설정과 환경 구성 파일들을 Claude로 재연결합니다. ClaudeClient는 다중 턴 도구 호출 및 MCP 위임을 포함합니다. Changes
Sequence DiagramsequenceDiagram
participant Slack as SlackAIService
participant Claude as ClaudeClient
participant API as Claude API
participant MCP as McpClient
Slack->>Claude: chat(userMessage)
Claude->>Claude: buildRequest(message, history, tools)
Claude->>API: POST /messages (model, prompt, tools, limits)
API-->>Claude: Response {stop_reason, content}
alt stop_reason == end_turn
Claude->>Claude: extractTextResponse()
Claude-->>Slack: finalText
else stop_reason == tool_use
Claude->>Claude: processToolCalls(toolUseBlocks)
loop for each tool call
Claude->>MCP: executeToolCall(toolName, args)
MCP-->>Claude: toolResult
end
Claude->>Claude: appendToolResults(toHistory)
Claude->>API: POST /messages (updated history)
API-->>Claude: nextResponse
Claude-->>Slack: finalText (after iterations or end_turn)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~32 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
이 PR은 Slack AI 서비스에서 사용하는 AI 에이전트를 Google Gemini에서 Anthropic Claude로 교체하는 리팩토링입니다. SlackAIService가 GeminiClient 대신 새로 추가된 ClaudeClient를 사용하도록 변경되었으며, 설정 프로퍼티와 테스트 인프라도 함께 업데이트되었습니다.
Changes:
ClaudeClient및ClaudeProperties신규 추가 (Anthropic API 호출, tool use 멀티턴 루프 지원)SlackAIService의 의존성을GeminiClient에서ClaudeClient로 교체- 설정 파일(
application-infrastructure.yml,application-test.yml) 및 테스트 설정(TestClaudeConfig,IntegrationTestSupport,ControllerTestSupport,KonectApplicationTests)을 Claude 기반으로 업데이트
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
ClaudeClient.java |
Anthropic Claude API를 호출하는 신규 클라이언트 (tool use 지원) |
ClaudeProperties.java |
Claude API 설정 바인딩 record 신규 추가 |
SlackAIService.java |
GeminiClient → ClaudeClient 의존성 교체 |
application-infrastructure.yml |
gemini 설정 블록을 claude로 교체 |
application-test.yml |
테스트용 gemini 설정을 claude로 교체 |
TestClaudeConfig.java |
테스트용 ClaudeClient mock 설정 신규 추가 |
IntegrationTestSupport.java |
@import에서 TestGeminiConfig → TestClaudeConfig로 변경 |
ControllerTestSupport.java |
@import에서 TestGeminiConfig → TestClaudeConfig로 변경 |
KonectApplicationTests.java |
@import에서 TestGeminiConfig → TestClaudeConfig로 변경 |
| @@ -13,9 +13,9 @@ slack: | |||
| event: ${SLACK_WEBHOOK_EVENT} | |||
| signing-secret: ${SLACK_SIGNING_SECRET} | |||
|
|
|||
There was a problem hiding this comment.
GeminiClient가 @Component로 등록된 상태로 남아있고, GeminiProperties도 @ConfigurationProperties(prefix = "gemini")로 바인딩을 시도합니다. 하지만 application-infrastructure.yml에서 gemini 설정 블록이 claude로 교체되었기 때문에, GeminiProperties의 apiKey와 model 값은 null이 됩니다. 더 이상 사용되지 않는 GeminiClient 및 GeminiProperties, 그리고 src/main/java/gg/agit/konect/infrastructure/gemini/ 디렉토리를 제거해야 합니다. 그렇지 않으면 죽은 코드가 애플리케이션 컨텍스트에 남아 유지보수 문제를 야기합니다.
| gemini: | |
| api-key: ${GEMINI_API_KEY:} | |
| model: ${GEMINI_MODEL:} |
| @SpringBootTest | ||
| @ActiveProfiles("test") | ||
| @Import({TestSecurityConfig.class, TestJpaConfig.class, TestGeminiConfig.class}) | ||
| @Import({TestSecurityConfig.class, TestJpaConfig.class, TestClaudeConfig.class}) |
There was a problem hiding this comment.
TestGeminiConfig 파일이 더 이상 어떤 테스트에서도 @Import에 참조되지 않지만 삭제되지 않고 남아있습니다. 이 파일은 이번 리팩토링에서 함께 제거되어야 합니다.
|
|
||
| for (int i = 0; i < MAX_TOOL_ITERATIONS; i++) { | ||
| Map<String, Object> request = buildRequest(messages); | ||
| String response = callClaudeApi(request); |
There was a problem hiding this comment.
callClaudeApi에서 RestClient의 응답이 null일 경우, 이후 objectMapper.readTree(response)에서 null이 readTree(String)에 전달되어 IllegalArgumentException 또는 NullPointerException이 발생할 수 있습니다. 외부 catch 블록이 이를 잡아주지만, null 응답에 대한 명시적인 처리가 없어 오류 원인 파악이 어렵습니다. callClaudeApi에서 null 응답을 명시적으로 검사하거나, readTree 호출 전에 null 체크를 추가하는 것이 좋습니다.
| String response = callClaudeApi(request); | |
| String response = callClaudeApi(request); | |
| if (response == null) { | |
| log.error("Claude API returned null response for request: {}", request); | |
| throw new RestClientException("Claude API returned null response"); | |
| } |
| if ("tool_use".equals(stopReason)) { | ||
| // Add assistant's response to messages | ||
| messages.add(Map.of( | ||
| "role", "assistant", | ||
| "content", objectMapper.convertValue(content, List.class) | ||
| )); | ||
|
|
||
| // Process tool calls and add results | ||
| List<Map<String, Object>> toolResults = processToolCalls(content); | ||
| messages.add(Map.of( | ||
| "role", "user", | ||
| "content", toolResults | ||
| )); |
There was a problem hiding this comment.
stop_reason이 "tool_use"임에도 processToolCalls(content)가 빈 리스트를 반환하는 경우(예: content 배열에 tool_use 타입 블록이 없는 경우), 빈 content 배열을 가진 user 메시지가 Claude API에 전송됩니다. Anthropic API는 content가 빈 배열인 메시지에 대해 오류를 반환할 수 있으므로, toolResults가 비어있을 때 적절히 처리해야 합니다.
| ## 주요 테이블 힌트 | ||
| - users: 사용자 정보 (deleted_at IS NULL = 활성 사용자) | ||
| - club: 동아리 정보 | ||
| - club_member: 동아리 멤버 (role: PRESIDENT, VICE_PRESIDENT, MEMBER) |
There was a problem hiding this comment.
SYSTEM_PROMPT의 club_member 테이블 힌트에 실제 ClubPosition enum 값인 MANAGER가 누락되어 있습니다. 실제 ClubPosition enum은 PRESIDENT, VICE_PRESIDENT, MANAGER, MEMBER 4개의 값을 가지지만, 프롬프트에는 PRESIDENT, VICE_PRESIDENT, MEMBER 3개만 기재되어 있습니다. AI가 MANAGER 역할을 가진 멤버를 누락시킬 수 있어 잘못된 쿼리 결과를 생성할 수 있습니다. MANAGER를 힌트에 추가해야 합니다.
| - club_member: 동아리 멤버 (role: PRESIDENT, VICE_PRESIDENT, MEMBER) | |
| - club_member: 동아리 멤버 (role: PRESIDENT, VICE_PRESIDENT, MANAGER, MEMBER) |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/gg/agit/konect/infrastructure/claude/client/ClaudeClient.java`:
- Around line 165-167: The catch in ClaudeClient currently swallows exceptions
and returns a user-facing string; instead propagate failure by throwing a
domain-specific exception (e.g., ClaudeException) or change the method signature
to return a Result/Optional that indicates failure so callers can handle it;
specifically, in ClaudeClient (the method containing the try/catch that
currently logs and returns "죄송합니다..."), replace the catch block to throw new
ClaudeException(e) (or return a failure Result) and update callers such as
SlackAIService to handle the exception/result path rather than treating it as a
normal response.
- Around line 180-182: The code currently logs full tool inputs and Claude
responses in ClaudeClient (e.g., the log.info calls around the executeToolCall
invocation and other logging at the places that record SQL and full responses);
change these to avoid recording sensitive plaintext by removing or redacting
request/response bodies and instead log only non-sensitive metadata such as
toolName, a requestId/correlation id, timestamps, success/failure status, and
error messages; update the log statements in the methods using executeToolCall
and any other methods that log SQL or Claude responses to mask or truncate
payloads (or omit them entirely) and ensure errors still log safe diagnostic
info (stack/message) without including sensitive content.
In
`@src/main/java/gg/agit/konect/infrastructure/claude/config/ClaudeProperties.java`:
- Around line 7-10: The ClaudeProperties record's `@NotBlank` constraints aren't
enforced because it lacks the `@Validated` annotation; add `@Validated` to the
ClaudeProperties declaration so Spring performs validation at bind time for the
apiKey and model fields (i.e., annotate the ClaudeProperties record with
`@Validated` to activate constraint checks on the `@NotBlank` apiKey and model
properties).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: f57153c1-f2cb-4c8b-a55e-b9f1f6d530fc
📒 Files selected for processing (9)
src/main/java/gg/agit/konect/infrastructure/claude/client/ClaudeClient.javasrc/main/java/gg/agit/konect/infrastructure/claude/config/ClaudeProperties.javasrc/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.javasrc/main/resources/application-infrastructure.ymlsrc/test/java/gg/agit/konect/KonectApplicationTests.javasrc/test/java/gg/agit/konect/support/ControllerTestSupport.javasrc/test/java/gg/agit/konect/support/IntegrationTestSupport.javasrc/test/java/gg/agit/konect/support/TestClaudeConfig.javasrc/test/resources/application-test.yml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Agent
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java
⚙️ CodeRabbit configuration file
src/main/java/**/*.java: 아래 원칙으로 리뷰 코멘트를 작성한다.
- 코멘트는 반드시 한국어로 작성한다.
- 반드시 수정이 필요한 항목만 코멘트로 남기고, 단순 취향 차이는 지적하지 않는다.
- 각 코멘트 첫 줄에 심각도를
[LEVEL: high|medium|low]형식으로 반드시 표기한다.- 심각도 기준: high=운영 장애 가능, medium=품질 저하, low=개선 권고.
- 각 코멘트는 "문제 -> 영향 -> 제안" 순서로 3문장 이내로 간결하게 작성한다.
- 가능하면 재현 조건 및 실패 시나리오도 포함한다.
- 제안은 현재 코드베이스(Spring Boot + JPA + Flyway) 패턴과 일치해야 한다.
- 보안, 트랜잭션 경계, 예외 처리, N+1, 성능 회귀 가능성을 우선 점검한다.
- 가독성: 변수/메서드 이름이 의도를 바로 드러내는지, 중첩과 메서드 길이가 과도하지 않은지 점검한다.
- 단순화: 불필요한 추상화, 중복 로직, 과한 방어 코드가 있으면 더 단순한 대안을 제시한다.
- 확장성: 새 요구사항 추가 시 변경 범위가 최소화되는 구조인지(하드코딩 분기/값 여부 포함) 점검한다.
Files:
src/main/java/gg/agit/konect/infrastructure/claude/client/ClaudeClient.javasrc/main/java/gg/agit/konect/infrastructure/claude/config/ClaudeProperties.javasrc/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.java
**/*
⚙️ CodeRabbit configuration file
**/*: 공통 리뷰 톤 가이드:
- 모든 코멘트는 첫 줄에
[LEVEL: ...]태그를 포함한다.- 과장된 표현 없이 사실 기반으로 작성한다.
- 한 코멘트에는 하나의 이슈만 다룬다.
- 코드 예시가 필요하면 최소 수정 예시를 제시한다.
- 가독성/단순화/확장성 이슈를 발견하면 우선순위를 높여 코멘트한다.
Files:
src/main/java/gg/agit/konect/infrastructure/claude/client/ClaudeClient.javasrc/test/java/gg/agit/konect/support/IntegrationTestSupport.javasrc/main/java/gg/agit/konect/infrastructure/claude/config/ClaudeProperties.javasrc/test/java/gg/agit/konect/KonectApplicationTests.javasrc/main/resources/application-infrastructure.ymlsrc/test/java/gg/agit/konect/support/ControllerTestSupport.javasrc/main/java/gg/agit/konect/infrastructure/slack/ai/SlackAIService.javasrc/test/resources/application-test.ymlsrc/test/java/gg/agit/konect/support/TestClaudeConfig.java
🪛 Checkov (3.2.334)
src/test/resources/application-test.yml
[low] 126-127: Base64 High Entropy String
(CKV_SECRET_6)
🔇 Additional comments (1)
src/test/java/gg/agit/konect/support/ControllerTestSupport.java (1)
45-45: [LEVEL: approve]LGTM! Gemini에서 Claude로의 테스트 구성 마이그레이션이 적절합니다.
TestGeminiConfig에서TestClaudeConfig로의 변경이 PR 목표(Agent를 Gemini에서 Claude로 전환)와 일치하며, 다른 테스트 파일들(KonectApplicationTests.java,IntegrationTestSupport.java)과도 일관성 있게 적용되었습니다.
🔍 개요
🚀 주요 변경 내용
💬 참고 사항
✅ Checklist (완료 조건)