[2024-11-22 15:47:36:218069] ERROR 69254 --- [nio-8080-exec-4] [0:0:0:0:0:0:0:1] c.d.s.c.e.GlobalExceptionHandler : handleException org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize value of type java.util.ArrayList<com.test.project.registrationstatement.dto.response.ExtractIndexResponseDto$ContentText> from String value (token JsonToken.VALUE_STRING) at org.springframework.http.codec.json.AbstractJackson2Decoder.processException(AbstractJackson2Decoder.java:275) Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s): *__checkpoint ⇢ Body from POST http://localhost:8000/extract/index [DefaultClientResponse] Original Stack Trace: at .. 생략
응답 데이터에서 reference_files > content > text가 일부는 리스트 형태이고 일부는 문자열로 오는 경우, 이를 처리하려면 Jackson 또는 데이터 파싱 로직에서 두 가지 타입을 모두 허용하도록 유연하게 구현해야 합니다. 아래와 같은 방안을 적용할 수 있습니다.
1. Jackson의 @JsonDeserialize를 활용하여 유연한 타입 처리
- ExtractIndexResponseDto.ContentText의 text 필드에 커스텀 디시리얼라이저를 적용해, JSON이 문자열 또는 리스트일 때 모두 처리하도록 작성합니다.
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ContentText {
private int page;
@JsonDeserialize(using = TextDeserializer.class)
private List<String> text;
}
// Custom Deserializer
public static class TextDeserializer extends JsonDeserializer<List<String>> {
@Override
public List<String> deserialize(JsonParser parser, DeserializationContext context) throws IOException {
ObjectCodec codec = parser.getCodec();
JsonNode node = codec.readTree(parser);
if (node.isArray()) {
// JSON이 배열인 경우
List<String> result = new ArrayList<>();
for (JsonNode element : node) {
result.add(element.asText());
}
return result;
} else if (node.isTextual()) {
// JSON이 문자열인 경우
return Collections.singletonList(node.asText());
} else {
throw new JsonMappingException(parser, "Unexpected JSON type for 'text'. Expected array or string.");
}
}
}
2. Jackson의 Object 타입 활용 후 데이터 타입에 따라 처리
text 필드를 Object로 선언하고, 런타임 시 타입을 체크하여 변환합니다.
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ContentText {
private int page;
private Object text;
public List<String> getTextAsList() {
if (text instanceof String) {
return Collections.singletonList((String) text);
} else if (text instanceof List) {
return (List<String>) text;
} else {
throw new IllegalArgumentException("Unsupported type for 'text'");
}
}
}
getTextAsList 메서드는 사용 시점에서 타입 변환을 수행합니다. 데이터를 사용하는 곳에서 getTextAsList()를 호출해 리스트로 변환된 데이터를 안전하게 사용할 수 있습니다.
3. WebClient 레벨에서 데이터 변환
Spring WebFlux의 WebClient에서 JSON 데이터를 수동으로 파싱하여 유연하게 처리할 수도 있습니다.
ExtractIndexResponseDto response = webClient.post()
.uri(extractIndexUrl)
.body(BodyInserters.fromMultipartData(bodyBuilder.build()))
.retrieve()
.bodyToMono(String.class) // JSON을 문자열로 먼저 받기
.map(json -> {
try {
ObjectMapper mapper = new ObjectMapper();
// String을 ArrayList<ExtractIndexResponseDto>로 변환
ExtractIndexResponseDto result = mapper.readValue(json, ExtractIndexResponseDto.class);
return result;
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON Parsing Error", e);
}
})
.block();
4. API 변경 요청
가능하다면, API 개발자에게 명확한 데이터 타입을 사용하도록 요청하는 것이 가장 이상적인 해결책입니다. 응답 데이터에서 text 필드가 항상 리스트로 오도록 통일하면 처리 로직이 간단해집니다.