ReturnValue Resolver

 

ReturnValue Resolver

이번 포스팅에서는 마지막으로 ReturnValue Resolver는 리턴값을 처리하는 오브젝트인 ReturnValue Resolver에 관하여 다루어 보도록 하겠습니다.

ReturnValue Resolver는 이전 장에서 다루어온 Argument Resolver와 쌍으로 동작하는 것을 추천하며 ReturnValue Resolver 단독으로도 동작 할수 있습니다.

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

실습

ReturnValue Resolver를 활용하여 반환된 객체를 클라이언트에서 사용 가능한 데이터로 변환하기

준비

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

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

< Configuration >

@Controller
public class AppController { }

< Controller >

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

	@Override
	public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {

	}
}

스프링의 ReturnValueResolver 는 addReturnValueHandlers 메서드를 활용하여 서비스에서 반환된 오브젝트를 클라이언트에서 처리 가능한 값으로 처리하는 전략을 가지고 있습니다.


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;
}

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

TodoReturnValueResolver

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

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

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

public class TodoReturnValueResolver implements HandlerMethodReturnValueHandler {

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {

	}

	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType
		, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	}
}

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

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

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

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

supportsReturnType

메소드의 리턴 타입을 기준으로 서포트 여부를 확인하도록 작성해보도록 하겠습니다.

public class TodoReturnValueResolver implements HandlerMethodReturnValueHandler{

	@Override
	public boolean supportsReturnType(MethodParameter parameter) {
		return ( returnType.getMethod().getReturnType() == Todo.class );
	}

	// ...

}

handleReturnValue

서비스에서 반환 된 오브젝트를 서비스에 맞도록 가공하는 handleReturnValue를 처리해보도록 하겠습니다.

handleReturnValue메소드는 서비스에서 반환한 Object와 모델과 뷰를 처리할 ModelAndViewContainer 그리고 요청과 처리를 위한 NativeWebRequest를 사용하여 값을 반환해 보도록 하겠습니다.

public class TodoReturnValueResolver implements HandlerMethodReturnValueHandler{

	// ...

	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest) throws Exception {
		
		Todo todo = (Todo)returnValue;  // (1)
		
		HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); // (2)
		
		StringBuilder builder = new StringBuilder();
		
		builder.append("id : ")   // (3)
		       .append(todo.getId())
		       .append(", contents : ")
		       .append(todo.getContents());
		
		mavContainer.setRequestHandled(true); // (4)

		response.getWriter().write(builder.toString()); // (5)
	}

}
  1. 리턴 된 값을 사용하여 Todo 객체로 변환합니다.
  2. 인자로 넘어온 HttpServletResponse 객체를 사용하여 값 반환을 위한 HttpServletResponse로 변환합니다.
  3. 실제 리턴 값을 문자열로 생성합니다.
  4. 현재 핸들러에서 리턴 값에 대한 핸들링을 하겠다고 표시합니다.(표시가 되면 다음 핸들러를 찾지 않습니다.)
  5. 문자로 된 리턴값을 Response를 사용하여 반환합니다.

실행

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

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

	@Override
	public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
		handlers.add(new TodoReturnValueResolver());
	}
}

이렇게 하면 이제 리졸버가 등록 되게 되며 해당 메소드에서 반환된 오브젝트를 사용하여 해당 리졸버를 순회하면서 처리할 것입니다.

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

@Controller
public class AppController {
	
	@RequestMapping(value= {"/",""})
	public Todo returnValueResolver() {
		
		Todo todo = new Todo(999, "Hello ReturnValue Resolver");

		return todo;
	}
}

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