FrontController

jihan kong·2022년 8월 8일
0

Spring MVC

목록 보기
8/12
post-thumbnail

본 시리즈는 인프런 학습 사이트의 김영한 강사님의 java spring mvc - 백엔드 웹 개발 핵심 기술 편을 학습한 내용을 바탕으로 정리하였습니다.

지난 포스팅에서 스프링 MVC 패턴에 대해서 학습하였다. MVC패턴이란 하나의 서블릿이나 JSP로 처리하던 것을 컨트롤러(Controller)뷰(View) 라는 영역으로 서로 역할을 나눈 것을 말한다. Model, View, Controller 세 부분으로 구성되어 체계적으로 애플리케이션을 구성할 수 있었다.

더할 나위 없는 MVC 구조... 그러나, 컨트롤러 부분에서 한 가지 아쉬운 점이 존재하게 되는데 이는 컨트롤러를 호출할 때마다 중복된 코드 혹은 필요없는 코드가 계속해서 호출되어야 한다는 점이다.

❗MVC 컨트롤러 구현시 아쉬운점

1. forward

먼저 포워드가 중복이 될 수 있다.

forward란❓
dispatcher.forward() 과 같이 다른 서블릿이나 JSP로 이동할 수 있는 기능

회원을 저장, 등록 또는 조회하는 코드마다 View로 이동시켜주는 다음의 코드를 중복해서 삽입해야 한다.

RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);

2. ViewPath

각 컨트롤러마다 각자 어떤 경로로 포워딩을 할 것인지를 또 중복해서 달고 있어야한다.

회원 등록

... // 중간생략

String viewPath = "/WEB-INF/views/new-form.jsp";

회원 저장

... // 중간생략

String viewPath = "/WEB-INF/views/save-result.jsp";

그리고 만약 jsp 가 아닌 thymeleaf 와 같은 다른 뷰로 변경한다면 전체 코드를 다 변경해야 하는 문제점도 있다.

3. Response

HttpServletRequest request, HttpServletResponse response

위 코드는 사용할 때도 있고, 사용하지 않을 때도 있다. 그리고 이런 HttpServletRequest, HttpServletResponse 를 사용하는 코드는 테스트 케이스를 작성하기도 어렵다.

4. 공통처리

기능이 복잡해질수록 컨트롤러에서 공통으로 처리해야 하는 부분이 점점 더 많이 증가할 것이다. 단순히 공통 기능을 메서드로 뽑으면 될 것 같지만 결과적으로 해당 메서드를 항상 호출해야 하고, 실수로 호출하지 않으면 문제가 될 것이다. 그리고 호출하는 것 자체도 중복이다.

📌 위의 내용을 종합해보면 결국 공통 처리가 어렵다는 문제가 있다.

중복의 문제를 해결하려면 컨트롤러 호출 전에 먼저 컨트롤러에서 공통된 기능들을 처리해야 한다. 소위 수문장 역할을 하는 기능이 필요하다. 어떻게 해결할 수 있을까? 프론트 컨트롤러(Front Controller) 패턴을 도입하면 이런 문제를 깔끔하게 해결할 수 있다.

💡 FrontController

프론트 컨트롤러 도입 전

프론트 컨트롤러 도입 후

FrontController 패턴 특징

  • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
  • 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출
  • 공통 처리 가능
  • 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨

📌 스프링 웹 MVC의 핵심도 바로 FrontController!
스프링 웹 MVC의 DispatcherServlet 이 FrontController 패턴으로 구현되어 있음


프론트 컨트롤러는 다음과 같이 구현할 수 있다. 물론 더 간결하게 만들 수 있는 코드이니 완벽히 구현된 모습은 아니다.

FrontController

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/frontcontroller/v1/*")
public class FrontControllerServletV1 extends HttpServlet {
		private Map<String, ControllerV1> controllerMap = new HashMap<>();

		public FrontControllerServletV1() {
				controllerMap.put("/front-controller/v1/members/new-form", new
			MemberFormControllerV1());
				controllerMap.put("/front-controller/v1/members/save", new
			MemberSaveControllerV1());
				controllerMap.put("/front-controller/v1/members", new
			MemberListControllerV1());
			}

			@Override
			protected void service(HttpServletRequest request, HttpServletResponse
			response)
							throws ServletException, IOException {

					System.out.println("FrontControllerServletV1.service");
					String requestURI = request.getRequestURI();

					ControllerV1 controller = controllerMap.get(requestURI);
					if (controller == null) {
							response.setStatus(HttpServletResponse.SC_NOT_FOUND);
							return;
					}

					controller.process(request, response);
			}
}

프론트 컨트롤러 분석

urlPatterns

  • urlPatterns = "/front-controller/v1/*" : /front-controller/v1 를 포함한 하위 모든 요청은 이 서블릿에서 받아들인다.
  • 예) /front-controller/v1 , /front-controller/v1/a , /front-controller/v1/a/

controllerMap

  • key: 매핑 URL
  • value: 호출될 컨트롤러

service()

  • 먼저 requestURI 를 조회해서 실제 호출할 컨트롤러를 controllerMap 에서 찾는다. 만약 없다면 404(SC_NOT_FOUND) 상태 코드를 반환한다.
  • 컨트롤러를 찾고 controller.process(request, response); 을 호출해서 해당 컨트롤러를 실행한다.
profile
학습하며 도전하는 것을 즐기는 개발자

0개의 댓글