본문 바로가기
Spring

Spring HandlerMethodArgumentResolver 인터페이스

by jayden jayden-lee 2019. 5. 5.

프로그래머스 강의 과제를 하던 중 클라이언트에서 보낸 쿼리 파라미터 값이 컨트롤러 파라미터에 바인딩이 되지 않아서 삽질을 했다. Spring 프레임워크에서 지원하는 Pageable 인터페이스를 사용하지 않고, 직접 Pageable 인터페이스를 생성해서 사용했다.

클라이언트에서 쿼리 파라미터로 값 보내기

포스트 목록을 조회하는데, 쿼리 파라미터 값으로 offset 값은 0, limit 값은 5를 보내고 있다.

 

컨트롤러에서 포스트 목록 조회 메서드

컨트롤러에서는 리스트 조회를 하기 위한 메서드를 다음과 같이 구현했다. 이 때, Pageable 인터페이스는 직접 생성했다.

@RestController
@RequestMapping("api")
public class PostRestController {

    @GetMapping(path = "user/{userId}/post/list")
    @ApiOperation(value = "포스트 목록 조회")
    public ApiResult<List<Post>> posts(
            @AuthenticationPrincipal JwtAuthentication authentication,
            @PathVariable
            Long userId,
            Pageable pageable
    ) {
        return new ApiResult<>(postService.findAll(
                Id.of(User.class, userId),
                authentication.id,
                pageable.getOffset(),
                pageable.getLimit()));
    }

}

클라이언트에서 보낸 요청 값을 이용하지 않고, 기본 생성자에 설정한 기본값으로 인스턴스가 생성되는 문제가 있었다. 왜 요청한 값을 받지 못하는 것일까? 구글 검색을 하던 중 HandlerMethodArgumentResolver 인터페이스를 사용해야 하는 것을 알 수 있었다.

HandlerMethodArgumentResolver

HandlerMethodArgumentResolver 인터페이스는 컨트롤러에서 파라미터를 바인딩 해주는 역할을 수행한다. 특정 클래스 또는 어노테이션에 대해서 파라미터를 처리해야 하는 경우에 사용한다.

 

HandlerMethodArgumentResolver 인터페이스에는 두 개의 메서드가 선언되어 있다.

public interface HandlerMethodArgumentResolver {

     boolean supportsParameter(MethodParameter parameter);

     @Nullable
     Object resolveArgument(MethodParameter parameter,
                            @Nullable ModelAndViewContainer mavContainer,
                            NativeWebRequest webRequest, 
                            @Nullable WebDataBinderFactory binderFactory) throws Exception;

}
  • supportsParameter 메서드 : 바인딩 하고자 하는 타겟팅 클래스를 지정한다.
  • resolveArgument 메서드 : 요청한 값을 타겟팅 객체에 바인딩 등 조작한다.

PageableMethodArgumentResolver 생성

PageableMethodArgumentResolver 클래스를 다음과 같이 생성한다. supportsParameter에서는 바인딩 하고자 하는 클래스인지 체크하고, resolveArgument 메서드에서는 바인딩 될 클래스의 인스턴스를 생성한다. 지금은 요청으로 넘어온 값을 단순히 타입만 변환해서 사용하고 있지만, 값에 대한 유효성 체크 등 여러 로직이 더 추가되어야 한다.

public class SimplePageableHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return Pageable.class.isAssignableFrom(parameter.getParameterType());
    }

    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory
    ) {
        String offsetString = webRequest.getParameter("offset");
        String limitString = webRequest.getParameter("limit");

        int offset = Integer.valueOf(offsetString);
        int limit = Integer.valueOf(limitString);

        return new SimplePageable(offset, limit);
    }

}

Custom ArgumentResolver 등록

HandlerMethodArgumentResolver 인터페이스를 구현한 클래스를 생성했으면, Spring MVC에서 알 수 있도록 등록해주는 작업을 반드시 해야한다.

 

WebMvcConfigurer 인터페이스를 구현한 클래스에서 addArgumentResolvers 메서드를 오버라이드(재정의) 한다.

 

HandlerMethodArgumentResolver 인터페이스를 구현한 SimplePageableHandlerMethodArgumentResolver 클래스를 Bean으로 등록하고, resolvers 리스트에 추가한다.

@Configuration
public class WebMvcConfigure implements WebMvcConfigurer {

    // 빈 생성
    @Bean
    public SimplePageableHandlerMethodArgumentResolver simplePageableResolver() {
        return new SimplePageableHandlerMethodArgumentResolver();
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(simplePageableResolver())
    }

}

댓글1

  • 한건국 2019.07.25 18:27

    굉장히 좋은 경험이네요 ~ 많은 도움을 받았습니다. 한겜하시죠 !
    답글