From 63714efe6cd52dbd7f97dc670b2518a5c5287dc1 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:24:13 +0900 Subject: [PATCH 01/11] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20PageRequest=20=EB=B0=8F=20ArgumentResolver=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom/request/CustomPageRequest.java | 51 ++++++ .../CustomPageRequestArgumentResolver.java | 82 ++++++++++ .../solidconnection/util/PagingUtils.java | 26 --- ...CustomPageRequestArgumentResolverTest.java | 151 ++++++++++++++++++ .../solidconnection/util/PagingUtilsTest.java | 61 ------- 5 files changed, 284 insertions(+), 87 deletions(-) create mode 100644 src/main/java/com/example/solidconnection/custom/request/CustomPageRequest.java create mode 100644 src/main/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolver.java delete mode 100644 src/main/java/com/example/solidconnection/util/PagingUtils.java create mode 100644 src/test/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolverTest.java delete mode 100644 src/test/java/com/example/solidconnection/util/PagingUtilsTest.java diff --git a/src/main/java/com/example/solidconnection/custom/request/CustomPageRequest.java b/src/main/java/com/example/solidconnection/custom/request/CustomPageRequest.java new file mode 100644 index 000000000..90f4b8dc8 --- /dev/null +++ b/src/main/java/com/example/solidconnection/custom/request/CustomPageRequest.java @@ -0,0 +1,51 @@ +package com.example.solidconnection.custom.request; + +import com.example.solidconnection.custom.exception.CustomException; +import lombok.Getter; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_PAGE; +import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_SIZE; + +@Getter +public class CustomPageRequest { + + public static final int MIN_PAGE = 1; + public static final int MIN_SIZE = 1; + public static final int MAX_SIZE = 50; + + private final int page; + private final int size; + private Sort sort = Sort.unsorted(); + + public CustomPageRequest(int page, int size) { + validatePageParameters(page, size); + this.page = page; + this.size = size; + } + + public CustomPageRequest(int page, int size, Sort sort) { + this(page, size); + this.sort = sort; + } + + // 1-based -> 0-based 변환 + public Pageable toPageable() { + return PageRequest.of(page - 1, size, sort); + } + + private static void validatePageParameters(int page, int size) { + if (page < MIN_PAGE) { + throw new CustomException(INVALID_PAGE); + } + if (size < MIN_SIZE || size > MAX_SIZE) { + throw new CustomException(INVALID_SIZE); + } + } + + public static CustomPageRequest of(int page, int size, Sort sort) { + return new CustomPageRequest(page, size, sort); + } +} diff --git a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolver.java b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolver.java new file mode 100644 index 000000000..c28103355 --- /dev/null +++ b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolver.java @@ -0,0 +1,82 @@ +package com.example.solidconnection.custom.resolver; + +import com.example.solidconnection.custom.request.CustomPageRequest; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.util.ArrayList; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class CustomPageRequestArgumentResolver implements HandlerMethodArgumentResolver { + + private static final String PAGE_PARAMETER = "page"; + private static final String SIZE_PARAMETER = "size"; + private static final String SORT_PARAMETER = "sort"; + private static final String DESC = "desc"; + private static final String COMMA = ","; + private static final int DEFAULT_PAGE = 1; + private static final int DEFAULT_SIZE = 10; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return CustomPageRequest.class.isAssignableFrom(parameter.getParameterType()); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + int page = extractIntParameter(request, PAGE_PARAMETER, DEFAULT_PAGE); + int size = extractIntParameter(request, SIZE_PARAMETER, DEFAULT_SIZE); + Sort sort = extractSortParameter(request); + return CustomPageRequest.of(page, size, sort); + } + + private int extractIntParameter(HttpServletRequest request, String paramName, int defaultValue) { + String paramValue = request.getParameter(paramName); + if (paramValue != null && !paramValue.isEmpty()) { + try { + return Integer.parseInt(paramValue); + } catch (NumberFormatException e) { + // 숫자로 변환할 수 없는 경우 기본값 사용 + } + } + return defaultValue; + } + + private Sort extractSortParameter(HttpServletRequest request) { + String[] sortParams = request.getParameterValues(SORT_PARAMETER); + if (sortParams == null || sortParams.length == 0) { + return Sort.unsorted(); + } + List orders = new ArrayList<>(); + for (String sortParam : sortParams) { + addSortOrder(sortParam, orders); + } + return orders.isEmpty() ? Sort.unsorted() : Sort.by(orders); + } + + private void addSortOrder(String sortParam, List orders) { + String[] parts = sortParam.split(COMMA); + if (parts.length < 1) { + return; + } + String property = parts[0]; + Sort.Direction direction = Sort.Direction.ASC; + if (parts.length >= 2 && DESC.equalsIgnoreCase(parts[1])) { + direction = Sort.Direction.DESC; + } + orders.add(new Sort.Order(direction, property)); + } +} diff --git a/src/main/java/com/example/solidconnection/util/PagingUtils.java b/src/main/java/com/example/solidconnection/util/PagingUtils.java deleted file mode 100644 index 5b4547410..000000000 --- a/src/main/java/com/example/solidconnection/util/PagingUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.example.solidconnection.util; - -import com.example.solidconnection.custom.exception.CustomException; - -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_PAGE; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_SIZE; - -public class PagingUtils { - - private static final int MIN_PAGE = 1; - private static final int MIN_SIZE = 1; - private static final int MAX_SIZE = 50; - private static final int MAX_PAGE = 50; - - private PagingUtils() { - } - - public static void validatePage(int page, int size) { - if (page < MIN_PAGE || page > MAX_PAGE) { - throw new CustomException(INVALID_PAGE); - } - if (size < MIN_SIZE || size > MAX_SIZE) { - throw new CustomException(INVALID_SIZE); - } - } -} diff --git a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolverTest.java new file mode 100644 index 000000000..00c727969 --- /dev/null +++ b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolverTest.java @@ -0,0 +1,151 @@ +package com.example.solidconnection.custom.resolver; + +import com.example.solidconnection.custom.request.CustomPageRequest; +import com.example.solidconnection.support.TestContainerSpringBootTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.MethodParameter; +import org.springframework.data.domain.Sort; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.ServletWebRequest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +@TestContainerSpringBootTest +@DisplayName("커스텀 페이지 요청 argument resolver 테스트") +class CustomPageRequestArgumentResolverTest { + + private static final String PAGE_PARAMETER = "page"; + private static final String SIZE_PARAMETER = "size"; + private static final String SORT_PARAMETER = "sort"; + private static final String NAME = "name"; + private static final int DEFAULT_PAGE = 1; + private static final int DEFAULT_SIZE = 10; + + @Autowired + private CustomPageRequestArgumentResolver customPageRequestArgumentResolver; + + private MockHttpServletRequest request; + private NativeWebRequest webRequest; + private MethodParameter parameter; + + @BeforeEach + void setUp() { + request = new MockHttpServletRequest(); + webRequest = new ServletWebRequest(request); + parameter = mock(MethodParameter.class); + } + + @Test + void CustomPageRequest_타입의_파라미터를_지원한다() { + // given + given(parameter.getParameterType()).willReturn((Class) CustomPageRequest.class); + + // when & then + assertThat(customPageRequestArgumentResolver.supportsParameter(parameter)).isTrue(); + } + + @Test + void CustomPageRequest_타입이_아닌_파라미터는_지원하지_않는다() { + // given + given(parameter.getParameterType()).willReturn((Class) String.class); + + // when & then + assertThat(customPageRequestArgumentResolver.supportsParameter(parameter)).isFalse(); + } + + @Nested + class 페이지_파라미터_추출_테스트 { + + @Test + void 페이지_파라미터가_없으면_기본값을_사용한다() { + // when + CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageRequest.getPage()).isEqualTo(DEFAULT_PAGE); + assertThat(pageRequest.getSize()).isEqualTo(DEFAULT_SIZE); + } + + @Test + void 페이지_파라미터가_있으면_해당_값을_사용한다() { + // given + int expectedPage = 2; + int expectedSize = 20; + request.setParameter(PAGE_PARAMETER, String.valueOf(expectedPage)); + request.setParameter(SIZE_PARAMETER, String.valueOf(expectedSize)); + + // when + CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageRequest.getPage()).isEqualTo(expectedPage); + assertThat(pageRequest.getSize()).isEqualTo(expectedSize); + } + + @Test + void 페이지_파라미터가_숫자가_아니면_기본값을_사용한다() { + // given + request.setParameter(PAGE_PARAMETER, "invalid"); + request.setParameter(SIZE_PARAMETER, "invalid"); + + // when + CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageRequest.getPage()).isEqualTo(DEFAULT_PAGE); + assertThat(pageRequest.getSize()).isEqualTo(DEFAULT_SIZE); + } + } + + @Nested + class 정렬_파라미터_추출_테스트 { + + @Test + void 정렬_파라미터가_없으면_정렬하지_않는다() { + // when + CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageRequest.getSort().isSorted()).isFalse(); + } + + @Test + void 정렬_파라미터가_있으면_해당_값으로_정렬한다() { + // given + request.addParameter(SORT_PARAMETER, NAME + ",asc"); + + // when + CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageRequest.getSort().isSorted()).isTrue(); + assertThat(pageRequest.getSort().getOrderFor(NAME).getDirection()).isEqualTo(Sort.Direction.ASC); + } + + @Test + void 정렬_방향을_명시하지_않으면_기본값으로_오름차순을_사용한다() { + // given + request.addParameter(SORT_PARAMETER, NAME); + + // when + CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageRequest.getSort().isSorted()).isTrue(); + assertThat(pageRequest.getSort().getOrderFor(NAME).getDirection()).isEqualTo(Sort.Direction.ASC); + } + } +} diff --git a/src/test/java/com/example/solidconnection/util/PagingUtilsTest.java b/src/test/java/com/example/solidconnection/util/PagingUtilsTest.java deleted file mode 100644 index f8a10a473..000000000 --- a/src/test/java/com/example/solidconnection/util/PagingUtilsTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.example.solidconnection.util; - -import com.example.solidconnection.custom.exception.CustomException; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_PAGE; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_SIZE; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; - -@DisplayName("PagingUtils 테스트") -class PagingUtilsTest { - - private static final int VALID_PAGE = 1; - private static final int VALID_SIZE = 10; - - private static final int MIN_PAGE = 1; - private static final int MAX_PAGE = 50; - private static final int MIN_SIZE = 1; - private static final int MAX_SIZE = 50; - - @Test - @DisplayName("유효한 페이지 번호와 크기가 주어지면 예외가 발생하지 않는다") - void validateValidPageAndSize() { - // when & then - assertThatCode(() -> PagingUtils.validatePage(VALID_PAGE, VALID_SIZE)) - .doesNotThrowAnyException(); - } - - @Test - void 최소_페이지_번호보다_작으면_예외_응답을_반환한다() { - // when & then - assertThatCode(() -> PagingUtils.validatePage(MIN_PAGE - 1, VALID_SIZE)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_PAGE.getMessage()); - } - - @Test - void 최대_페이지_번호보다_크면_예외_응답을_반환한다() { - // when & then - assertThatCode(() -> PagingUtils.validatePage(MAX_PAGE + 1, VALID_SIZE)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_PAGE.getMessage()); - } - - @Test - void 최소_페이지_크기보다_작으면_예외_응답을_반환한다() { - // when & then - assertThatCode(() -> PagingUtils.validatePage(VALID_PAGE, MIN_SIZE - 1)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_SIZE.getMessage()); - } - - @Test - void 최대_페이지_크기보다_크면_예외_응답을_반환한다() { - // when & then - assertThatCode(() -> PagingUtils.validatePage(VALID_PAGE, MAX_SIZE + 1)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_SIZE.getMessage()); - } -} From c91aa31864f1cdb24b165ec1b48194b4f30835c2 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:24:35 +0900 Subject: [PATCH 02/11] =?UTF-8?q?refactor:=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20PageRequest=20=EB=B0=8F=20ArgumentResolver=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AdminScoreController.java | 26 ++++++++----------- .../config/web/WebMvcConfig.java | 5 +++- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java b/src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java index ca0692936..bea1066f9 100644 --- a/src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java +++ b/src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java @@ -9,18 +9,14 @@ import com.example.solidconnection.admin.dto.ScoreSearchCondition; import com.example.solidconnection.admin.service.AdminGpaScoreService; import com.example.solidconnection.admin.service.AdminLanguageTestScoreService; +import com.example.solidconnection.custom.request.CustomPageRequest; import com.example.solidconnection.custom.response.PageResponse; -import com.example.solidconnection.util.PagingUtils; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -35,15 +31,15 @@ public class AdminScoreController { private final AdminGpaScoreService adminGpaScoreService; private final AdminLanguageTestScoreService adminLanguageTestScoreService; - // todo: 추후 커스텀 페이지 객체 & argumentResolver를 적용 필요 @GetMapping("/gpas") public ResponseEntity> searchGpaScores( @Valid @ModelAttribute ScoreSearchCondition scoreSearchCondition, - @PageableDefault(page = 1) Pageable pageable + CustomPageRequest customPageRequest ) { - PagingUtils.validatePage(pageable.getPageNumber(), pageable.getPageSize()); - Pageable internalPageable = PageRequest.of(pageable.getPageNumber() - 1, pageable.getPageSize()); - Page page = adminGpaScoreService.searchGpaScores(scoreSearchCondition, internalPageable); + Page page = adminGpaScoreService.searchGpaScores( + scoreSearchCondition, + customPageRequest.toPageable() + ); return ResponseEntity.ok(PageResponse.of(page)); } @@ -56,15 +52,15 @@ public ResponseEntity updateGpaScore( return ResponseEntity.ok(response); } - // todo: 추후 커스텀 페이지 객체 & argumentResolver를 적용 필요 @GetMapping("/language-tests") public ResponseEntity> searchLanguageTestScores( @Valid @ModelAttribute ScoreSearchCondition scoreSearchCondition, - @PageableDefault(page = 1) Pageable pageable + CustomPageRequest customPageRequest ) { - PagingUtils.validatePage(pageable.getPageNumber(), pageable.getPageSize()); - Pageable internalPageable = PageRequest.of(pageable.getPageNumber() - 1, pageable.getPageSize()); - Page page = adminLanguageTestScoreService.searchLanguageTestScores(scoreSearchCondition, internalPageable); + Page page = adminLanguageTestScoreService.searchLanguageTestScores( + scoreSearchCondition, + customPageRequest.toPageable() + ); return ResponseEntity.ok(PageResponse.of(page)); } diff --git a/src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java b/src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java index 10e468f56..7ae90b834 100644 --- a/src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java +++ b/src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java @@ -2,6 +2,7 @@ import com.example.solidconnection.custom.resolver.AuthorizedUserResolver; +import com.example.solidconnection.custom.resolver.CustomPageRequestArgumentResolver; import com.example.solidconnection.custom.resolver.ExpiredTokenResolver; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; @@ -16,12 +17,14 @@ public class WebMvcConfig implements WebMvcConfigurer { private final AuthorizedUserResolver authorizedUserResolver; private final ExpiredTokenResolver expiredTokenResolver; + private final CustomPageRequestArgumentResolver customPageRequestArgumentResolver; @Override public void addArgumentResolvers(List resolvers) { resolvers.addAll(List.of( authorizedUserResolver, - expiredTokenResolver + expiredTokenResolver, + customPageRequestArgumentResolver )); } } From 8e1487c76d09861c6a78a2172e2ca4e985112772 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:26:17 +0900 Subject: [PATCH 03/11] =?UTF-8?q?refactor:=20=EB=A7=A4=EC=A7=81=20?= =?UTF-8?q?=EB=84=98=EB=B2=84=20=EC=83=81=EC=88=98=EB=A1=9C=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../solidconnection/custom/exception/ErrorCode.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java index 52bab0980..eb7bb590d 100644 --- a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java @@ -5,6 +5,9 @@ import org.springframework.http.HttpStatus; import static com.example.solidconnection.application.service.ApplicationSubmissionService.APPLICATION_UPDATE_COUNT_LIMIT; +import static com.example.solidconnection.custom.request.CustomPageRequest.MAX_SIZE; +import static com.example.solidconnection.custom.request.CustomPageRequest.MIN_PAGE; +import static com.example.solidconnection.custom.request.CustomPageRequest.MIN_SIZE; import static com.example.solidconnection.siteuser.service.SiteUserService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; @Getter @@ -97,8 +100,8 @@ public enum ErrorCode { REJECTED_REASON_REQUIRED(HttpStatus.BAD_REQUEST.value(), "거절 사유가 필요합니다."), // page - INVALID_PAGE(HttpStatus.BAD_REQUEST.value(), "페이지 번호는 1 이상 50 이하만 가능합니다."), - INVALID_SIZE(HttpStatus.BAD_REQUEST.value(), "페이지 크기는 1 이상 50 이하만 가능합니다."), + INVALID_PAGE(HttpStatus.BAD_REQUEST.value(), "페이지 번호는 " + MIN_PAGE + " 이상만 가능합니다."), + INVALID_SIZE(HttpStatus.BAD_REQUEST.value(), "페이지 크기는 " + MIN_SIZE + " 이상 " + MAX_SIZE + " 이하만 가능합니다."), // general JSON_PARSING_FAILED(HttpStatus.BAD_REQUEST.value(), "JSON 파싱을 할 수 없습니다."), From d4f128ab143cab234e8a40b63bd3b45f2d523c4b Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:07:36 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9A=94=EC=B2=AD=20argument=20r?= =?UTF-8?q?esolver=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom/exception/ErrorCode.java | 6 +- ...PageableHandlerMethodArgumentResolver.java | 67 ++++++++ ...ableHandlerMethodArgumentResolverTest.java | 154 ++++++++++++++++++ 3 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java create mode 100644 src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java diff --git a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java index eb7bb590d..4afb89dc2 100644 --- a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java @@ -5,9 +5,9 @@ import org.springframework.http.HttpStatus; import static com.example.solidconnection.application.service.ApplicationSubmissionService.APPLICATION_UPDATE_COUNT_LIMIT; -import static com.example.solidconnection.custom.request.CustomPageRequest.MAX_SIZE; -import static com.example.solidconnection.custom.request.CustomPageRequest.MIN_PAGE; -import static com.example.solidconnection.custom.request.CustomPageRequest.MIN_SIZE; +import static com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver.MAX_SIZE; +import static com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver.MIN_PAGE; +import static com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver.MIN_SIZE; import static com.example.solidconnection.siteuser.service.SiteUserService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; @Getter diff --git a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java new file mode 100644 index 000000000..8e6ca732a --- /dev/null +++ b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java @@ -0,0 +1,67 @@ +package com.example.solidconnection.custom.resolver; + +import com.example.solidconnection.custom.exception.CustomException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.MethodParameter; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableHandlerMethodArgumentResolver; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.ModelAndViewContainer; + +import jakarta.servlet.http.HttpServletRequest; +import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_PAGE; +import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_SIZE; + +@Component +public class CustomPageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolver { + + public static final int MIN_PAGE = 1; + public static final int MIN_SIZE = 1; + public static final int MAX_SIZE = 50; + private static final String PAGE_PARAMETER = "page"; + private static final String SIZE_PARAMETER = "size"; + + public CustomPageableHandlerMethodArgumentResolver() { + setMaxPageSize(MAX_SIZE); + setOneIndexedParameters(true); + setFallbackPageable(PageRequest.of(0, 10)); + } + + @Override + public Pageable resolveArgument(MethodParameter methodParameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); + if (request != null) { + validateParameters(request); + } + return super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); + } + + private void validateParameters(HttpServletRequest request) { + int page = extractIntParameter(request, PAGE_PARAMETER, 1); + int size = extractIntParameter(request, SIZE_PARAMETER, 10); + if (page < MIN_PAGE) { + throw new CustomException(INVALID_PAGE); + } + if (size < MIN_SIZE || size > MAX_SIZE) { + throw new CustomException(INVALID_SIZE); + } + } + + private int extractIntParameter(HttpServletRequest request, String paramName, int defaultValue) { + String paramValue = request.getParameter(paramName); + if (StringUtils.isBlank(paramValue)) { + return defaultValue; + } + try { + return Integer.parseInt(paramValue); + } catch (NumberFormatException e) { + return defaultValue; + } + } +} diff --git a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java new file mode 100644 index 000000000..2054de246 --- /dev/null +++ b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java @@ -0,0 +1,154 @@ +package com.example.solidconnection.custom.resolver; + +import com.example.solidconnection.custom.exception.CustomException; +import com.example.solidconnection.support.TestContainerSpringBootTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.MethodParameter; +import org.springframework.data.domain.Pageable; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.ServletWebRequest; + +import java.lang.reflect.Method; + +import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_PAGE; +import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_SIZE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; + +@TestContainerSpringBootTest +@DisplayName("커스텀 페이지 요청 argument resolver 테스트") +class CustomPageableHandlerMethodArgumentResolverTest { + + private static final String PAGE_PARAMETER = "page"; + private static final String SIZE_PARAMETER = "size"; + private static final int DEFAULT_PAGE = 1; + private static final int DEFAULT_SIZE = 10; + + @Autowired + private CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver; + + private MockHttpServletRequest request; + private NativeWebRequest webRequest; + private MethodParameter parameter; + + @BeforeEach + void setUp() throws NoSuchMethodException { + request = new MockHttpServletRequest(); + webRequest = new ServletWebRequest(request); + Method method = TestController.class.getMethod("pageableMethod", Pageable.class); + parameter = new MethodParameter(method, 0); + } + + @Test + void 파라미터가_없으면_기본값을_사용한다() { + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); + } + + @Test + void 유효한_파라미터가_있으면_해당_값을_사용한다() { + // given + int expectedPage = 2; + int expectedSize = 20; + request.setParameter(PAGE_PARAMETER, String.valueOf(expectedPage)); + request.setParameter(SIZE_PARAMETER, String.valueOf(expectedSize)); + + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(expectedPage - 1); + assertThat(pageable.getPageSize()).isEqualTo(expectedSize); + } + + @Test + void 파라미터가_숫자가_아니면_기본값을_사용한다() { + // given + request.setParameter(PAGE_PARAMETER, "invalid"); + request.setParameter(SIZE_PARAMETER, "invalid"); + + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); + } + + @Test + void 페이지_파라미터가_최소값보다_작으면_예외_응답을_반환한다() { + // given + request.setParameter(PAGE_PARAMETER, "0"); + + // when & then + assertThatCode(() -> customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null)) + .isInstanceOf(CustomException.class) + .hasMessage(INVALID_PAGE.getMessage()); + } + + @Test + void 페이지_파라미터가_음수이면_예외_응답을_반환한다() { + // given + request.setParameter(PAGE_PARAMETER, "-1"); + + // when & then + assertThatCode(() -> customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null)) + .isInstanceOf(CustomException.class) + .hasMessage(INVALID_PAGE.getMessage()); + } + + @Test + void 사이즈_파라미터가_최소값보다_작으면_예외_응답을_반환한다() { + // given + request.setParameter(SIZE_PARAMETER, "0"); + + // when & then + assertThatCode(() -> customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null)) + .isInstanceOf(CustomException.class) + .hasMessage(INVALID_SIZE.getMessage()); + } + + @Test + void 사이즈_파라미터가_최대값보다_크면_예외가_발생한다() { + // given + request.setParameter(SIZE_PARAMETER, String.valueOf(CustomPageableHandlerMethodArgumentResolver.MAX_SIZE + 1)); + + // when & then + assertThatCode(() -> customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null)) + .isInstanceOf(CustomException.class) + .hasMessage(INVALID_SIZE.getMessage()); + } + + @Test + void 사이즈_파라미터가_음수이면_예외_응답을_반환한다() { + // given + request.setParameter(SIZE_PARAMETER, "-1"); + + // when & then + assertThatCode(() -> customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null)) + .isInstanceOf(CustomException.class) + .hasMessage(INVALID_SIZE.getMessage()); + } + + private static class TestController { + + public void pageableMethod(Pageable pageable) { + } + } +} From c2f0d606b6a134686adad23bd53d6d2fca5bb42f Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Wed, 5 Mar 2025 15:07:52 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9A=94=EC=B2=AD=20argument=20r?= =?UTF-8?q?esolver=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AdminScoreController.java | 10 +- .../config/web/WebMvcConfig.java | 7 +- .../custom/request/CustomPageRequest.java | 51 ------ .../CustomPageRequestArgumentResolver.java | 82 ---------- ...CustomPageRequestArgumentResolverTest.java | 151 ------------------ 5 files changed, 8 insertions(+), 293 deletions(-) delete mode 100644 src/main/java/com/example/solidconnection/custom/request/CustomPageRequest.java delete mode 100644 src/main/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolver.java delete mode 100644 src/test/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolverTest.java diff --git a/src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java b/src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java index bea1066f9..47bac37e1 100644 --- a/src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java +++ b/src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java @@ -9,11 +9,11 @@ import com.example.solidconnection.admin.dto.ScoreSearchCondition; import com.example.solidconnection.admin.service.AdminGpaScoreService; import com.example.solidconnection.admin.service.AdminLanguageTestScoreService; -import com.example.solidconnection.custom.request.CustomPageRequest; import com.example.solidconnection.custom.response.PageResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; @@ -34,11 +34,11 @@ public class AdminScoreController { @GetMapping("/gpas") public ResponseEntity> searchGpaScores( @Valid @ModelAttribute ScoreSearchCondition scoreSearchCondition, - CustomPageRequest customPageRequest + Pageable pageable ) { Page page = adminGpaScoreService.searchGpaScores( scoreSearchCondition, - customPageRequest.toPageable() + pageable ); return ResponseEntity.ok(PageResponse.of(page)); } @@ -55,11 +55,11 @@ public ResponseEntity updateGpaScore( @GetMapping("/language-tests") public ResponseEntity> searchLanguageTestScores( @Valid @ModelAttribute ScoreSearchCondition scoreSearchCondition, - CustomPageRequest customPageRequest + Pageable pageable ) { Page page = adminLanguageTestScoreService.searchLanguageTestScores( scoreSearchCondition, - customPageRequest.toPageable() + pageable ); return ResponseEntity.ok(PageResponse.of(page)); } diff --git a/src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java b/src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java index 7ae90b834..6d16694cc 100644 --- a/src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java +++ b/src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java @@ -1,8 +1,7 @@ package com.example.solidconnection.config.web; - import com.example.solidconnection.custom.resolver.AuthorizedUserResolver; -import com.example.solidconnection.custom.resolver.CustomPageRequestArgumentResolver; +import com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver; import com.example.solidconnection.custom.resolver.ExpiredTokenResolver; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; @@ -17,14 +16,14 @@ public class WebMvcConfig implements WebMvcConfigurer { private final AuthorizedUserResolver authorizedUserResolver; private final ExpiredTokenResolver expiredTokenResolver; - private final CustomPageRequestArgumentResolver customPageRequestArgumentResolver; + private final CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver; @Override public void addArgumentResolvers(List resolvers) { resolvers.addAll(List.of( authorizedUserResolver, expiredTokenResolver, - customPageRequestArgumentResolver + customPageableHandlerMethodArgumentResolver )); } } diff --git a/src/main/java/com/example/solidconnection/custom/request/CustomPageRequest.java b/src/main/java/com/example/solidconnection/custom/request/CustomPageRequest.java deleted file mode 100644 index 90f4b8dc8..000000000 --- a/src/main/java/com/example/solidconnection/custom/request/CustomPageRequest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.example.solidconnection.custom.request; - -import com.example.solidconnection.custom.exception.CustomException; -import lombok.Getter; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; - -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_PAGE; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_SIZE; - -@Getter -public class CustomPageRequest { - - public static final int MIN_PAGE = 1; - public static final int MIN_SIZE = 1; - public static final int MAX_SIZE = 50; - - private final int page; - private final int size; - private Sort sort = Sort.unsorted(); - - public CustomPageRequest(int page, int size) { - validatePageParameters(page, size); - this.page = page; - this.size = size; - } - - public CustomPageRequest(int page, int size, Sort sort) { - this(page, size); - this.sort = sort; - } - - // 1-based -> 0-based 변환 - public Pageable toPageable() { - return PageRequest.of(page - 1, size, sort); - } - - private static void validatePageParameters(int page, int size) { - if (page < MIN_PAGE) { - throw new CustomException(INVALID_PAGE); - } - if (size < MIN_SIZE || size > MAX_SIZE) { - throw new CustomException(INVALID_SIZE); - } - } - - public static CustomPageRequest of(int page, int size, Sort sort) { - return new CustomPageRequest(page, size, sort); - } -} diff --git a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolver.java b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolver.java deleted file mode 100644 index c28103355..000000000 --- a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolver.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.example.solidconnection.custom.resolver; - -import com.example.solidconnection.custom.request.CustomPageRequest; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import org.springframework.core.MethodParameter; -import org.springframework.data.domain.Sort; -import org.springframework.stereotype.Component; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.method.support.ModelAndViewContainer; - -import java.util.ArrayList; -import java.util.List; - -@Component -@RequiredArgsConstructor -public class CustomPageRequestArgumentResolver implements HandlerMethodArgumentResolver { - - private static final String PAGE_PARAMETER = "page"; - private static final String SIZE_PARAMETER = "size"; - private static final String SORT_PARAMETER = "sort"; - private static final String DESC = "desc"; - private static final String COMMA = ","; - private static final int DEFAULT_PAGE = 1; - private static final int DEFAULT_SIZE = 10; - - @Override - public boolean supportsParameter(MethodParameter parameter) { - return CustomPageRequest.class.isAssignableFrom(parameter.getParameterType()); - } - - @Override - public Object resolveArgument(MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) { - HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); - int page = extractIntParameter(request, PAGE_PARAMETER, DEFAULT_PAGE); - int size = extractIntParameter(request, SIZE_PARAMETER, DEFAULT_SIZE); - Sort sort = extractSortParameter(request); - return CustomPageRequest.of(page, size, sort); - } - - private int extractIntParameter(HttpServletRequest request, String paramName, int defaultValue) { - String paramValue = request.getParameter(paramName); - if (paramValue != null && !paramValue.isEmpty()) { - try { - return Integer.parseInt(paramValue); - } catch (NumberFormatException e) { - // 숫자로 변환할 수 없는 경우 기본값 사용 - } - } - return defaultValue; - } - - private Sort extractSortParameter(HttpServletRequest request) { - String[] sortParams = request.getParameterValues(SORT_PARAMETER); - if (sortParams == null || sortParams.length == 0) { - return Sort.unsorted(); - } - List orders = new ArrayList<>(); - for (String sortParam : sortParams) { - addSortOrder(sortParam, orders); - } - return orders.isEmpty() ? Sort.unsorted() : Sort.by(orders); - } - - private void addSortOrder(String sortParam, List orders) { - String[] parts = sortParam.split(COMMA); - if (parts.length < 1) { - return; - } - String property = parts[0]; - Sort.Direction direction = Sort.Direction.ASC; - if (parts.length >= 2 && DESC.equalsIgnoreCase(parts[1])) { - direction = Sort.Direction.DESC; - } - orders.add(new Sort.Order(direction, property)); - } -} diff --git a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolverTest.java deleted file mode 100644 index 00c727969..000000000 --- a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageRequestArgumentResolverTest.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.example.solidconnection.custom.resolver; - -import com.example.solidconnection.custom.request.CustomPageRequest; -import com.example.solidconnection.support.TestContainerSpringBootTest; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.MethodParameter; -import org.springframework.data.domain.Sort; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.context.request.ServletWebRequest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -@TestContainerSpringBootTest -@DisplayName("커스텀 페이지 요청 argument resolver 테스트") -class CustomPageRequestArgumentResolverTest { - - private static final String PAGE_PARAMETER = "page"; - private static final String SIZE_PARAMETER = "size"; - private static final String SORT_PARAMETER = "sort"; - private static final String NAME = "name"; - private static final int DEFAULT_PAGE = 1; - private static final int DEFAULT_SIZE = 10; - - @Autowired - private CustomPageRequestArgumentResolver customPageRequestArgumentResolver; - - private MockHttpServletRequest request; - private NativeWebRequest webRequest; - private MethodParameter parameter; - - @BeforeEach - void setUp() { - request = new MockHttpServletRequest(); - webRequest = new ServletWebRequest(request); - parameter = mock(MethodParameter.class); - } - - @Test - void CustomPageRequest_타입의_파라미터를_지원한다() { - // given - given(parameter.getParameterType()).willReturn((Class) CustomPageRequest.class); - - // when & then - assertThat(customPageRequestArgumentResolver.supportsParameter(parameter)).isTrue(); - } - - @Test - void CustomPageRequest_타입이_아닌_파라미터는_지원하지_않는다() { - // given - given(parameter.getParameterType()).willReturn((Class) String.class); - - // when & then - assertThat(customPageRequestArgumentResolver.supportsParameter(parameter)).isFalse(); - } - - @Nested - class 페이지_파라미터_추출_테스트 { - - @Test - void 페이지_파라미터가_없으면_기본값을_사용한다() { - // when - CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageRequest.getPage()).isEqualTo(DEFAULT_PAGE); - assertThat(pageRequest.getSize()).isEqualTo(DEFAULT_SIZE); - } - - @Test - void 페이지_파라미터가_있으면_해당_값을_사용한다() { - // given - int expectedPage = 2; - int expectedSize = 20; - request.setParameter(PAGE_PARAMETER, String.valueOf(expectedPage)); - request.setParameter(SIZE_PARAMETER, String.valueOf(expectedSize)); - - // when - CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageRequest.getPage()).isEqualTo(expectedPage); - assertThat(pageRequest.getSize()).isEqualTo(expectedSize); - } - - @Test - void 페이지_파라미터가_숫자가_아니면_기본값을_사용한다() { - // given - request.setParameter(PAGE_PARAMETER, "invalid"); - request.setParameter(SIZE_PARAMETER, "invalid"); - - // when - CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageRequest.getPage()).isEqualTo(DEFAULT_PAGE); - assertThat(pageRequest.getSize()).isEqualTo(DEFAULT_SIZE); - } - } - - @Nested - class 정렬_파라미터_추출_테스트 { - - @Test - void 정렬_파라미터가_없으면_정렬하지_않는다() { - // when - CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageRequest.getSort().isSorted()).isFalse(); - } - - @Test - void 정렬_파라미터가_있으면_해당_값으로_정렬한다() { - // given - request.addParameter(SORT_PARAMETER, NAME + ",asc"); - - // when - CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageRequest.getSort().isSorted()).isTrue(); - assertThat(pageRequest.getSort().getOrderFor(NAME).getDirection()).isEqualTo(Sort.Direction.ASC); - } - - @Test - void 정렬_방향을_명시하지_않으면_기본값으로_오름차순을_사용한다() { - // given - request.addParameter(SORT_PARAMETER, NAME); - - // when - CustomPageRequest pageRequest = (CustomPageRequest) customPageRequestArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageRequest.getSort().isSorted()).isTrue(); - assertThat(pageRequest.getSort().getOrderFor(NAME).getDirection()).isEqualTo(Sort.Direction.ASC); - } - } -} From e75eb4669b61380cbc4a651f9c4d0b561a8a67ea Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:18:12 +0900 Subject: [PATCH 06/11] =?UTF-8?q?refactor:=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EA=B0=92=EC=97=90=20=EB=8C=80=ED=95=B4=EC=84=9C=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom/exception/ErrorCode.java | 9 +-- ...PageableHandlerMethodArgumentResolver.java | 43 ++--------- ...ableHandlerMethodArgumentResolverTest.java | 77 ++++++++++--------- 3 files changed, 48 insertions(+), 81 deletions(-) diff --git a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java index 4afb89dc2..1a4e46b72 100644 --- a/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java +++ b/src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java @@ -5,10 +5,7 @@ import org.springframework.http.HttpStatus; import static com.example.solidconnection.application.service.ApplicationSubmissionService.APPLICATION_UPDATE_COUNT_LIMIT; -import static com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver.MAX_SIZE; -import static com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver.MIN_PAGE; -import static com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver.MIN_SIZE; -import static com.example.solidconnection.siteuser.service.SiteUserService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; +import static com.example.solidconnection.siteuser.service.MyPageService.MIN_DAYS_BETWEEN_NICKNAME_CHANGES; @Getter @AllArgsConstructor @@ -99,10 +96,6 @@ public enum ErrorCode { USER_DO_NOT_HAVE_GPA(HttpStatus.BAD_REQUEST.value(), "해당 유저의 학점을 찾을 수 없음"), REJECTED_REASON_REQUIRED(HttpStatus.BAD_REQUEST.value(), "거절 사유가 필요합니다."), - // page - INVALID_PAGE(HttpStatus.BAD_REQUEST.value(), "페이지 번호는 " + MIN_PAGE + " 이상만 가능합니다."), - INVALID_SIZE(HttpStatus.BAD_REQUEST.value(), "페이지 크기는 " + MIN_SIZE + " 이상 " + MAX_SIZE + " 이하만 가능합니다."), - // general JSON_PARSING_FAILED(HttpStatus.BAD_REQUEST.value(), "JSON 파싱을 할 수 없습니다."), JWT_EXCEPTION(HttpStatus.BAD_REQUEST.value(), "JWT 토큰을 처리할 수 없습니다."), diff --git a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java index 8e6ca732a..2be34796f 100644 --- a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java +++ b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java @@ -1,7 +1,5 @@ package com.example.solidconnection.custom.resolver; -import com.example.solidconnection.custom.exception.CustomException; -import org.apache.commons.lang3.StringUtils; import org.springframework.core.MethodParameter; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -11,21 +9,13 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; -import jakarta.servlet.http.HttpServletRequest; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_PAGE; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_SIZE; - @Component public class CustomPageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolver { - public static final int MIN_PAGE = 1; - public static final int MIN_SIZE = 1; public static final int MAX_SIZE = 50; - private static final String PAGE_PARAMETER = "page"; - private static final String SIZE_PARAMETER = "size"; + private static final int DEFAULT_SIZE = 10; public CustomPageableHandlerMethodArgumentResolver() { - setMaxPageSize(MAX_SIZE); setOneIndexedParameters(true); setFallbackPageable(PageRequest.of(0, 10)); } @@ -35,33 +25,10 @@ public Pageable resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { - HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); - if (request != null) { - validateParameters(request); - } - return super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); - } - - private void validateParameters(HttpServletRequest request) { - int page = extractIntParameter(request, PAGE_PARAMETER, 1); - int size = extractIntParameter(request, SIZE_PARAMETER, 10); - if (page < MIN_PAGE) { - throw new CustomException(INVALID_PAGE); - } - if (size < MIN_SIZE || size > MAX_SIZE) { - throw new CustomException(INVALID_SIZE); - } - } - - private int extractIntParameter(HttpServletRequest request, String paramName, int defaultValue) { - String paramValue = request.getParameter(paramName); - if (StringUtils.isBlank(paramValue)) { - return defaultValue; - } - try { - return Integer.parseInt(paramValue); - } catch (NumberFormatException e) { - return defaultValue; + Pageable pageable = super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); + if (pageable.getPageSize() > MAX_SIZE) { + return PageRequest.of(pageable.getPageNumber(), DEFAULT_SIZE, pageable.getSort()); } + return pageable; } } diff --git a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java index 2054de246..9f78a1a3f 100644 --- a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java +++ b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java @@ -1,6 +1,5 @@ package com.example.solidconnection.custom.resolver; -import com.example.solidconnection.custom.exception.CustomException; import com.example.solidconnection.support.TestContainerSpringBootTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -14,10 +13,8 @@ import java.lang.reflect.Method; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_PAGE; -import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_SIZE; +import static com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver.MAX_SIZE; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; @TestContainerSpringBootTest @DisplayName("커스텀 페이지 요청 argument resolver 테스트") @@ -87,63 +84,73 @@ void setUp() throws NoSuchMethodException { } @Test - void 페이지_파라미터가_최소값보다_작으면_예외_응답을_반환한다() { + void 페이지_파라미터가_최소값보다_작으면_기본값을_사용한다() { // given request.setParameter(PAGE_PARAMETER, "0"); - // when & then - assertThatCode(() -> customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_PAGE.getMessage()); + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @Test - void 페이지_파라미터가_음수이면_예외_응답을_반환한다() { + void 페이지_파라미터가_음수이면_기본값을_사용한다() { // given request.setParameter(PAGE_PARAMETER, "-1"); - // when & then - assertThatCode(() -> customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_PAGE.getMessage()); + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @Test - void 사이즈_파라미터가_최소값보다_작으면_예외_응답을_반환한다() { + void 사이즈_파라미터가_최소값보다_작으면_기본값을_사용한다() { // given request.setParameter(SIZE_PARAMETER, "0"); - // when & then - assertThatCode(() -> customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_SIZE.getMessage()); + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @Test - void 사이즈_파라미터가_최대값보다_크면_예외가_발생한다() { + void 사이즈_파라미터가_최대값보다_크면_기본값을_사용한다() { // given - request.setParameter(SIZE_PARAMETER, String.valueOf(CustomPageableHandlerMethodArgumentResolver.MAX_SIZE + 1)); + request.setParameter(SIZE_PARAMETER, String.valueOf(MAX_SIZE + 1)); - // when & then - assertThatCode(() -> customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_SIZE.getMessage()); + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @Test - void 사이즈_파라미터가_음수이면_예외_응답을_반환한다() { + void 사이즈_파라미터가_음수이면_기본값을_사용한다() { // given request.setParameter(SIZE_PARAMETER, "-1"); - // when & then - assertThatCode(() -> customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null)) - .isInstanceOf(CustomException.class) - .hasMessage(INVALID_SIZE.getMessage()); + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } private static class TestController { From a8731a31133e40790ec1aea0b245c4f3170e3056 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Fri, 4 Apr 2025 22:45:15 +0900 Subject: [PATCH 07/11] =?UTF-8?q?refactor:=20=EC=B5=9C=EB=8C=80=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EC=A6=88=20=EC=B4=88=EA=B3=BC=20=EC=8B=9C=20?= =?UTF-8?q?=EC=B5=9C=EB=8C=80=20=EC=82=AC=EC=9D=B4=EC=A6=88=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomPageableHandlerMethodArgumentResolver.java | 12 +++++------- ...tomPageableHandlerMethodArgumentResolverTest.java | 4 ++-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java index 2be34796f..4634fe15d 100644 --- a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java +++ b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java @@ -12,12 +12,14 @@ @Component public class CustomPageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolver { - public static final int MAX_SIZE = 50; + private static final int DEFAULT_PAGE = 0; + private static final int MAX_SIZE = 50; private static final int DEFAULT_SIZE = 10; public CustomPageableHandlerMethodArgumentResolver() { + setMaxPageSize(MAX_SIZE); setOneIndexedParameters(true); - setFallbackPageable(PageRequest.of(0, 10)); + setFallbackPageable(PageRequest.of(DEFAULT_PAGE, DEFAULT_SIZE)); } @Override @@ -25,10 +27,6 @@ public Pageable resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { - Pageable pageable = super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); - if (pageable.getPageSize() > MAX_SIZE) { - return PageRequest.of(pageable.getPageNumber(), DEFAULT_SIZE, pageable.getSort()); - } - return pageable; + return super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); } } diff --git a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java index 9f78a1a3f..f0ae052d3 100644 --- a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java +++ b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java @@ -13,7 +13,6 @@ import java.lang.reflect.Method; -import static com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver.MAX_SIZE; import static org.assertj.core.api.Assertions.assertThat; @TestContainerSpringBootTest @@ -24,6 +23,7 @@ class CustomPageableHandlerMethodArgumentResolverTest { private static final String SIZE_PARAMETER = "size"; private static final int DEFAULT_PAGE = 1; private static final int DEFAULT_SIZE = 10; + private static final int MAX_SIZE = 50; @Autowired private CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver; @@ -136,7 +136,7 @@ void setUp() throws NoSuchMethodException { // then assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); - assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); + assertThat(pageable.getPageSize()).isEqualTo(MAX_SIZE); } @Test From 758345934bc8d8aab7950bac179523c2e0537b68 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Fri, 4 Apr 2025 22:47:29 +0900 Subject: [PATCH 08/11] =?UTF-8?q?refactor:=20DEFAULT=5FPAGE=200=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ageableHandlerMethodArgumentResolverTest.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java index f0ae052d3..3647fc017 100644 --- a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java +++ b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java @@ -21,7 +21,7 @@ class CustomPageableHandlerMethodArgumentResolverTest { private static final String PAGE_PARAMETER = "page"; private static final String SIZE_PARAMETER = "size"; - private static final int DEFAULT_PAGE = 1; + private static final int DEFAULT_PAGE = 0; private static final int DEFAULT_SIZE = 10; private static final int MAX_SIZE = 50; @@ -47,7 +47,7 @@ void setUp() throws NoSuchMethodException { .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @@ -79,7 +79,7 @@ void setUp() throws NoSuchMethodException { .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @@ -93,7 +93,7 @@ void setUp() throws NoSuchMethodException { .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @@ -107,7 +107,7 @@ void setUp() throws NoSuchMethodException { .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @@ -121,7 +121,7 @@ void setUp() throws NoSuchMethodException { .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } @@ -135,7 +135,7 @@ void setUp() throws NoSuchMethodException { .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); assertThat(pageable.getPageSize()).isEqualTo(MAX_SIZE); } @@ -149,7 +149,7 @@ void setUp() throws NoSuchMethodException { .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE - 1); + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } From c3c133004dde2eee5b45286b6948b7315687d390 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Fri, 4 Apr 2025 23:09:25 +0900 Subject: [PATCH 09/11] =?UTF-8?q?refactor:=20ParameterizedTest=EB=A5=BC=20?= =?UTF-8?q?=ED=99=9C=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ableHandlerMethodArgumentResolverTest.java | 117 +++++------------- 1 file changed, 32 insertions(+), 85 deletions(-) diff --git a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java index 3647fc017..9cff06cb4 100644 --- a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java +++ b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java @@ -4,6 +4,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.data.domain.Pageable; @@ -12,6 +15,7 @@ import org.springframework.web.context.request.ServletWebRequest; import java.lang.reflect.Method; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -40,17 +44,6 @@ void setUp() throws NoSuchMethodException { parameter = new MethodParameter(method, 0); } - @Test - void 파라미터가_없으면_기본값을_사용한다() { - // when - Pageable pageable = customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); - assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); - } - @Test void 유효한_파라미터가_있으면_해당_값을_사용한다() { // given @@ -68,89 +61,43 @@ void setUp() throws NoSuchMethodException { assertThat(pageable.getPageSize()).isEqualTo(expectedSize); } - @Test - void 파라미터가_숫자가_아니면_기본값을_사용한다() { - // given - request.setParameter(PAGE_PARAMETER, "invalid"); - request.setParameter(SIZE_PARAMETER, "invalid"); - - // when - Pageable pageable = customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); - assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); - } - - @Test - void 페이지_파라미터가_최소값보다_작으면_기본값을_사용한다() { - // given - request.setParameter(PAGE_PARAMETER, "0"); - - // when - Pageable pageable = customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); - assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); - } - - @Test - void 페이지_파라미터가_음수이면_기본값을_사용한다() { - // given - request.setParameter(PAGE_PARAMETER, "-1"); - - // when - Pageable pageable = customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); - assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); - } - - @Test - void 사이즈_파라미터가_최소값보다_작으면_기본값을_사용한다() { + @ParameterizedTest(name = "{0}") + @MethodSource("provideInvalidParameters") + void 파라미터가_유효하지_않으면_기본_값을_사용한다(String testName, String pageParam, String sizeParam, int expectedPage, int expectedSize) { // given - request.setParameter(SIZE_PARAMETER, "0"); + request.setParameter(PAGE_PARAMETER, pageParam); + request.setParameter(SIZE_PARAMETER, sizeParam); // when Pageable pageable = customPageableHandlerMethodArgumentResolver .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); - assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); - } - - @Test - void 사이즈_파라미터가_최대값보다_크면_기본값을_사용한다() { - // given - request.setParameter(SIZE_PARAMETER, String.valueOf(MAX_SIZE + 1)); - - // when - Pageable pageable = customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); - assertThat(pageable.getPageSize()).isEqualTo(MAX_SIZE); + assertThat(pageable.getPageNumber()).isEqualTo(expectedPage); + assertThat(pageable.getPageSize()).isEqualTo(expectedSize); } - @Test - void 사이즈_파라미터가_음수이면_기본값을_사용한다() { - // given - request.setParameter(SIZE_PARAMETER, "-1"); - - // when - Pageable pageable = customPageableHandlerMethodArgumentResolver - .resolveArgument(parameter, null, webRequest, null); - - // then - assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); - assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); + static Stream provideInvalidParameters() { + return Stream.of( + Arguments.of("Page null", null, "20", DEFAULT_PAGE, 20), + Arguments.of("Page 빈 문자열", "", "20", DEFAULT_PAGE, 20), + Arguments.of("Page 0", "0", "20", DEFAULT_PAGE, 20), + Arguments.of("Page 음수", "-1", "20", DEFAULT_PAGE, 20), + Arguments.of("Page 문자열", "invalid", "20", DEFAULT_PAGE, 20), + + Arguments.of("Size null", "2", null, 1, DEFAULT_SIZE), + Arguments.of("Size 빈 문자열", "2", "", 1, DEFAULT_SIZE), + Arguments.of("Size 0", "2", "0", 1, DEFAULT_SIZE), + Arguments.of("Size 음수", "2", "-1", 1, DEFAULT_SIZE), + Arguments.of("Size 문자열", "2", "invalid", 1, DEFAULT_SIZE), + Arguments.of("Size 최대값 초과", "2", String.valueOf(MAX_SIZE + 1), 1, MAX_SIZE), + + Arguments.of("모두 null", null, null, DEFAULT_PAGE, DEFAULT_SIZE), + Arguments.of("모두 빈 문자열", "", "", DEFAULT_PAGE, DEFAULT_SIZE), + Arguments.of("모두 0", "0", "0", DEFAULT_PAGE, DEFAULT_SIZE), + Arguments.of("모두 음수", "-1", "-1", DEFAULT_PAGE, DEFAULT_SIZE), + Arguments.of("모두 문자열", "invalid", "invalid", DEFAULT_PAGE, DEFAULT_SIZE) + ); } private static class TestController { From 53aabb88e794f77e0ea9292f694d1ed7e49aa408 Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:44:32 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20override=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...CustomPageableHandlerMethodArgumentResolver.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java index 4634fe15d..418c6867f 100644 --- a/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java +++ b/src/main/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolver.java @@ -1,13 +1,8 @@ package com.example.solidconnection.custom.resolver; -import org.springframework.core.MethodParameter; import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableHandlerMethodArgumentResolver; import org.springframework.stereotype.Component; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.ModelAndViewContainer; @Component public class CustomPageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolver { @@ -21,12 +16,4 @@ public CustomPageableHandlerMethodArgumentResolver() { setOneIndexedParameters(true); setFallbackPageable(PageRequest.of(DEFAULT_PAGE, DEFAULT_SIZE)); } - - @Override - public Pageable resolveArgument(MethodParameter methodParameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) { - return super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory); - } } From 1f4819cad6e2597e8c9eab70f980a6d7e5da5e6e Mon Sep 17 00:00:00 2001 From: Gyuhyeok99 <126947828+Gyuhyeok99@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:56:55 +0900 Subject: [PATCH 11/11] =?UTF-8?q?refactor:=20=ED=8E=98=EC=9D=B4=EC=A7=95?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EA=B0=80=EB=8F=85?= =?UTF-8?q?=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ableHandlerMethodArgumentResolverTest.java | 73 +++++++++++++------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java index 9cff06cb4..dc628bc67 100644 --- a/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java +++ b/src/test/java/com/example/solidconnection/custom/resolver/CustomPageableHandlerMethodArgumentResolverTest.java @@ -45,12 +45,10 @@ void setUp() throws NoSuchMethodException { } @Test - void 유효한_파라미터가_있으면_해당_값을_사용한다() { + void 유효한_페이지_파라미터가_있으면_해당_값을_사용한다() { // given int expectedPage = 2; - int expectedSize = 20; request.setParameter(PAGE_PARAMETER, String.valueOf(expectedPage)); - request.setParameter(SIZE_PARAMETER, String.valueOf(expectedSize)); // when Pageable pageable = customPageableHandlerMethodArgumentResolver @@ -58,14 +56,55 @@ void setUp() throws NoSuchMethodException { // then assertThat(pageable.getPageNumber()).isEqualTo(expectedPage - 1); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); + } + + @Test + void 유효한_사이즈_파라미터가_있으면_해당_값을_사용한다() { + // given + int expectedSize = 20; + request.setParameter(SIZE_PARAMETER, String.valueOf(expectedSize)); + + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); assertThat(pageable.getPageSize()).isEqualTo(expectedSize); } + @Test + void 사이즈_파라미터가_최대값을_초과하면_최대값을_사용한다() { + // given + request.setParameter(SIZE_PARAMETER, String.valueOf(MAX_SIZE + 1)); + + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageSize()).isEqualTo(MAX_SIZE); + } + @ParameterizedTest(name = "{0}") @MethodSource("provideInvalidParameters") - void 파라미터가_유효하지_않으면_기본_값을_사용한다(String testName, String pageParam, String sizeParam, int expectedPage, int expectedSize) { + void 페이지_파라미터가_유효하지_않으면_기본_값을_사용한다(String testName, String pageParam) { // given request.setParameter(PAGE_PARAMETER, pageParam); + + // when + Pageable pageable = customPageableHandlerMethodArgumentResolver + .resolveArgument(parameter, null, webRequest, null); + + // then + assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("provideInvalidParameters") + void 사이즈_파라미터가_유효하지_않으면_기본_값을_사용한다(String testName, String sizeParam) { + // given request.setParameter(SIZE_PARAMETER, sizeParam); // when @@ -73,30 +112,16 @@ void setUp() throws NoSuchMethodException { .resolveArgument(parameter, null, webRequest, null); // then - assertThat(pageable.getPageNumber()).isEqualTo(expectedPage); - assertThat(pageable.getPageSize()).isEqualTo(expectedSize); + assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE); } static Stream provideInvalidParameters() { return Stream.of( - Arguments.of("Page null", null, "20", DEFAULT_PAGE, 20), - Arguments.of("Page 빈 문자열", "", "20", DEFAULT_PAGE, 20), - Arguments.of("Page 0", "0", "20", DEFAULT_PAGE, 20), - Arguments.of("Page 음수", "-1", "20", DEFAULT_PAGE, 20), - Arguments.of("Page 문자열", "invalid", "20", DEFAULT_PAGE, 20), - - Arguments.of("Size null", "2", null, 1, DEFAULT_SIZE), - Arguments.of("Size 빈 문자열", "2", "", 1, DEFAULT_SIZE), - Arguments.of("Size 0", "2", "0", 1, DEFAULT_SIZE), - Arguments.of("Size 음수", "2", "-1", 1, DEFAULT_SIZE), - Arguments.of("Size 문자열", "2", "invalid", 1, DEFAULT_SIZE), - Arguments.of("Size 최대값 초과", "2", String.valueOf(MAX_SIZE + 1), 1, MAX_SIZE), - - Arguments.of("모두 null", null, null, DEFAULT_PAGE, DEFAULT_SIZE), - Arguments.of("모두 빈 문자열", "", "", DEFAULT_PAGE, DEFAULT_SIZE), - Arguments.of("모두 0", "0", "0", DEFAULT_PAGE, DEFAULT_SIZE), - Arguments.of("모두 음수", "-1", "-1", DEFAULT_PAGE, DEFAULT_SIZE), - Arguments.of("모두 문자열", "invalid", "invalid", DEFAULT_PAGE, DEFAULT_SIZE) + Arguments.of("null", null), + Arguments.of("빈 문자열", ""), + Arguments.of("0", "0"), + Arguments.of("음수", "-1"), + Arguments.of("문자열", "invalid") ); }