RequestMappingHandlerAdapter
는 ArgumentResolver
를 호출하여 Controller가 필요한 다양한 파라미터의 값을 생성한다.HttpServletRequest
, Model
, HttpEntity
, @ModelAttribute
, @RequestBody
, @RequestParam
등 다양한 파라미터 바인딩을 할 수 있는 이유이다.ArgumentResolver
를 통하여 값이 준비되면 해당 값을 가지고 실제 Controller를 호출한다.Argument, 매개변수, 파라미터는 모두 같은말이다.
Spring MVC에서 컨트롤러 메서드의 파라미터를 자동으로 바인딩하는 역할을 하는 인터페이스로 요청이 컨트롤러 메서드에 전달될 때 각 파라미터를 적절한 객체로 반환하여 주입하는 것을 담당한다.
ArgumentResolver를 사용하면 컨트롤러 메서드의 파라미터 중 특정 조건의 맞는 파라미터가 있다면, 요청에 들어온 값을 이용해 원하는 객체를 만들어 바인딩해줄 수 있다.
Argument Resolver를 만들기 위해서는 클래스가 HandlerMethodArgumentResolver를 구현해야한다. HandlerMethodArgumentResolver 는 두개의 메소드를 가지고 있다.
ArgumentResolver
의 실제 이름supportsParameter(MethodParameter parameter);
resolveArgument()
메서드를 통해 Object(객체)로 만들어준다.supportsParameter()
를 사용하는 다양한 ArgumentResolver
구현체supportsParameter() 에서는 parameter 객체의 getParameterType() 메소드를 통해 컨트롤러 메소드의 파라미터가 CustomUserDetails 타입인지 확인한다. 그리고 일치 여부를 boolean 타입으로 반환한다.
resolveArgument() 에서는 컨트롤러에서 반복된 HTTP 헤더로부터 JWT 가져오는 로직, JWT 검증 로직, JWT 페이로드 추출 로직, 유저 정보 반환 로직을 넣어준다. 그리고 최종적으로 CustomUserDetails 를 생성해서 반환한다.
HandlerMethodArgumentResolver를 사용하는 이유는, 매개변수로 사용되는 인자에 대해 공통적으로 처리해야할 로직 등이 있을 경우, 중복 코드를 줄이고 공통 기능으로 추출하여 사용할 수 있다.
(예시 @LoginUser를 만들어서 로그인이 되었는지 확인하는 커스텀 어노테이션)
Spring에서 Resolver의 동작은 아래와 같은 과정으로 이루어진다.
정리하자면 특정 Request가 Handler로 Mapping되는 과정에서 invoke 되기 전, Interceptor > Resolver > MessageConverter 순으로 처리된 후, Controller의 Method가 invoke 된다.
다음으로 WebMvcConfigurer를 구현한 WebConfig 클래스에서 위와 같이 우리가 만든 LoginUserDetailsResolver 를 Argument Resolver로 등록한다.