티스토리 뷰

2) 아규먼트 리졸버를 이용해 HTTP Header정보를 Map객체에 담아서 Controller에게 전달하기

  • 참고로 Map이나 Map을 상속받고 있는 객체는 스프링에서 내부적으로 사용하고 있는 아규먼트 리졸버가 선처리하고 있기 때문에 직접 사용할 수 없다고 그랬습니다. 그래서 Map객체를 전달하려면 Map을 필드로 가지고 있는 별도의 객체를 선언한 후 사용해야 합니다.
  • 그렇기 때문에 Map을 필드로 가지고 있는 HeaderInfo라는 클래스를 작성하겠습니다.
  • 그 전에 해당 클래스랑 아규먼트 리졸버를 모아둔 패키지를 생성하겠습니다. argumentresolver 라는 이름의 패키지를 생성합니다. 그리고 해당 패키지에 헤더의 정보를 담을 HeaderInfo.java 클래스를 생성합니다.
  • 이러한 HeaderInfo 클래스는 Map을 직접 사용할 수 없기 때문에 그냥 필드로 Map을 하나 가지고 있고 실제 HeaderInfo 생성자에서 Map이 생성되도록 작성합니다. 그리고 값을 넣고 꺼낼 수 있는 put(), get() 메서드를 정의합니다.

  • 다음은 HandlerMathodArgumentResolver 인터페이스를 구현하고 있는 HeaderMapArgumentResolver.java 라는 클래스를 작성합니다HandlerMathodArgumentResolver 인터페이스는 resolveArgument와 supportsParameter, 두 가지 메서드를 가지고 있습니다.
  • Controller 메서드의 인자가 네 개일 경우에 supportsParameter 메서드가 네 번 호출이 됩니다. 그러니까 supportsParameter는 resolver가 적용 가능한지 검사하는 역할을 합니다.
  • supportsParameter는 컨트롤러 메서드의 인자의 정보를 파라미터로 전달받는데, 해당 파라미터 정보에 원하는 정보가 있다면 true를 반환하고, 없다면 false를 반환하도록 합니다.
  • 여기서 파라미터 타입이 방금 만든 HeaderInfo라는 클래스 타입이면 true를 리턴하도록 메서드를 작성하겠습니다.
  • 그리고 supportsParameter true를 리턴할 경우에만 resolveArgument 메서드가 호출이 돼요. 그리고  resolveArgument가 리턴한 값은 Controller 메서드의 인자로 전달이 되는 겁니다.
  • resolveArgument로 넘어온 모든 헤더 정보를 HeaderInfo 객체에 담아서 리턴을 합니다.
  • 먼저 HeaderInfo 객체를 하나 생성하고 이때 넘어온 NativeWebRequest 로부터 헤더의 name들을 Iterator 형태로 꺼냅니다. 그리고 while 문 안에서 헤더의 이름과 헤더의 value를 얻어내서 headerInfo에다가 put() 메서드로 값을 넣어줍니다.
  • 그리고 확인을 위해서 임시로 콘솔에 출력을 합니다. 해당 부분이 실행이 되면 헤더에 어떤 어떤 정보들이 들어있는지 콘솔에 출력해서 알아볼 수 있을 것 같습니다.

  • 생성한 아규먼트 리졸버를 적용을 하려면 WebMvcConfiguration 클래스에다가 addArgumentResolver라는 메서드를 오버라이딩하고 인자로 넘어온 아규먼트 리졸버에 앞에서 생성한 아규먼트 리졸버를 넘겨줄 수 있도록 추가합니다.

  • 여기까지 아규먼트 리졸버를 생성하고 생성한 아규먼트 리졸버를 등록을 하였습니다.
  • 그러면 Controller에서 메서드를 호출할 때 아규먼트 리졸버가 메서드의 파라미터에 대한 작업을 수행하여 조건에 맞으면 해당 파라미터에 대한 처리를 합니다.
  • 그래서 메서드의 파라미터에 해당 객체인 headerInfo를 넘겨야 합니다. list 메서드의 파라미터에 headerInfo를 추가하고 headerInfo get() 메서드에다가 user-agent를 넘기면 값이 출력됩니다.

  • 페이지에 접속하면 콘솔에 헤더 정보와 user-agent 정보가 나오고 있는 것을 확인할 수 있습니다.

실습코드

HeaderInfo.java

package kr.or.connect.guestbook.argumentresolver;

 

import java.util.HashMap;

import java.util.Map;

 

public class HeaderInfo {

           private Map<String, String> map;

          

           public HeaderInfo() {

                      map = new HashMap<>();

           }

 

           public void put(String name, String value) {

                      map.put(name,  value);

           }

          

           public String get(String name) {

                      return map.get(name);

           }

 

}

 

HeaderMapArgumentResolver.java

package kr.or.connect.guestbook.argumentresolver;

 

import java.util.Iterator;

 

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 HeaderMapArgumentResolver implements HandlerMethodArgumentResolver {

 

           @Override

           public boolean supportsParameter(MethodParameter parameter) {

                      return parameter.getParameterType() == HeaderInfo.class;

           }

 

           @Override

           public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,

                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

 

                      HeaderInfo headerInfo = new HeaderInfo();

                     

                      Iterator<String> headerNames = webRequest.getHeaderNames();

                      while(headerNames.hasNext()) {

                                  String headerName = headerNames.next();

                                  String headerValue = webRequest.getHeader(headerName);

//                               System.out.println(headerName + " , " + headerValue);

                                  headerInfo.put(headerName, headerValue);

                      }

                     

                      return headerInfo;

           }

 

}

 

생각해보기

  • 컨트롤러 메소드에서 자주 사용되는 값이 있을 경우 아규먼트 리졸버를 만들어서 넘겨주도록 하면 편리하게 사용할 수 있습니다. 아규먼트 리졸버에 대한 예를 인터넷에서 찾아보시고 어떤 값을 넘기면 유용할지 고민해 보세요.

참고 자료

[참고링크] A Custom Data Binder in Spring MVC
http://www.baeldung.com/spring-mvc-custom-data-binder

[참고링크] Using custom arguments in Spring MVC controllers
https://sdqali.in/blog/2016/01/29/using-custom-arguments-in-spring-mvc-controllers/

 

Comments