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

2025. 3. 7. 23:51·Spring
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() 사용

 

LocalDateTime을 Date로 변환할 때 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 사용

 

LocalDateTime을 Date로 변환할 때 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() 사용

 

LocalDate를 Date로 변환할 때 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 사용

 

LocalDate를 Date로 변환할 때 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.Timestamp와 java.sql.Date는 Date와 호환이 가능하므로 변환 시 사용할 수 있다.

 

 

 

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

'Spring' 카테고리의 다른 글

[Lombok] @Builder, DTO 접근제한자 적절한 설정 방법  (0) 2025.03.11
[Lombok] @Builder를 클래스가 아닌 메서드에 적용하는 이유  (0) 2025.03.11
[Github] Yml파일 서브모듈에서 불러와서 안전하게 사용하기  (0) 2025.02.17
[Restful API] Restful한 API 문서 작성법, REST API 성숙도 모델  (0) 2025.02.13
[Spring / Swagger] Cors , Fail to Fetch 해결방법  (0) 2025.01.24
'Spring' 카테고리의 다른 글
  • [Lombok] @Builder, DTO 접근제한자 적절한 설정 방법
  • [Lombok] @Builder를 클래스가 아닌 메서드에 적용하는 이유
  • [Github] Yml파일 서브모듈에서 불러와서 안전하게 사용하기
  • [Restful API] Restful한 API 문서 작성법, REST API 성숙도 모델
공부하고 기억하는 공간
공부하고 기억하는 공간
IT 비전공자로 시작하여 훌륭한 개발자가 되기 위해 공부하고 있는 공간입니다. 틀린 내용이나 부족한 부분이 있으면 댓글로 알려주세요 바로 수정하겠습니다.
    250x250
  • 공부하고 기억하는 공간
    IT - railroad
    공부하고 기억하는 공간
  • 전체
    오늘
    어제
    • 분류 전체보기 (325) N
      • 면접 준비 (22) N
        • OS (6)
        • Spring Security (0)
        • Java (3)
        • DB (11)
        • Network (3)
      • ElasticSearch (2)
      • Kafka (4)
      • Spring (22)
        • Spring Cloud (7)
        • Security6 (5)
        • JPA (12)
        • 프로젝트 리팩토링 회고록 (4)
        • Logging (8)
        • Batch (2)
      • Redis (17)
        • Redis 개념 (8)
        • Redis 채팅 (5)
        • Redis 읽기쓰기 전략 (1)
      • AWS (11)
      • 리눅스 (29)
        • 리눅스 마스터 2급 (5)
        • 네트워크(기초) (7)
        • 리눅스의 이해 (6)
        • 리눅스의 설치 (2)
        • 리눅스 운영 및 관리 (6)
      • JAVA-기초 (16)
        • JAVA기본 (11)
        • Design Pattern (5)
      • JSP (27)
        • JSP 기본 개념 (10)
        • JSP (1)
      • SQL (1)
      • TIL (36)
      • 문제 풀이 (2)
        • Programmers (9)
        • 백준 문제풀이 (28)
      • JavaScript (10)
      • HTML (17)
      • Ngrinder (1)
        • Ngrinder 문서 정리 (1)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      spring redis
      Spring Data Redis
      springsecurity
      자바기초
      자바 면접
      JS
      스프링프레임워크
      리눅스마스터2급정리
      자바스크립트
      java
      JavaScript
      CSS
      Springframework
      redis 채팅
      redis
      HTML
      자바
      자바 반복문
      자바 면접질문
      리눅스마스터2급
      리눅스
      Spring
      jsp기초
      프로그래머스
      자바 알고리즘
      JSP
      jsp request
      Til
      백준
      레디스
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    공부하고 기억하는 공간
    [Spring/Serialization-Deserialization] Date, LocalDate, LocalDateTime 형변환 방법, 직렬화/역직렬화 문제 해결하기
    상단으로

    티스토리툴바