🚨 문제 상황
11:39:38.226 WARN o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolved [kr.co.logosai.assistant.api.common.exception.CommonException: LLM 서비스 호출 중 오류가 발생했습니다.]
11:39:39.913 ERROR k.c.l.a.a.c.service.LlmChatService - 🚨[ERROR] LLM error ← duration: 14300ms, url: <http://101.79.10.38:8000/api/v1/rag/query>, botUid: 66, room: web-a804bcad-be30-4dde-8025-2f0cfbe822ea
org.springframework.web.client.RestClientException: Error while extracting response for type [kr.co.logosai.assistant.api.chat.service.bo.ChatBo$LlmResponse] and content type [application/json]
...
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Illegal unquoted character ((CTRL-CHAR, code 13)): has to be escaped using backslash to be included in string value
...
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Illegal unquoted character ((CTRL-CHAR, code 13)): has to be escaped using backslash to be included in string value
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 13051] (through reference chain: kr.co.logosai.assistant.api.chat.service.bo.ChatBo$LlmResponse["content_origin"]->kr.co.logosai.assistant.api.chat.service.bo.ChatBo$RAGJsonResponse["response"]->java.util.ArrayList[30]->kr.co.logosai.assistant.api.chat.service.bo.ChatBo$ContentItem["data"])
...
Caused by: com.fasterxml.jackson.core.JsonParseException: Illegal unquoted character ((CTRL-CHAR, code 13)): has to be escaped using backslash to be included in string value
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 13051]
...
11:39:39.934 ERROR k.c.l.a.a.c.r.ApiExceptionAdvice - [500] E00003 - LLM 서비스 호출 중 오류가 발생했습니다.
kr.co.logosai.assistant.api.common.exception.CommonException: LLM 서비스 호출 중 오류가 발생했습니다.
rBase.java:52)
JsonParseException
RestClient가 데이터를 스트리밍으로 끝까지 다 받았는데, RAG API가 보낸 JSON 자체에 ‘깨진 데이터(비표준 문자)‘가 포함되어 있어서 Jackson 파서가 객체로 변환하다 실패하는 상황.
로그 분석
> Caused by: com.fasterxml.jackson.core.JsonParseException: Illegal unquoted character ((CTRL-CHAR, code 13)): has to be escaped using backslash...
>
>
> at [Source: ... line: 1, column: 13051]
>RAG API가 보낸 JSON 데이터의 13,051번째 글자쯤에, 이스케이프(\\) 처리되지 않은 ‘제어 문자(Code 13, 캐리지 리턴 \\r)‘가 포함되어 있다는 뜻
💡
\\r은 **눈에 보이지 않는 ‘제어 문자(Control Character)‘**입니다.
‘a’, ‘b’, ‘c’처럼 화면에 ‘인쇄(print)‘되는 문자가 아니라, “커서(cursor)야, 이제 동작해!”라고 명령을 내리는 문자입니다.
\\r의 정확한 명령은 “커서를 현재 줄의 맨 앞으로 이동시켜라”입니다. (== Carriage Return)
✅ 해결 방법
코드 수정
RestClientConfig.java
@Bean
@Qualifier("longTimeoutRestClient")
public RestClient longTimeoutRestClient(ObjectMapper objectMapper) {
JdkClientHttpRequestFactory requestFactory = createRequestFactory(Duration.ofMinutes(5));
// 기본 ObjectMapper를 복사해서 ALLOW_UNESCAPED_CONTROL_CHARS 설정 추가
ObjectMapper relaxedObjectMapper = objectMapper.copy();
relaxedObjectMapper.getFactory().enable(ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature());
// 위 ObjectMapper를 사용하는 HttpMessageConverter 생성
MappingJackson2HttpMessageConverter relaxedConverter = new MappingJackson2HttpMessageConverter(relaxedObjectMapper);
return RestClient.builder()
.requestFactory(requestFactory)
.messageConverters(converters -> {
converters.clear(); // 새로 만든 컨버터만 사용하도록 설정
converters.add(relaxedConverter);
})
.build();
}longTimeoutRestClientBean을 생성할 때,ALLOW_UNESCAPED_CONTROL_CHARS옵션을 가진ObjectMapper를 직접 주입해 줌