스프링 부트 날짜 타입을 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 설정
-
하짜증 2021.08.19 17:29
저의 책 속의 선생님...
이 글이 2018년도 글인데, 현 시점의 스프링부트 버전에서 이것보다 더욱 좋아진 방법이 있을까요?
-
DEPENDENCY GOD 2022.06.25 15:38
springboot 2+ mybatis 환경에서 쿼리에서 resultType을 Vo로 받고 mapper Interface 실행 방식으로 조회시 시간이 한국시간으로 조회되는데
추가로 요구사항이 조회 방식을 sqlSessionTemplate를 사용하고 쿼리 resultType도 Map객체로 받아서 Vo를 사용하지 않는 방식으로 조회 중인데
이 방식으로 조회시에는 오라클 디비의 Date 시간과 다르게 화면에 내려가면 utc 시간으로 받아 지네요. controller 리턴 전까지도 디비의 한국시간인데
화면에만 가면 utc 시간으로 9시간 느린 시간으로 나옵니다. 이럴 경우 어떤 설정을 살펴야 해결 될까요?