스프링 부트 날짜 타입을 JSON으로 응답할 때 별도 설정을 하지 않으면 부트 버전에 따라 응답 형식이 다르다. 먼저 간다한 테스트를 위해 다음과 같이 세 개의 날짜 형식을 갖는 Now 클래스를 사용하자.
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Date;
public class Now {
private LocalDateTime localTime;
private OffsetDateTime offsetTime;
private Date date;
public Now() {
localTime = LocalDateTime.now();
offsetTime = OffsetDateTime.now();
date = new Date();
}
...getter 생략
}
Now를 생성해서 JSON으로 응답하는 컨트롤러를 다음과 같이 작성했다.
@RestController
public class SampleController {
@GetMapping("/now")
public Now time() {
return new Now();
}
}
이 글에서 사용한 코드는 https://github.com/madvirus/boot-jackson 리포지토리에서 참고할 수 있다.
부트 2.0에서 기본 JSON 시간 타입 포맷팅
부트 2.0으로 테스트하면 응답 결과가 다음과 같다. 각 타입을 ISO-8601 형식으로 출력하고 있다.
{
"localTime": "2018-03-01T17:03:50.445428",
"offsetTime": "2018-03-01T17:03:50.445428+09:00",
"date": "2018-03-01T08:03:50.445+0000"
}
시간대 정보가 있는 OffsetDateTime 타입은 "+09:00"이 뒤에 붙어 있다. 반면에 Date 타입은 UTC 기준 시간을 사용했다. 또 다른 차이는 OffsetDateTime과 Date의 시간대 표시가 다르다는 것이다. OffsetDateTime 타입은 "+09:00"와 같이 콜론이 포함되어 있고 Date 타입은 "+0000"과 같이 콜론이 없다. LocalDateTime의 경우 오프셋 정보가 없으므로 시간대 부분이 없다.
부트 1.5에서 기본 JSON 시간 타입 포맷팅
부트 1.5에서 테스하면 응답 결과가 다음과 같다. 아주 난리다!
{
"localTime": {
"month": "MARCH",
"dayOfWeek": "THURSDAY",
"dayOfYear": 67,
"nano": 644321400,
"year": 2018,
"monthValue": 3,
"dayOfMonth": 1,
"hour": 17,
"minute": 28,
"second": 2,
"chronology": {
"id": "ISO",
"calendarType": "iso8601"
}
},
"offsetTime": {
"offset": {
"totalSeconds": 32400,
"id": "+09:00",
"rules": {
"fixedOffset": true,
"transitions": [],
"transitionRules": []
}
},
"month": "MARCH",
"dayOfWeek": "THURSDAY",
"dayOfYear": 67,
"nano": 644321400,
"year": 2018,
"monthValue": 3,
"dayOfMonth": 1,
"hour": 17,
"minute": 28,
"second": 2
},
"date": 1519894518644
}
부트 1.5에서 jackson-datatype-jsr310 모듈 추가
부트 1.5에 아래와 같이 jackson-datatype-jsr310 모듈을 추가해보자. 이 모듈은 LocalDateTime이나 OffsetDateTime과 같이 자바 8의 시간 타입을 지원하는 모듈이다.
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
이 모듈을 추가한 뒤에 JSON 생성 결과를 보면 다음과 같다.
{
"localTime": [
2018,
3,
8,
21,
16,
30,
166225000
],
"offsetTime": 1520511390.166383,
"date": 1520511390166
}
부트 1.5 Date 포맷: WRITE_DATES_AS_TIMESTAMPS 비활성, StdFormat 사용
부트 1.5에서 아래 프로퍼티를 application.properties 파일에 추가하면 Date 타입을 ISO-8601 포맷을 사용해서 변환한다.
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
다음 프로퍼티를 설정해도 결과가 같다.
spring.jackson.date-format=com.fasterxml.jackson.databind.util.StdDateFormat
실제 출력 결과는 다음과 같다. UTC 기준으로 출력하고 있다.
"date": "2018-03-08T08:51:53.972+0000"
부트 1.5, 2.0: Jackson2ObjectMapperBuilderCustomizer로 포맷팅 설정
부트는 Jackson2ObjectMapperBuilderCustomizer 인터페이스를 제공하는데 이 인터페이스를 구현한 클래스를 빈으로 등록하면 변환 포맷을 설정할 수 있다. 다음 코드는 사용 예이다.
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.OffsetDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
@SpringBootApplication
public class Boot15JacksonApplication implements Jackson2ObjectMapperBuilderCustomizer {
public static void main(String[] args) {
SpringApplication.run(Boot15JacksonApplication.class, args);
}
// Jackson2ObjectMapperBuilderCustomizer 인터페이스 메서드
@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
// LocalDateTime은 오프셋 정보가 없으므로 패턴에 시간대에 해당하는 Z가 없다.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
LocalDateTimeSerializer localSerializer = new LocalDateTimeSerializer(formatter);
DateTimeFormatter formatter2 =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
CustomOffsetDateTimeSerializer offsetSerializer =
new CustomOffsetDateTimeSerializer(formatter2);
builder
.simpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.serializerByType(LocalDateTime.class, localSerializer)
.serializerByType(OffsetDateTime.class, offsetSerializer)
;
}
public class CustomOffsetDateTimeSerializer extends OffsetDateTimeSerializer {
public CustomOffsetDateTimeSerializer(DateTimeFormatter formatter) {
super(OffsetDateTimeSerializer.INSTANCE, false, formatter);
}
}
}
- simpleDateFormat: Date 타입을 위한 변환 포맷 설정
- serializerByType: 타입을 위한 Jackson의 Serializer 설정