[Spring MVC] 프론트 컨트롤러 패턴
프론트 컨트롤러 패턴 소개
프론트 컨트롤러 도입 전
- 요청이 들어오는 입구가 여러 곳
프론트 컨트롤러 도입 후
FrontController 패턴 특징
- 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
- 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출
- 요청이 들어오는 입구가 하나
- 공통 처리 가능
- 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨
스프링 웹 MVC와 프론트 컨트롤러
스프링 웹 MVC의 핵심은 FrontController이다.
스프링 웹 MVC의 DispatcherServlet이 FrontController 패턴으로 구현되어 있다.
프론트 컨트롤러 도입 - V1
프론트 컨트롤러를 단계적으로 도입해 보자.
이번 목표는 기존 코드를 최대한 유지하면서, 프론트 컨트롤러를 도입하는 것이다.
먼저 구조를 맞추어두고 점진적으로 리펙터링 해보자.
V1 구조
앞서 공부했던 서블릿과 JSP를 통해,
서블릿을 컨트롤러로 사용하고 JSP를 뷰로 사용하여 MVC 패턴을 적용해 보았다.
이번에는 그림과 같이 MVC 패턴 위에서 FrontController를 점진적으로 리펙터링 해보겠다.
ControllerV1
public interface ControllerV1 {
void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
서블릿과 비슷한 모양의 컨트롤러 인터페이스를 도입한다.
각 컨트롤러들은 이 인터페이스를 구현하면 된다.
프론트 컨트롤러는 이 인터페이스를 호출해서 구현과 관계없이 로직의 일관성을 가져갈 수 있다.
이제 이 인터페이스를 구현한 컨트롤러를 만들어보자.
지금 단계에서는 기존 로직을 최대한 유지하는 게 핵심이다.
MemberFormControllerV1 - 회원 등록 컨트롤러
public class MemberFormControllerV1 implements ControllerV1 {
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
MemberFormControllerV1은 ControllerV1의 구현체이다.
MemberFormControllerV1의 process 메서드를 통해, 해당 클래스의 viewPath 경로를 지정해 주었다.
Form 클래스는 뷰만 띄워주면 되기에 별다른 작업 없이 dispatcher를 통해 forward 해주었다.
MemberSaveControllerV1 - 회원 저장 컨트롤러
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
MemberSaveControllerV1 클래스에서는 사용자의 이름과 나이를 저장소에 저장하는 기능을 한다.HttpServletRequest를 통해 Form에서 받아온 데이터 parameter를 변수에 저장하고,객체를 생성해서 Repository에 저장해 주면 된다.
//Model에 데이터 보관
request.setAttribute("member", member);
또한 HTTP 요청이 유지되는 동안 사용할 수 있는 간이 저장고 (Model)에 데이터를 보관함으로써,
JSP 뷰에서 사용하고자 하는 데이터를 유지할 수 있다.
JSP에서는 Model을 참조하여 객체를 사용할 수 있다.
MemberListControllerV1 - 회원 목록 컨트롤러
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
//Model에 데이터 보관
request.setAttribute("members", members);
MemberListControllerV1은 모든 사용자의 목록을 띄워주는 역할을 한다.
MemberRepository의 findAll() 메서드를 통해 모든 사용자를 List 형태로 불러왔다.
이후 마찬가지로 .setAttribute를 통해 모델에 데이터를 보관한다.
내부 로직은 앞서 포스팅한 기존 서블릿과 거의 같다.
이제 프론트 컨트롤러를 통해 입구를 통일시켜 보자.
FrontControllerServletV1 - 프론트 컨트롤러
@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/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());
}
FrontControllerServletV1의 앞단이다.
@WebServlet을 통해 name, urlPatterns을 지정해 준다.
Map<>을 통해 컨트롤러의 경로를 key, 클래스 객체를 value 값으로 넣어줬다.
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
ControllerV1 controller = controllerMap.get(requestURI);
controller.process(request, response);
그렇게 하면 .getRequestURI()를 통해 요청한 주소를 추출해서 Map<>과 대조하여 해당 클래스 객체를 생성할 수 있다.
어떤 URI를 통해 요청하더라도 "/fornt-controller/v1/..." 아래에 있는 URI일 경우,
무조건 FrontControllerV1을 거쳐서 동작할 것이다.
입구가 하나인 셈이다.
프론트 컨트롤러 분석
urlPatterns
- urlPatterns = "/front-controller/v1/*" : /front-controller/v1를 포함한 하위 모든 요청은 이 서블릿에서 받아들인다.
- 예) /front-controller/v1 , /front-controller/v1/a , /front-controller/v1/a/b
controllerMap
- key : 매핑 URL
- value : 호출될 컨트롤러
service()
먼저 requestURI를 조회해서 실제 호출할 컨트롤러를 controllerMap에서 찾는다.
컨트롤러를 찾고 controller.process(request, response); 을 호출해서 해당 컨트롤러를 실행한다.
JSP
JSP는 이전 MVC에서 사용했던 것을 그대로 사용한다.
실행
- 등록 : http://localhost:8080/front-controller/v1/members/new-form
- 목록 : http://localhost:8080/front-controller/v1/members
출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1