티스토리 뷰
Controller 란
controller 는 스프링에서 HTTP 요청(과 응답, 예외 등)을 처리한다. controller 임을 나타내기 위해 어노테이션을 사용할 수 있다. @RestController 는 @Controller 와 함께 Annotated Controllers 에 해당한다.
HTTP 요청은 HTTP Messages 를 통해 이루어지는데 이에 대해서는 따로 정리했다.
@RestController 란
@Controller 가 화면에 해당하는 View 를 반환하기 위해 사용한다면 @RestController 는 data 를 반환하기 위해 사용한다. @Controller 도 데이터를 반환할 수 있으나 또 다른 annotation 인 @ResponseBody 를 사용해야 한다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(
annotation = Controller.class
)
String value() default "";
}
RestController 코드를 확인해보면 @Controller, @ResponseBody 를 담고 있다. @Controller 와 @ResponseBody 를 선언할 필요없이 @RestController 하나만 선언해서 데이터를 반환할 수도 있다.
@Controller 가 View 를 반환하는 과정

// org.springframework.web.servlet.DispatcherServlet.class
// (필요한 부분만 나타내기 위해 많은 부분이 생략 됐습니다)
public class DispatcherServlet extends FrameworkServlet {
@Nullable
private List<HandlerMapping> handlerMappings;
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
HandlerMapping mapping = (HandlerMapping)var2.next();
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
}
프론트 컨트롤러 (Dispatcher Servlet) 가 요청을 받으면 해당 요청을 처리할 컨트롤러(핸들러)를 handlerMappings 에서 찾는다. 이를 Handler Mapping 이라고 한다.
// org.springframework.web.servlet.DispatcherServlet.class
// (필요한 부분만 나타내기 위해 많은 부분이 생략 됐습니다)
public class DispatcherServlet extends FrameworkServlet {
@Nullable
private List<HandlerAdapter> handlerAdapters;
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter adapter = (HandlerAdapter)var2.next();
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
Exception dispatchException = null;
// handler(컨트롤러) 조회
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
// handlerAdapter 를 조회
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
// handlerAdatper 는 ModelAndView 타입을 반환
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
}
}
Handler Mapping 을 통해 컨트롤러를 찾으면 Handler Adapter 에게 컨트롤러의 실행을 위임한다. Controller 가 한 종류라면 Handler Mapping 으로 찾은 컨트롤러를 직접 실행해도 문제가 없겠지만 서로 다른 종류의 컨트롤러를 사용하게 되면 컨트롤러를 종류별로 관리해줄 대상이 필요하다.
이 역할을 Handler Adapter 가 한다. 서로 다른 종류의 컨트롤러들을 각각의 Adapter 로 관리하고 이 Adapter 들을 Handler Adapter 가 관리한다. 어떤 컨트롤러는 ModelAndView 를 리턴하고, 어떤 컨트롤러는 그렇지 않을 수 있다. Handler Adapter 가 공통된 리턴 타입으로 반환되도록 조정한다.
프론트 컨트롤러는 Handler Adapter 를 통해 컨트롤러를 실행하고 반환된 ModelAndView 객체를 활용한다.
// org.springframework.web.servlet.DispatcherServlet.class
// (필요한 부분만 나타내기 위해 많은 부분이 생략 됐습니다)
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// ModelAndView 객체에서 viewName 을 조회한다
String viewName = mv.getViewName();
View view;
// viewName 이 null 이 아니면 viewResolvers 를 통해 View 객체를 얻는다
if (viewName != null) {
view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
// viewName 이 null 이면 ModelAndView 객체에서 View 객체를 얻는다
} else {
view = mv.getView();
}
try {
view.render(mv.getModelInternal(), request, response);
} catch (Exception var8) {
Exception ex = var8;
throw ex;
}
}
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
Iterator var5 = this.viewResolvers.iterator();
while(var5.hasNext()) {
ViewResolver viewResolver = (ViewResolver)var5.next();
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
}
프론트 컨트롤러는 ModelAndView 객체에서 viewName 을 조회한다. viewName 이 null 이 아니면 viewResolvers 를 통해 적절한 viewResolver 를 찾고 viewResolver 를 통해 View 객체를 얻는다. 얻은 View 객체의 render 함수를 호출한다.
@RestController 가 data 를 반환하는 과정

소스코드를 보다보니 Handler Adapter 는 ModelAndView 를 반환하는데 어떻게 @RestController 가 View 를 렌더링하지 않고 Data 를 리턴할 수 있는지 의문이 들었다.
결론적으로 @ResponseBody 어노테이션은 Handler Adapter 로 RequestMappingHandlerAdapter 가 실행이 되는데, RequestMappingHandlerAdapter 는 HandlerMethodReturnValueHandler 를 @ResponseBody 어노테이션이 사용된 경우(@RestController 는 @ResponseBody 어노테이션을 포함) RequestResponseBodyMethodProcessor 를 실행한다. RequestResponseBodyMethodProcessor 가 ModelAndView 를 null 을 반환하도록 처리해서 RequestMappingHandlerAdapter 에서는 ModelAndView 를 null 을 반환한다. ModelAndView 가 null 이라 render 할 대상이 없어서 viewResolvers 를 찾지 않는다.
@RestController 와 관련된 내용들을 보면서 어떻게 ModelAndView 가 null 이 반환되는지 알아보려고 한다. 그리고 어떻게 @RestController 는 View 를 렌더링하는게 아니라 Data 를 리턴할 수 있는지 알아보려고 한다.
아래에서 보게될 소스코드는 필요한 부분만 볼 수 있도록 많은 부분이 생략 됐습니다.
RequestMappingHandlerMapping
스프링 부트 3.0(스프링 프레임워크 6.0)부터는 클래스 레벨에
@RequestMapping
이 있어도 스프링 컨트롤러로 인식하지 않는다. 오직@Controller
가 있어야 스프링 컨트롤러로 인식한다. 참고로@RestController
는 해당 애노테이션 내부에@Controller
를 포함하고 있으므로 인식 된다. 따라서@Controller
가 없는 위의 두 코드는 스프링 컨트롤러로 인식되지 않는다. (RequestMappingHandlerMapping
에서@RequestMapping
는 이제 인식하지 않고,Controller
만 인식한다.) 스프링 MVC 1편
HandlerExecutionChain 조회
public class DispatcherServlet extends FrameworkServlet {
@Nullable
private List<HandlerMapping> handlerMappings;
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {}
}
// org.springframework.web.servlet.HandlerMapping.class
public interface HandlerMapping {
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
// org.springframework.web.servlet.handler.AbstractHandlerMapping.class
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = this.getHandlerInternal(request);
HandlerExecutionChain executionChain = this.getHandlerExecutionChain(handler, request);
return executionChain;
}
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain var10000;
if (handler instanceof HandlerExecutionChain handlerExecutionChain) {
var10000 = handlerExecutionChain;
} else {
var10000 = new HandlerExecutionChain(handler);
}
HandlerExecutionChain chain = var10000;
Iterator var7 = this.adaptedInterceptors.iterator();
while(var7.hasNext()) {
HandlerInterceptor interceptor = (HandlerInterceptor)var7.next();
if (interceptor instanceof MappedInterceptor mappedInterceptor) {
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
} else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
}
우선 getHandler 메소드가 DispatcherServlet 에도 있고 HandlerMapping 인터페이스에도 있다는 점에 유의해야 한다.
DispatcherServlet 의 getHandler 메소드가 는 HandlerExecutionChain 을 반환한다. DispatcherServlet 의 getHandler 메소드는 내부적으로 HandlerMapping 객체를 찾는데 @Controller 는 HandlerMapping 인터페이스의 구현체인 RequestMappingHandlerMapping 객체를 찾는다.
HandlerMapping 인터페이스에는 getHandler 가 있고 이를 AbstractHandlerMapping 가 구현했다. RequestMappingHandlerMapping 의 상위 객체로 RequestMappingHandlerMapping 가 getHandler 를 호출하면 부모 객체의 메소드 탐색을 하면서 실행된다. getHandler 메소드를 실행하면서 getHandlerExecutionChain 도 내부적으로 실행해서 HandlerExecutionChain 을 반환한다.
RequestMappingHandlerAdapter
DispatcherServlet 에서 Handler Adapter 를 조회(@Controller 의 경우 RequestMappingHandlerAdapter 가 반환됨) 후 handle 메서드를 호출한다.
handler 실행
// org.springframework.web.servlet.HandlerExecutionChain.class
public class HandlerExecutionChain {
private final Object handler;
}
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerExecutionChain mappedHandler = null;
mappedHandler = this.getHandler(processedRequest);
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
}
// org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.class
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
mav = this.invokeHandlerMethod(request, response, handlerMethod);
}
}
DispatcherServlet 은 RequestMappingHandlerMapping 을 통해 얻은 handler 의 실행을 HandlerAdapter 에게 위임한다. handler 는 HandlerExecutionChain 에 변수로 등록되어 있는데, HandlerExecutionChain 는 DispatcherServlet 이 Handler Mapping 을 통해 RequestMappingHandlerMapping 로부터 얻게된다. @Controller 의 경우 handler 는 클래스가 아니라 실행할 메소드다.
즉, handle 메소드를 RequestMappingHandlerAdapter 가 실행하게 된다. handle 메소드를 실행하면서 넣은 HandlerExecutionChain 의 getHandler 인자(mappedHandler.getHandler())는 RequestMappingHandlerAdapter 의 handleInternal 메소드의 파라미터를 보면 HandlerMethod 로 선언되어 있다.
HandlerAdapter 인터페이스의 handle 메서드는 RequestMappingHandlerAdapter 에는 없고 부모 클래스인 AbstractHandlerMethodAdapter 에 정의되어 있다. handle 메서드는 handleInternal 를 호출하는데 이는 RequestMappingHandlerAdapter 에 구현되어 있다. handleInternal 는 invokeHandlerMethod 를 호출하고 ModelAndView 를 반환한다.
ModelAndView 반환
public class DispatcherServlet extends FrameworkServlet {
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter adapter = (HandlerAdapter)var2.next();
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
}
getHandlerAdapter 메서드에서 Handler Adapter 를 조회하면 RequestMappingHandlerAdapter 를 반환한다.
RequestMappingHandlerAdapter 는 @Controller 어노테이션을 사용하는 경우 반환된다. @RestController 는 @Controller 를 포함하기 때문에 마찬가지로 RequestMappingHandlerAdapter 가 반환된다.
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// handlerAdapter 를 조회
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
// handlerAdatper 는 ModelAndView 타입을 반환
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
}
// org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.class
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Object 로 선언한 handler 를 HandlerMethod 로 캐스팅한다
return this.handleInternal(request, response, (HandlerMethod)handler);
}
}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
mav = this.invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// HandlerMethod 를 ServletInvocableHandlerMethod 로 변환한다
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
}
}
RequestMappingHandlerAdapter 는 invocableMethod를 실행하는데 invocableMethod 는 handler 메소드를 토대로 생성한 객체다.
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
public void afterPropertiesSet() {
if (this.returnValueHandlers == null) {
resolvers = this.getDefaultReturnValueHandlers();
// 여러 종류의 HandlerMethodReturnValueHandler 를 모은 resolvers 리스트를 HandlerMethodReturnValueHandlerComposite 객체에 등록하고
// HandlerMethodReturnValueHandlerComposite 객체를 returnValueHandlers 로 지정한다
this.returnValueHandlers = (new HandlerMethodReturnValueHandlerComposite()).addHandlers(resolvers);
}
}
// RequestResponseBodyMethodProcessor 외에 다양한 객체들을 handlers 에 등록한다
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList(20);
handlers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
return handlers;
}
}
ServletInvocableHandlerMethod 의 invokeAndHandle 를 보기 전에 먼저 봐야할 것이 있다.
RequestMappingHandlerAdapter 는 응답 후 처리를 할 HandlerMethodReturnValueHandler 들을 ServletInvocableHandlerMethod 에 설정 해준다.
적절한 HandlerMethodReturnValueHandler 객체의 실행은 HandlerMethodReturnValueHandlerComposite 에서 수행하고, HandlerMethodReturnValueHandlerComposite 의 수행을 ServletInvocableHandlerMethod 에서 한다.
// org.springframework.web.servlet.mvc.method.ServletInvocableHandlerMethod.class
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
try {
// HandlerMethodReturnValueHandlerComposite 의 handleReturnValue 를 호출한다
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception var6) {
}
}
}
ServletInvocableHandlerMethod 의 invokeAndHandle 로 돌아왔다. RequestMappingHandlerAdapter 에서 설정해준 returnValueHandlers 의 handleReturnValue 메소드를 호출한다.
// org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.class
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// HandlerMethodReturnValueHandler 를 선택
HandlerMethodReturnValueHandler handler = this.selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
} else {
// HandlerMethodReturnValueHandler 의 handleReturnValue 를 실행
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = this.isAsyncReturnValue(value, returnType);
Iterator var4 = this.returnValueHandlers.iterator();
HandlerMethodReturnValueHandler handler;
do {
do {
if (!var4.hasNext()) {
return null;
}
handler = (HandlerMethodReturnValueHandler)var4.next();
} while(isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler));
} while(!handler.supportsReturnType(returnType));
return handler;
}
}
// org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.class
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
public boolean supportsReturnType(MethodParameter returnType) {
return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class);
}
}
HandlerMethodReturnValueHandlerComposite 은 자신이 호출할 HandlerMethodReturnValueHandler 를 찾는데 @ResponseBody 어노테이션이 클래스 레벨이나 메소드 레벨에 있으면 RequestResponseBodyMethodProcessor 가 선택된다.
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 이곳에서 requestHandled 를 true 로 처리
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest);
// Response 생성하고 이 객체에 write 한다
ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest);
if (returnValue instanceof ProblemDetail detail) {
outputMessage.setStatusCode(HttpStatusCode.valueOf(detail.getStatus()));
if (detail.getInstance() == null) {
URI path = URI.create(inputMessage.getServletRequest().getRequestURI());
detail.setInstance(path);
}
}
this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// RequestResponseBodyMethodProcessor 가
// requestHandled 를 true 로 처리해서 null 을 반환한다
if (mavContainer.isRequestHandled()) {
return null;
} else {
}
}
}
RequestResponseBodyMethodProcessor 에서 ModelAndViewContainer 의 requestHandled 변수의 값을 true 로 설정한다. 그리고 RequestMappingHandlerAdapter 에서는 requestHandled 의 값을 isRequestHandled 함수로 확인한다. isRequestHandled 가 true 면 null 을 리턴한다. ModelAndView 타입을 반환하는 getModelAndView 함수는 null 을 반환한다.
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
// @RestController 의 경우 이 if 문을 충족하지 않아서 render 로직을 실행하지 않는다
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("No view rendering, null ModelAndView returned.");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
// client 에게 response 를 반환하고 나머지 처리를 진행한다
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
}
HandlerAdapter 의 handle 메소드 이후에 DispatcherServlet 은 processDispatchResult 를 호출한다. processDispatchResult 를 호출하면서 handle 함수의 결과로 받은 ModelAndView 객체를 인자로 넘겨주는데 앞서 이 값은 null 임을 확인했다.
mv 가 null 이라서 mv != null && !mv.wasCleared() 조건을 만족하지 못하고 render 함수를 호출하지 않는다.
HttpMessageConverter
@RestController 는 RequestMappingHandlerAdapter 에서 반환되는 ModelAndView 가 null 이라 DispatcherServlet 가 View 를 그리지 않는다는 것은 알게 됐다.
이제 어떻게 View 렌더링 대신 Data (Response) 가 반환 되는지 알아보는게 남았다.
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
}
// 부모 클래스인 AbstractMessageConverterMethodProcessor 의 writeWithMessageConverters 를 호출해서 리턴할 data 를 설정한다
this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler {
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
// 적절한 HttpMessageConverter 를 찾는다
HttpMessageConverter converter;
GenericHttpMessageConverter genericConverter;
while(true) {
if (!var21.hasNext()) {
break label166;
}
converter = (HttpMessageConverter)var21.next();
if (genericConverter != null) {
// HttpMessageConverter 구현체 중에서 어떤 구현체가 처리 가능한지 조회한다
} else if (converter.canWrite(valueType, selectedMediaType)) {
break;
}
}
// 찾은 구현체의 write 메소드를 호출하고 인자로 담은 outputMessage 에 write 한다
converter.write(body, selectedMediaType, outputMessage);
}
public class ServletServerHttpResponse implements ServerHttpResponse {
private final HttpServletResponse servletResponse;
private final HttpHeaders headers;
}
RequestResponseBodyMethodProcessor 는 부모 클래스인 AbstractMessageConverterMethodProcessor 의 writeWithMessageConverters 를 호출한다. AbstractMessageConverterMethodProcessor 는 응답을 처리할 HttpMessageConverter 를 찾고, RequestResponseBodyMethodProcessor 가 생성해서 넘겨준 ServletServerHttpResponse 객체에 HttpMessageConverter 가 write 하도록 호출한다. 최종적으로는 Servlet Container 가 Data (Response) 를 클라이언트에게 반환한다.
<참고>
https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages
https://martinfowler.com/eaaCatalog/frontController.html
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet.html
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html
https://spring.io/guides/gs/serving-web-content#initial
https://mangkyu.tistory.com/49
https://mangkyu.tistory.com/216
https://duooo-story.tistory.com/9
https://happy-coding-day.tistory.com/191
ChatGPT
'Dev > Spring' 카테고리의 다른 글
JPA - N + 1 (2) (1) | 2024.11.05 |
---|---|
JPA - N+1 문제 (0) | 2024.09.20 |
JPA - @JoinColumn (0) | 2024.04.11 |