요약
- ObjectMapper의 PropertyNamingStrategy SNAKE_CASE로 설정한 뒤 record 클래스의 역직렬화를 시도했을 때 해당 예외가 발생
- 이 외에도
@JaonNaming
등 NamingStrategy를 변경하고 record 클래스를 역직렬화하는 경우에 해당 예외가 발생 - 해당 문제는 Jackson 라이브러리가 record 클래스를 지원하기 시작한 2.12 버전부터 해당 문제가 수정되기 이전인 2.15 버전 사이에 발생할 수 있음
예외 발생 상황
최근 실무를 접하면서 새로운 로직을 개발할 일이 있었습니다. 원격 저장소의 json 파일을 읽어들여 프로덕트 DB에 적재하는 일이었습니다. 해당 파일의 json 필드들은 스네이크 케이스(field_name)로 되어 있었고, bean으로 동록되어 있는 objectMapper를 그대로 쓸 수는 없었고 네이밍 전략을 수정해야 했습니다. 예를 들면 다음과 같이요.
public class SomethingJsonImporter implements SomethingImporter {
private final ObjectMapper mapper;
public SomethingJsonImporter(final ObjectMapper mapper) {
this.mapper = mapper.copy()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
}
}
문제는 record 클래스를 이용해 역직렬화를 시도하면서 발생했습니다.
예외 내용
com.fasterxml.jackson.databind.JsonMappingException: Can not set final {필드 타입} field {클래스명.필드명} to {필드 타입}
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:274)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:623)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:611)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty._throwAsIOE(SettableBeanProperty.java:634)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.set(FieldProperty.java:193)
...
프로젝트에서 사용하고 있던 Jackson 라이브러리 버전은 record 클래스를 지원하기 시작한 2.12 이후 버전이었습니다. 또한, 이미 record 클래스를 여기저기서 사용하고 있었기 때문에 이런 오류가 발생했다면 네이밍 전략을 변경으로 인한 것이라 추측할 수 있었습니다.
한국어로 된 자료를 찾지 못해 잠시 헤맸는데 다행히 영어로 검색하니 jackson-databind 깃허브 리포지토리에 이슈가 올라와있었습니다.
해결
선택할 수 있는 해결법은 세 가지입니다.
- 해당 이슈가 수정된 버전인 2.15 버전으로 jackson 라이브러리 버전을 업데이트 하기
@JsonProperty
를 사용하여서 필드 하나하나마다 스네이크 케이스로 이름을 지정해주기(예.@JsonProperty("test_field")
)- record를 사용하지 않고 class를 사용하기
제가 선택한 방법은 3번이었습니다.
1번은 프로젝트의 다른 의존성들과 호환성을 체크해야 하는 문제가 있었습니다. 변경으로 인해 어떤 문제가 터질지 알 수 없고, 해당 문제를 해결하기 위해 투자할 수 있는 시간은 충분치 않았습니다.
2번은 개발자의 실수가 발생할 여지가 있었습니다. 이것만으로도 충분히 위험한데 수십개의 필드에 일일이 @JsonProperty
를 붙여주는 것도 넌센스였습니다.
3번을 선택한 이유는 인텔리제이의 도움을 받아 record를 class로 컨버팅해주는 기능을 지원해주기도 하고, 둘의 모양새만 다를 뿐 동일하게 불변 데이터를 전달하는 클래스로 사용할 수 있기 때문이었습니다.
끝으로
이 문제는 개인 프로젝트를 진행한다면 왠만해서는 만날일이 없는 문제이지 않나 생각합니다. 담당하고 있는 프로젝트에서
- Jackson 라이브러리의 버전이 2.12 ~ 2.14 사이의 버전이었던 점
- 읽어와야 할 json 데이터 필드가 스네이크 케이스로 되어있던 점
두 가지가 우연히 겹쳐서 발생한 문제입니다. 실무가 아니면 만나볼 수 없는 문제였고, 고민이었기에 인상적인 경험이었습니다.
'백엔드' 카테고리의 다른 글
[우아한 티켓팅] 대기열 시스템 응답 시간 스파이크 해결하기 (4) | 2024.10.06 |
---|---|
[우아한 티켓팅] 대기열 시스템 10,000명 부하 테스트하기 (4) | 2024.09.29 |
직접 구현한 스텁으로 이상적인 테스트 작성하기 (0) | 2024.08.05 |
테스트 더블, 목과 스텁 이야기 (0) | 2024.07.08 |
테스트 더블과 mock 객체 사용 시 주의점 (1) | 2024.01.27 |