Skip to content

Commit d5117fb

Browse files
committed
feat: 랭킹 API date 파라미터 유효성 검증 추가
1 parent 57fe5a7 commit d5117fb

2 files changed

Lines changed: 51 additions & 2 deletions

File tree

apps/commerce-api/src/main/java/com/loopers/application/ranking/RankingFacade.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import org.springframework.stereotype.Component;
1414
import org.springframework.transaction.annotation.Transactional;
1515

16+
import java.time.LocalDate;
17+
import java.time.format.DateTimeFormatter;
18+
import java.time.format.DateTimeParseException;
1619
import java.util.Collections;
1720
import java.util.List;
1821
import java.util.Map;
@@ -25,6 +28,8 @@
2528
@Transactional(readOnly = true)
2629
public class RankingFacade {
2730

31+
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
32+
2833
private final ProductRankingCache productRankingCache;
2934
private final ProductRepository productRepository;
3035
private final BrandRepository brandRepository;
@@ -37,8 +42,8 @@ public class RankingFacade {
3742
* @return 랭킹 페이지 정보
3843
*/
3944
public RankingPageInfo getRankings(String date, int page, int size) {
40-
// 날짜 기본값: 오늘
41-
String targetDate = date != null ? date : productRankingCache.getTodayDate();
45+
// 날짜 검증 및 기본값 처리
46+
String targetDate = validateAndNormalizeDate(date);
4247

4348
// 1. ZSET에서 랭킹 조회
4449
List<RankingEntry> rankingEntries = productRankingCache.getTopRankings(targetDate, page, size);
@@ -95,4 +100,23 @@ public RankingPageInfo getRankings(String date, int page, int size) {
95100

96101
return RankingPageInfo.of(rankings, targetDate, page, size, totalCount);
97102
}
103+
104+
/**
105+
* 날짜 파라미터 검증 및 정규화
106+
* @param date 날짜 문자열 (yyyyMMdd 형식) 또는 null
107+
* @return 검증된 날짜 문자열 (null이면 오늘 날짜)
108+
* @throws IllegalArgumentException 유효하지 않은 날짜 형식
109+
*/
110+
private String validateAndNormalizeDate(String date) {
111+
if (date == null || date.isBlank()) {
112+
return productRankingCache.getTodayDate();
113+
}
114+
115+
try {
116+
LocalDate.parse(date, DATE_FORMATTER);
117+
return date;
118+
} catch (DateTimeParseException e) {
119+
throw new IllegalArgumentException("Invalid date format. Expected yyyyMMdd, got: " + date);
120+
}
121+
}
98122
}

apps/commerce-api/src/test/java/com/loopers/application/ranking/RankingFacadeTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.List;
2121

2222
import static org.assertj.core.api.Assertions.assertThat;
23+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2324
import static org.mockito.ArgumentMatchers.*;
2425
import static org.mockito.Mockito.*;
2526

@@ -174,4 +175,28 @@ private Brand createBrand(Long id, String name) {
174175
when(brand.getName()).thenReturn(name);
175176
return brand;
176177
}
178+
179+
@Test
180+
@DisplayName("getRankings - 잘못된 날짜 형식은 예외 발생")
181+
void getRankings_throwsExceptionForInvalidDateFormat() {
182+
// given
183+
String invalidDate = "2025-12-26"; // yyyy-MM-dd 형식 (잘못됨)
184+
185+
// when & then
186+
assertThatThrownBy(() -> rankingFacade.getRankings(invalidDate, 0, 10))
187+
.isInstanceOf(IllegalArgumentException.class)
188+
.hasMessageContaining("Invalid date format");
189+
}
190+
191+
@Test
192+
@DisplayName("getRankings - 존재하지 않는 날짜는 예외 발생")
193+
void getRankings_throwsExceptionForNonExistentDate() {
194+
// given
195+
String invalidDate = "20251332"; // 13월 32일 (존재하지 않는 날짜)
196+
197+
// when & then
198+
assertThatThrownBy(() -> rankingFacade.getRankings(invalidDate, 0, 10))
199+
.isInstanceOf(IllegalArgumentException.class)
200+
.hasMessageContaining("Invalid date format");
201+
}
177202
}

0 commit comments

Comments
 (0)