Spring MVC/04.Rest

[Swagger] Pageable 객체 처리

  • -

이번 포스트에서는 Pageable 타입을 반환하는 Controller를 Swagger에서 제대로 표현해주지 못하는 문제의 해결책에 대해서 살펴보자.

 

Swagger를 위한 Pageable 타입 변경

 

Controller 구성

JPA를 이용해서 모델단을 구성한 후 전체 목록을 반환하는 과정에서 페이징 처리를 위해 아래와 같이 Controller를 구성한 상태이다.

@GetMapping
public ResponseEntity<Page<CustomerDto>> search(Pageable pageable) {
	log.debug("pageable: {}", pageable);
	return new ResponseEntity<Page<CustomerDto>>(service.selectCustomer(pageable), HttpStatus.OK);
}

 

단위테스트

아래와 같이 단위테스트를 통해 Controller의 동작을 점검해보자.

@Test
public void pagingTest() throws Exception{
	// given
	String page = "1";
	String size = "5";
	String sort="email,DESC";
	RequestBuilder request = MockMvcRequestBuilders.get("/customer/api")
			.param("page", page).param("size", size).param("sort", sort);
	// when
	ResultActions actions = mockMvc.perform(request);
	// then
	actions.andExpect(status().isOk()).andExpect(jsonPath("$.content.length()", equalTo(5)));
}

결과는 성공적이다. Pageable 객체 설정을 위해 page, size, sort를 key로 적절한 값을 전달하면 작업이 잘 진행 되고 Controller쪽에 남긴 로거를 통해서도 전달된 파라미터를 확인할 수 있다.

[05-04 18:26:43] [DEBUG] [x.q.m.c.c.CustomerRestController.search- 42] 
                                  pageable: Page request [number: 1, size 5, sort: email: DESC]

 

Swagger에서의 문제

하지만 swagger를 통해서 API를 테스트하려하면 약간 의외의 화면을 볼 수 있다.

number나 size, sort등을 설정할 수 있는 화면이 없다. 대신 비슷한 녀석들이 있기는 한데 이렇게 나온 이유는 Pageable interface의 property 이름이 위와 같기 때문이다. 하지만 Pageable의 실제 구현체는 PageRequest이고 이녀석의 생성자는 아래와 같다. 그리고 PageRequest의 상위 클래스인 AbstractPageRequest에서 page와 size를 사용한다.

protected PageRequest(int page, int size, Sort sort) {
	super(page, size);
	Assert.notNull(sort, "Sort must not be null!");
	this.sort = sort;
}

즉 파라미터로 선언한 interface인 Pageable과 구현체의 property 이름이 서로 달라서 생긴 문제이다. 따라서 전체적으로 Pageable을 걷어내고 PageRequest를 써도 되겠지만 Swagger를 위해서 유연성을 헤친다는 것은 또 웃긴 일이다.

 

Swagger 설정 수정

따라서 Swagger에게 어떤 타입이 왔을 때 이것을 다른 것으로 변경해서 처리할 수 있도록 설정해줄 필요가 있다. 이를 위해 페이징 정보를 저장하기 위한 클래스를 만들어보자.

@Data
@ApiModel
static class MyPagable {
    @ApiModelProperty(value = "페이지 번호(0..N)")
    private Integer page;

    @ApiModelProperty(value = "페이지 크기", allowableValues="range[0, 100]")
    private Integer size;

    @ApiModelProperty(value = "정렬(사용법: 컬럼명,ASC|DESC)")
    private List<String> sort;
}

그리고 Docket 객체를 만들면서 Pageable 대신 MyPageable을 사용하도록 설정해준다.

TypeResolver typeResolver = new TypeResolver();

@Bean
public Docket api() {
	final ApiInfo apiInfo = new ApiInfoBuilder()
			.title("QuietJun's 이것 저것")
			.description("<h3>xyz.quietjun에서 제공되는 RestApi에 대한 문서 제공</h3>")
			.license("MIT License")
			.version("1.0")
			.build();

	return new Docket(DocumentationType.SWAGGER_2)
			.alternateTypeRules(AlternateTypeRules.newRule(typeResolver.resolve(Pageable.class), 
                                                           typeResolver.resolve((MyPagable.class))))
			.apiInfo(apiInfo)
			.select()
			.apis(RequestHandlerSelectors.basePackage("xyz.quietjun.multiplex"))
			.paths(PathSelectors.ant("/**/api/**"))
			.build();
}

 

다시 Swagger를 이용해보면 원하는 컬럼 설정이 가능할 것을 볼 수 있다.

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.