최근 공공 API를 이용해 데이터를 수집하는 어플리케이션을 개발했고, 테스트 후 실제 데이터를 처리하기 위해 어플리케이션을 가동 했다.
특별한 문제 없이 잘 동작할 거라고 생각했지만 이상한 에러를 발견했고 (사실 이 전에도 숫자 타입인데 문자열이 튀어나와서 오류가 발생하는 등의 자질구레한 문제가 있었다.)
원인을 파악 해 봤는데....
원래 아래와 같이 item 필드는 배열로 결과가 나와야 한다.
{"Response": {
"head": {
"pageNo": "1",
"resultCode": "0",
"totalCount": "17",
"numOfRows": "1000",
"resultMsg": "NORMAL_SERVICE"
},
"items": {"item": [
{
"tong": "",
"male50AgeNmprCnt": "735632",
"feml40AgeNmprCnt": "734484",
"male80AgeNmprCnt": "128005",
...
},
{...},
...
]}
}}
그런데 결과가 1건인 경우 배열로 나오지 않고 그냥 Object 형태로 제공 하고 있었다.
{
"Response": {
"head": {
"pageNo": "1",
"resultCode": "0",
"totalCount": "1",
...
},
"items": {
"item": {
"tong": "11",
"male50AgeNmprCnt": "4",
"feml40AgeNmprCnt": "3",
"male80AgeNmprCnt": "0",
"male20AgeNmprCnt": "0",
...
}
}
}
}
왜 저렇게 결과를 제공하는지 잘 모르겠지만 API를 고쳐달라고 할 수 없으니 해결 방법을 찾아봤다.
다행히 ObjectMapper 에는 직렬화나 역직렬화(Serialization, Deserialization) 작업시 다양한 옵션을 적용할 수 있었다.
옵션을 적용하는 방법은 enable() 함수에 enum 타입의 SerializationFeature 또는 DeserializationFeature를 전달하여 적용한다.
ObjectMapper om = new ObjectMapper();
om.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
위에서 적용한 DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY 를 적용하면 단일 값을 배열로 처리가 가능해져서 맨 위에서 발생했던 오류를 해결 할 수 있다.
(해당 기능 말고도 더 다양한 기능들이 있으니 SerializationFeature, DeserializationFeature를 참고 하자)
그런데 API 호출을 OpenFeign을 이용하다보니 ObjectMapper를 사용하지 않아 위의 해결 방법을 사용 할 수 없었다.
그래서 다른 방법을 찾던중 @JsonFormat 어노테이션의 with 속성을 이용해서 위 기능을 구현 할 수 있는 것을 발견했다. with 속성은 enum 타입의 JsonFormat.Feature를 매개변수로 받으며, 사용법은 아래와 같다
@Getter
@Setter
public static class Response {
private Head head;
private Items items;
public Response() {
}
public Response(Head head, Items items) {
this.head = head;
this.items = items;
}
}
@Getter
@Setter
@NoArgsConstructor
public static class Items {
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
private List<Item> item;
}
ObjectMapper 에 기능을 적용하면 모든 필드에 적용 되지만 어노테이션을 이용하면 문제가 될만한 특정 필드에만 해당 기능을 적용 할 수 있게 되어 예상치 못한 동작을 방지 할 수 있을 것 이라고 생각 한다.
'Java' 카테고리의 다른 글
JUnit5 테스트 순서 지정하기 (0) | 2023.12.02 |
---|---|
java.time.format.DateTimeParseException (...could not be parsed: Unable to obtain LocalDate from TemporalAccessor) (1) | 2023.11.25 |
(spring boot 3.1 이상) 간단한 설정(@ServiceConnection)으로 testcontainer 사용하기 (0) | 2023.10.16 |
Java - Srping 시작할 때 동작하는 코드 작성하는 법 (0) | 2023.09.23 |
Java - LocalDateTime (LocalDate, LocalTime), ZonedDateTime, Period, Duration (0) | 2023.09.17 |