Refactor: Json파싱 표준화로 MCP Servie 리팩토링#412
Conversation
WalkthroughBrave MCP 응답을 항상 {"results":[...]} 형태로 정규화하는 JSON 파이프라인(재귀 깊이 제어, legacy 파싱 포함)을 추가하고, MCP 클라이언트 탐색/초기화 실패에 대해 재시도 및 오류 처리를 강화했습니다. RAG 변환은 각 result를 개별 Document로 변환하도록 단순화되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Caller
participant Svc as BraveSearchMcpService
participant MCP as Brave MCP Client
App->>Svc: search(query)
Svc->>MCP: listTools()
alt McpError contains "initialized"
Svc->>MCP: initialize()
Svc->>MCP: listTools() 재시도
end
Svc->>MCP: call BRAVE_WEB_TOOL
MCP-->>Svc: raw JsonNode / 텍스트
Svc->>Svc: normalizeContent (looksLikeJson, 재귀 parseJsonBlockIntoResults, parseLegacyBlock)
Svc-->>App: {"results":[{url,title,description}, ...]}
sequenceDiagram
participant App as Caller
participant Rag as BraveSearchRagService
App->>Rag: toDocuments(Optional<JsonNode>)
alt results not array
Rag-->>App: []
else results array
loop for each result
Rag->>Rag: extract url, title, description
Rag->>Rag: description HTML 제거 / looksLikeJson 검사 / legacy 파싱
alt url/title/description 존재
Rag->>Rag: create Document (id=url||title, body=description||title||url, metadata title/url)
else
Rag-->>Rag: skip
end
end
Rag-->>App: List<Document>
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java (3)
39-41: 로그 레벨을 DEBUG로 변경해야 합니다운영 환경에서 모든 검색 응답의 전체 내용을 INFO 레벨로 로깅하면 로그 볼륨이 과도하게 증가할 수 있습니다. 디버깅 목적이라면 DEBUG 레벨이 더 적절합니다.
- log.info("[Brave MCP Response Raw content]: {}", raw.toPrettyString()); + log.debug("[Brave MCP Response Raw content]: {}", raw.toPrettyString());
114-117: JSON 검증 로직이 불완전합니다현재 구현은 단순히 시작과 끝 문자만 확인하는데,
"{invalid json}"같은 잘못된 형식도 JSON으로 판단할 수 있습니다. 하지만 실제 파싱은parseJsonBlockIntoResults에서 try-catch로 처리하므로 큰 문제는 없습니다.
200-208: 조건 검사가 불완전합니다
flushOne메서드는 title과 url이 모두 존재하고 body가 있을 때만 결과를 추가합니다. 하지만 body가 없어도 title과 url만으로 유효한 검색 결과가 될 수 있습니다.private void flushOne(ArrayNode out, String title, String url, StringBuilder body) { - if (title != null && url != null && body.length() > 0) { + if (title != null && url != null) { ObjectNode one = objectMapper.createObjectNode(); one.put("title", title); one.put("url", url); - one.put("description", body.toString().trim()); + String description = body.toString().trim(); + if (!description.isEmpty()) { + one.put("description", description); + } out.add(one); } }cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java (2)
34-36: null 처리 로직 개선 필요title이 null이거나 빈 값일 때 url을 사용하고, url도 없으면 "Web result"를 사용하는 로직이 좋습니다. 하지만 url이 빈 문자열인 경우에도 docTitle로 사용될 수 있습니다.
String docTitle = (title != null && !title.isBlank()) - ? title : (url != null ? url : "Web result"); + ? title : (url != null && !url.isBlank() ? url : "Web result");
40-40: 메타데이터의 일관성 확인url이 null일 때 빈 문자열로 설정하는 것은 좋은 방어 코딩입니다. 하지만 docTitle과 metadata의 title이 다를 수 있습니다 (docTitle은 url이나 "Web result"가 될 수 있지만 metadata의 title은 항상 docTitle).
documents.add(new Document( docTitle, body, - Map.of("title", docTitle, "url", url == null ? "" : url) + Map.of("title", title != null ? title : "", "url", url == null ? "" : url) ));
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java(3 hunks)cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java(1 hunks)
🔇 Additional comments (1)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java (1)
56-59: JSON 파싱 시도 전 빈 문자열 검사가 중복됩니다
looksLikeJson메서드는 이미 내부에서trim()을 수행하므로, 빈 문자열은 JSON 형태가 아닌 것으로 판단됩니다. 위의 52-53 라인에서 이미 빈 문자열을 검사했으므로 이 부분은 항상 false가 되지 않습니다.
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java
Show resolved
Hide resolved
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java
Outdated
Show resolved
Hide resolved
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (7)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java (2)
198-220: URL 스킴 검증으로 잠재적 스니펫/자바스크립트 URL 필터링 권장javascript:, data: 등 비의도적 스킴을 간단히 배제하면 후속 RAG 파이프라인의 안전성이 좋아집니다.
예시:
String url = getTrimmed(obj, "url"); String title = getTrimmed(obj, "title"); String desc = getTrimmed(obj, "description"); // 세 필드 중 하나라도 있으면 결과로 채택 - if ((url != null && !url.isBlank()) || + boolean validUrl = url != null && !url.isBlank() + && (url.startsWith("http://") || url.startsWith("https://")); + if (validUrl || (title != null && !title.isBlank()) || (desc != null && !desc.isBlank())) { ObjectNode one = objectMapper.createObjectNode(); - if (url != null) { + if (validUrl) { one.put("url", url); }
57-78: 에러/엣지 케이스용 단위 테스트 추가 제안중첩 JSON, {results: [...]}, text 래핑, 레거시 Title/URL, 빈/노이즈 입력 등 케이스별 유닛 테스트가 있으면 회귀 방지에 유리합니다. 필요하시면 테스트 스캐폴딩을 제공하겠습니다.
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java (5)
29-31: text 폴백은 현재 경로에선 도달 불가에 가깝습니다MCP에서 text를 제거하고 description으로 표준화하므로, 이 분기는 방어적 코드 이상으로 쓰이지 않을 가능성이 큽니다. 유지 자체는 해가 없지만, 복잡도 축소 차원에서 제거하거나 주석으로 “디버그용 폴백”임을 명시해도 좋습니다.
34-36: HTML 제거 정규식은 간단/안전하지만 엔티티는 남습니다 — 선택적으로 디코딩 고려필요 시
&,<등 엔티티 디코딩까지 수행하면 가독성이 좋아집니다. 의존성 추가가 부담되면 소수 엔티티만 수동 치환해도 충분합니다.예시:
description = description .replaceAll("<[^>]+>", "") .replace("&", "&") .replace("<", "<") .replace(">", ">");
21-22: results가 배열이 아닐 때의 진단 로그 추가 제안입력 구조가 예상과 다를 때 조용히 무시되면 디버깅이 어렵습니다. warn 레벨로 한 줄 남기면 운용성이 좋아집니다.
예시:
- if (results != null && results.isArray()) { + if (results != null && results.isArray()) { results.forEach(r -> { // ... }); - } + } else { + // 예상 외 구조 진단용 + if (results != null) { + // 길이 제한하여 로그 + String preview = results.toString(); + if (preview.length() > 1000) preview = preview.substring(0, 1000) + "…"; + // 필요 시 debug로 낮추셔도 됩니다. + org.slf4j.LoggerFactory.getLogger(BraveSearchRagService.class) + .warn("BraveSearchRagService: results 배열이 아닙니다: {}", preview); + } + }
24-41: 사소한 정제: 트리밍으로 공백 노이즈 제거title/url/description에 앞뒤 공백이 포함될 수 있습니다. body 조합 전 간단히 trim하면 결과 품질이 개선됩니다.
예시:
- String title = r.path("title").asText(""); - String url = r.path("url").asText(""); - String description = r.path("description").asText(""); + String title = r.path("title").asText("").trim(); + String url = r.path("url").asText("").trim(); + String description = r.path("description").asText("").trim();
16-50: 유닛 테스트 보강 제안다음 케이스를 포함한 toDocuments 테스트를 제안합니다.
- 정상 케이스: url/title/description 조합 다양성
- description 공백 시 title 폴백
- HTML 포함/제거
- results가 배열이 아닌 경우 처리
- url만 존재하는 항목의 스킵/포함 정책 확인
필요하시면 테스트 템플릿을 제공하겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java(3 hunks)cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java(1 hunks)
🔇 Additional comments (3)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java (2)
165-169: 재귀 깊이 제한 추가 훌륭합니다중첩 문자열화 JSON 방어를 위해 최대 깊이 제한을 둔 점이 안정성 측면에서 적절합니다.
119-134: 정규화 파이프라인의 형태 다양성 처리 좋습니다배열/객체/문자열(JSON 포함)의 다양한 입력 형태를 흡수하여 단일 results로 수렴시키는 접근이 명확합니다.
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java (1)
24-27: 정규화된 필드(title/url/description) 우선 사용으로 일관성 확보BraveSearchMcpService의 표준화 결과와 정합적입니다. 과거 text 필드 의존 문제를 해소했습니다.
| JsonNode raw = objectMapper.valueToTree(result.content()); | ||
| log.info("[Brave MCP Response Raw content]: {}", raw); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
과도한 원본 응답 로그는 크기/민감도 리스크가 있습니다 — 로그 레벨 하향 및 트렁케이션 제안
대용량/다중 중첩 응답이 그대로 info 로그에 남습니다. 디버깅 구간에서만 필요하므로 debug 레벨과 길이 제한을 권장합니다.
아래와 같이 수정을 제안합니다.
- JsonNode raw = objectMapper.valueToTree(result.content());
- log.info("[Brave MCP Response Raw content]: {}", raw);
+ JsonNode raw = objectMapper.valueToTree(result.content());
+ log.debug("[Brave MCP Response Raw content]: {}",
+ truncate(raw != null ? raw.toString() : "null", 2000));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| JsonNode raw = objectMapper.valueToTree(result.content()); | |
| log.info("[Brave MCP Response Raw content]: {}", raw); | |
| JsonNode raw = objectMapper.valueToTree(result.content()); | |
| log.debug("[Brave MCP Response Raw content]: {}", | |
| truncate(raw != null ? raw.toString() : "null", 2000)); |
🤖 Prompt for AI Agents
In
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java
around lines 67-69, the code currently logs the full raw JsonNode at info level
which risks large and sensitive output; change this to emit at debug level and
truncate the serialized JSON to a safe max length (e.g. 1000 chars) to avoid
huge logs and leakage. Implement by checking logger.isDebugEnabled() before
serializing, convert the JsonNode to a string, truncate it to the configured max
length (append "…(truncated)" when shortened), and then call log.debug with the
truncated string instead of log.info; ensure no heavy work runs when debug
logging is disabled.
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java
Show resolved
Hide resolved
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java
Show resolved
Hide resolved
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java
Show resolved
Hide resolved
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java (1)
67-68: 원본 MCP 응답을 info로 전체 로그 → debug + 트렁케이션 권장대용량 또는 민감 정보가 포함될 수 있는 원본 응답을 info 레벨로 그대로 출력하면 로그 폭증/민감도 리스크가 큽니다. debug로 내리고 길이 제한을 두세요.
아래처럼 변경을 제안합니다.
- JsonNode raw = objectMapper.valueToTree(result.content()); - log.info("[Brave MCP Response Raw content]: {}", raw); + JsonNode raw = objectMapper.valueToTree(result.content()); + if (log.isDebugEnabled()) { + String rawStr = raw != null ? raw.toString() : "null"; + log.debug("[Brave MCP Response Raw content]: {}", truncate(rawStr, 2000)); + }
🧹 Nitpick comments (2)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java (1)
65-78: callTool 예외 전파 시 컨텍스트를 보강해 주세요 (선택사항)외부 호출 실패 시 상위로 예외를 그대로 전파하면 호출자 측 트러블슈팅이 어렵습니다. 로그에 query/count/offset 등 최소 컨텍스트를 포함하고 IllegalStateException으로 래핑해 재던지길 권장합니다. 위의 로그 레벨/트렁케이션 변경과 충돌을 피하려면 한 PR 안에서 순차 반영하세요.
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java (1)
29-39: text 필드의 JSON-fallback 처리 합리적이나 results 객체도 고려 가능현재는 text가 JSON 객체일 때 url/title/description만 추출합니다. 드물게 text가 {"results":[...]} 형태일 수 있으므로, 이 경우 첫 요소나 전체를 병합하는 전략을 추가하면 복원력이 더 좋아집니다. 필요 시 알려주시면 패치 드리겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java(3 hunks)cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java(2 hunks)
🔇 Additional comments (5)
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java (2)
90-112: 미초기화 감지 및 1회 재시도 로직은 타당합니다에러 메시지 기반 미초기화 판별, initialize 1회 재시도, 실패 시 다음 클라이언트로 진행하는 흐름이 합리적입니다. 로그 레벨과 메시지도 적절합니다.
122-137: 정규화 파이프라인 진입점 구조가 명확합니다any-shape JSON을 배열/단일 케이스로 분기해 normalizeOne으로 위임하는 방식이 간결하고 확장 가능한 형태입니다.
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java (3)
22-27: 표준 필드 우선 추출 흐름이 목적(결과 표준화)에 부합합니다url/title/description을 1차로 읽고 이후 단계에서만 보강하는 전략이 깔끔합니다.
55-64: Document id 우선순위(URL > title)와 body 구성 로직이 적절합니다중복/변동 가능성이 큰 title을 지양하고 URL을 우선 id로 사용하는 선택이 실무적으로 안정적입니다. description이 없을 때 title/url로 대체하는 폴백도 합리적입니다.
Also applies to: 65-72
80-83: 보조 헬퍼들의 방어적 구현이 적절합니다 (텍스트형만 취급, trim, JSON 힌트 검사 등)getText/isBlank/looksLikeJson의 보수적인 동작이 다운스트림 오류를 줄입니다. looksLikeJson은 과잉매칭 가능성이 있으나 현재 용도(폴백 트리거)에서는 수용 가능합니다.
Also applies to: 89-95
| private void normalizeOne(JsonNode node, ArrayNode out) { | ||
| if (node == null || node.isNull()) { | ||
| return; | ||
| } | ||
|
|
||
| // 이미 {url,title,description} 형태 | ||
| if (node.isObject() && (node.has("url") || node.has("title") || node.has("description"))) { | ||
| addObjectToResults((ObjectNode) node, out); | ||
| return; | ||
| } | ||
|
|
||
| // MCP가 주는 content item: { "type":"text", "text":"{...json...}" } 같은 형태를 방어 | ||
| if (node.isObject() && node.has("text")) { | ||
| String text = node.path("text").asText(""); | ||
| if (looksLikeJson(text)) { | ||
| parseJsonBlockIntoResults(text, out, 0); | ||
| } else { | ||
| // "Title: ..." 라인 포맷 등 레거시 텍스트 포맷 처리(옵션) | ||
| parseLegacyBlock(text, out); | ||
| } | ||
| return; | ||
| } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
normalizeOne가 객체 루트의 "results" 배열을 직접 처리하지 않습니다
현재는 문자열 JSON으로 들어온 경우(parseJsonBlockIntoResults)만 루트에 results 배열을 순회합니다. 실제로 raw가 이미 객체(JSON)인 경우에도 results 배열을 지원하도록 분기를 추가해야 누락이 없습니다.
아래와 같이 분기를 추가해 주세요.
private void normalizeOne(JsonNode node, ArrayNode out) {
if (node == null || node.isNull()) {
return;
}
// 이미 {url,title,description} 형태
if (node.isObject() && (node.has("url") || node.has("title") || node.has("description"))) {
addObjectToResults((ObjectNode) node, out);
return;
}
+ // 루트에 results 배열이 있는 객체 형태 처리
+ if (node.isObject() && node.has("results") && node.get("results").isArray()) {
+ for (JsonNode n : node.get("results")) {
+ normalizeOne(n, out);
+ }
+ return;
+ }
+
// MCP가 주는 content item: { "type":"text", "text":"{...json...}" } 같은 형태를 방어
if (node.isObject() && node.has("text")) {
String text = node.path("text").asText("");
if (looksLikeJson(text)) {
parseJsonBlockIntoResults(text, out, 0);
} else {
// "Title: ..." 라인 포맷 등 레거시 텍스트 포맷 처리(옵션)
parseLegacyBlock(text, out);
}
return;
}| for (String line : lines) { | ||
| String trimmed = line.trim(); | ||
| if (trimmed.regionMatches(true, 0, "Title:", 0, 6)) { | ||
| if (title != null && url != null && body.length() > 0) { | ||
| ObjectNode one = objectMapper.createObjectNode(); | ||
| one.put("title", title); | ||
| one.put("url", url); | ||
| one.put("description", body.toString().trim()); | ||
| out.add(one); | ||
| body.setLength(0); | ||
| } | ||
| title = trimmed.substring(6).trim(); | ||
| } else if (trimmed.regionMatches(true, 0, "URL:", 0, 4)) { | ||
| url = trimmed.substring(4).trim(); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
레거시 파서의 중간 flush 조건에서 본문(body) 유무를 요구하면 유효 결과가 누락됩니다
Title/URL만 있는 케이스도 결과로 인정해야 합니다. 중간 flush 시 body.length() > 0 조건을 제거하세요.
- if (trimmed.regionMatches(true, 0, "Title:", 0, 6)) {
- if (title != null && url != null && body.length() > 0) {
+ if (trimmed.regionMatches(true, 0, "Title:", 0, 6)) {
+ if (title != null && url != null) {
ObjectNode one = objectMapper.createObjectNode();
one.put("title", title);
one.put("url", url);
one.put("description", body.toString().trim());
out.add(one);
body.setLength(0);
}
title = trimmed.substring(6).trim();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (String line : lines) { | |
| String trimmed = line.trim(); | |
| if (trimmed.regionMatches(true, 0, "Title:", 0, 6)) { | |
| if (title != null && url != null && body.length() > 0) { | |
| ObjectNode one = objectMapper.createObjectNode(); | |
| one.put("title", title); | |
| one.put("url", url); | |
| one.put("description", body.toString().trim()); | |
| out.add(one); | |
| body.setLength(0); | |
| } | |
| title = trimmed.substring(6).trim(); | |
| } else if (trimmed.regionMatches(true, 0, "URL:", 0, 4)) { | |
| url = trimmed.substring(4).trim(); | |
| for (String line : lines) { | |
| String trimmed = line.trim(); | |
| if (trimmed.regionMatches(true, 0, "Title:", 0, 6)) { | |
| if (title != null && url != null) { | |
| ObjectNode one = objectMapper.createObjectNode(); | |
| one.put("title", title); | |
| one.put("url", url); | |
| one.put("description", body.toString().trim()); | |
| out.add(one); | |
| body.setLength(0); | |
| } | |
| title = trimmed.substring(6).trim(); | |
| } else if (trimmed.regionMatches(true, 0, "URL:", 0, 4)) { | |
| url = trimmed.substring(4).trim(); | |
| } | |
| // ... | |
| } |
🤖 Prompt for AI Agents
In
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchMcpService.java
around lines 246 to 259, the intermediate "flush" condition currently requires
body.length() > 0 which causes entries that have only Title and URL to be
dropped; remove the body.length() > 0 check so the block flushes whenever title
and url are present, and keep the existing behavior of creating the ObjectNode,
putting title/url/description (use body.toString().trim() which may be empty),
adding it to out, and resetting body with body.setLength(0).
| private final ObjectMapper objectMapper = new ObjectMapper(); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
ObjectMapper는 직접 생성 대신 주입받으세요
테스트 용이성/설정 일관성을 위해 Spring 컨테이너에서 주입받는 편이 바람직합니다. 현재 @requiredargsconstructor가 있으므로 필드 초기화만 제거하면 됩니다.
- private final ObjectMapper objectMapper = new ObjectMapper();
+ private final ObjectMapper objectMapper;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private final ObjectMapper objectMapper = new ObjectMapper(); | |
| - private final ObjectMapper objectMapper = new ObjectMapper(); | |
| + private final ObjectMapper objectMapper; |
🤖 Prompt for AI Agents
In
cs25-service/src/main/java/com/example/cs25service/domain/ai/service/BraveSearchRagService.java
around lines 17 to 18, the ObjectMapper is being instantiated directly which
hinders testability and central configuration; remove the new ObjectMapper()
initialization and rely on constructor injection (the class already uses
@RequiredArgsConstructor) by making the field final without an initializer so
Spring will inject the application-wide ObjectMapper bean.
🔎 작업 내용
callTool(...).content()에서 type=text의 text 값을 꺼내 JSON으로 재파싱
결과를 통일된 results: [ {url,title,description} ... ] 형태로 반환
Summary by CodeRabbit
New Features
Bug Fixes