들어가기 앞서
이번 주 스터디 주제가 java였는데 마침 직장에서 프로젝트를 하며 java의 Date 클래스를 사용할 일이 생겼다. 어디선가 java의 Date, Calendar 대신 java8의 LocalDate와 LocalDateTime 그리고 LocalTime을 사용하라고 권장 받은 적이 있었는데, 그 이유가 무엇일까 궁금해져 java의 시간/날짜 관련 클래스들을 공부하게 되었다. (이번 주 스터디 주제가 java인 부분이 크게 한몫했다 사실.)
이 글은 Naver D2의 Java의 날짜와 시간 API를 참고, 학습한 이후 작성한 글임을 밝힙니다!
Java 시간/날짜 API 시대 계보도
- JDK 1.0 : java.util.Date (java의 유일한 시간/날짜 API)
- JDK 1.1 : java.util.Calendar (java.util.Date의 날짜간의 연산, 국제화 지원 등의 기능을 java.util.Calendar가 맡게 됨)
- JDK 1.8 (JSR-310) : java.time (오픈소스 Joda-Time에서 많은 영향을 받아 만들어진 새로운 시간/날짜 관련 패키지)
Calendar, Date 클래스의 문제점
좋은 API는 오용하기 어려워야 하고, 문서가 없어도 쉽게 사용할 수 있어야 한다. 그러나 Java의 기본 API는 문서를 열심히 보기 전까지는 제대로 사용하기 어렵다.
출처) Naver D2의 Java의 날짜와 시간 API
결론부터 말하자면 Calendar, Date는 사용하기 어려워도 너무 어렵다! 이유를 하나하나 살펴보자.
문제점 1. API가 현실의 사회 제도, 과학, 역사적 문제와 복잡하게 얽혀 있음
Calendar 클래스는 그레고리력, 율리우스력 등 다양한 역법을 지원한다. 심지어 Locale 정보에 따라 JapaneseImperialCalendar, BuddhistCalendar 등도 지원하는데, 문제는 어떤 역법을 사용하느냐에 따라 프로그래머의 예상과는 다른 동작을 할 수 있다는 것. 코드상에서 날짜를 하루 추가하였는데, 10일이 추가되는 이상현상이 발생할 수도 있음.
즉, Calendar와 Date를 제대로 사용하기 위해서는 그레고리력, 율리우스력 등과 같은 역사적, 천문학적 지식을 알아야 한다는 것.
문제점 2. Calendar와 Date는 불변 객체가 아님
날짜, 시간, 돈과 관련된 객체는 Value Object(VO) 즉, 값에 의해 동등성이 판단되는 객체이다. VO는 별칭 문제, 스레드 불안정성 등의 부작용에서 자유롭고 여러 객체에 공유되더라도 값이 변하지 않게 한번 생성된 이후 내부 속성을 바꿀 수 없게 설계해야 하는데 java의 Calendar와 Date는 불변 객체가 아니다. C#, Python과 같은 언어에서는 날짜 클래스가 한번 생성되면 이후에 내부 속성을 바꿀 수 없는데, java의 Calendar와 Date는 그 값을 바꿀 수 있는 것. 만약 이러한 객체가 여러 객체에 의해 공유되는 상황이라면 그 객체의 정보를 믿을 수 없기 때문에 프로그래밍 시 불필요한 추가적인 객체를 생성하거나 하는 일이 발생할 수 있다!
문제점 3. 헷갈리는 날짜 지정
Calendar와 Date는 날짜와 시간을 1이 아닌 0에서부터 세기 시작한다. 예를 들어 8월을 기입할 때 8을 그대로 적으면 9월이 되어 버린다. 값을 입력할 때 생각해야 하는 불필요한 고려사항이 있는 것인데, 이 부분은 잘 알지 못하고 사용하는 경우 실수할 수 있는 가능성이 매우 높다(생각한 것과 다르게 날짜나 시간이 들어갈 수 있는 여지가 매우 높은 것!). 일단 너무 헷갈림... 편하려고 사용하는게 API인데 불필요하게 너무 헷갈린다.
문제점 4. Date와 Calendar의 불편한 역할 분담
java가 처음 생겼을 때 Date는 날짜 연산을 지원하는 java 내 유일한 클래스였다. 이후 JDK 1.1 때 Calendar 클래스가 포함되면서 기존의 Date가 지원하던 역할 중 날짜간의 연산, 국제화 지원 등의 기능을 Calendar 클래스가 담당하게 되었다. 날짜/시간 관련된 부분으로 코딩을 하다보면 년/월/일 계산을 하게 되는 경우가 많은데, 그러다보니 일반적으로 Date와 Calendar를 함께 사용하는 경우가 많다. 문제는, 한개의 연산을 위해 객체를 두 개 생성해야 하니 불필요한 코드가 발생하고 무엇보다 Calendar 클래스의 생성 비용이 작은 것이 아니기 때문에 매우 불편하다.
문제점 5. 오류에 둔감한 시간대 ID 지정
서울 시간은 'Asia/Seoul'인데 프로그래머가 실수로 'Seoul/Asia'라고 적었다고 해보자. 오류가 발생할까? 오류가 발생하지 않는다. 오류가 발생하지 않기 때문에 추후 찾기 어려운 버그가 생길 수 있다. (생각만해도 머리가 지끈지끈 아프다.)
문제점 6. java.util.Date 하위 클래스의 문제
java.util.Date의 하위 클래스인 java.sql.Date와 java.util.Date의 이름이 같다. 사용법이 비슷한 클래스가 이름이 같은 경우 코딩을 할 때 실수를 할 여지가 매우 크다. 지금은 프로그래머가 직접 import 정보를 작성하지 않고 IDE가 이를 대신 작성해 주는 시대인데 실수로 java.sql.Date를 사용해야 할 환경에 java.util.Date를 사용할 확률이 충분히 있다. 이를 인지하지 못하고 코딩을 마칠 경우 예상치 못한 동작이 발생할 확률 또한 무시하지 못한다. (java.util.Date와 java.sql.Date의 이름이 같은 부분 때문에 'java 플랫폼 설계자는 클래스 이름을 지으면서 깜빡 졸았나보다'라는 조롱까지 있었다고 한다.)
또, java.sql.TimeStamp 클래스는 java.util.Date 클래스에 나노초 필드를 더한 클래스인데, Date 타입과 TimeStamp 타입을 함께 쓰면 a.equals(b)가 true여도 b.equals(a)가 false인 경우가 생길 수 있다고 한다.
자바 8 이후 사용 가능한 java.time
오픈소스 JodaTime의 영향을 강하게 받아 탄생한 java.time 패키지는 Java8부터 사용이 가능하며 LocalDate, LocalTime, LocalDateTime을 갖는다. 각각의 용도는 아래와 같다.
- LocalDate : 년, 월, 일
- LocalTime : 시, 분, 초, 나노초
- LocalDateTime : 년, 월, 일, 시, 분, 초, 나노초
클래스의 이름에서 유추 가능하듯 LocalDate는 날짜와 관련된 정보를, LocalTime은 시간과 관련된 정보를, LocalDateTime은 날짜와 시간 모두에 관한 정보를 제공한다. 정말 직관적이다!
java.time이 java.util.Date, java.util.Calendar와 다른 가장 큰 차이점은 java.util.* 의 날짜/시간 관련 클래스들이 불변 객체들이 아니었던과는 달리 java.time 내의 클래스들은 불변객체라는 것. java.util.* 의 클래스들 보다 사용하기 훨씬 안전하다. 또한 메소드 체이닝을 지원하기 때문에 코드를 훨씬 깔끔하고 직관적으로 짤 수 있다. 정말 짱!
자세한 용례는 TCP School의 LocalDate와 LocalTime 내의 내용을 살펴보도록 하자. 사실 회사 내 프로젝트가 Date와 Calendar를 기반으로 되어 있기 때문에 아직 java.time을 제대로 학습하지 못했다. 나중에 다른 프로젝트에서 사용하게 될 때를 위해, 공부를 미룬다.
'JAVA' 카테고리의 다른 글
[JPA] 연관관계 매핑 기초 (0) | 2022.05.21 |
---|---|
[번역] cron4j quickstart (0) | 2022.04.01 |
JVM(Java Virtual Machine)이란? (0) | 2022.02.19 |
JVM의 Garbage Collector 동작 원리 (0) | 2022.02.14 |
[Java] Array와 ArrayList의 차이점 (0) | 2022.02.11 |