사용자 도구

사이트 도구


springframework:springboot:json

문서의 이전 판입니다!


SpringBoot and JSON

날짜 시간 처리 Date Time Format

  • Java의 기존 java.util.Date와 신규 Java 8 시간에 대한 처리
  • java.util.Date는 기본적으로 Unix timestamp(long 값)으로 표현된다.

기본 설정

  • 의존성에 다음 추가해야 JSR310 날짜/시간 지원됨.
    compile('com.fasterxml.jackson.datatype:jackson-datatype-jsr310')
    compile('com.fasterxml.jackson.datatype:jackson-datatype-jdk8')
  • 예제 컨트롤러
    @GetMapping("/dateTest")
    public MyClock dateTest2() {
        return new MyClock();
    }
     
    @Getter
    public static class MyClock {
        private Date date;
        private LocalDateTime localDateTime;
        private LocalDate localDate;
        private LocalTime localTime;
        private OffsetDateTime offsetDateTime;
        private ZonedDateTime zonedDateTime;
     
        public MyClock() {
            Instant instant = Instant.now();
            date = Date.from(instant);
            localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
            localTime = localDateTime.toLocalTime();
            localDate = localDateTime.toLocalDate();
            offsetDateTime = OffsetDateTime.ofInstant(instant, ZoneId.systemDefault());
            zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault());
        }
    }

WRITE_DATES_AS_TIMESTAMPS=false 사용

  • 제일 좋은 것은 처음부터 WRITE_DATES_AS_TIMESTAMPS=false 상태로 운영하는 것이다. application.yml
    spring.jackson.serialization.write-dates-as-timestamps: false
  • 이렇게 하면 java.util.Date,java.time.* 모두 ISO 혹은 그 유사 포맷으로 직렬화된다.
    {  
       "date":"2018-07-18T06:16:33.647+0000", ## java.util.Date가 약간 다르게 출력됨.
       "localDateTime":"2018-07-18T15:16:33.647",
       "localDate":"2018-07-18",
       "localTime":"15:16:33.647",
       "offsetDateTime":"2018-07-18T15:16:33.647+09:00",
       "zonedDateTime":"2018-07-18T15:16:33.647+09:00"
    }
  • 그러나 WRITE_DATES_AS_TIMESTAMPS 옵션을 변경할 경우 기존에 작동하던 java.util.Date의 포맷이 변경되므로 프로젝트가 이미 운영중일 때는 java.util.Date를 받는 모든 부분에 대해 기존 unix timestamp 받던것을 올바로 처리하게 변경해야만 한다.

모든 타입에 대해 일관성 있는 커스텀 형식 지원

  • 만약, 모든 날짜 객체에 대해 일관성있는 포맷을 지정하고자 한다면(ISO 포맷) 다음과 같이 한다.
    @SpringBootApplication
    public class DemoApplication implements Jackson2ObjectMapperBuilderCustomizer {
     
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
     
        @Override
        public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
            LocalDateTimeSerializer localDateTimeSerializer = new LocalDateTimeSerializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            LocalDateSerializer localDateSerializer = new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE);
            LocalTimeSerializer localTimeSerializer = new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME);
     
            OffsetDateTimeSerializer offsetDateTimeSerializer = new CustomOffsetDateTimeSerializer();
            ZonedDateTimeSerializer zonedDateTimeSerializer = new CustomZonedDateTimeSerializer();
     
            jacksonObjectMapperBuilder
                    .simpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
                    .serializerByType(LocalDateTime.class, localDateTimeSerializer)
                    .serializerByType(LocalDate.class, localDateSerializer)
                    .serializerByType(LocalTime.class, localTimeSerializer)
                    .serializerByType(OffsetDateTime.class, offsetDateTimeSerializer)
                    .serializerByType(ZonedDateTime.class, zonedDateTimeSerializer);
        }
     
        public static class CustomOffsetDateTimeSerializer extends OffsetDateTimeSerializer {
            protected CustomOffsetDateTimeSerializer() {
                super(OffsetDateTimeSerializer.INSTANCE, false, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
            }
        }
     
        public static class CustomZonedDateTimeSerializer extends ZonedDateTimeSerializer {
            public CustomZonedDateTimeSerializer() {
                // ISO_OFFSET_DATE_TIME 로 바꾸면 OffsetDateTime과 동일하게 출력됨.
                super(ZonedDateTimeSerializer.INSTANCE, false, DateTimeFormatter.ISO_DATE_TIME, true);
            }
        }
    }
  • 위의 경우 출력이 모두 일관성있게 나온다.
    {  
       "date":"2018-07-18T15:11:49.693+09:00",
       "localDateTime":"2018-07-18T15:11:49.693",
       "localDate":"2018-07-18",
       "localTime":"15:11:49.693",
       "offsetDateTime":"2018-07-18T15:11:49.693+09:00",
       "zonedDateTime":"2018-07-18T15:11:49.693+09:00[Asia/Seoul]"
    }

@EnableWebMvc

@EnableWebMvcWebMvcConfigurerAdapter를 사용하는 순간 더이상 SpringBoot가 아니고 Spring 이기 때문에 위의 설정이 먹지 않게 된다. Spring Boot Issue 2116

이때는 WebMvcConfigurerAdapter#configureMessageConverters를 override 해야한다.

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
       super.configureMessageConverters(converters);
       converters.add(new MappingJackson2HttpMessageConverter(jackson2ObjectMapperBuilder().build()));
    }
 
    @Bean
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
       return new Jackson2ObjectMapperBuilder()
               .failOnUnknownProperties(false) // SpringBoot default
               .featuresToDisable(MapperFeature.DEFAULT_VIEW_INCLUSION) // SpringBoot default
               .featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // SpringBoot default
               .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
               .serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_DATE));
    }
}

Controller Get Parameter 에 대한 포맷지원

이것은 Jackson JSON 직렬화와는 다른 문제이므로 포맷을 지정해야한다.

@GetMapping("/test")
public Result test(@RequestParam("datetime") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime datetime) {
    // ...
}
// 파라미터를 ?datetime=2018-07-11T20:22:55.123 형태로 호출

기타 참조

springframework/springboot/json.1531901080.txt.gz · 마지막으로 수정됨: 2018/07/18 17:04 저자 kwon37xi