개요
회사에서 새로운 기능 추가를 위해 팀원 분이 Date를 쓴 것을 발견하였다.
이펙티브 자바 스터디에서 Date클래스는 사용을 지양해야 한다고 공부했기에 이 부분에 대해 자세한 기록을 남겨 곱씹어야겠다고 다짐했다.
DATE
java의 util 클래스다. 공식 문서를 살펴 보면, Date 클래스가 구현한 인터페이스는 다음과 같다.
- Serializable: 특별한 메소드를 포함하지 않는 마커 인터페이스다. 객체의 상태를 저장하거나(메모리에 존재하는 객체를 디스크에 저장) 네트워크를 통해 전송할 수있도록 바이트 스트림으로 변환하는 것이다.
- Clonable: 이또한 마커 인터페이스다. 객체를 복사할 때 사용된다.
- Comparable<Date>: Date의 compareTo 메소드를 지원하는 인터페이스다.
DATE 클래스의 단점이되는 특징
Mutable
Date 클래스는 가변적이기에 개발함에 있어서 단점을 갖는다. 객체가 mutable하면 의도치 않게 객체가 수정되어 버그를 발생시킨다. 특히 멀티 스레드 환경에서는 공유 자원에 동시에 접근하고, 여러 번 수정을 할 수 있기에 더욱 위험하다. 이를 제어하기 위해 동기화 처리를 해야하고... 상당히 복잡해진다.
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // DATE 메소드를 통해 p의 내부를 변경 가능
이를 안전하게 구현하기 위해서 객체의 복사본을 만들어 방지하는 방법이(방어적 복사) 있다. 그런데 이때 또 주의할 점이 있다. (E-JAVA item50)
- 멀티스레딩 환경이라면 유효성 검사 후 복사를 하게되면, 이 찰나의 취약한 순간에 다른 스레드가 원본 객체를 수정할 위험이 있다. 이러한 공격을 time-of-check/time-of-use(TOCTOU) 공격이라고 한다. 따라서 복사본을 만들고, 유효성 검사를 해야한다.
- Date는 final이 아니기 때문에 clone을 사용하면 안된다. clone이 Date가 정의한게 아닐 수 있어, 악의를 가진 하위클래스의 인스턴스를 반환할 수 있다.
Calender Class와 함께 써야 연산이 그나마 편리하다.
특정 시간대의 날짜를 생성하거나, 날짜 단위의 계산은 Date만으로 수행하기 어렵다. 그래서 Calander 객체를 생성하고, 다시 Calender 객체에서 Date 객체를 생성하는 식으로 중간 객체를 거쳐야 한다.
실무에서는 Date 연산에 Apache commons Lang 라이브러리의 DateUtils 클래스의 plusDays나 plusMonth와 같은 메서드를 활용한다. 하지만 DateUtils 클래스를 쓰더라도 중간 객체로 Calander를 생성하는 것은 마찬가지다. (참고 자료)
이외에도 여러 특징과 단점이 있다.
위와 같은 단점으로 Date는 사용을 지양하고 있는 추세다.
LocalDateTime
Java 8에서 등장한 java.time 패키지의 API다.
LocalDate, LocalTime, LocalDateTime 이 세가지가 있는데, 이번 포스팅에서는 LocalDateTime에 대해서만 설명하도록 한다.
LocalDateTime은 날짜와 시간 정보를 모두 나타낸다. 간단하게 생각해서 LocalDate와 LocalTime을 합쳐놨다고 보면 된다.
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now);
// 결과 now = 2024-03-08T18:04:20.108948
* java.time 패키지에 속한 클래스들은 불변이다!
LocalDateTime 클래스의 특징
TimeZone 정보가 없다.
API명에서도 유추가 가능하지만, Local의 시간과 날짜를 다룬다. 위의 간단한 코드에서 now를 찍었을 때 현재 내가 있는 대한민국의 시간을 반영한 결과값이 도출되었다. 보통 국가별로 고유한 타임존을 사용한다. (서버를 다룰 때 흔히들 말 하는 region이라고 생각하면 된다.)
각지 다른 타임존을 다루기 위해서는 다음에 설명할 ZoneDateTime을 이용하자. 항상 리전을 설정하는 것이 정답은 아니다. 서비스 기획의 용도와 취지에 맞는 클래스를 선택하자!
A date-time without a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.
ISO-8601 달력 시스템을 사용하고, 형식은 위와 같다.
Immutable
앞서 Date 클래스와 비교하여 LocalDateTime은 불변 객체다. Date 클래스가 갖는 단점을 보완한 API이므로, 비교적 thread-safe하다.
특별한 경우가 아니면 이 클래스를 우선적으로 고려하자.
다양한 메서드
get, with, plus, minus, isBefore, isEqual, between, until 등등 정말 다양한 메서드가 있다.
공식 문서를 통해 메서드 정보를 확인할 수 있다.
ZoneDateTime
ZoneDateTime도 마찬가지로 java.time 패키지의 API다. LocalDateTimer과 다르게 타임존 / 시차 개념을 포함한 클래스다.
공식 문서를 보면 LocalDateTime과 달리 뒤에 타임존이 함께 도출된다.
A date-time with a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30+01:00 Europe/Paris.
// 결과 nowEuropeParis = 2024-03-08T10:38:54.364929+01:00[Europe/Paris]
ZonedDateTime nowEuropeParis = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
//결과 nowAsiaSeoul = 2024-03-08T18:38:54.366085+09:00[Asia/Seoul]
ZonedDateTime nowAsiaSeoul = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));
- of()를 사용한 이유는 객체 생성시 public 생성자를 제공하지않기 때문에 now 또는 of와 같은 정적 메소드를 사용한다.
- 위 예시와 달리 now()에 ZoneId를 인자로 넘겨주지 않으면, 로컬 PC의 타임존 그대로 값이 도출된다.
'언어 > JAVA' 카테고리의 다른 글
[JAVA] Effective JAVA. 9장 일반적인 프로그래밍 원칙 1편(item 57, 58, 59, 60) (0) | 2024.03.24 |
---|---|
[JAVA] Effective Java. 6장 열거 타입과 애너테이션 (2) | 2024.01.12 |
[JAVA] 익명 클래스(Anonymous Class), 람다식(Lambda) (1) | 2023.10.22 |
[JAVA] JAVA String | StringBuilder | 문자열 결합 연산 | 문자열 연산의 복잡도 (0) | 2023.05.19 |