프로그래밍/Spring

[Spring] ObjectMapper - LocalDateTime 직렬화 문제

개인 프로젝트 과제를 진행 중에, fillter에서 인증 실패 시, response에 에러메세지를 전달하기 위해

ObjectMapper를 이용하던 중 문제가 발생했습니다.

 


LocalDateTime 직렬화 문제

ErrorResponse 객체에 들어있는 데이터중 LocaldateTime.now()로 문제가 발생한 시간 데이터가 있습니다.

해당 response를 ObjectMapper로 직렬화를 하여 반환할 때, 다음과 같은 예외가 발생합니다.

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.sparta.todo.dto.response.ErrorResponse["timestamp"])
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.15.3.jar:2.15.3]
	at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1308) ~[jackson-databind-2.15.3.jar:2.15.3]
	at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35) ~[jackson-databind-2.15.3.jar:2.15.3]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:732) ~[jackson-databind-2.15.3.jar:2.15.3]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:772) ~[jackson-databind-2.15.3.jar:2.15.3]

 

메시지를 직역하면 다음과 같습니다.

Java 8의 날짜/시간 유형은 기본적으로 지원되지 않습니다.

이를 처리하도록 사용하려면 com.fastxml.jackson.datatype:jsr310 모듈을 추가해야 합니다.

 

하지만 SpringBoot starter 모듈에는 이미 해당 라이브러리가 존재합니다.

 

라이브러리가 존재하지만 예외가 발생한 이유는 다음과 같습니다. 

 

ObjectMapper는 직렬화 및 역직렬화 작업을 수행하는 다양한 메서드를 제공합니다.

다만 컬렉션 타입, 날짜/시간 타입과 같은 복잡한 데이터 유형을 적절하게 처리하기 위해서는 사용자 정의 모듈을 추가해야합니다.

 

결국 복잡한 데이터 유형을 적절하게 처리하기 위해서는 사용자 정의 모듈을 추가해야 함을 알 수 있습니다. 날짜/시간 타입의 데이터를 처리하기 위해 추가해야 하는 모듈은 JavaTimeModule입니다.

 

JavaTimeModule


JavaTimeModule은 Java 8에 도입된 새로운 날짜 및 시간 API(LocalDate, LocalTime, LocalDateTime)를 Jackson 라이브러리에서 적절하게 처리할 수 있게 해주는 모듈입니다. 기본적으로 Jackson 라이브러리는 Java 8의 새로운 날짜 및 시간 타입들을 인식하지 못하기 때문에 해당 타입들을 JSON으로 직렬화하거나 JSON에서 역직렬화할 때 문제가 발생할 수 있습니다.

 

이러한 문제를 해결하기 위해 JavaTimeModule을 ObjectMapper에 등록하면 날짜/시간 타입들을 적절하게 직렬화하고 역직렬화할 수 있게 됩니다.

 

왜 등록되어있지 않을까?

기본 설정으로 Java 8의 날짜/시간 타입을 정확히 처리하지 못하는 이유는 해당 타입들은 표준 Java 라이브러리 일부가 아니라 Java 8에 처음 도입된 JSR 310 날짜/시간 API의 일부이기 때문입니다. 따라서 ObjectMapper에 해당 모듈 등록이 필요합니다.

 

 

해결


해결방법은 아래와 같이 objectMapper.registerModule에  JavaTimeModule을 세팅해주면 됩니다. 

 

또는 ObjectMapper를 Bean으로 등록하여 사용하시면 됩니다.

  @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        return objectMapper;
    }

LocalDateTime 배열로 직렬화 되는 문제


JavaTimeModule을 세팅하여 직렬화를하니 날짜는 잘 나오는데, 다음과 같은 문제가 발생합니다.

 

문자열이 아닌 배열로 처리가 됩니다.

기본조건으로 java8에서 등장한 LocalDateTime, LocalDate, LocalTime과 같은 객체들을 위와같이 직렬화하게 되어있습니다.

저에게는 년,월,일 시간,분 처럼 분리할 필요없이 하나의 문자열이 필요합니다.

 

해결


해결방법은 다음과 같습니다.

1. objectMapper disalbe 메소드에 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 세팅하기

objectMapper
        .registerModule(new JavaTimeModule())
        .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)

 

2. application.yaml 또는 application.properties에 설정하기

spring:
  jackson:
    serialization:
      WRITE_DATES_AS_TIMESTAMPS: false

 

설정해주면 문자열로 잘 반환하게 됩니다.

 

728x90