✨ 문제 상황
서비스 내 특정 도메인 객체가 언제 생성되었는지(createdAt) 기준으로
“얼마나 지났는가?“를 아래와 같이 문자열로 표현하는 로직이 필요했다.
- 생성된 지 1년 이상이면: n년
- 생성된 지 1개월 이상 1년 미만이면: n개월
- 생성된 지 1개월 미만이면: n일
처음에는 java.time.Period.between()을 사용해 이렇게 구현했다:
public String calculateActivityDuration(LocalDate now) {
if (this.createdAt == null) return;
LocalDate createDate = createdAt.toLocalDate();
Period period = Period.between(createDate, now);
String activityDuration;
if (period.getYears() >= 1) {
activityDuration = period.getYears() + "년";
} else if (period.getMonths() >= 1) {
activityDuration = period.getMonths() + "개월";
} else {
activityDuration = period.getDays() + "일";
}
return actitvityDuration;
}
❗ 그런데 테스트가 깨졌다
@Test
@DisplayName("ActivityDuration은 생성된지 1개월 미만이면 일단위로 설정된다.")
public void calculateActivityDuration_lessThanOneMonth() throws Exception {
// given
Expert expert = Expert.builder()
.createdAt(LocalDateTime.of(2025, 6, 14, 15, 33))
.build();
// when
String activityDuration = expert.calculateActivityDuration(LocalDate.of(2025, 7, 13));
// then
assertThat(activityDuration).isEqualTo("29일");
}

내가 기대한 출력은 “29일”이었다.
왜냐하면 6월 14일 ~ 7월 13일은 달수 기준으로는 1개월이 안 되었기 때문이다.
하지만 테스트 결과는 "1개월"이 반환되었다. 왜일까?
🕵️ 문제 원인: Period.between() 의 달력 단위 계산 방식
Period.between(LocalDate.of(2025, 6, 14), LocalDate.of(2025, 7, 13));
위 코드는 실제로는 29일 차이지만, Period는 1개월로 계산한다.
왜냐하면 Period는 ‘달력상 기준 월 차이’를 계산하기 때문이다.
즉, 아래 둘은 완전히 다른 결과를 준다:
| 날짜 범위 | 실제 일수 | Period 계산 결과 |
| 2025-06-14 ~ 2025-07-13 | 29일 | 1개월 |
| 2025-06-13 ~ 2025-07-13 | 30일 | 1개월 |
달력상 월이 바뀌는 순간, Period는 바로 “1개월”이라고 판단해버린다.
극단적인 예시로, 6월 30일, 7월 1일이어도 1개월이라고 판단한다.
그래서 날짜 경계값 테스트가 예상대로 동작하지 않는다.
🛠 해결 방법: ChronoUnit 으로 직접 계산
우리는 원하는 단위(년/월/일)를 보다 정확하게 구분하고 싶다.
그래서 ChronoUnit.YEARS, MONTHS, DAYS를 사용해서 절대값 기반으로 계산하도록 변경했다:
public String calculateActivityDuration(LocalDate now) {
if (this.createdAt == null) return null;
LocalDate createDate = createdAt.toLocalDate();
long totalYears = ChronoUnit.YEARS.between(createDate, now);
long totalMonths = ChronoUnit.MONTHS.between(createDate, now);
long totalDays = ChronoUnit.DAYS.between(createDate, now);
String activityDuration;
if (totalYears >= 1) {
activityDuration = totalYears + "년";
} else if (totalMonths >= 1) {
activityDuration = totalMonths + "개월";
} else {
activityDuration = totalDays + "일";
}
return activityDuration;
}
이 방식은 다음과 같이 정확한 경계 구분이 가능하다:
| createdAt | now | 결과 |
| 2025-06-14 | 2025-07-13 | “29일” |
| 2025-06-13 | 2025-07-13 | “1개월” |
| 2024-07-13 | 2025-07-13 | “1년” |
✅ 결론
- Period.between()는 달력 기준으로 작동하기 때문에,
- 정확한 일수/월수 경계 테스트에 부적합할 수 있다.
- ChronoUnit을 활용하면 절대 시간 차이 기반으로 계산할 수 있어서,
- 예상 가능한 결과를 만들 수 있다.
- 경계값 테스트를 작성할 땐, 내부 로직이 어떻게 동작하는지 알고 써야 한다.