Spring

[Spring/Serialization-Deserialization] Date, LocalDate, LocalDateTime 형변환 방법, 직렬화/역직렬화 문제 해결하기

공부하고 기억하는 공간 2025. 3. 7. 23:51
728x90
반응형
SMALL

평소에 Token을 만들때 IssuedAt에 Date 타입을 User의 LocalDateTime 타입으로 전환하려고 할때  또는 LocalDateTime을 사용해서 Date 타입의 필드에 넣으려고 할 때 항상 애를 먹었다.

그래서 타입 변환에 필요한 메서드들을 다음번엔 이 글을 보고 바로 처리할 수 있게 차례대로 정리해 두려고 한다.

또한 직렬화/역직렬화에서 생겼던 문제를 다뤄볼것이다.

 

1. Date → LocalDate, LocalDateTime 변환

 

 

1-1. Date → LocalDate 변환

 

Date 객체를 LocalDate로 변환하려면, 먼저 Instant로 변환한 후 ZoneId를 지정하여 ZonedDateTime으로 변환한 뒤 toLocalDate()를 호출한다.

 

코드 예제

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

public class DateToLocalDateExample {
    public static void main(String[] args) {
        Date date = new Date(); 
        LocalDate localDate = date.toInstant()
                                  .atZone(ZoneId.systemDefault())
                                  .toLocalDate();

        System.out.println("Date: " + date);
        System.out.println("LocalDate: " + localDate);
    }
}

 

1-2. Date → LocalDateTime 변환

LocalDateTime으로 변환하려면 toLocalDateTime()을 호출하면 된다.

 

코드 예제

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

public class DateToLocalDateTimeExample {
    public static void main(String[] args) {
        Date date = new Date();
        LocalDateTime localDateTime = date.toInstant()
                                          .atZone(ZoneId.systemDefault())
                                          .toLocalDateTime();

        System.out.println("Date: " + date);
        System.out.println("LocalDateTime: " + localDateTime);
    }
}

 

1-3. Java 9 이상에서는 ofInstant() 사용 가능

 

Java 9 이상에서는 ofInstant()를 사용하면 더 간결하게 변환할 수 있다.

 

코드 예제

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

public class Java9DateConversion {
    public static void main(String[] args) {
        Date date = new Date();

        LocalDate localDate = LocalDate.ofInstant(date.toInstant(), ZoneId.systemDefault());
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());

        System.out.println("LocalDate: " + localDate);
        System.out.println("LocalDateTime: " + localDateTime);
    }
}

 

 


2. LocalDateTime → Date 변환

 

2-1. java.sql.Timestamp.valueOf() 사용

 

LocalDateTimeDate로 변환할 때 java.sql.Timestamp.valueOf()를 사용할 수 있다.

 

코드 예제

import java.time.LocalDateTime;
import java.util.Date;

public class LocalDateTimeToDateExample {
    public static void main(String[] args) {
        LocalDateTime localDateTime = LocalDateTime.now();
        Date date = java.sql.Timestamp.valueOf(localDateTime);

        System.out.println("LocalDateTime: " + localDateTime);
        System.out.println("Date: " + date);
    }
}

2-2. Date.from() + Instant 사용

 

LocalDateTimeDate로 변환할 때 Instant을 이용하는 방법도 있다.

 

코드 예제

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

public class LocalDateTimeToDateExample {
    public static void main(String[] args) {
        LocalDateTime localDateTime = LocalDateTime.now();
        Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
        Date date = Date.from(instant);

        System.out.println("LocalDateTime: " + localDateTime);
        System.out.println("Date: " + date);
    }
}

 

 


3. LocalDate → Date 변환

 

3-1. java.sql.Date.valueOf() 사용

 

LocalDateDate로 변환할 때 java.sql.Date.valueOf()를 사용할 수 있다.

 

코드 예제

import java.time.LocalDate;
import java.util.Date;

public class LocalDateToDateExample {
    public static void main(String[] args) {
        LocalDate localDate = LocalDate.now();
        Date date = java.sql.Date.valueOf(localDate);

        System.out.println("LocalDate: " + localDate);
        System.out.println("Date: " + date);
    }
}

3-2. Date.from() + Instant 사용

 

LocalDateDate로 변환할 때 Instant을 이용하는 방법도 가능하다.

 

코드 예제

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

public class LocalDateToDateExample {
    public static void main(String[] args) {
        LocalDate localDate = LocalDate.now();
        Instant instant = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant();
        Date date = Date.from(instant);

        System.out.println("LocalDate: " + localDate);
        System.out.println("Date: " + date);
    }
}

 

 


4. 변환 요약 정리

변환 유형방법

Date → LocalDate date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
Date → LocalDateTime date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
Date → LocalDate (Java 9 이상) LocalDate.ofInstant(date.toInstant(), ZoneId.systemDefault())
Date → LocalDateTime (Java 9 이상) LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())
LocalDateTime → Date java.sql.Timestamp.valueOf(localDateTime)
LocalDateTime → Date (Instant 방식) Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant())
LocalDate → Date java.sql.Date.valueOf(localDate)
LocalDate → Date (Instant 방식) Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant())

 

 


 

Java 8 이상에서는 Instant, ZoneId, ZonedDateTime을 활용하여 Date, LocalDate, LocalDateTime을 변환할 수 있다.

Java 9 이상에서는 LocalDate.ofInstant()LocalDateTime.ofInstant()를 사용하여 변환을 더 간결하게 처리할 수 있다.

java.sql.Timestampjava.sql.DateDate와 호환이 가능하므로 변환 시 사용할 수 있다.

 

 

 

JAVA 8 LocalDateTime 직렬화/ 역직렬화 문제 

 

난 자바17을 사용하지만 DTO에서 LocalDateTime 타입의 필드를 해결하는 과정에서 문제가 발생했다. WebClient에서 LocalDateTime 필드를 요청하여 반환받는 과정에서 해당 타입이 직렬화가 수행되지 않는 문제였다.

 

@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TokenResponse implements Serializable {
    private static final long serialVersionUID = 1L; // 버전 관리용 ID

    private String token;
    private LocalDateTime tokenIssuedAt;
}

 

이 직렬화 과정에서 내가 설정한 직렬화 타입은 Jackson 타입이였는데 LocalDateTime은 직렬화가 불가능한 모양이였다.

 

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.jrkBoard.user.presentation.dto.response.UserResponseDto["tokenIssuedAt"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.18.2.jar:2.18.2]

 

해결 방법

2가지 의존성을 추가 해준다.

 

jsr310은 Java8에서 날짜 및 시간에 대한 API를 확장하는 라이브러리라고 한다. LocalDateTime, LocalDate, ZoneDateTime 등과 같은 Java8 날짜/시간 개체를 JSON data로 직렬화 및 역직렬화 할 수 있다.

 

databaind 는 JSON구조 데이터에 대한 바인딩을 Java객체에 제공하는 라이브러리이다. Json타입으로 받아오는 데이터들을 Java 타입으로 역직렬화 또는 반대로 직렬화도 가능하게 해준다.

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
implementation 'com.fasterxml.jackson.core:jackson-databind'

 

아래와 같이 직렬화와 역직렬화에 대해서 어떻게 할지 설정하거나 Format도 설정할 수 있다.

 

JsonProperty 어노테이션의 용도

  • Json필드명과 Java필드명이 다를 때 맵핑하기 위해 사용한다.
  • 직렬화(Serialization, 객체 -> JSON)와 역직렬화(Deserialization, JSON -> 객체)시 필드명을 변경하기 위해 사용된다.
  • Snake_case, CamelCase간 변환이 필요할때
  • 데이터를 특정 형식으로 변형하는 경우 사용한다.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserResponseDto {
    private Long userId;
    private String email;
    private String name;
    @JsonProperty("tokenIssuedAt")
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS") // "T" 포함한 ISO 8601 형식
    private LocalDateTime tokenIssuedAt;

}

 

 

결과적으로 데이터를 직렬화/역직렬화하여 정상적으로 가져올 수 있었다.

requestTokenIssuedAt: 2025-03-07T13:22:12Z
userTokenIssuedAt: 2025-03-07T13:22:12Z
728x90
반응형
SMALL