6. 연습 문제
문제 1
BusinessDays 에 previousBusinessDayOrSame(LocalDate) 을 추가하고, "매월 25일 급여 지급, 휴일이면 직전 영업일" 규칙으로 2026년 1월~12월 지급일을 출력하세요. 공휴일은 1/1, 3/1, 5/5, 6/6, 8/15, 10/3, 10/9, 12/25 로 두세요.
정답 보기
public LocalDate previousBusinessDayOrSame(LocalDate d) {
while (!isBusinessDay(d)) d = d.minusDays(1);
return d;
}
// Main
for (int m = 1; m <= 12; m++) {
LocalDate pay = biz.previousBusinessDayOrSame(YearMonth.of(2026, m).atDay(25));
System.out.println(m + "월: " + pay + " " + pay.getDayOfWeek());
}
// 예: 1월 25일(일) → 1월 23일(금), 4월 25일(토) → 4월 24일(금), 12월 25일(금, 성탄절) → 12월 24일(목)급여는 "미리" 주고 정산 지급은 "다음 영업일" 로 미루는 것이 관례라 방향이 다릅니다. 방향을 매개변수로 받는 adjust(LocalDate, boolean forward) 하나로 합칠 수도 있습니다.
문제 2
splitByMonth(변형 3)를 이용해 2026-01-15 ~ 2026-03-10 계약의 월별 청구액을 계산하세요. 월 요금은 300,000원이고 부분 월은 해당 월 일수 기준 일할(BigDecimal, 소수점 이하 반올림)입니다.
정답 보기
BigDecimal monthly = new BigDecimal("300000");
for (BusinessDays.Period p : splitByMonth(LocalDate.of(2026, 1, 15), LocalDate.of(2026, 3, 10))) {
YearMonth ym = YearMonth.from(p.from());
long days = ChronoUnit.DAYS.between(p.from(), p.to()) + 1; // 양끝 포함
BigDecimal amount = days == ym.lengthOfMonth() ? monthly
: monthly.multiply(BigDecimal.valueOf(days)).divide(BigDecimal.valueOf(ym.lengthOfMonth()), 0, RoundingMode.HALF_UP);
System.out.println(ym + " " + days + "일 → " + amount + "원");
}
// 2026-01 17일 → 164516원 (300000 × 17 / 31)
// 2026-02 28일 → 300000원 (전체 월)
// 2026-03 10일 → 96774원 (300000 × 10 / 31)일할 기준(해당 월 일수 vs 고정 30일)은 계약서에 따르는 업무 규칙입니다. ym.lengthOfMonth() 가 28/29/30/31 을 알아서 돌려주므로 윤년도 처리됩니다.
문제 3
JWT 의 exp(epoch 초)가 1788931828 일 때 (1) 서울 시각으로 언제인지, (2) 지금(Instant.now())으로부터 몇 분 남았는지, (3) 만료 5분 전이면 갱신이 필요하다고 판단하는 needsRefresh(long exp, Clock clock) 를 작성하세요.
정답 보기
Instant exp = Instant.ofEpochSecond(1788931828L);
System.out.println(exp.atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime()); // (1) 2026-09-09T14:30:28
System.out.println(Duration.between(Instant.now(), exp).toMinutes() + "분 남음"); // (2) 실행 시각에 따라 다름. 과거면 음수
static boolean needsRefresh(long exp, Clock clock) { // (3)
return Instant.ofEpochSecond(exp).minus(5, ChronoUnit.MINUTES).isBefore(Instant.now(clock));
}토큰 만료는 시간대와 무관한 절대 시각이므로 Instant 로만 다룹니다. 서울 시각으로 바꾸는 것은 사람에게 보여줄 때뿐입니다. Clock 을 받으면 "만료 4분 전" 상황을 테스트에서 고정할 수 있습니다(변형 1).