uncategorized

서버 시간대에 의존하지 않는 데이터 구현하기

문제 상황

기리기리 서비스는 대여 서비스라서 대여 기간과 실제 대여 수령 날짜, 대여 반납 날짜가 중요하다. 문제는 서버 시간대 설정에 따라 실제로 의도한 시간이 맞지 않을 수 있게 된다는 점이다. 그래서 서버를 한국 시간대로 모두 설정해주는 일이 필요하다.

이런 문제는 AWS EC2 인스턴스를 생성할 때 서버 시간대를 한국으로 적용하는 방식으로 해결할 수 있다. 하지만 기리기리 팀은 여기서 몇가지 문제점을 발견했다. 첫째로 만약 다른 클라우드 서비스로 바뀌게 된다면 서버 시간대를 적용하는 코드를 또 설정해야 한다. 만약 이 과정을 까먹고 배포하면 서비스에 큰 문제가 생기게 된다. 두번째로 Github Actions에 사용되는 러너에도 한국 시간대를 적용해야 하는 문제가 생긴다. 만약 그렇지 않고 CI를 실행하면 예상하지 못한 문제가 발생할 수 있다.

개선 방법

기존의 방법

일반적으로 사용하는 것이 LocalDateTimeLocalDate이다. 클래스 이름을 보면 알 수 있듯이 어플리케이션이 실행되는 서버의 시간대에 맞춰서 생성이 된다.

Java 8 Instant

Instant 클래스는 특정 시점(1970년 1월 1일 00:00:00(UTC))에서 얼마나 많은 시간이 흘렀는지를 기록하는 클래스이다. 이 클래스를 활용하면 시간대와 상관없이 현재 시간 데이터를 일관적으로 사용할 수 있다. 하지만 Instant 그 자체는 사람들이 이해할 수 있는 시간 데이터가 아니므로 사람들이 이해할 수 있는 시간대로 표현해줘야 한다.

도메인 클래스로 시간 데이터 타입에 대해 캡슐화하기

우리 도메인 로직에서 Instant로 바꾸기 위해 기존의 LocalDate를 도메인 클래스로 바꿔서 사용한다. 그리고 내부의 데이터를 LocalDate를 Instant로 바꾼다.
다음은 대여에 사용되는 시간 데이터를 나타내는 RentalDateTime 이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Getter
@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RentalDateTime {

private Instant instant;

private RentalDateTime(final Instant instant) {
this.instant = instant.truncatedTo(ChronoUnit.MILLIS);
}

public static RentalDateTime now() {
return new RentalDateTime(Instant.now());
}

public static RentalDateTime from(final LocalDateTime localDateTime) {
return new RentalDateTime(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}

public static RentalDateTime from(final LocalDate localDate) {
LocalDateTime localDateTime = localDate.atStartOfDay();
return RentalDateTime.from(localDateTime);
}

public RentalDateTime calculateDay(final int days) {
final Instant instant =
days < 0 ? this.instant.minus(Duration.ofDays(-days)) : this.instant.plus(Duration.ofDays(days));
return new RentalDateTime(instant);
}

public LocalDateTime toLocalDateTime(final ZoneId zoneId)) {
return this.instant.atZone(zoneId).toLocalDateTime().truncatedTo(ChronoUnit.MILLIS);
}

public LocalDate toLocalDate(final ZoneId zoneId) {
return this.instant.atZone(zoneId).toLocalDate();
}
}

LocalDate나 LocalDateTime을 내부적으로 Instant로 바꿔서 초기화하고, Instant를 특정 시간대에 맞춰서 LocalDate나 LocalDateTime으로 바꾼다.

결과

이제 서버의 시간대와 상관없이 일관적인 데이터르 저장할 수 있게 됐다. 서버에 시간대를 적용하는 코드를 제거해도 문제가 생기지 않게 된다.

Share