Spring에서 날짜 처리하기

Lechros·2024년 6월 7일

Spring에서 날짜/시간을 처리하며 배운 점을 정리해 보았다.

표준 시간대

어떤 표준 시간대를 기준으로 요청을 처리해야 할까? 개인적인 결론은 API 요청/응답, 프로그램 로직, DB 저장까지 UTC 기준으로 처리하는 것이다. 항상 UTC로 전달하고 사용자에게 표시하는 순간에 클라이언트가 적절하게 표현해주는 것이 가장 편리했다. Open API인 경우 시간대 정보를 함께 받고 싶을 수도 있는데 이 부분은 다음 글에서 GitHub REST API의 표준 시간대를 구현해 보겠다.

DB에 저장하기

JDK 8 버전의 JDBC에서는 날짜/시간 타입이 다음과 같이 연결된다고 한다.

DATE - LocalDate
TIME - LocalTime
TIMESTAMP WITHOUT TIME ZONE - LocalDateTime
TIMESTAMP WITH TIME ZONE - OffsetDateTime

그러므로 필요에 따라 Local* 친구들을 쓰는 것도 좋겠다.

API에서 날짜/시간 전달받기

RequestParam, PathVariable, ModelAttribute

RequestBody를 제외한 @RequestParam, @PathVariable, @ModelAttribute에는 Spring의 Converter 인터페이스 구현체가 적용된다.
String -> LocalDateTimeJsr310Converters.StringToLocalDateTimeConverter이 사용되고, LocalDateTime -> String은 별도의 구현체가 없다.
Converter<String, LocalDateTime>을 구현하는 클래스를 등록해서 변환 방식을 설정할 수 있다.

RequestBody

RequestBody의 직렬화/역직렬화에는 Jackson이 사용된다. 예전에는 따로 설정해줘야 했는데 3.2.5 버전에서 테스트해보니 DTO 내의 LocalDateTime 속성도 잘 역직렬화/직렬화 된다. (만약 배열 형태로 응답한다면 설정의 spring.jackson.serialization.write-dates-as-timestamps: false로 설정해주자)

"2024-06-01T13:30:50"

시간대 정보가 포함되지 않으니 JS에서 파싱할 때 UTC임을 명시해줘야 한다. 필드에 @JsonFormat 어노테이션을 붙여서 설정할 수 있다.

@JsonFormat(
	shape = JsonFormat.Shape.STRING,
	pattern = "yyyy-MM-dd HH:mm:ss 'HELLO'")
private LocalDateTime jsonFormat; // -> "2024-06-01 13:30:50 HELLO"

그런데 필드마다 이걸 붙이려니 정말 고생길이 훤하다. 다행히 Configuration에서 한 번에 설정할 수도 있다. (스프링 부트에서는 AutoConfigure를 망칠 수 있으므로 ObjectMapper 빈을 등록하지 말고 커스터마이저를 사용하자)

@Configuration
public class JsonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder
            .serializers(localDateTimeSerializer());
    }

    private LocalDateTimeSerializer localDateTimeSerializer() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss 'DEFAULT'");
        return new LocalDateTimeSerializer(formatter);
    }

}

그러면 등록한 패턴에 맞게 잘 응답한다.

"2024-06-01 13:30:50 DEFAULT"

필요한 경우 StdDeserializer<?>, StdSerializer<?>를 상속받아 구현할 수도 있다.

다음 글에서는 GitHub REST API의 표준 시간대 결정 방식을 따라서 구현해 보자!!!(https://docs.github.com/ko/rest/using-the-rest-api/timezones-and-the-rest-api)

참고

0개의 댓글