Skip to content

Commit df3fc91

Browse files
authored
refactor: @PageableDefault 및 페이지 검증 로직을 ArgumentResolver로 개선 (#247)
* refactor: 커스텀 PageRequest 및 ArgumentResolver 추가 * refactor: 커스텀 PageRequest 및 ArgumentResolver 등록 및 적용 * refactor: 매직 넘버 상수로 교체 * feat: 커스텀 페이지 요청 argument resolver 추가 * feat: 커스텀 페이지 요청 argument resolver 적용 * refactor: 모든 유효하지 않는 값에 대해서 기본값으로 대체 * refactor: 최대 사이즈 초과 시 최대 사이즈 반환하도록 수정 * refactor: DEFAULT_PAGE 0으로 변경 * refactor: ParameterizedTest를 활용하여 중복 테스트 코드 제거 * refactor: 불필요한 override 제거 * refactor: 페이징 처리 테스트코드 분리 및 가독성 개선
1 parent 7c0ab90 commit df3fc91

File tree

7 files changed

+166
-107
lines changed

7 files changed

+166
-107
lines changed

src/main/java/com/example/solidconnection/admin/controller/AdminScoreController.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,13 @@
1010
import com.example.solidconnection.admin.service.AdminGpaScoreService;
1111
import com.example.solidconnection.admin.service.AdminLanguageTestScoreService;
1212
import com.example.solidconnection.custom.response.PageResponse;
13-
import com.example.solidconnection.util.PagingUtils;
1413
import jakarta.validation.Valid;
1514
import lombok.RequiredArgsConstructor;
1615
import org.springframework.data.domain.Page;
17-
import org.springframework.data.domain.PageRequest;
1816
import org.springframework.data.domain.Pageable;
19-
import org.springframework.data.web.PageableDefault;
2017
import org.springframework.http.ResponseEntity;
2118
import org.springframework.web.bind.annotation.GetMapping;
2219
import org.springframework.web.bind.annotation.ModelAttribute;
23-
import org.springframework.web.bind.annotation.PatchMapping;
2420
import org.springframework.web.bind.annotation.PathVariable;
2521
import org.springframework.web.bind.annotation.PutMapping;
2622
import org.springframework.web.bind.annotation.RequestBody;
@@ -35,15 +31,15 @@ public class AdminScoreController {
3531
private final AdminGpaScoreService adminGpaScoreService;
3632
private final AdminLanguageTestScoreService adminLanguageTestScoreService;
3733

38-
// todo: 추후 커스텀 페이지 객체 & argumentResolver를 적용 필요
3934
@GetMapping("/gpas")
4035
public ResponseEntity<PageResponse<GpaScoreSearchResponse>> searchGpaScores(
4136
@Valid @ModelAttribute ScoreSearchCondition scoreSearchCondition,
42-
@PageableDefault(page = 1) Pageable pageable
37+
Pageable pageable
4338
) {
44-
PagingUtils.validatePage(pageable.getPageNumber(), pageable.getPageSize());
45-
Pageable internalPageable = PageRequest.of(pageable.getPageNumber() - 1, pageable.getPageSize());
46-
Page<GpaScoreSearchResponse> page = adminGpaScoreService.searchGpaScores(scoreSearchCondition, internalPageable);
39+
Page<GpaScoreSearchResponse> page = adminGpaScoreService.searchGpaScores(
40+
scoreSearchCondition,
41+
pageable
42+
);
4743
return ResponseEntity.ok(PageResponse.of(page));
4844
}
4945

@@ -56,15 +52,15 @@ public ResponseEntity<GpaScoreResponse> updateGpaScore(
5652
return ResponseEntity.ok(response);
5753
}
5854

59-
// todo: 추후 커스텀 페이지 객체 & argumentResolver를 적용 필요
6055
@GetMapping("/language-tests")
6156
public ResponseEntity<PageResponse<LanguageTestScoreSearchResponse>> searchLanguageTestScores(
6257
@Valid @ModelAttribute ScoreSearchCondition scoreSearchCondition,
63-
@PageableDefault(page = 1) Pageable pageable
58+
Pageable pageable
6459
) {
65-
PagingUtils.validatePage(pageable.getPageNumber(), pageable.getPageSize());
66-
Pageable internalPageable = PageRequest.of(pageable.getPageNumber() - 1, pageable.getPageSize());
67-
Page<LanguageTestScoreSearchResponse> page = adminLanguageTestScoreService.searchLanguageTestScores(scoreSearchCondition, internalPageable);
60+
Page<LanguageTestScoreSearchResponse> page = adminLanguageTestScoreService.searchLanguageTestScores(
61+
scoreSearchCondition,
62+
pageable
63+
);
6864
return ResponseEntity.ok(PageResponse.of(page));
6965
}
7066

src/main/java/com/example/solidconnection/config/web/WebMvcConfig.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.example.solidconnection.config.web;
22

3-
43
import com.example.solidconnection.custom.resolver.AuthorizedUserResolver;
4+
import com.example.solidconnection.custom.resolver.CustomPageableHandlerMethodArgumentResolver;
55
import com.example.solidconnection.custom.resolver.ExpiredTokenResolver;
66
import lombok.RequiredArgsConstructor;
77
import org.springframework.context.annotation.Configuration;
@@ -16,12 +16,14 @@ public class WebMvcConfig implements WebMvcConfigurer {
1616

1717
private final AuthorizedUserResolver authorizedUserResolver;
1818
private final ExpiredTokenResolver expiredTokenResolver;
19+
private final CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver;
1920

2021
@Override
2122
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
2223
resolvers.addAll(List.of(
2324
authorizedUserResolver,
24-
expiredTokenResolver
25+
expiredTokenResolver,
26+
customPageableHandlerMethodArgumentResolver
2527
));
2628
}
2729
}

src/main/java/com/example/solidconnection/custom/exception/ErrorCode.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,6 @@ public enum ErrorCode {
9696
USER_DO_NOT_HAVE_GPA(HttpStatus.BAD_REQUEST.value(), "해당 유저의 학점을 찾을 수 없음"),
9797
REJECTED_REASON_REQUIRED(HttpStatus.BAD_REQUEST.value(), "거절 사유가 필요합니다."),
9898

99-
// page
100-
INVALID_PAGE(HttpStatus.BAD_REQUEST.value(), "페이지 번호는 1 이상 50 이하만 가능합니다."),
101-
INVALID_SIZE(HttpStatus.BAD_REQUEST.value(), "페이지 크기는 1 이상 50 이하만 가능합니다."),
102-
10399
// general
104100
JSON_PARSING_FAILED(HttpStatus.BAD_REQUEST.value(), "JSON 파싱을 할 수 없습니다."),
105101
JWT_EXCEPTION(HttpStatus.BAD_REQUEST.value(), "JWT 토큰을 처리할 수 없습니다."),
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.example.solidconnection.custom.resolver;
2+
3+
import org.springframework.data.domain.PageRequest;
4+
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
5+
import org.springframework.stereotype.Component;
6+
7+
@Component
8+
public class CustomPageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolver {
9+
10+
private static final int DEFAULT_PAGE = 0;
11+
private static final int MAX_SIZE = 50;
12+
private static final int DEFAULT_SIZE = 10;
13+
14+
public CustomPageableHandlerMethodArgumentResolver() {
15+
setMaxPageSize(MAX_SIZE);
16+
setOneIndexedParameters(true);
17+
setFallbackPageable(PageRequest.of(DEFAULT_PAGE, DEFAULT_SIZE));
18+
}
19+
}

src/main/java/com/example/solidconnection/util/PagingUtils.java

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package com.example.solidconnection.custom.resolver;
2+
3+
import com.example.solidconnection.support.TestContainerSpringBootTest;
4+
import org.junit.jupiter.api.BeforeEach;
5+
import org.junit.jupiter.api.DisplayName;
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.params.ParameterizedTest;
8+
import org.junit.jupiter.params.provider.Arguments;
9+
import org.junit.jupiter.params.provider.MethodSource;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.core.MethodParameter;
12+
import org.springframework.data.domain.Pageable;
13+
import org.springframework.mock.web.MockHttpServletRequest;
14+
import org.springframework.web.context.request.NativeWebRequest;
15+
import org.springframework.web.context.request.ServletWebRequest;
16+
17+
import java.lang.reflect.Method;
18+
import java.util.stream.Stream;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
22+
@TestContainerSpringBootTest
23+
@DisplayName("커스텀 페이지 요청 argument resolver 테스트")
24+
class CustomPageableHandlerMethodArgumentResolverTest {
25+
26+
private static final String PAGE_PARAMETER = "page";
27+
private static final String SIZE_PARAMETER = "size";
28+
private static final int DEFAULT_PAGE = 0;
29+
private static final int DEFAULT_SIZE = 10;
30+
private static final int MAX_SIZE = 50;
31+
32+
@Autowired
33+
private CustomPageableHandlerMethodArgumentResolver customPageableHandlerMethodArgumentResolver;
34+
35+
private MockHttpServletRequest request;
36+
private NativeWebRequest webRequest;
37+
private MethodParameter parameter;
38+
39+
@BeforeEach
40+
void setUp() throws NoSuchMethodException {
41+
request = new MockHttpServletRequest();
42+
webRequest = new ServletWebRequest(request);
43+
Method method = TestController.class.getMethod("pageableMethod", Pageable.class);
44+
parameter = new MethodParameter(method, 0);
45+
}
46+
47+
@Test
48+
void 유효한_페이지_파라미터가_있으면_해당_값을_사용한다() {
49+
// given
50+
int expectedPage = 2;
51+
request.setParameter(PAGE_PARAMETER, String.valueOf(expectedPage));
52+
53+
// when
54+
Pageable pageable = customPageableHandlerMethodArgumentResolver
55+
.resolveArgument(parameter, null, webRequest, null);
56+
57+
// then
58+
assertThat(pageable.getPageNumber()).isEqualTo(expectedPage - 1);
59+
assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE);
60+
}
61+
62+
@Test
63+
void 유효한_사이즈_파라미터가_있으면_해당_값을_사용한다() {
64+
// given
65+
int expectedSize = 20;
66+
request.setParameter(SIZE_PARAMETER, String.valueOf(expectedSize));
67+
68+
// when
69+
Pageable pageable = customPageableHandlerMethodArgumentResolver
70+
.resolveArgument(parameter, null, webRequest, null);
71+
72+
// then
73+
assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE);
74+
assertThat(pageable.getPageSize()).isEqualTo(expectedSize);
75+
}
76+
77+
@Test
78+
void 사이즈_파라미터가_최대값을_초과하면_최대값을_사용한다() {
79+
// given
80+
request.setParameter(SIZE_PARAMETER, String.valueOf(MAX_SIZE + 1));
81+
82+
// when
83+
Pageable pageable = customPageableHandlerMethodArgumentResolver
84+
.resolveArgument(parameter, null, webRequest, null);
85+
86+
// then
87+
assertThat(pageable.getPageSize()).isEqualTo(MAX_SIZE);
88+
}
89+
90+
@ParameterizedTest(name = "{0}")
91+
@MethodSource("provideInvalidParameters")
92+
void 페이지_파라미터가_유효하지_않으면_기본_값을_사용한다(String testName, String pageParam) {
93+
// given
94+
request.setParameter(PAGE_PARAMETER, pageParam);
95+
96+
// when
97+
Pageable pageable = customPageableHandlerMethodArgumentResolver
98+
.resolveArgument(parameter, null, webRequest, null);
99+
100+
// then
101+
assertThat(pageable.getPageNumber()).isEqualTo(DEFAULT_PAGE);
102+
}
103+
104+
@ParameterizedTest(name = "{0}")
105+
@MethodSource("provideInvalidParameters")
106+
void 사이즈_파라미터가_유효하지_않으면_기본_값을_사용한다(String testName, String sizeParam) {
107+
// given
108+
request.setParameter(SIZE_PARAMETER, sizeParam);
109+
110+
// when
111+
Pageable pageable = customPageableHandlerMethodArgumentResolver
112+
.resolveArgument(parameter, null, webRequest, null);
113+
114+
// then
115+
assertThat(pageable.getPageSize()).isEqualTo(DEFAULT_SIZE);
116+
}
117+
118+
static Stream<Arguments> provideInvalidParameters() {
119+
return Stream.of(
120+
Arguments.of("null", null),
121+
Arguments.of("빈 문자열", ""),
122+
Arguments.of("0", "0"),
123+
Arguments.of("음수", "-1"),
124+
Arguments.of("문자열", "invalid")
125+
);
126+
}
127+
128+
private static class TestController {
129+
130+
public void pageableMethod(Pageable pageable) {
131+
}
132+
}
133+
}

src/test/java/com/example/solidconnection/util/PagingUtilsTest.java

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)