본문으로 바로가기

자주 사용하게될 HandlerMethodArgumentResolver에 대해서 공부한다. 


Interceptor는 서블릿 필터와 비슷한 역할로 클라이언트의 요청이 컨트롤러에 들어가기 전에 가로채거나, 컨트롤러의 응답이 클라이언트에 가기 전에 가로채서 가공해주는 역할이다. 인터셉터는 Spring MVC 내부에서만 사용한다. 보통 Request 요청이 오면, 요청 전에 해야 할 것 기술, 요청이 성공했을 때 해야 할 부분 기술, 요청이 끝났을 때 해야 할 것들을 처리하거나, 로그 기록 및 헤더 처리 등 공통 처리 부분이 있을 때 사용된다.


HandlerMethodArgumentResolver는 컨트롤러에 들어오는 파라미터를 커스터마이징할 때 사용한다. 예를 들어, 특정 클래스나 특정 어노테이션 등의 요청 파라미터를 수정해야한다거나 또는 클래스의 파라미터를 조작, 혹은 공통적으로 써야하는 파라미터들을 바인딩해주는 역할을 한다. 컨트롤러에 들어가기 전에 클라이언트로 받은 파라미터들을 전부가 아닌 원하는 것으로 가공해서 컨트롤러로 전달하기 위해서 사용한다. 요청 파라미터들을 입맛에 바꾼다고 생각하면 된다. 컨트롤러에 들어올 파라미터를 직접 정의해 줄 수 있다.


1
2
@RequestMapping("/user")
public ModelAndView step3(User user) throws Exception{...}
cs

user의 변수에는 무엇이 들어올 지 모른다. 그럴 때 HandlerMethodArgumentResolver를 사용해서 user에 무엇이 들어갈 지 직접 정할 수 있다. Request 파라미터 또는 헤더 값을 빼내서 객체를 만들 수도 있고, Session에서 값을 빼서 넣어 줄 수도 있다. 그래서 공통적인 규악이 있을 때 자주 사용된다. Spring MVC에서 HttpServletRequest 또는 Response 클래스를 직접 사용하는 것을 좋아하지 않는다. 그래서 직접 값을 빼내지 않고 가공하는 형태로 많이 사용한다.


여기서 주의할 것은 Model과는 다른 역할인 것을 알아둬야 한다. 데이터의 흐름을 보면,

jsp -> HandlerInterceptor -> HandlerMethodArgumentResolver -> Controller -> Model -> jsp

컨트롤러가 jsp에서 데이터를 받을 때 사용 되는 것은 HandlerMethodArgumentResolver이고, 컨트롤러에서 jsp로 데이터를 줄 때 사용되는 것이 Model이다.  Model은 컨트롤러에서 데이터를 jsp로 보낼 때 Model 이라는 Map 형태의 공간에 넣어서 전달하는 것이다. HandlerMethodArgumentResolver는 클라이언트의 요청에 담긴 파라미터를 컨트롤러가 받기 전에 관여해서 데이터를 특정 클래스로 객체화하여 컨트롤러로 보내주는 역할이다.




HandlerMethodArgumentResolver의 예를 들어보자.


1
2
3
<form>
  <input type="text" name="userDeviceType" />
</form>
cs

이와 같은 view가 있다고 하자.

단순하게 Controller에서 userDeviceType 값을 처리하고 싶다면


1
2
3
4
@PostMapping
public String controllerMethod(String userDeviceType) throw Exception {
  ....
}
cs

이와 같이 해주면 된다.


그런데 view에서 넘어오는 userDeviceType을 통해 사용자가 어떤 디바이스로 접속했는지 구별해야 된다고 해보자.

그리고 이러한 처리해야 하는 로직이 필요한 컨트롤러가 30개 이상이라고 해보자.


HandlerMethodArgumentResolver를 사용하지 않는다면, 30개 이상의 컨트롤러에서 각각 디바이스를 구분해야 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@PostMapping
public String controllerMethod(String userDeviceType) throw Exception {
 
  Boolean usingAndroid = null;
 
  switch(userDeviceType) {
    case "galaxy" :
      usingAndroid  = true;
      break;
    case "optimus" : 
      usingAndroid  = true;
      break;
    case "nexus" :
      usingAndroid  = true;
      break;
    case "iphone" : 
      usingAndroid  = false;
      break;
  }
 
  if(usingAndroid) {
     ...
  }
}
cs

이렇게 userDeviceType이 어떤 것인지 컨트롤러에서 판단해서 사용해야할 것이다. 상당히 복잡하다.

그리고 디바이스의 정보를 확인하는 작업을 30개 이상의 컨트롤러에서 똑같이 작업하기 때문에 코드 중복이 생긴다.



HandlerMethodArgumentResolver를 사용한다면, Controller에서 커스터마이징된 파라미터를 받게 된다.


1
2
3
4
5
6
7
8
9
10
11
12
public class DeviceHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return userDevice.class.isAssignableFrom(parameter.getParameterType());
  }
 
  @Override
  public userDevice resolveArgument(MethodParameter parameter, ...) throws Exception {
    //....디바이스 구분 로직
    return device;
  }
}
cs

Controller에에 들어가기전에 파라미터들을 가져와서 먼저 다바이스를 구분하여 device객체를 컨트롤러에 보내준다.


1
2
3
4
5
6
7
8
@PostMapping
public String controllerMethod(Device device) throw Exception {
 
  if(device.usingAndroid()) {
    ...
  }
 
}
cs

이렇게 HandlerMethodArgumentResolver를 이용해서 커스터마이징된 파라미터를 받게 되면,

코드 중복을 포함해 여러가지 이득을 볼 수 있다. 




그럼 이제 HandlerMethodArgumentResolver를 살펴보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.springframework.core.MethodParameter;
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;
 
public class TestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
 
 @Override
 public Object resolveArgument(MethodParameter arg0, ModelAndViewContainer arg1,
                         NativeWebRequest arg2, WebDataBinderFactory arg3) throws Exception{
    // TODO Auto-generated method stub
    return null;
 }
 
 @Override
 public boolean supportsParameter(MethodParameter arg0) {
    // TODO Auto-generated method stub
    return false;
 }
 
}
cs

HandlerMethodArgumentResolver를 implements 받으면 두가지 메소드 반드시 구현해주어야 한다.

  • supportParameter

바인딩할 클래스를 지정한다. 즉, resolver 적용이 가능한지 검사하는 역할을 한다. boolean값을 리턴해준다.

보통 test.class.isAssignableFrom(parameter.getParameterType()); 를 써서 컨트롤러의 파라미터가 해당 클래스와 같은 타입인지 검사한다.


  • resolverArgument

바인딩할 객체를 조작할 수 있는 메서드이다. 가장 주용한 메서드이다. 여기서 원하는 파라미터로 커스터마이징해서 반환해준다.





참고 및 출처

https://okky.kr/article/314457

https://okky.kr/article/377842

http://addio3305.tistory.com/75?category=772645

https://xmfpes.github.io/java/spring-resolver/

http://millky.com/@origoni/post/1177