스프링 6.2부터 Duration을 다양한 포맷 스타일을 지원하는 새로운 기능이 추가되었다.
Kotlin에서는 이미 Duration.parse
를 통해 다양한 표현식을 제공하고 있다.
val duration1 = Duration.parse("1.5h")
val duration2 = Duration.parse("1h 30m")
이와 비슷하게 스프링에서도 DurationFormatUtils를 통해 Duration의 다양한 표현식을 지원하게 되었다.
기존에는 밀리초 단위로만 입력했었는데 이제는 다양하게 표현할 수 있다.
크게 3가지 형태로 나눌 수 있다.
@Scheduled
애노테이션에 개선@Component
public class MyScheduledTask {
@Scheduled(fixedRateString = "2h 15m")
public void taskWithFixedRate() {
System.out.println("2시간 15분 간격으로 실행됩니다.");
}
@Scheduled(fixedDelayString = "10m")
public void taskWithFixedDelay() {
System.out.println("이전 작업이 종료된 후 10분 후에 실행됩니다.");
}
@Scheduled(initialDelayString = "5s", fixedRateString = "1h")
public void taskWithInitialDelay() {
System.out.println("5초 후에 시작하며, 매 1시간 간격으로 실행됩니다.");
}
}
이제 @Scheduled
애노테이션에서 다양한 스타일을 인식하고 문자열로 표현된 초기 지연, 고정 지연 및 고정 속도를 파싱할 수 있다.
yaml파일이나 properties 파일에서 SIMPLE 또는 COMPOSITE 스타일을 사용해 유지보수성과 가독성을 향상시킬 수 있다.
task:
rate: "1h 15m"
delay: "30s"
다양한 비즈니스 로직에서 파싱 기능을 활용해 동적으로 시간을 설정할 수 있다.
String durationString = fetchFromDatabase();
Duration duration = DurationFormatterUtils.detectAndParse(durationString);
scheduleTask(duration);
DurationFormatterUtils
클래스를 보면 다양한 포맷 스타일을 처리하기 위해 다음과 같이 동작한다.
public static DurationFormat.Style detect(String value) {
Assert.notNull(value, "Value must not be null");
// warning: the order of parsing starts to matter if multiple patterns accept a plain integer (no unit suffix)
if (ISO_8601_PATTERN.matcher(value).matches()) {
return DurationFormat.Style.ISO8601;
}
if (SIMPLE_PATTERN.matcher(value).matches()) {
return DurationFormat.Style.SIMPLE;
}
if (COMPOSITE_PATTERN.matcher(value).matches()) {
return DurationFormat.Style.COMPOSITE;
}
throw new IllegalArgumentException("'" + value + "' is not a valid duration, cannot detect any known style");
}
public static Duration parse(String value, DurationFormat.Style style, DurationFormat.@Nullable Unit unit) {
return switch (style) {
case ISO8601 -> parseIso8601(value);
case SIMPLE -> parseSimple(value, unit);
case COMPOSITE -> parseComposite(value);
};
}
나머지 부분들은 각 스타일에 맞게 분리하여 Duration 객체로 변환하는 작업이다.
이 기능들을 살펴보고 테스트하는중 버그를 발견할 수 있었고 바로 스프링 프로젝트에 기여했다.
DurationFormatterUtils을 테스트하는 도중에 parseComposite(value)
부분에서 빈 문자열("", " ")이 전달되면 예외를 던지지 않고 기본값으로 PT0S를 반환했다.
이는 빈 문자열이 유효하지 않은 입력임에도 불구하고 오해를 불러일으킬 수 있는 동작이여서 PR을 올리고 무사히 병합되었다.
내가 추가한 부분은 아주 간단했다.
public static Duration parse(String value, DurationFormat.Style style, DurationFormat.@Nullable Unit unit) {
Assert.hasText(value, () -> "Value must not be empty");
return switch (style) {
case ISO8601 -> parseIso8601(value);
case SIMPLE -> parseSimple(value, unit);
case COMPOSITE -> parseComposite(value);
};
}
parse
메서드에 Assert.hasText(value, () -> "Value must not be empty");
를 추가한게 전부다.
@ParameterizedTest
@EnumSource(DurationFormat.Style.class)
void parseEmptyStringFailsWithDedicatedException(DurationFormat.Style style) {
assertThatIllegalArgumentException()
.isThrownBy(() -> DurationFormatterUtils.parse("", style))
.withMessage("Value must not be empty");
}
@ParameterizedTest
@EnumSource(DurationFormat.Style.class)
void parseNullStringFailsWithDedicatedException(DurationFormat.Style style) {
assertThatIllegalArgumentException()
.isThrownBy(() -> DurationFormatterUtils.parse(null, style))
.withMessage("Value must not be empty");
}
그리고 그에 따른 테스트코드도 추가했다.
Spring 6.2에서 추가된 Duration 포맷 스타일 기능은 기존의 불편함을 해소하고 코드를 더욱 가독성 있게 만든다.
특히 다양한 포맷 스타일과 직관적인 표현 방식을 통해 개발자들은 더 생산적인 코드를 작성할 수 있다.
Spring의 새롭게 커밋된 내용을 보다가 새로운 기능도 알게되고 스프링 기여까지 하게 되었다.
Spring 코드들을 보다보면 의외로 다양하게 기여할 기회가 은근히 많은 것 같다.
다들 기회를 잡아보시길!
해당 기능에 대한 PR은 여기서 확인 가능하다.
기여한 PR은 여기서 확인 가능하다.
Spring 7.0 부터 도입되는 Jspecify에 대해 알아보기! (0) | 2025.01.02 |
---|---|
Spring Assert클래스는 왜 Supplier를 활용하고 있을까? (0) | 2024.06.05 |
Spring 프로젝트 컨트리뷰터 된 후기 (ThreadLocal.set(null) vs ThreadLocal.remove() ) (0) | 2024.05.24 |
분산락을 이용한 메시지 좋아요 기능 동시성 문제 해결 방안 (0) | 2024.05.16 |
Spring Retry를 활용한 메시지 좋아요 기능의 동시성 문제 해결 방안 (0) | 2024.04.11 |