REST AP를 구현할 때, 한개의 URL에 하나의 서블릿을 대응해야만 했던 구조에서 코드의 중복을 줄이기 위해 고안된 패턴
따라서 프론트 컨트롤러를 여러 서블릿 컨트롤러들의 앞단에 두어 일종의 ‘수문장’ 역할을 하게 둠
→ 이를 통해 컨트롤러 공통영역을 처리해 줄 수 있도록 함.
Front Controller 패턴의 특징을 살펴보자
Front controller 이 공통 로직을 처리하고, 각 상황에 따라 해당 역할을 수행할 컨트롤러를 실행한다.
그렇다면 프론트 컨트롤러를 회원 등록을 하고 회원 리스트를 볼 수 있는 코드에 적용해보자.
우선 현재는 클라이언트로 이름와 나이를 입력 받아 그 정보로 회원을 등록하기 위한 서블릿, 입력 받은 정보를 전달받아 회원을 저장하는 서블릿,모든 회원 목록 리스트를 반환하는 서블릿, MvcMemberFormServlet
,MemberSaveServlet
,MemberListServlet
이렇게 세가지가 있다.
@WebServlet(name = "mvcMemberFormServlet",urlPatterns ="/servlet-mvc/members/new-form")
public class MvcMemberFormServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String viewPath ="/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request,response);
}
}
@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
//Model에 데이터를 보관한다.
request.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
@WebServlet(name = "mvcMemberListServlet",urlPatterns = "/servlet-mvc/members")
public class MvcMemberListServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
request.setAttribute("members",members);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request,response);
}
}
우선 인터페이스를 만들어준다 → 각 컨트롤러들은 이 인터페이스를 통해 구현한다. 프론트 컨트롤러에서 이 인터페이스를 호출해 각 구현체들을 컨트롤러로써 사용한다. → 이는 로직의 일관성을 가져갈 수 있다.
public interface ControllerV1 {
void process(HttpServletRequest request , HttpServletResponse response) throws ServletException, IOException;
}
@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());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
ControllerV1 controller = controllerMap.get(requestURI);
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
controller.process(request, response);
}
}
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);
}
}
public class MemberSaveControllerV1 implements ControllerV1 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
//Model에 데이터를 보관한다.
request.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
public class MemberListControllerV1 implements ControllerV1 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
request.setAttribute("members", members);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
위의 프런트 컨트롤러를 적용함으로써
Front controller 이 공통 로직을 처리하고, 각 상황에 따라 해당 역할을 수행할 컨트롤러를 실행할 수 있게 되었다.
또한 맵핑 되어 있지 않은 url을 통한 접근을 404에러로 처리할 수 있게 되었다.