Argument Resolver

 

Argument Resolver

이번 포스팅에서는 RequestHandler를 찾은 다음 해당 RequestHandler를 실행 할때 파라미터를 아규먼트로 매핑하는 전략인 Argument Resolver에 관하여 다루어 보겠습니다.

Argument ResolverRequestMappingHandlerAdapter 클래스의 기본 전략 기법에 따라서 아래와 같이 기본적으로 등록되어 있는것을 확인 할수 있습니다.

또한 Argument Resolver는 이전 다루었던 HttpMessageConvert의 하위 종속체이기 때문에 HttpMessageConvert를 사용하였다면 고민할 필요 없이 넘어가도 되겠습니다.

ReturnValue Resolver는 다음 포스팅에서 다룰 예정이며 그럼 이제 본격적으로 Argument Resolver을 사용해보도록 하겠습니다.

실습

Argument Resolver를 활용하여 파라미터를 오브젝트로 변환하기

준비

간단한 실습을 해보도록 하겠습니다.

먼저 Argument Resolver를 활용할 컨트롤러와 설정 파일을 만들어 모델을 매핑하기 위한 준비 작업을 하겠습니다.

< Configuration >

@Controller
public class AppController { }

< Controller >

@EnableWebMvc
@ComponentScan(basePackageClasses = ApplicationConfiguration.class)
public class ApplicationConfiguration implements WebMvcConfigurer {

	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {

	}
}

스프링의 ArgumentResolver는 addArgumentResolvers 메서드를 활용하여 사용자의 요청 파라미터들을 처리하는 핸들러의 아규먼트로 매핑하는 전략을 가지고 있습니다.


Tip

@EnableWebMvc에너테이션을 등록한 이유는 Message Converter 포스팅에서 다루어진 내용이니 넘어가도록 하겠습니다.


먼저 web.xml을 대신할 ApplicationInitializer를 생성하러 가보겠습니다.

public class ApplicationInitializer 
	extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return null;
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[] { ApplicationConfiguration.class };
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}

}

실제 서비스할 도메인을 만들어 보겠습니다.

도메인은 간단한 실습을 위하여 Todo를 위한 도메인을 작성해 보겠습니다.

public class Todo {
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getContents() {
		return contents;
	}
	public void setContents(String contents) {
		this.contents = contents;
	}
	
	public Todo() { }
	
	public Todo(int id, String contents) {
		this.id = id;
		this.contents = contents;
	}

	private int id;
	private String contents;
}

ArgumentResolver를 실습하기 위한 모든 준비는 끝났습니다.

TodoArgumentResolver

이번에는 실제로 ArgumentResolver 생성하고 설정에 추가하여 사용 해보도록 하겠습니다.

ArgumentResolver는 기본 구조체로써 HandlerMethodArgumentResolver인터페이스를 상속받아 구현하도록 되어 있습니다.

이제 우리는 TodoArgumentResolver라는 이름으로 해당 인터페이스를 상속받아 구현해 보도록 하겠습니다.

public class TodoArgumentResolver implements HandlerMethodArgumentResolver {

	@Override
	public boolean supportsParameter(MethodParameter parameter) {

	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

	}
}

인터페이스를 구현하게 되면 해당 코드의 두 메소드를 구현하므로 두 메소드에 관하여 설명 하도록 하겠습니다.

메서드 설명
supportsParameter 아규먼트 핸들러가 동작하도록 하기 위한 영역을 지정.
resolveArgument 실제 아규먼트를 처리하여 원하는 데이터 포맷으로 변경하도록 동작.

먼저 supportsParameter메소드를 구현해보도록 하겠습니다.

인자로 넘어온 MethodParameter는 해당 아규먼트를 요청한 메소드 아키텍트이며 해당 메소드 아키텍트를 기반으로 TodoArgumentResolver의 동작 여부를 판별합니다.


Tip 메소드 아키텍트

아키텍트란?

구조를 설명하는 모형을 말합니다.

따라서 메소드 아키텍트(Method Architect)란 해당 메소드를 설명하는 메소드의 구조 정보를 가진 객체를 말합니다.


supportsParameter

파라미터의 타입을 기준으로 서포트 여부를 확인하도록 작성해보도록 하겠습니다.

public class TodoArgumentResolver implements HandlerMethodArgumentResolver{

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

	// ...

}

resolveArgument

다음은 resolveArgument 메서드를 구현해보도록 하겠습니다.

resolveArgument메서드에서는 사용자의 요청이 구조화된 NativeWebRequest오브젝트를 기준으로 요청에 맞는 오브젝트를 생성합니다.

우리는 입력 폼을 아래와 같이 파라미터 형식으로 전달 받을 것입니다.

url?id=*&contents=*

이 파라미터를 활용하여 id와 contents의 정보를 추출하여 도메인으로 변환해 보도록 하겠습니다.

public class TodoArgumentResolver implements HandlerMethodArgumentResolver{

	// ...

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		
		int id          = Integer.parseInt(webRequest.getParameter("id"));
		String contents = webRequest.getParameter("contents");

		return new Todo(id, contents);
	}

}

실행

실행을 위해서는 이전 설정 파일로 돌아간 다음 WebMvcConfigurer의 구현체로써 addArgumentResolvers메소드에 우리가 만든 TodoArgumentResolver를 추가하도록 하겠습니다.

@EnableWebMvc
@ComponentScan(basePackageClasses = ApplicationConfiguration.class)
public class ApplicationConfiguration implements WebMvcConfigurer {

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

이렇게 하면 이제 리졸버가 등록 되게 되며 해당 메소드에서 아규먼트를 요청 시 해당 리졸버를 순회하면서 처리할 것입니다.

이제는 실제 사용자의 요청을 받을 컨트롤러를 생성하겠습니다.

컨트롤러는 간단하게 컨텐츠에 __를 접두사로 붙이고 $$를 접미사로 붙여 주는 간단한 컨트롤러를 생성하겠습니다.

@Controller
public class AppController {
	
	private final String prefix = "__";
	private final String suffix = "$$";
	
	@RequestMapping(value= {"/",""})
	@ResponseBody
	public String argumentResolve(Todo todo) {
		
		todo.setContents(prefix + todo.getContents() + suffix);
		
		return new StringBuilder().append("id : ")
                                  .append(todo.getId())
                                  .append(", contents : ")
                                  .append(todo.getContents())
                                  .toString();
	}
}

이제 포스트맨과 같은 테스팅 툴을 사용하여 실행 결과를 확인해 보세요