Spring framework 의 Presentation layer 에서 사용되는 MVC Pattern에 대해 공부해보자.
하나의 서블릿을 통해 비지니스 로직과 뷰 렌더링을 모두 처리하게 되면, 하나의 영역에서 너무 많은 역할을 부담지게 되고, 이는 필연적으로 유지보수가 어려워지는 결과를 만든다.
생각해보자, 비지니스 로직과 뷰 렌더링 기타 등등..을 수정해야 하는 상황에서 매번 하나의 자바 파일에서 코드를 수정해야하는데, 엄청나게 힘들 것으로 생각된다.
Controller : HTTP 요청을 받아 파라미터를 검증하고 비지니스 로직을 실행한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.
Model : 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아 전달해주기 때문에, 뷰는 비지니스 로직이나 데이터 접근에 대해 알 필요 없고 화면 렌더링에만 집중하면 된다.
View : 모델에 담겨있는 데이터를 사용해 화면을 그리는일에 집중한다. 여기서 HTML을 생성하는 부분을 말한다.
#cf) MVC만 보면 비지니스 로직을 전부 컨트롤러에 두는 것으로 보일 수 있다. 그렇기 때문에, 일반적으로 Servcie layer를 두고 거기서 비지니스 로직을 따로 만들어 처리한다.
아래 사진은 스프링 웹 MVC에서 사용되는 구조이다
기존의 MVC Pattern만을 사용한 경우 공통부분의 처리가 힘들고, 많은 부분이 중복되서 사용한다는 단점이 있다. 이를 해결하기 위해서 프론트 컨트롤러 패턴을 사용한다. 컨트롤러 패턴은 서블릿 하나로 클라이언트의 요청을 받아 이 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출을 해주는 역할로 공통 영역을 한군대에 몰아넣을 수 있다고 한다.
실제로 내가 사용하고 있는 Spring에서도 DispathcerServlet이 이 역할을 해주고 있다.
두 사진으로 각 패턴을 비교해보자
기존 mvc pattern
프론트 컨트롤러 패턴
각각 어떤 역할을 하는지 알아보자
이 서블릿도 부모 클래스에서 HttpServlet상속 받아 사용한다.
스프링 부트 구동시 DispatcherServelet을 서블릿으로 자동등록하며, 모든 경로(urlPatterns::"/")에 대해 맵핑한다.
요청흐름
메서드 이름대로 해당 메서드에서 적절한 콘트롤러를 찾아 매핑해주고 뷰까지 찾아 view를 반환해 렌더링까지 해주는 메서드이다.
이 메소드가 핵심역할을 한다고 해서.. 일단 Debugging 모드를 통해서 해당 메소드를 가져와 봤따..
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
아래의 내용은 참고한 블로그에서 가져온 내용이다.. 이런 역할들을 한다는 것을 알 수 있었다.
#cf)
1. MVC에 대해 조사하면서 Servlet에 대해 이야기가 많이 나왔다. 솔직히 많이 들어보고, 변수명으로 사용되는것은 본적있지만, 실제로 이 개념에 대해 알지 못했고 간단히 조사해본 결과 springboot 의 내장되어있는데 톰캣 서버 에서 처리하는 무엇인가로 생각된다. 이건 꼭 조사해서 따로 이해하는 시간을 갖도록 해야겠다.
2. Spring MVC에서 사용하는 주요 인터페이스에 대해서도 알아볼 필요가 있겠다. 필요하면 ,custom하고 오류가 날 때, debugging에 유리할 것으로 생각된다.
https://catsbi.oopy.io/441b4af6-e877-4dc5-9695-2983bbe22799#e64f58ba-c6cd-4697-b039-544e77541d03